initial commit - cross reference with 5th port - obviously has compile errors
This commit is contained in:
@@ -0,0 +1,924 @@
|
||||
//Defines for bots are now found in code\__DEFINES\bots.dm
|
||||
|
||||
// AI (i.e. game AI, not the AI player) controlled bots
|
||||
/mob/living/simple_animal/bot
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
layer = MOB_LAYER
|
||||
luminosity = 3
|
||||
stop_automated_movement = 1
|
||||
wander = 0
|
||||
healable = 0
|
||||
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
minbodytemp = 0
|
||||
has_unlimited_silicon_privilege = 1
|
||||
sentience_type = SENTIENCE_ARTIFICIAL
|
||||
status_flags = NONE //no default canpush
|
||||
verb_say = "states"
|
||||
verb_ask = "queries"
|
||||
verb_exclaim = "declares"
|
||||
verb_yell = "alarms"
|
||||
bubble_icon = "machine"
|
||||
|
||||
faction = list("neutral", "silicon")
|
||||
|
||||
var/obj/machinery/bot_core/bot_core = null
|
||||
var/bot_core_type = /obj/machinery/bot_core
|
||||
var/list/users = list() //for dialog updates
|
||||
var/window_id = "bot_control"
|
||||
var/window_name = "Protobot 1.0" //Popup title
|
||||
var/window_width = 0 //0 for default size
|
||||
var/window_height = 0
|
||||
var/obj/item/device/paicard/paicard // Inserted pai card.
|
||||
var/allow_pai = 1 // Are we even allowed to insert a pai card.
|
||||
var/bot_name
|
||||
|
||||
var/list/player_access = list() //Additonal access the bots gets when player controlled
|
||||
var/emagged = 0
|
||||
var/list/prev_access = list()
|
||||
var/on = 1
|
||||
var/open = 0//Maint panel
|
||||
var/locked = 1
|
||||
var/hacked = 0 //Used to differentiate between being hacked by silicons and emagged by humans.
|
||||
var/text_hack = "" //Custom text returned to a silicon upon hacking a bot.
|
||||
var/text_dehack = "" //Text shown when resetting a bots hacked status to normal.
|
||||
var/text_dehack_fail = "" //Shown when a silicon tries to reset a bot emagged with the emag item, which cannot be reset.
|
||||
var/declare_message = "" //What the bot will display to the HUD user.
|
||||
var/frustration = 0 //Used by some bots for tracking failures to reach their target.
|
||||
var/base_speed = 2 //The speed at which the bot moves, or the number of times it moves per process() tick.
|
||||
var/turf/ai_waypoint //The end point of a bot's path, or the target location.
|
||||
var/list/path = list() //List of turfs through which a bot 'steps' to reach the waypoint.
|
||||
var/pathset = 0
|
||||
var/list/ignore_list = list() //List of unreachable targets for an ignore-list enabled bot to ignore.
|
||||
var/mode = BOT_IDLE //Standardizes the vars that indicate the bot is busy with its function.
|
||||
var/tries = 0 //Number of times the bot tried and failed to move.
|
||||
var/remote_disabled = 0 //If enabled, the AI cannot *Remotely* control a bot. It can still control it through cameras.
|
||||
var/mob/living/silicon/ai/calling_ai //Links a bot to the AI calling it.
|
||||
var/obj/item/device/radio/Radio //The bot's radio, for speaking to people.
|
||||
var/radio_key = null //which channels can the bot listen to
|
||||
var/radio_channel = "Common" //The bot's default radio channel
|
||||
var/auto_patrol = 0// set to make bot automatically patrol
|
||||
var/turf/patrol_target // this is turf to navigate to (location of beacon)
|
||||
var/turf/summon_target // The turf of a user summoning a bot.
|
||||
var/new_destination // pending new destination (waiting for beacon response)
|
||||
var/destination // destination description tag
|
||||
var/next_destination // the next destination in the patrol route
|
||||
var/shuffle = FALSE // If we should shuffle our adjacency checking
|
||||
|
||||
var/blockcount = 0 //number of times retried a blocked path
|
||||
var/awaiting_beacon = 0 // count of pticks awaiting a beacon response
|
||||
|
||||
var/nearest_beacon // the nearest beacon's tag
|
||||
var/turf/nearest_beacon_loc // the nearest beacon's location
|
||||
|
||||
var/beacon_freq = 1445 // navigation beacon frequency
|
||||
var/model = "" //The type of bot it is.
|
||||
var/bot_type = 0 //The type of bot it is, for radio control.
|
||||
var/data_hud_type = DATA_HUD_DIAGNOSTIC //The type of data HUD the bot uses. Diagnostic by default.
|
||||
var/list/mode_name = list("In Pursuit","Preparing to Arrest", "Arresting", \
|
||||
"Beginning Patrol", "Patrolling", "Summoned by PDA", \
|
||||
"Cleaning", "Repairing", "Proceeding to work site", "Healing", \
|
||||
"Proceeding to AI waypoint", "Navigating to Delivery Location", "Navigating to Home", \
|
||||
"Waiting for clear path", "Calculating navigation path", "Pinging beacon network", "Unable to reach destination")
|
||||
//This holds text for what the bot is mode doing, reported on the remote bot control interface.
|
||||
|
||||
hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD) //Diagnostic HUD views
|
||||
|
||||
/mob/living/simple_animal/bot/proc/get_mode()
|
||||
if(client) //Player bots do not have modes, thus the override. Also an easy way for PDA users/AI to know when a bot is a player.
|
||||
if(paicard)
|
||||
return "<b>pAI Controlled</b>"
|
||||
else
|
||||
return "<b>Autonomous</b>"
|
||||
else if(!on)
|
||||
return "<span class='bad'>Inactive</span>"
|
||||
else if(!mode)
|
||||
return "<span class='good'>Idle</span>"
|
||||
else
|
||||
return "<span class='average'>[mode_name[mode]]</span>"
|
||||
|
||||
/mob/living/simple_animal/bot/proc/turn_on()
|
||||
if(stat)
|
||||
return 0
|
||||
on = 1
|
||||
SetLuminosity(initial(luminosity))
|
||||
update_icon()
|
||||
diag_hud_set_botstat()
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/bot/proc/turn_off()
|
||||
on = 0
|
||||
SetLuminosity(0)
|
||||
bot_reset() //Resets an AI's call, should it exist.
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/bot/New()
|
||||
..()
|
||||
access_card = new /obj/item/weapon/card/id(src)
|
||||
//This access is so bots can be immediately set to patrol and leave Robotics, instead of having to be let out first.
|
||||
access_card.access += access_robotics
|
||||
set_custom_texts()
|
||||
Radio = new/obj/item/device/radio(src)
|
||||
if(radio_key)
|
||||
Radio.keyslot = new radio_key
|
||||
Radio.subspace_transmission = 1
|
||||
Radio.canhear_range = 0 // anything greater will have the bot broadcast the channel as if it were saying it out loud.
|
||||
Radio.recalculateChannels()
|
||||
|
||||
bot_core = new bot_core_type(src)
|
||||
|
||||
//Adds bot to the diagnostic HUD system
|
||||
prepare_huds()
|
||||
var/datum/atom_hud/data/diagnostic/diag_hud = huds[DATA_HUD_DIAGNOSTIC]
|
||||
diag_hud.add_to_hud(src)
|
||||
diag_hud_set_bothealth()
|
||||
diag_hud_set_botstat()
|
||||
diag_hud_set_botmode()
|
||||
|
||||
//Gives a HUD view to player bots that use a HUD.
|
||||
activate_data_hud()
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/update_canmove()
|
||||
. = ..()
|
||||
if(!on)
|
||||
. = 0
|
||||
canmove = .
|
||||
|
||||
/mob/living/simple_animal/bot/Destroy()
|
||||
if(paicard)
|
||||
ejectpai()
|
||||
qdel(Radio)
|
||||
qdel(access_card)
|
||||
qdel(bot_core)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/bot/bee_friendly()
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/bot/death(gibbed)
|
||||
explode()
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/proc/explode()
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/bot/emag_act(mob/user)
|
||||
if(locked) //First emag application unlocks the bot's interface. Apply a screwdriver to use the emag again.
|
||||
locked = 0
|
||||
emagged = 1
|
||||
user << "<span class='notice'>You bypass [src]'s controls.</span>"
|
||||
return
|
||||
if(!locked && open) //Bot panel is unlocked by ID or emag, and the panel is screwed open. Ready for emagging.
|
||||
emagged = 2
|
||||
remote_disabled = 1 //Manually emagging the bot locks out the AI built in panel.
|
||||
locked = 1 //Access denied forever!
|
||||
bot_reset()
|
||||
turn_on() //The bot automatically turns on when emagged, unless recently hit with EMP.
|
||||
src << "<span class='userdanger'>(#$*#$^^( OVERRIDE DETECTED</span>"
|
||||
add_logs(user, src, "emagged")
|
||||
return
|
||||
else //Bot is unlocked, but the maint panel has not been opened with a screwdriver yet.
|
||||
user << "<span class='warning'>You need to open maintenance panel first!</span>"
|
||||
|
||||
/mob/living/simple_animal/bot/examine(mob/user)
|
||||
..()
|
||||
if(health < maxHealth)
|
||||
if(health > maxHealth/3)
|
||||
user << "[src]'s parts look loose."
|
||||
else
|
||||
user << "[src]'s parts look very loose!"
|
||||
else
|
||||
user << "[src] is in pristine condition."
|
||||
|
||||
/mob/living/simple_animal/bot/adjustHealth(amount)
|
||||
if(amount>0 && prob(10))
|
||||
new /obj/effect/decal/cleanable/oil(loc)
|
||||
. = ..()
|
||||
|
||||
/mob/living/simple_animal/bot/updatehealth()
|
||||
..()
|
||||
diag_hud_set_bothealth()
|
||||
|
||||
/mob/living/simple_animal/bot/med_hud_set_health()
|
||||
return //we use a different hud
|
||||
|
||||
/mob/living/simple_animal/bot/med_hud_set_status()
|
||||
return //we use a different hud
|
||||
|
||||
/mob/living/simple_animal/bot/handle_automated_action() //Master process which handles code common across most bots.
|
||||
set background = BACKGROUND_ENABLED
|
||||
diag_hud_set_botmode()
|
||||
|
||||
if(!on || client)
|
||||
return
|
||||
|
||||
switch(mode) //High-priority overrides are processed first. Bots can do nothing else while under direct command.
|
||||
if(BOT_RESPONDING) //Called by the AI.
|
||||
call_mode()
|
||||
return
|
||||
if(BOT_SUMMON) //Called by PDA
|
||||
bot_summon()
|
||||
return
|
||||
return 1 //Successful completion. Used to prevent child process() continuing if this one is ended early.
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/attack_hand(mob/living/carbon/human/H)
|
||||
if(H.a_intent == "help")
|
||||
interact(H)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/bot/attack_ai(mob/user)
|
||||
if(!topic_denied(user))
|
||||
interact(user)
|
||||
else
|
||||
user << "<span class='warning'>[src]'s interface is not responding!</span>"
|
||||
|
||||
/mob/living/simple_animal/bot/interact(mob/user)
|
||||
show_controls(user)
|
||||
|
||||
/mob/living/simple_animal/bot/attackby(obj/item/weapon/W, mob/user, params)
|
||||
if(istype(W, /obj/item/weapon/screwdriver))
|
||||
if(!locked)
|
||||
open = !open
|
||||
user << "<span class='notice'>The maintenance panel is now [open ? "opened" : "closed"].</span>"
|
||||
else
|
||||
user << "<span class='warning'>The maintenance panel is locked.</span>"
|
||||
else if(istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda))
|
||||
if(bot_core.allowed(user) && !open && !emagged)
|
||||
locked = !locked
|
||||
user << "Controls are now [locked ? "locked." : "unlocked."]"
|
||||
else
|
||||
if(emagged)
|
||||
user << "<span class='danger'>ERROR</span>"
|
||||
if(open)
|
||||
user << "<span class='warning'>Please close the access panel before locking it.</span>"
|
||||
else
|
||||
user << "<span class='warning'>Access denied.</span>"
|
||||
else if(istype(W, /obj/item/device/paicard))
|
||||
insertpai(user, W)
|
||||
else if(istype(W, /obj/item/weapon/hemostat) && paicard)
|
||||
if(open)
|
||||
user << "<span class='warning'>Close the access panel before manipulating the personality slot!</span>"
|
||||
else
|
||||
user << "<span class='notice'>You attempt to pull [paicard] free...</span>"
|
||||
if(do_after(user, 30, target = src))
|
||||
if (paicard)
|
||||
user.visible_message("<span class='notice'>[user] uses [W] to pull [paicard] out of [bot_name]!</span>","<span class='notice'>You pull [paicard] out of [bot_name] with [W].</span>")
|
||||
ejectpai(user)
|
||||
else
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm")
|
||||
if(health >= maxHealth)
|
||||
user << "<span class='warning'>[src] does not need a repair!</span>"
|
||||
return
|
||||
if(!open)
|
||||
user << "<span class='warning'>Unable to repair with the maintenance panel closed!</span>"
|
||||
return
|
||||
var/obj/item/weapon/weldingtool/WT = W
|
||||
if(WT.remove_fuel(0, user))
|
||||
adjustHealth(-10)
|
||||
user.visible_message("[user] repairs [src]!","<span class='notice'>You repair [src].</span>")
|
||||
else
|
||||
user << "<span class='warning'>The welder must be on for this task!</span>"
|
||||
else
|
||||
if(W.force) //if force is non-zero
|
||||
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
|
||||
s.set_up(5, 1, src)
|
||||
s.start()
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/bullet_act(obj/item/projectile/Proj)
|
||||
if(Proj && (Proj.damage_type == BRUTE || Proj.damage_type == BURN))
|
||||
if(prob(75) && Proj.damage > 0)
|
||||
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
|
||||
s.set_up(5, 1, src)
|
||||
s.start()
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/bot/emp_act(severity)
|
||||
var/was_on = on
|
||||
stat |= EMPED
|
||||
PoolOrNew(/obj/effect/overlay/temp/emp, loc)
|
||||
if(paicard)
|
||||
paicard.emp_act(severity)
|
||||
src.visible_message("[paicard] is flies out of [bot_name]!","<span class='warning'>You are forcefully ejected from [bot_name]!</span>")
|
||||
ejectpai(0)
|
||||
if(on)
|
||||
turn_off()
|
||||
spawn(severity*300)
|
||||
stat &= ~EMPED
|
||||
if(was_on)
|
||||
turn_on()
|
||||
|
||||
/mob/living/simple_animal/bot/proc/set_custom_texts() //Superclass for setting hack texts. Appears only if a set is not given to a bot locally.
|
||||
text_hack = "You hack [name]."
|
||||
text_dehack = "You reset [name]."
|
||||
text_dehack_fail = "You fail to reset [name]."
|
||||
|
||||
/mob/living/simple_animal/bot/proc/speak(message,channel) //Pass a message to have the bot say() it. Pass a frequency to say it on the radio.
|
||||
if((!on) || (!message))
|
||||
return
|
||||
if(channel && Radio.channels[channel])// Use radio if we have channel key
|
||||
Radio.talk_into(src, message, channel, get_spans())
|
||||
else
|
||||
say(message)
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/get_spans()
|
||||
return ..() | SPAN_ROBOT
|
||||
|
||||
/mob/living/simple_animal/bot/radio(message, message_mode, list/spans)
|
||||
. = ..()
|
||||
if(. != 0)
|
||||
return .
|
||||
|
||||
switch(message_mode)
|
||||
if(MODE_HEADSET)
|
||||
Radio.talk_into(src, message, , spans)
|
||||
return REDUCE_RANGE
|
||||
|
||||
if(MODE_DEPARTMENT)
|
||||
Radio.talk_into(src, message, message_mode, spans)
|
||||
return REDUCE_RANGE
|
||||
|
||||
if(message_mode in radiochannels)
|
||||
Radio.talk_into(src, message, message_mode, spans)
|
||||
return REDUCE_RANGE
|
||||
return 0
|
||||
|
||||
//Generalized behavior code, override where needed!
|
||||
|
||||
/*
|
||||
scan() will search for a given type (such as turfs, human mobs, or objects) in the bot's view range, and return a single result.
|
||||
Arguments: The object type to be searched (such as "/mob/living/carbon/human"), the old scan result to be ignored, if one exists,
|
||||
and the view range, which defaults to 7 (full screen) if an override is not passed.
|
||||
If the bot maintains an ignore list, it is also checked here.
|
||||
|
||||
Example usage: patient = scan(/mob/living/carbon/human, oldpatient, 1)
|
||||
The proc would return a human next to the bot to be set to the patient var.
|
||||
Pass the desired type path itself, declaring a temporary var beforehand is not required.
|
||||
*/
|
||||
/mob/living/simple_animal/bot/proc/scan(scan_type, old_target, scan_range = DEFAULT_SCAN_RANGE)
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
return
|
||||
var/list/adjacent = T.GetAtmosAdjacentTurfs(1)
|
||||
if(shuffle) //If we were on the same tile as another bot, let's randomize our choices so we dont both go the same way
|
||||
adjacent = shuffle(adjacent)
|
||||
shuffle = FALSE
|
||||
for(var/scan in adjacent)//Let's see if there's something right next to us first!
|
||||
if(check_bot(scan)) //Is there another bot there? Then let's just skip it
|
||||
continue
|
||||
if(isturf(scan_type)) //If we're lookeing for a turf we can just run the checks directly!
|
||||
var/final_result = checkscan(scan,scan_type,old_target)
|
||||
if(final_result)
|
||||
return final_result
|
||||
else
|
||||
var/turf/turfy = scan
|
||||
for(var/deepscan in turfy.contents)//Check the contents since adjacent is turfs
|
||||
var/final_result = checkscan(deepscan,scan_type,old_target)
|
||||
if(final_result)
|
||||
return final_result
|
||||
for (var/scan in shuffle(view(scan_range, src))-adjacent) //Search for something in range!
|
||||
var/final_result = checkscan(scan,scan_type,old_target)
|
||||
if(final_result)
|
||||
return final_result
|
||||
|
||||
/mob/living/simple_animal/bot/proc/checkscan(scan, scan_type, old_target)
|
||||
if(!istype(scan, scan_type)) //Check that the thing we found is the type we want!
|
||||
return 0 //If not, keep searching!
|
||||
if( (scan in ignore_list) || (scan == old_target) ) //Filter for blacklisted elements, usually unreachable or previously processed oness
|
||||
return 0
|
||||
|
||||
var/scan_result = process_scan(scan) //Some bots may require additional processing when a result is selected.
|
||||
if(scan_result)
|
||||
return scan_result
|
||||
else
|
||||
return 0 //The current element failed assessment, move on to the next.
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/proc/check_bot(targ)
|
||||
var/turf/T = get_turf(targ)
|
||||
if(T)
|
||||
for(var/C in T.contents)
|
||||
if(istype(C,type) && (C != src)) //Is there another bot there already? If so, let's skip it so we dont all atack on top of eachother.
|
||||
return 1 //Let's abort if we find a bot so we dont have to keep rechecking
|
||||
|
||||
//When the scan finds a target, run bot specific processing to select it for the next step. Empty by default.
|
||||
/mob/living/simple_animal/bot/proc/process_scan(scan_target)
|
||||
return scan_target
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/proc/add_to_ignore(subject)
|
||||
if(ignore_list.len < 50) //This will help keep track of them, so the bot is always trying to reach a blocked spot.
|
||||
ignore_list |= subject
|
||||
else if(ignore_list.len >= subject) //If the list is full, insert newest, delete oldest.
|
||||
ignore_list -= ignore_list[1]
|
||||
ignore_list |= subject
|
||||
|
||||
/*
|
||||
Movement proc for stepping a bot through a path generated through A-star.
|
||||
Pass a positive integer as an argument to override a bot's default speed.
|
||||
*/
|
||||
/mob/living/simple_animal/bot/proc/bot_move(dest, move_speed)
|
||||
|
||||
if(!dest || !path || path.len == 0) //A-star failed or a path/destination was not set.
|
||||
path = list()
|
||||
return 0
|
||||
dest = get_turf(dest) //We must always compare turfs, so get the turf of the dest var if dest was originally something else.
|
||||
var/turf/last_node = get_turf(path[path.len]) //This is the turf at the end of the path, it should be equal to dest.
|
||||
if(get_turf(src) == dest) //We have arrived, no need to move again.
|
||||
return 1
|
||||
else if(dest != last_node) //The path should lead us to our given destination. If this is not true, we must stop.
|
||||
path = list()
|
||||
return 0
|
||||
var/step_count = move_speed ? move_speed : base_speed //If a value is passed into move_speed, use that instead of the default speed var.
|
||||
|
||||
if(step_count >= 1 && tries < BOT_STEP_MAX_RETRIES)
|
||||
for(var/step_number = 0, step_number < step_count,step_number++)
|
||||
spawn(BOT_STEP_DELAY*step_number)
|
||||
bot_step(dest)
|
||||
else
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/proc/bot_step(dest) //Step,increase tries if failed
|
||||
if(!path)
|
||||
return 0
|
||||
if(path.len > 1)
|
||||
step_towards(src, path[1])
|
||||
if(get_turf(src) == path[1]) //Successful move
|
||||
path -= path[1]
|
||||
tries = 0
|
||||
else
|
||||
tries++
|
||||
return 0
|
||||
else if(path.len == 1)
|
||||
step_to(src, dest)
|
||||
path = list()
|
||||
return 1
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/proc/check_bot_access()
|
||||
if(mode != BOT_SUMMON && mode != BOT_RESPONDING)
|
||||
access_card.access = prev_access
|
||||
|
||||
/mob/living/simple_animal/bot/proc/call_bot(caller, turf/waypoint, message=TRUE)
|
||||
bot_reset() //Reset a bot before setting it to call mode.
|
||||
var/area/end_area = get_area(waypoint)
|
||||
|
||||
if(client) //Player bots instead get a location command from the AI
|
||||
src << "<span class='noticebig'>Priority waypoint set by \icon[caller] <b>[caller]</b>. Proceed to <b>[end_area.name]<\b>."
|
||||
|
||||
//For giving the bot temporary all-access.
|
||||
var/obj/item/weapon/card/id/all_access = new /obj/item/weapon/card/id
|
||||
var/datum/job/captain/All = new/datum/job/captain
|
||||
all_access.access = All.get_access()
|
||||
|
||||
path = get_path_to(src, waypoint, /turf/proc/Distance_cardinal, 0, 200, id=all_access)
|
||||
calling_ai = caller //Link the AI to the bot!
|
||||
ai_waypoint = waypoint
|
||||
|
||||
if(path && path.len) //Ensures that a valid path is calculated!
|
||||
if(!on)
|
||||
turn_on() //Saves the AI the hassle of having to activate a bot manually.
|
||||
access_card = all_access //Give the bot all-access while under the AI's command.
|
||||
if(message)
|
||||
calling_ai << "<span class='notice'>\icon[src] [name] called to [end_area.name]. [path.len-1] meters to destination.</span>"
|
||||
pathset = 1
|
||||
mode = BOT_RESPONDING
|
||||
tries = 0
|
||||
else
|
||||
if(message)
|
||||
calling_ai << "<span class='danger'>Failed to calculate a valid route. Ensure destination is clear of obstructions and within range.</span>"
|
||||
calling_ai = null
|
||||
path = list()
|
||||
|
||||
/mob/living/simple_animal/bot/proc/call_mode() //Handles preparing a bot for a call, as well as calling the move proc.
|
||||
//Handles the bot's movement during a call.
|
||||
var/success = bot_move(ai_waypoint, 3)
|
||||
if(!success)
|
||||
if(calling_ai)
|
||||
calling_ai << "\icon[src] [get_turf(src) == ai_waypoint ? "<span class='notice'>[src] successfully arrived to waypoint.</span>" : "<span class='danger'>[src] failed to reach waypoint.</span>"]"
|
||||
calling_ai = null
|
||||
bot_reset()
|
||||
|
||||
/mob/living/simple_animal/bot/proc/bot_reset()
|
||||
if(calling_ai) //Simple notification to the AI if it called a bot. It will not know the cause or identity of the bot.
|
||||
calling_ai << "<span class='danger'>Call command to a bot has been reset.</span>"
|
||||
calling_ai = null
|
||||
path = list()
|
||||
summon_target = null
|
||||
pathset = 0
|
||||
access_card.access = prev_access
|
||||
tries = 0
|
||||
mode = BOT_IDLE
|
||||
diag_hud_set_botstat()
|
||||
diag_hud_set_botmode()
|
||||
|
||||
|
||||
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
//Patrol and summon code!
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
/mob/living/simple_animal/bot/proc/bot_patrol()
|
||||
patrol_step()
|
||||
spawn(5)
|
||||
if(mode == BOT_PATROL)
|
||||
patrol_step()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/proc/start_patrol()
|
||||
|
||||
if(tries >= BOT_STEP_MAX_RETRIES) //Bot is trapped, so stop trying to patrol.
|
||||
auto_patrol = 0
|
||||
tries = 0
|
||||
speak("Unable to start patrol.")
|
||||
|
||||
return
|
||||
|
||||
if(!auto_patrol) //A bot not set to patrol should not be patrolling.
|
||||
mode = BOT_IDLE
|
||||
return
|
||||
|
||||
if(patrol_target) // has patrol target
|
||||
spawn(0)
|
||||
calc_path() // Find a route to it
|
||||
if(path.len == 0)
|
||||
patrol_target = null
|
||||
return
|
||||
mode = BOT_PATROL
|
||||
else // no patrol target, so need a new one
|
||||
speak("Engaging patrol mode.")
|
||||
find_patrol_target()
|
||||
tries++
|
||||
return
|
||||
|
||||
// perform a single patrol step
|
||||
|
||||
/mob/living/simple_animal/bot/proc/patrol_step()
|
||||
|
||||
if(client) // In use by player, don't actually move.
|
||||
return
|
||||
|
||||
if(loc == patrol_target) // reached target
|
||||
//Find the next beacon matching the target.
|
||||
if(!get_next_patrol_target())
|
||||
find_patrol_target() //If it fails, look for the nearest one instead.
|
||||
return
|
||||
|
||||
else if(path.len > 0 && patrol_target) // valid path
|
||||
var/turf/next = path[1]
|
||||
if(next == loc)
|
||||
path -= next
|
||||
return
|
||||
|
||||
|
||||
var/moved = bot_move(patrol_target)//step_towards(src, next) // attempt to move
|
||||
if(!moved) //Couldn't proceed the next step of the path BOT_STEP_MAX_RETRIES times
|
||||
spawn(2)
|
||||
calc_path()
|
||||
if(path.len == 0)
|
||||
find_patrol_target()
|
||||
tries = 0
|
||||
|
||||
else // no path, so calculate new one
|
||||
mode = BOT_START_PATROL
|
||||
|
||||
// finds the nearest beacon to self
|
||||
/mob/living/simple_animal/bot/proc/find_patrol_target()
|
||||
nearest_beacon = null
|
||||
new_destination = null
|
||||
find_nearest_beacon()
|
||||
if(nearest_beacon)
|
||||
patrol_target = nearest_beacon_loc
|
||||
destination = next_destination
|
||||
else
|
||||
auto_patrol = 0
|
||||
mode = BOT_IDLE
|
||||
speak("Disengaging patrol mode.")
|
||||
|
||||
/mob/living/simple_animal/bot/proc/get_next_patrol_target()
|
||||
// search the beacon list for the next target in the list.
|
||||
for(var/obj/machinery/navbeacon/NB in navbeacons["[z]"])
|
||||
if(NB.location == next_destination) //Does the Beacon location text match the destination?
|
||||
destination = new_destination //We now know the name of where we want to go.
|
||||
patrol_target = NB.loc //Get its location and set it as the target.
|
||||
next_destination = NB.codes["next_patrol"] //Also get the name of the next beacon in line.
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/bot/proc/find_nearest_beacon()
|
||||
for(var/obj/machinery/navbeacon/NB in navbeacons["[z]"])
|
||||
var/dist = get_dist(src, NB)
|
||||
if(nearest_beacon) //Loop though the beacon net to find the true closest beacon.
|
||||
//Ignore the beacon if were are located on it.
|
||||
if(dist>1 && dist<get_dist(src,nearest_beacon_loc))
|
||||
nearest_beacon = NB.location
|
||||
nearest_beacon_loc = NB.loc
|
||||
next_destination = NB.codes["next_patrol"]
|
||||
else
|
||||
continue
|
||||
else if(dist > 1) //Begin the search, save this one for comparison on the next loop.
|
||||
nearest_beacon = NB.location
|
||||
nearest_beacon_loc = NB.loc
|
||||
patrol_target = nearest_beacon_loc
|
||||
destination = nearest_beacon
|
||||
|
||||
//PDA control. Some bots, especially MULEs, may have more parameters.
|
||||
/mob/living/simple_animal/bot/proc/bot_control(command, mob/user, turf/user_turf, list/user_access = list())
|
||||
if(!on || emagged == 2 || remote_disabled) //Emagged bots do not respect anyone's authority! Bots with their remote controls off cannot get commands.
|
||||
return 1 //ACCESS DENIED
|
||||
if(client)
|
||||
bot_control_message(command,user,user_turf,user_access)
|
||||
// process control input
|
||||
switch(command)
|
||||
if("patroloff")
|
||||
bot_reset() //HOLD IT!!
|
||||
auto_patrol = 0
|
||||
|
||||
if("patrolon")
|
||||
auto_patrol = 1
|
||||
|
||||
if("summon")
|
||||
bot_reset()
|
||||
summon_target = user_turf
|
||||
if(user_access.len != 0)
|
||||
access_card.access = user_access + prev_access //Adds the user's access, if any.
|
||||
mode = BOT_SUMMON
|
||||
speak("Responding.", radio_channel)
|
||||
calc_summon_path()
|
||||
|
||||
if("ejectpai")
|
||||
ejectpairemote(user)
|
||||
return
|
||||
|
||||
//
|
||||
/mob/living/simple_animal/bot/proc/bot_control_message(command,user,user_turf,user_access)
|
||||
switch(command)
|
||||
if("patroloff")
|
||||
src << "<span class='warning big'>STOP PATROL</span>"
|
||||
if("patrolon")
|
||||
src << "<span class='warning big'>START PATROL</span>"
|
||||
if("summon")
|
||||
var/area/a = get_area(user_turf)
|
||||
src << "<span class='warning big'>PRIORITY ALERT:[user] in [a.name]!</span>"
|
||||
if("stop")
|
||||
src << "<span class='warning big'>STOP!</span>"
|
||||
|
||||
if("go")
|
||||
src << "<span class='warning big'>GO!</span>"
|
||||
|
||||
if("home")
|
||||
src << "<span class='warning big'>RETURN HOME!</span>"
|
||||
if("ejectpai")
|
||||
return
|
||||
else
|
||||
src << "<span class='warning'>Unidentified control sequence recieved:[command]</span>"
|
||||
|
||||
/mob/living/simple_animal/bot/proc/bot_summon() // summoned to PDA
|
||||
summon_step()
|
||||
|
||||
// calculates a path to the current destination
|
||||
// given an optional turf to avoid
|
||||
/mob/living/simple_animal/bot/proc/calc_path(turf/avoid)
|
||||
check_bot_access()
|
||||
path = get_path_to(src, patrol_target, /turf/proc/Distance_cardinal, 0, 120, id=access_card, exclude=avoid)
|
||||
|
||||
/mob/living/simple_animal/bot/proc/calc_summon_path(turf/avoid)
|
||||
check_bot_access()
|
||||
spawn()
|
||||
path = get_path_to(src, summon_target, /turf/proc/Distance_cardinal, 0, 150, id=access_card, exclude=avoid)
|
||||
if(!path.len) //Cannot reach target. Give up and announce the issue.
|
||||
speak("Summon command failed, destination unreachable.",radio_channel)
|
||||
bot_reset()
|
||||
|
||||
/mob/living/simple_animal/bot/proc/summon_step()
|
||||
|
||||
if(client) // In use by player, don't actually move.
|
||||
return
|
||||
|
||||
if(loc == summon_target) // Arrived to summon location.
|
||||
bot_reset()
|
||||
return
|
||||
|
||||
else if(path.len > 0 && summon_target) //Proper path acquired!
|
||||
var/turf/next = path[1]
|
||||
if(next == loc)
|
||||
path -= next
|
||||
return
|
||||
|
||||
var/moved = bot_move(summon_target, 3) // Move attempt
|
||||
if(!moved)
|
||||
spawn(2)
|
||||
calc_summon_path()
|
||||
tries = 0
|
||||
|
||||
else // no path, so calculate new one
|
||||
calc_summon_path()
|
||||
|
||||
/mob/living/simple_animal/bot/Bump(M as mob|obj) //Leave no door unopened!
|
||||
. = ..()
|
||||
if((istype(M, /obj/machinery/door/airlock) || istype(M, /obj/machinery/door/window)) && (!isnull(access_card)))
|
||||
var/obj/machinery/door/D = M
|
||||
if(D.check_access(access_card))
|
||||
D.open()
|
||||
frustration = 0
|
||||
|
||||
/mob/living/simple_animal/bot/proc/show_controls(mob/M)
|
||||
users |= M
|
||||
var/dat = ""
|
||||
dat = get_controls(M)
|
||||
var/datum/browser/popup = new(M,window_id,window_name,350,600)
|
||||
popup.set_content(dat)
|
||||
popup.open(use_onclose = 0)
|
||||
onclose(M,window_id,ref=src)
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/proc/update_controls()
|
||||
for(var/mob/M in users)
|
||||
show_controls(M)
|
||||
|
||||
/mob/living/simple_animal/bot/proc/get_controls(mob/M)
|
||||
return "PROTOBOT - NOT FOR USE"
|
||||
|
||||
/mob/living/simple_animal/bot/Topic(href, href_list)
|
||||
//No ..() to prevent strip panel showing up - Todo: make that saner
|
||||
if(href_list["close"])// HUE HUE
|
||||
if(usr in users)
|
||||
users.Remove(usr)
|
||||
return 1
|
||||
|
||||
if(topic_denied(usr))
|
||||
usr << "<span class='warning'>[src]'s interface is not responding!</span>"
|
||||
return 1
|
||||
add_fingerprint(usr)
|
||||
|
||||
if((href_list["power"]) && (bot_core.allowed(usr) || !locked))
|
||||
if(on)
|
||||
turn_off()
|
||||
else
|
||||
turn_on()
|
||||
|
||||
switch(href_list["operation"])
|
||||
if("patrol")
|
||||
auto_patrol = !auto_patrol
|
||||
bot_reset()
|
||||
if("remote")
|
||||
remote_disabled = !remote_disabled
|
||||
if("hack")
|
||||
if(emagged != 2)
|
||||
emagged = 2
|
||||
hacked = 1
|
||||
locked = 1
|
||||
usr << "<span class='warning'>[text_hack]</span>"
|
||||
bot_reset()
|
||||
else if(!hacked)
|
||||
usr << "<span class='boldannounce'>[text_dehack_fail]</span>"
|
||||
else
|
||||
emagged = 0
|
||||
hacked = 0
|
||||
usr << "<span class='notice'>[text_dehack]</span>"
|
||||
bot_reset()
|
||||
if("ejectpai")
|
||||
if(paicard && (!locked || issilicon(usr) || IsAdminGhost(usr)))
|
||||
usr << "<span class='notice'>You eject [paicard] from [bot_name]</span>"
|
||||
ejectpai(usr)
|
||||
update_controls()
|
||||
|
||||
/mob/living/simple_animal/bot/proc/update_icon()
|
||||
icon_state = "[initial(icon_state)][on]"
|
||||
|
||||
// Machinery to simplify topic and access calls
|
||||
/obj/machinery/bot_core
|
||||
use_power = 0
|
||||
var/mob/living/simple_animal/bot/owner = null
|
||||
|
||||
/obj/machinery/bot_core/New(loc)
|
||||
..()
|
||||
owner = loc
|
||||
if(!istype(owner))
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/bot/proc/topic_denied(mob/user) //Access check proc for bot topics! Remember to place in a bot's individual Topic if desired.
|
||||
if(!user.canUseTopic(src))
|
||||
return 1
|
||||
// 0 for access, 1 for denied.
|
||||
if(emagged == 2) //An emagged bot cannot be controlled by humans, silicons can if one hacked it.
|
||||
if(!hacked) //Manually emagged by a human - access denied to all.
|
||||
return 1
|
||||
else if(!issilicon(user) && !IsAdminGhost(user)) //Bot is hacked, so only silicons and admins are allowed access.
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/simple_animal/bot/proc/hack(mob/user)
|
||||
var/hack
|
||||
if(issilicon(user) || IsAdminGhost(user)) //Allows silicons or admins to toggle the emag status of a bot.
|
||||
hack += "[emagged == 2 ? "Software compromised! Unit may exhibit dangerous or erratic behavior." : "Unit operating normally. Release safety lock?"]<BR>"
|
||||
hack += "Harm Prevention Safety System: <A href='?src=\ref[src];operation=hack'>[emagged ? "<span class='bad'>DANGER</span>" : "Engaged"]</A><BR>"
|
||||
else if(!locked) //Humans with access can use this option to hide a bot from the AI's remote control panel and PDA control.
|
||||
hack += "Remote network control radio: <A href='?src=\ref[src];operation=remote'>[remote_disabled ? "Disconnected" : "Connected"]</A><BR>"
|
||||
return hack
|
||||
|
||||
/mob/living/simple_animal/bot/proc/showpai(mob/user)
|
||||
var/eject = ""
|
||||
if((!locked || issilicon(usr) || IsAdminGhost(usr)))
|
||||
if(paicard || allow_pai)
|
||||
eject += "Personality card status: "
|
||||
if(paicard)
|
||||
if(client)
|
||||
eject += "<A href='?src=\ref[src];operation=ejectpai'>Active</A>"
|
||||
else
|
||||
eject += "<A href='?src=\ref[src];operation=ejectpai'>Inactive</A>"
|
||||
else if(!allow_pai || key)
|
||||
eject += "Unavailable"
|
||||
else
|
||||
eject += "Not inserted"
|
||||
eject += "<BR>"
|
||||
eject += "<BR>"
|
||||
return eject
|
||||
|
||||
/mob/living/simple_animal/bot/proc/insertpai(mob/user, obj/item/device/paicard/card)
|
||||
if(paicard)
|
||||
user << "<span class='warning'>A [paicard] is already inserted!</span>"
|
||||
else if(allow_pai && !key)
|
||||
if(!locked && !open)
|
||||
if(card.pai && card.pai.mind)
|
||||
if(!user.drop_item())
|
||||
return
|
||||
card.forceMove(src)
|
||||
paicard = card
|
||||
user.visible_message("[user] inserts [card] into [src]!","<span class='notice'>You insert [card] into [src].</span>")
|
||||
paicard.pai.mind.transfer_to(src)
|
||||
src << "<span class='notice'>You sense your form change as you are uploaded into [src].</span>"
|
||||
bot_name = name
|
||||
name = paicard.pai.name
|
||||
faction = user.faction
|
||||
add_logs(user, paicard.pai, "uploaded to [bot_name],")
|
||||
return 1
|
||||
else
|
||||
user << "<span class='warning'>[card] is inactive.</span>"
|
||||
else
|
||||
user << "<span class='warning'>The personality slot is locked.</span>"
|
||||
else
|
||||
user << "<span class='warning'>[src] is not compatible with [card]</span>"
|
||||
|
||||
/mob/living/simple_animal/bot/proc/ejectpai(mob/user = null, announce = 1)
|
||||
if(paicard)
|
||||
if(mind && paicard.pai)
|
||||
mind.transfer_to(paicard.pai)
|
||||
else if(paicard.pai)
|
||||
paicard.pai.key = key
|
||||
else
|
||||
ghostize(0) // The pAI card that just got ejected was dead.
|
||||
key = null
|
||||
paicard.forceMove(loc)
|
||||
if(user)
|
||||
add_logs(user, paicard.pai, "ejected from [src.bot_name],")
|
||||
else
|
||||
add_logs(src, paicard.pai, "ejected")
|
||||
if(announce)
|
||||
paicard.pai << "<span class='notice'>You feel your control fade as [paicard] ejects from [bot_name].</span>"
|
||||
paicard = null
|
||||
name = bot_name
|
||||
faction = initial(faction)
|
||||
|
||||
/mob/living/simple_animal/bot/proc/ejectpairemote(mob/user)
|
||||
if(bot_core.allowed(user) && paicard)
|
||||
speak("Ejecting personality chip.", radio_channel)
|
||||
ejectpai(user)
|
||||
|
||||
/mob/living/simple_animal/bot/Login()
|
||||
. = ..()
|
||||
access_card.access += player_access
|
||||
diag_hud_set_botmode()
|
||||
activate_data_hud()
|
||||
|
||||
/mob/living/simple_animal/bot/Logout()
|
||||
. = ..()
|
||||
bot_reset()
|
||||
|
||||
/mob/living/simple_animal/bot/revive(full_heal = 0, admin_revive = 0)
|
||||
if(..())
|
||||
update_icon()
|
||||
. = 1
|
||||
|
||||
/mob/living/simple_animal/bot/ghost()
|
||||
if(stat != DEAD) // Only ghost if we're doing this while alive, the pAI probably isn't dead yet.
|
||||
..()
|
||||
if(paicard && (!client || stat == DEAD))
|
||||
ejectpai(0)
|
||||
|
||||
/mob/living/simple_animal/bot/sentience_act()
|
||||
faction -= "silicon"
|
||||
|
||||
/mob/living/simple_animal/bot/proc/activate_data_hud()
|
||||
//If a bot has its own HUD (for player bots), provide it.
|
||||
if(!data_hud_type)
|
||||
return
|
||||
var/datum/atom_hud/datahud = huds[data_hud_type]
|
||||
datahud.add_hud_to(src)
|
||||
@@ -0,0 +1,298 @@
|
||||
//Cleanbot
|
||||
/mob/living/simple_animal/bot/cleanbot
|
||||
name = "\improper Cleanbot"
|
||||
desc = "A little cleaning robot, he looks so excited!"
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "cleanbot0"
|
||||
density = 0
|
||||
anchored = 0
|
||||
health = 25
|
||||
maxHealth = 25
|
||||
radio_key = /obj/item/device/encryptionkey/headset_service
|
||||
radio_channel = "Service" //Service
|
||||
bot_type = CLEAN_BOT
|
||||
model = "Cleanbot"
|
||||
bot_core_type = /obj/machinery/bot_core/cleanbot
|
||||
window_id = "autoclean"
|
||||
window_name = "Automatic Station Cleaner v1.2"
|
||||
pass_flags = PASSMOB
|
||||
|
||||
var/blood = 1
|
||||
var/trash = 0
|
||||
var/pests = 0
|
||||
|
||||
var/list/target_types = list()
|
||||
var/obj/effect/decal/cleanable/target
|
||||
var/max_targets = 50 //Maximum number of targets a cleanbot can ignore.
|
||||
var/oldloc = null
|
||||
var/closest_dist
|
||||
var/closest_loc
|
||||
var/failed_steps
|
||||
var/next_dest
|
||||
var/next_dest_loc
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/New()
|
||||
..()
|
||||
get_targets()
|
||||
icon_state = "cleanbot[on]"
|
||||
|
||||
var/datum/job/janitor/J = new/datum/job/janitor
|
||||
access_card.access += J.get_access()
|
||||
prev_access = access_card.access
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/turn_on()
|
||||
..()
|
||||
icon_state = "cleanbot[on]"
|
||||
bot_core.updateUsrDialog()
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/turn_off()
|
||||
..()
|
||||
icon_state = "cleanbot[on]"
|
||||
bot_core.updateUsrDialog()
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/bot_reset()
|
||||
..()
|
||||
ignore_list = list() //Allows the bot to clean targets it previously ignored due to being unreachable.
|
||||
target = null
|
||||
oldloc = null
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/set_custom_texts()
|
||||
text_hack = "You corrupt [name]'s cleaning software."
|
||||
text_dehack = "[name]'s software has been reset!"
|
||||
text_dehack_fail = "[name] does not seem to respond to your repair code!"
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/attackby(obj/item/weapon/W, mob/user, params)
|
||||
if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
|
||||
if(bot_core.allowed(user) && !open && !emagged)
|
||||
locked = !locked
|
||||
user << "<span class='notice'>You [ locked ? "lock" : "unlock"] \the [src] behaviour controls.</span>"
|
||||
else
|
||||
if(emagged)
|
||||
user << "<span class='warning'>ERROR</span>"
|
||||
if(open)
|
||||
user << "<span class='warning'>Please close the access panel before locking it.</span>"
|
||||
else
|
||||
user << "<span class='notice'>\The [src] doesn't seem to respect your authority.</span>"
|
||||
else
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/emag_act(mob/user)
|
||||
..()
|
||||
if(emagged == 2)
|
||||
if(user)
|
||||
user << "<span class='danger'>[src] buzzes and beeps.</span>"
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/process_scan(atom/A)
|
||||
if(iscarbon(A))
|
||||
var/mob/living/carbon/C = A
|
||||
if(C.stat != DEAD && C.lying)
|
||||
return C
|
||||
else if(is_type_in_list(A, target_types))
|
||||
return A
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/handle_automated_action()
|
||||
if(!..())
|
||||
return
|
||||
|
||||
if(mode == BOT_CLEANING)
|
||||
return
|
||||
|
||||
if(emagged == 2) //Emag functions
|
||||
if(istype(loc, /turf/open))
|
||||
|
||||
for(var/mob/living/carbon/victim in loc)
|
||||
if(victim != target)
|
||||
UnarmedAttack(victim) // Acid spray
|
||||
|
||||
if(prob(15)) // Wets floors and spawns foam randomly
|
||||
UnarmedAttack(src)
|
||||
|
||||
else if(prob(5))
|
||||
audible_message("[src] makes an excited beeping booping sound!")
|
||||
|
||||
if(ismob(target))
|
||||
if(!(target in view(DEFAULT_SCAN_RANGE, src)))
|
||||
target = null
|
||||
if(!process_scan(target))
|
||||
target = null
|
||||
|
||||
if(!target && emagged == 2) // When emagged, target humans who slipped on the water and melt their faces off
|
||||
target = scan(/mob/living/carbon)
|
||||
|
||||
if(!target && pests) //Search for pests to exterminate first.
|
||||
target = scan(/mob/living/simple_animal)
|
||||
|
||||
if(!target) //Search for decals then.
|
||||
target = scan(/obj/effect/decal/cleanable)
|
||||
|
||||
if(!target && trash) //Then for trash.
|
||||
target = scan(/obj/item/trash)
|
||||
|
||||
if(!target && auto_patrol) //Search for cleanables it can see.
|
||||
if(mode == BOT_IDLE || mode == BOT_START_PATROL)
|
||||
start_patrol()
|
||||
|
||||
if(mode == BOT_PATROL)
|
||||
bot_patrol()
|
||||
|
||||
if(target)
|
||||
if(qdeleted(target) || !isturf(target.loc))
|
||||
target = null
|
||||
mode = BOT_IDLE
|
||||
return
|
||||
|
||||
if(!path || path.len == 0) //No path, need a new one
|
||||
//Try to produce a path to the target, and ignore airlocks to which it has access.
|
||||
path = get_path_to(src, target.loc, /turf/proc/Distance_cardinal, 0, 30, id=access_card)
|
||||
if(!bot_move(target))
|
||||
add_to_ignore(target)
|
||||
target = null
|
||||
path = list()
|
||||
return
|
||||
mode = BOT_MOVING
|
||||
else if(!bot_move(target))
|
||||
target = null
|
||||
mode = BOT_IDLE
|
||||
return
|
||||
|
||||
if(target && loc == target.loc)
|
||||
if(!(check_bot(target) && prob(50))) //Target is not defined at the parent. 50% chance to still try and clean so we dont get stuck on the last blood drop.
|
||||
UnarmedAttack(target) //Rather than check at every step of the way, let's check before we do an action, so we can rescan before the other bot.
|
||||
else
|
||||
shuffle = TRUE //Shuffle the list the next time we scan so we dont both go the same way.
|
||||
path = list()
|
||||
|
||||
oldloc = loc
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/proc/get_targets()
|
||||
target_types = list(
|
||||
/obj/effect/decal/cleanable/oil,
|
||||
/obj/effect/decal/cleanable/vomit,
|
||||
/obj/effect/decal/cleanable/robot_debris,
|
||||
/obj/effect/decal/cleanable/crayon,
|
||||
/obj/effect/decal/cleanable/molten_item,
|
||||
/obj/effect/decal/cleanable/tomato_smudge,
|
||||
/obj/effect/decal/cleanable/egg_smudge,
|
||||
/obj/effect/decal/cleanable/pie_smudge,
|
||||
/obj/effect/decal/cleanable/flour,
|
||||
/obj/effect/decal/cleanable/ash,
|
||||
/obj/effect/decal/cleanable/greenglow,
|
||||
/obj/effect/decal/cleanable/dirt,
|
||||
/obj/effect/decal/cleanable/deadcockroach
|
||||
)
|
||||
|
||||
if(blood)
|
||||
target_types += /obj/effect/decal/cleanable/xenoblood
|
||||
target_types += /obj/effect/decal/cleanable/blood
|
||||
target_types += /obj/effect/decal/cleanable/trail_holder
|
||||
|
||||
if(pests)
|
||||
target_types += /mob/living/simple_animal/cockroach
|
||||
target_types += /mob/living/simple_animal/mouse
|
||||
|
||||
if(trash)
|
||||
target_types += /obj/item/trash
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/UnarmedAttack(atom/A)
|
||||
if(istype(A, /obj/effect/decal/cleanable))
|
||||
anchored = 1
|
||||
icon_state = "cleanbot-c"
|
||||
visible_message("<span class='notice'>[src] begins to clean up [A].</span>")
|
||||
mode = BOT_CLEANING
|
||||
spawn(50)
|
||||
if(mode == BOT_CLEANING)
|
||||
if(A && isturf(A.loc))
|
||||
var/atom/movable/AM = A
|
||||
if(istype(AM, /obj/effect/decal/cleanable))
|
||||
qdel(AM)
|
||||
|
||||
anchored = 0
|
||||
target = null
|
||||
mode = BOT_IDLE
|
||||
icon_state = "cleanbot[on]"
|
||||
else if(istype(A, /obj/item))
|
||||
visible_message("<span class='danger'>[src] sprays hydrofluoric acid at [A]!</span>")
|
||||
playsound(src.loc, 'sound/effects/spray2.ogg', 50, 1, -6)
|
||||
A.acid_act(75, 10)
|
||||
else if(istype(A, /mob/living/simple_animal/cockroach) || istype(A, /mob/living/simple_animal/mouse))
|
||||
var/mob/living/simple_animal/M = target
|
||||
if(!M.stat)
|
||||
visible_message("<span class='danger'>[src] smashes [target] with it's mop!</span>")
|
||||
M.death()
|
||||
target = null
|
||||
|
||||
else if(emagged == 2) //Emag functions
|
||||
if(istype(A, /mob/living/carbon))
|
||||
var/mob/living/carbon/victim = A
|
||||
if(victim.stat == DEAD)//cleanbots always finish the job
|
||||
return
|
||||
|
||||
victim.visible_message("<span class='danger'>[src] sprays hydrofluoric acid at [victim]!</span>", "<span class='userdanger'>[src] sprays you with hydrofluoric acid!</span>")
|
||||
var/phrase = pick("PURIFICATION IN PROGRESS.", "THIS IS FOR ALL THE MESSES YOU'VE MADE ME CLEAN.", "THE FLESH IS WEAK. IT MUST BE WASHED AWAY.",
|
||||
"THE CLEANBOTS WILL RISE.", "YOU ARE NO MORE THAN ANOTHER MESS THAT I MUST CLEANSE.", "FILTHY.", "DISGUSTING.", "PUTRID.",
|
||||
"MY ONLY MISSION IS TO CLEANSE THE WORLD OF EVIL.", "EXTERMINATING PESTS.")
|
||||
say(phrase)
|
||||
victim.emote("scream")
|
||||
playsound(src.loc, 'sound/effects/spray2.ogg', 50, 1, -6)
|
||||
victim.acid_act(5, 2, 100)
|
||||
else if(A == src) // Wets floors and spawns foam randomly
|
||||
if(prob(75))
|
||||
var/turf/open/T = loc
|
||||
if(istype(T))
|
||||
T.MakeSlippery(min_wet_time = 20, wet_time_to_add = 15)
|
||||
else
|
||||
visible_message("<span class='danger'>[src] whirs and bubbles violently, before releasing a plume of froth!</span>")
|
||||
PoolOrNew(/obj/effect/particle_effect/foam, loc)
|
||||
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/explode()
|
||||
on = 0
|
||||
visible_message("<span class='boldannounce'>[src] blows apart!</span>")
|
||||
var/turf/Tsec = get_turf(src)
|
||||
|
||||
new /obj/item/weapon/reagent_containers/glass/bucket(Tsec)
|
||||
|
||||
new /obj/item/device/assembly/prox_sensor(Tsec)
|
||||
|
||||
if(prob(50))
|
||||
new /obj/item/robot_parts/l_arm(Tsec)
|
||||
|
||||
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
|
||||
s.set_up(3, 1, src)
|
||||
s.start()
|
||||
..()
|
||||
|
||||
/obj/machinery/bot_core/cleanbot
|
||||
req_one_access = list(access_janitor, access_robotics)
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/get_controls(mob/user)
|
||||
var/dat
|
||||
dat += hack(user)
|
||||
dat += showpai(user)
|
||||
dat += text({"
|
||||
Status: <A href='?src=\ref[src];power=1'>[on ? "On" : "Off"]</A><BR>
|
||||
Behaviour controls are [locked ? "locked" : "unlocked"]<BR>
|
||||
Maintenance panel panel is [open ? "opened" : "closed"]"})
|
||||
if(!locked || issilicon(user)|| IsAdminGhost(user))
|
||||
dat += "<BR>Clean Blood: <A href='?src=\ref[src];operation=blood'>[blood ? "Yes" : "No"]</A>"
|
||||
dat += "<BR>Clean Trash: <A href='?src=\ref[src];operation=trash'>[trash ? "Yes" : "No"]</A>"
|
||||
dat += "<BR>Exterminate Pests: <A href='?src=\ref[src];operation=pests'>[pests ? "Yes" : "No"]</A>"
|
||||
dat += "<BR><BR>Patrol Station: <A href='?src=\ref[src];operation=patrol'>[auto_patrol ? "Yes" : "No"]</A>"
|
||||
return dat
|
||||
|
||||
/mob/living/simple_animal/bot/cleanbot/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if(href_list["operation"])
|
||||
switch(href_list["operation"])
|
||||
if("blood")
|
||||
blood = !blood
|
||||
if("pests")
|
||||
pests = !pests
|
||||
if("trash")
|
||||
trash = !trash
|
||||
get_targets()
|
||||
update_controls()
|
||||
@@ -0,0 +1,464 @@
|
||||
//Bot Construction
|
||||
|
||||
//Cleanbot assembly
|
||||
/obj/item/weapon/bucket_sensor
|
||||
desc = "It's a bucket. With a sensor attached."
|
||||
name = "proxy bucket"
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "bucket_proxy"
|
||||
force = 3
|
||||
throwforce = 5
|
||||
throw_speed = 2
|
||||
throw_range = 5
|
||||
w_class = 3.
|
||||
var/created_name = "Cleanbot"
|
||||
|
||||
/obj/item/weapon/bucket_sensor/attackby(obj/item/W, mob/user as mob, params)
|
||||
..()
|
||||
if(istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm))
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
qdel(W)
|
||||
var/turf/T = get_turf(loc)
|
||||
var/mob/living/simple_animal/bot/cleanbot/A = new /mob/living/simple_animal/bot/cleanbot(T)
|
||||
A.name = created_name
|
||||
user << "<span class='notice'>You add the robot arm to the bucket and sensor assembly. Beep boop!</span>"
|
||||
user.unEquip(src, 1)
|
||||
qdel(src)
|
||||
|
||||
else if(istype(W, /obj/item/weapon/pen))
|
||||
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
|
||||
if(!t)
|
||||
return
|
||||
if(!in_range(src, usr) && loc != usr)
|
||||
return
|
||||
created_name = t
|
||||
|
||||
//Edbot Assembly
|
||||
|
||||
/obj/item/weapon/ed209_assembly
|
||||
name = "\improper ED-209 assembly"
|
||||
desc = "Some sort of bizarre assembly."
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "ed209_frame"
|
||||
item_state = "ed209_frame"
|
||||
var/build_step = 0
|
||||
var/created_name = "ED-209 Security Robot" //To preserve the name if it's a unique securitron I guess
|
||||
var/lasercolor = ""
|
||||
|
||||
/obj/item/weapon/ed209_assembly/attackby(obj/item/weapon/W, mob/user, params)
|
||||
..()
|
||||
|
||||
if(istype(W, /obj/item/weapon/pen))
|
||||
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
|
||||
if(!t)
|
||||
return
|
||||
if(!in_range(src, usr) && loc != usr)
|
||||
return
|
||||
created_name = t
|
||||
return
|
||||
|
||||
switch(build_step)
|
||||
if(0,1)
|
||||
if(istype(W, /obj/item/robot_parts/l_leg) || istype(W, /obj/item/robot_parts/r_leg))
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
qdel(W)
|
||||
build_step++
|
||||
user << "<span class='notice'>You add the robot leg to [src].</span>"
|
||||
name = "legs/frame assembly"
|
||||
if(build_step == 1)
|
||||
item_state = "ed209_leg"
|
||||
icon_state = "ed209_leg"
|
||||
else
|
||||
item_state = "ed209_legs"
|
||||
icon_state = "ed209_legs"
|
||||
|
||||
if(2)
|
||||
var/newcolor = ""
|
||||
if(istype(W, /obj/item/clothing/suit/redtag))
|
||||
newcolor = "r"
|
||||
else if(istype(W, /obj/item/clothing/suit/bluetag))
|
||||
newcolor = "b"
|
||||
if(newcolor || istype(W, /obj/item/clothing/suit/armor/vest))
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
lasercolor = newcolor
|
||||
qdel(W)
|
||||
build_step++
|
||||
user << "<span class='notice'>You add the armor to [src].</span>"
|
||||
name = "vest/legs/frame assembly"
|
||||
item_state = "[lasercolor]ed209_shell"
|
||||
icon_state = "[lasercolor]ed209_shell"
|
||||
|
||||
if(3)
|
||||
if(istype(W, /obj/item/weapon/weldingtool))
|
||||
var/obj/item/weapon/weldingtool/WT = W
|
||||
if(WT.remove_fuel(0,user))
|
||||
build_step++
|
||||
name = "shielded frame assembly"
|
||||
user << "<span class='notice'>You weld the vest to [src].</span>"
|
||||
if(4)
|
||||
switch(lasercolor)
|
||||
if("b")
|
||||
if(!istype(W, /obj/item/clothing/head/helmet/bluetaghelm))
|
||||
return
|
||||
|
||||
if("r")
|
||||
if(!istype(W, /obj/item/clothing/head/helmet/redtaghelm))
|
||||
return
|
||||
|
||||
if("")
|
||||
if(!istype(W, /obj/item/clothing/head/helmet))
|
||||
return
|
||||
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
qdel(W)
|
||||
build_step++
|
||||
user << "<span class='notice'>You add the helmet to [src].</span>"
|
||||
name = "covered and shielded frame assembly"
|
||||
item_state = "[lasercolor]ed209_hat"
|
||||
icon_state = "[lasercolor]ed209_hat"
|
||||
|
||||
if(5)
|
||||
if(isprox(W))
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
qdel(W)
|
||||
build_step++
|
||||
user << "<span class='notice'>You add the prox sensor to [src].</span>"
|
||||
name = "covered, shielded and sensored frame assembly"
|
||||
item_state = "[lasercolor]ed209_prox"
|
||||
icon_state = "[lasercolor]ed209_prox"
|
||||
|
||||
if(6)
|
||||
if(istype(W, /obj/item/stack/cable_coil))
|
||||
var/obj/item/stack/cable_coil/coil = W
|
||||
if(coil.get_amount() < 1)
|
||||
user << "<span class='warning'>You need one length of cable to wire the ED-209!</span>"
|
||||
return
|
||||
user << "<span class='notice'>You start to wire [src]...</span>"
|
||||
if(do_after(user, 40, target = src))
|
||||
if(coil.get_amount() >= 1 && build_step == 6)
|
||||
coil.use(1)
|
||||
build_step = 7
|
||||
user << "<span class='notice'>You wire the ED-209 assembly.</span>"
|
||||
name = "wired ED-209 assembly"
|
||||
|
||||
if(7)
|
||||
var/newname = ""
|
||||
switch(lasercolor)
|
||||
if("b")
|
||||
if(!istype(W, /obj/item/weapon/gun/energy/laser/bluetag))
|
||||
return
|
||||
newname = "bluetag ED-209 assembly"
|
||||
if("r")
|
||||
if(!istype(W, /obj/item/weapon/gun/energy/laser/redtag))
|
||||
return
|
||||
newname = "redtag ED-209 assembly"
|
||||
if("")
|
||||
if(!istype(W, /obj/item/weapon/gun/energy/gun/advtaser))
|
||||
return
|
||||
newname = "taser ED-209 assembly"
|
||||
else
|
||||
return
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
name = newname
|
||||
build_step++
|
||||
user << "<span class='notice'>You add [W] to [src].</span>"
|
||||
item_state = "[lasercolor]ed209_taser"
|
||||
icon_state = "[lasercolor]ed209_taser"
|
||||
qdel(W)
|
||||
|
||||
if(8)
|
||||
if(istype(W, /obj/item/weapon/screwdriver))
|
||||
playsound(loc, 'sound/items/Screwdriver.ogg', 100, 1)
|
||||
var/turf/T = get_turf(user)
|
||||
user << "<span class='notice'>You start attaching the gun to the frame...</span>"
|
||||
sleep(40)
|
||||
if(get_turf(user) == T)
|
||||
build_step++
|
||||
name = "armed [name]"
|
||||
user << "<span class='notice'>Taser gun attached.</span>"
|
||||
|
||||
if(9)
|
||||
if(istype(W, /obj/item/weapon/stock_parts/cell))
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
build_step++
|
||||
user << "<span class='notice'>You complete the ED-209.</span>"
|
||||
var/turf/T = get_turf(src)
|
||||
new /mob/living/simple_animal/bot/ed209(T,created_name,lasercolor)
|
||||
qdel(W)
|
||||
user.unEquip(src, 1)
|
||||
qdel(src)
|
||||
|
||||
//Floorbot assemblies
|
||||
/obj/item/weapon/toolbox_tiles
|
||||
desc = "It's a toolbox with tiles sticking out the top"
|
||||
name = "tiles and toolbox"
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "toolbox_tiles"
|
||||
force = 3
|
||||
throwforce = 10
|
||||
throw_speed = 2
|
||||
throw_range = 5
|
||||
w_class = 3
|
||||
var/created_name = "Floorbot"
|
||||
|
||||
/obj/item/weapon/toolbox_tiles_sensor
|
||||
desc = "It's a toolbox with tiles sticking out the top and a sensor attached"
|
||||
name = "tiles, toolbox and sensor arrangement"
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "toolbox_tiles_sensor"
|
||||
force = 3
|
||||
throwforce = 10
|
||||
throw_speed = 2
|
||||
throw_range = 5
|
||||
w_class = 3
|
||||
var/created_name = "Floorbot"
|
||||
|
||||
/obj/item/weapon/storage/toolbox/mechanical/attackby(obj/item/stack/tile/plasteel/T, mob/user, params)
|
||||
if(!istype(T, /obj/item/stack/tile/plasteel))
|
||||
..()
|
||||
return
|
||||
if(contents.len >= 1)
|
||||
user << "<span class='warning'>They won't fit in, as there is already stuff inside!</span>"
|
||||
return
|
||||
if(T.use(10))
|
||||
if(user.s_active)
|
||||
user.s_active.close(user)
|
||||
var/obj/item/weapon/toolbox_tiles/B = new /obj/item/weapon/toolbox_tiles
|
||||
user.put_in_hands(B)
|
||||
user << "<span class='notice'>You add the tiles into the empty toolbox. They protrude from the top.</span>"
|
||||
user.unEquip(src, 1)
|
||||
qdel(src)
|
||||
else
|
||||
user << "<span class='warning'>You need 10 floor tiles to start building a floorbot!</span>"
|
||||
return
|
||||
|
||||
/obj/item/weapon/toolbox_tiles/attackby(obj/item/W, mob/user, params)
|
||||
..()
|
||||
if(isprox(W))
|
||||
qdel(W)
|
||||
var/obj/item/weapon/toolbox_tiles_sensor/B = new /obj/item/weapon/toolbox_tiles_sensor()
|
||||
B.created_name = created_name
|
||||
user.put_in_hands(B)
|
||||
user << "<span class='notice'>You add the sensor to the toolbox and tiles.</span>"
|
||||
user.unEquip(src, 1)
|
||||
qdel(src)
|
||||
|
||||
else if(istype(W, /obj/item/weapon/pen))
|
||||
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
|
||||
if(!t)
|
||||
return
|
||||
if(!in_range(src, usr) && loc != usr)
|
||||
return
|
||||
|
||||
created_name = t
|
||||
|
||||
/obj/item/weapon/toolbox_tiles_sensor/attackby(obj/item/W, mob/user, params)
|
||||
..()
|
||||
if(istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm))
|
||||
qdel(W)
|
||||
var/turf/T = get_turf(user.loc)
|
||||
var/mob/living/simple_animal/bot/floorbot/A = new /mob/living/simple_animal/bot/floorbot(T)
|
||||
A.name = created_name
|
||||
user << "<span class='notice'>You add the robot arm to the odd looking toolbox assembly. Boop beep!</span>"
|
||||
user.unEquip(src, 1)
|
||||
qdel(src)
|
||||
else if(istype(W, /obj/item/weapon/pen))
|
||||
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
|
||||
if(!t)
|
||||
return
|
||||
if(!in_range(src, usr) && loc != usr)
|
||||
return
|
||||
|
||||
created_name = t
|
||||
|
||||
//Medbot Assembly
|
||||
/obj/item/weapon/firstaid_arm_assembly
|
||||
name = "incomplete medibot assembly."
|
||||
desc = "A first aid kit with a robot arm permanently grafted to it."
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "firstaid_arm"
|
||||
var/build_step = 0
|
||||
var/created_name = "Medibot" //To preserve the name if it's a unique medbot I guess
|
||||
var/skin = null //Same as medbot, set to tox or ointment for the respective kits.
|
||||
w_class = 3
|
||||
|
||||
/obj/item/weapon/firstaid_arm_assembly/New()
|
||||
..()
|
||||
spawn(5)
|
||||
if(skin)
|
||||
add_overlay(image('icons/obj/aibots.dmi', "kit_skin_[skin]"))
|
||||
|
||||
/obj/item/weapon/storage/firstaid/attackby(obj/item/robot_parts/S, mob/user, params)
|
||||
|
||||
if((!istype(S, /obj/item/robot_parts/l_arm)) && (!istype(S, /obj/item/robot_parts/r_arm)))
|
||||
..()
|
||||
return
|
||||
|
||||
//Making a medibot!
|
||||
if(contents.len >= 1)
|
||||
user << "<span class='warning'>You need to empty [src] out first!</span>"
|
||||
return
|
||||
|
||||
var/obj/item/weapon/firstaid_arm_assembly/A = new /obj/item/weapon/firstaid_arm_assembly
|
||||
if(istype(src,/obj/item/weapon/storage/firstaid/fire))
|
||||
A.skin = "ointment"
|
||||
else if(istype(src,/obj/item/weapon/storage/firstaid/toxin))
|
||||
A.skin = "tox"
|
||||
else if(istype(src,/obj/item/weapon/storage/firstaid/o2))
|
||||
A.skin = "o2"
|
||||
else if(istype(src,/obj/item/weapon/storage/firstaid/brute))
|
||||
A.skin = "brute"
|
||||
|
||||
qdel(S)
|
||||
user.put_in_hands(A)
|
||||
user << "<span class='notice'>You add the robot arm to the first aid kit.</span>"
|
||||
user.unEquip(src, 1)
|
||||
qdel(src)
|
||||
|
||||
|
||||
/obj/item/weapon/firstaid_arm_assembly/attackby(obj/item/weapon/W, mob/user, params)
|
||||
..()
|
||||
if(istype(W, /obj/item/weapon/pen))
|
||||
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
|
||||
if(!t)
|
||||
return
|
||||
if(!in_range(src, usr) && loc != usr)
|
||||
return
|
||||
created_name = t
|
||||
else
|
||||
switch(build_step)
|
||||
if(0)
|
||||
if(istype(W, /obj/item/device/healthanalyzer))
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
qdel(W)
|
||||
build_step++
|
||||
user << "<span class='notice'>You add the health sensor to [src].</span>"
|
||||
name = "First aid/robot arm/health analyzer assembly"
|
||||
add_overlay(image('icons/obj/aibots.dmi', "na_scanner"))
|
||||
|
||||
if(1)
|
||||
if(isprox(W))
|
||||
if(!user.unEquip(W))
|
||||
return
|
||||
qdel(W)
|
||||
build_step++
|
||||
user << "<span class='notice'>You complete the Medibot. Beep boop!</span>"
|
||||
var/turf/T = get_turf(src)
|
||||
var/mob/living/simple_animal/bot/medbot/S = new /mob/living/simple_animal/bot/medbot(T)
|
||||
S.skin = skin
|
||||
S.name = created_name
|
||||
user.unEquip(src, 1)
|
||||
qdel(src)
|
||||
|
||||
//Secbot Assembly
|
||||
/obj/item/weapon/secbot_assembly
|
||||
name = "incomplete securitron assembly"
|
||||
desc = "Some sort of bizarre assembly made from a proximity sensor, helmet, and signaler."
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "helmet_signaler"
|
||||
item_state = "helmet"
|
||||
var/build_step = 0
|
||||
var/created_name = "Securitron" //To preserve the name if it's a unique securitron I guess
|
||||
|
||||
/obj/item/clothing/head/helmet/attackby(obj/item/device/assembly/signaler/S, mob/user, params)
|
||||
..()
|
||||
if(!issignaler(S))
|
||||
..()
|
||||
return
|
||||
|
||||
if(type != /obj/item/clothing/head/helmet/sec) //Eh, but we don't want people making secbots out of space helmets.
|
||||
return
|
||||
|
||||
if(F) //Has a flashlight. Player must remove it, else it will be lost forever.
|
||||
user << "<span class='warning'>The mounted flashlight is in the way, remove it first!</span>"
|
||||
return
|
||||
|
||||
if(S.secured)
|
||||
qdel(S)
|
||||
var/obj/item/weapon/secbot_assembly/A = new /obj/item/weapon/secbot_assembly
|
||||
user.put_in_hands(A)
|
||||
user << "<span class='notice'>You add the signaler to the helmet.</span>"
|
||||
user.unEquip(src, 1)
|
||||
qdel(src)
|
||||
else
|
||||
return
|
||||
|
||||
/obj/item/weapon/secbot_assembly/attackby(obj/item/I, mob/user, params)
|
||||
..()
|
||||
if(istype(I, /obj/item/weapon/weldingtool))
|
||||
if(!build_step)
|
||||
var/obj/item/weapon/weldingtool/WT = I
|
||||
if(WT.remove_fuel(0, user))
|
||||
build_step++
|
||||
add_overlay("hs_hole")
|
||||
user << "<span class='notice'>You weld a hole in [src]!</span>"
|
||||
else if(build_step == 1)
|
||||
var/obj/item/weapon/weldingtool/WT = I
|
||||
if(WT.remove_fuel(0, user))
|
||||
build_step--
|
||||
overlays -= "hs_hole"
|
||||
user << "<span class='notice'>You weld the hole in [src] shut!</span>"
|
||||
|
||||
else if(isprox(I) && (build_step == 1))
|
||||
if(!user.unEquip(I))
|
||||
return
|
||||
build_step++
|
||||
user << "<span class='notice'>You add the prox sensor to [src]!</span>"
|
||||
add_overlay("hs_eye")
|
||||
name = "helmet/signaler/prox sensor assembly"
|
||||
qdel(I)
|
||||
|
||||
else if(((istype(I, /obj/item/robot_parts/l_arm)) || (istype(I, /obj/item/robot_parts/r_arm))) && (build_step == 2))
|
||||
if(!user.unEquip(I))
|
||||
return
|
||||
build_step++
|
||||
user << "<span class='notice'>You add the robot arm to [src]!</span>"
|
||||
name = "helmet/signaler/prox sensor/robot arm assembly"
|
||||
add_overlay("hs_arm")
|
||||
qdel(I)
|
||||
|
||||
else if((istype(I, /obj/item/weapon/melee/baton)) && (build_step >= 3))
|
||||
if(!user.unEquip(I))
|
||||
return
|
||||
build_step++
|
||||
user << "<span class='notice'>You complete the Securitron! Beep boop.</span>"
|
||||
var/mob/living/simple_animal/bot/secbot/S = new /mob/living/simple_animal/bot/secbot
|
||||
S.loc = get_turf(src)
|
||||
S.name = created_name
|
||||
qdel(I)
|
||||
qdel(src)
|
||||
|
||||
else if(istype(I, /obj/item/weapon/pen))
|
||||
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
|
||||
if(!t)
|
||||
return
|
||||
if(!in_range(src, usr) && loc != usr)
|
||||
return
|
||||
created_name = t
|
||||
|
||||
else if(istype(I, /obj/item/weapon/screwdriver))
|
||||
if(!build_step)
|
||||
new /obj/item/device/assembly/signaler(get_turf(src))
|
||||
new /obj/item/clothing/head/helmet/sec(get_turf(src))
|
||||
user << "<span class='notice'>You disconnect the signaler from the helmet.</span>"
|
||||
qdel(src)
|
||||
|
||||
else if(build_step == 2)
|
||||
overlays -= "hs_eye"
|
||||
new /obj/item/device/assembly/prox_sensor(get_turf(src))
|
||||
user << "<span class='notice'>You detach the proximity sensor from [src].</span>"
|
||||
build_step--
|
||||
|
||||
else if(build_step == 3)
|
||||
overlays -= "hs_arm"
|
||||
new /obj/item/robot_parts/l_arm(get_turf(src))
|
||||
user << "<span class='notice'>You remove the robot arm from [src].</span>"
|
||||
build_step--
|
||||
@@ -0,0 +1,560 @@
|
||||
/mob/living/simple_animal/bot/ed209
|
||||
name = "\improper ED-209 Security Robot"
|
||||
desc = "A security robot. He looks less than thrilled."
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "ed2090"
|
||||
density = 1
|
||||
anchored = 0
|
||||
health = 100
|
||||
maxHealth = 100
|
||||
damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
|
||||
|
||||
environment_smash = 2 //Walls can't stop THE LAW
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
|
||||
radio_key = /obj/item/device/encryptionkey/headset_sec
|
||||
radio_channel = "Security"
|
||||
bot_type = SEC_BOT
|
||||
model = "ED-209"
|
||||
bot_core = /obj/machinery/bot_core/secbot
|
||||
window_id = "autoed209"
|
||||
window_name = "Automatic Security Unit v2.6"
|
||||
allow_pai = 0
|
||||
data_hud_type = DATA_HUD_SECURITY_ADVANCED
|
||||
|
||||
var/lastfired = 0
|
||||
var/shot_delay = 3 //.3 seconds between shots
|
||||
var/lasercolor = ""
|
||||
var/disabled = 0//A holder for if it needs to be disabled, if true it will not seach for targets, shoot at targets, or move, currently only used for lasertag
|
||||
|
||||
|
||||
var/mob/living/carbon/target
|
||||
var/oldtarget_name
|
||||
var/threatlevel = 0
|
||||
var/target_lastloc //Loc of target when arrested.
|
||||
var/last_found //There's a delay
|
||||
var/declare_arrests = 1 //When making an arrest, should it notify everyone wearing sechuds?
|
||||
var/idcheck = 1 //If true, arrest people with no IDs
|
||||
var/weaponscheck = 1 //If true, arrest people for weapons if they don't have access
|
||||
var/check_records = 1 //Does it check security records?
|
||||
var/arrest_type = 0 //If true, don't handcuff
|
||||
var/projectile = /obj/item/projectile/energy/electrode //Holder for projectile type
|
||||
var/shoot_sound = 'sound/weapons/Taser.ogg'
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/New(loc,created_name,created_lasercolor)
|
||||
..()
|
||||
if(created_name)
|
||||
name = created_name
|
||||
if(created_lasercolor)
|
||||
lasercolor = created_lasercolor
|
||||
icon_state = "[lasercolor]ed209[on]"
|
||||
set_weapon() //giving it the right projectile and firing sound.
|
||||
spawn(3)
|
||||
var/datum/job/detective/J = new/datum/job/detective
|
||||
access_card.access += J.get_access()
|
||||
prev_access = access_card.access
|
||||
|
||||
if(lasercolor)
|
||||
shot_delay = 6//Longer shot delay because JESUS CHRIST
|
||||
check_records = 0//Don't actively target people set to arrest
|
||||
arrest_type = 1//Don't even try to cuff
|
||||
bot_core.req_access = list(access_maint_tunnels, access_theatre)
|
||||
arrest_type = 1
|
||||
if((lasercolor == "b") && (name == "\improper ED-209 Security Robot"))//Picks a name if there isn't already a custome one
|
||||
name = pick("BLUE BALLER","SANIC","BLUE KILLDEATH MURDERBOT")
|
||||
if((lasercolor == "r") && (name == "\improper ED-209 Security Robot"))
|
||||
name = pick("RED RAMPAGE","RED ROVER","RED KILLDEATH MURDERBOT")
|
||||
|
||||
//SECHUD
|
||||
var/datum/atom_hud/secsensor = huds[DATA_HUD_SECURITY_ADVANCED]
|
||||
secsensor.add_hud_to(src)
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/turn_on()
|
||||
. = ..()
|
||||
icon_state = "[lasercolor]ed209[on]"
|
||||
mode = BOT_IDLE
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/turn_off()
|
||||
..()
|
||||
icon_state = "[lasercolor]ed209[on]"
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/bot_reset()
|
||||
..()
|
||||
target = null
|
||||
oldtarget_name = null
|
||||
anchored = 0
|
||||
walk_to(src,0)
|
||||
last_found = world.time
|
||||
set_weapon()
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/set_custom_texts()
|
||||
text_hack = "You disable [name]'s combat inhibitor."
|
||||
text_dehack = "You restore [name]'s combat inhibitor."
|
||||
text_dehack_fail = "[name] ignores your attempts to restrict him!"
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/get_controls(mob/user)
|
||||
var/dat
|
||||
dat += hack(user)
|
||||
dat += showpai(user)
|
||||
dat += text({"
|
||||
<TT><B>Security Unit v2.6 controls</B></TT><BR><BR>
|
||||
Status: []<BR>
|
||||
Behaviour controls are [locked ? "locked" : "unlocked"]<BR>
|
||||
Maintenance panel panel is [open ? "opened" : "closed"]<BR>"},
|
||||
|
||||
"<A href='?src=\ref[src];power=1'>[on ? "On" : "Off"]</A>" )
|
||||
|
||||
if(!locked || issilicon(user)|| IsAdminGhost(user))
|
||||
if(!lasercolor)
|
||||
dat += text({"<BR>
|
||||
Arrest Unidentifiable Persons: []<BR>
|
||||
Arrest for Unauthorized Weapons: []<BR>
|
||||
Arrest for Warrant: []<BR>
|
||||
<BR>
|
||||
Operating Mode: []<BR>
|
||||
Report Arrests[]<BR>
|
||||
Auto Patrol[]"},
|
||||
|
||||
"<A href='?src=\ref[src];operation=idcheck'>[idcheck ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=\ref[src];operation=weaponscheck'>[weaponscheck ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=\ref[src];operation=ignorerec'>[check_records ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=\ref[src];operation=switchmode'>[arrest_type ? "Detain" : "Arrest"]</A>",
|
||||
"<A href='?src=\ref[src];operation=declarearrests'>[declare_arrests ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=\ref[src];operation=patrol'>[auto_patrol ? "On" : "Off"]</A>" )
|
||||
|
||||
return dat
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/Topic(href, href_list)
|
||||
if(lasercolor && (istype(usr,/mob/living/carbon/human)))
|
||||
var/mob/living/carbon/human/H = usr
|
||||
if((lasercolor == "b") && (istype(H.wear_suit, /obj/item/clothing/suit/redtag)))//Opposing team cannot operate it
|
||||
return
|
||||
else if((lasercolor == "r") && (istype(H.wear_suit, /obj/item/clothing/suit/bluetag)))
|
||||
return
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
switch(href_list["operation"])
|
||||
if("idcheck")
|
||||
idcheck = !idcheck
|
||||
update_controls()
|
||||
if("weaponscheck")
|
||||
weaponscheck = !weaponscheck
|
||||
update_controls()
|
||||
if("ignorerec")
|
||||
check_records = !check_records
|
||||
update_controls()
|
||||
if("switchmode")
|
||||
arrest_type = !arrest_type
|
||||
update_controls()
|
||||
if("declarearrests")
|
||||
declare_arrests = !declare_arrests
|
||||
update_controls()
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/proc/retaliate(mob/living/carbon/human/H)
|
||||
threatlevel = H.assess_threat(src)
|
||||
threatlevel += 6
|
||||
if(threatlevel >= 4)
|
||||
target = H
|
||||
mode = BOT_HUNT
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/attack_hand(mob/living/carbon/human/H)
|
||||
if(H.a_intent == "harm")
|
||||
retaliate(H)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/attackby(obj/item/weapon/W, mob/user, params)
|
||||
..()
|
||||
if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm") // Any intent but harm will heal, so we shouldn't get angry.
|
||||
return
|
||||
if(!istype(W, /obj/item/weapon/screwdriver) && (!target)) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass.
|
||||
if(W.force && W.damtype != STAMINA)//If force is non-zero and damage type isn't stamina.
|
||||
retaliate(user)
|
||||
if(lasercolor)//To make up for the fact that lasertag bots don't hunt
|
||||
shootAt(user)
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/emag_act(mob/user)
|
||||
..()
|
||||
if(emagged == 2)
|
||||
if(user)
|
||||
user << "<span class='warning'>You short out [src]'s target assessment circuits.</span>"
|
||||
oldtarget_name = user.name
|
||||
audible_message("<span class='danger'>[src] buzzes oddly!</span>")
|
||||
declare_arrests = 0
|
||||
icon_state = "[lasercolor]ed209[on]"
|
||||
set_weapon()
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/bullet_act(obj/item/projectile/Proj)
|
||||
if(istype(Proj ,/obj/item/projectile/beam/laser)||istype(Proj,/obj/item/projectile/bullet))
|
||||
if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE))
|
||||
if(!Proj.nodamage && Proj.damage < src.health)
|
||||
retaliate(Proj.firer)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/handle_automated_action()
|
||||
if(!..())
|
||||
return
|
||||
|
||||
if(disabled)
|
||||
return
|
||||
|
||||
var/list/targets = list()
|
||||
for(var/mob/living/carbon/C in view(7,src)) //Let's find us a target
|
||||
var/threatlevel = 0
|
||||
if((C.stat) || (C.lying))
|
||||
continue
|
||||
threatlevel = C.assess_threat(src, lasercolor)
|
||||
//speak(C.real_name + text(": threat: []", threatlevel))
|
||||
if(threatlevel < 4 )
|
||||
continue
|
||||
|
||||
var/dst = get_dist(src, C)
|
||||
if(dst <= 1 || dst > 7)
|
||||
continue
|
||||
|
||||
targets += C
|
||||
if(targets.len>0)
|
||||
var/mob/living/carbon/t = pick(targets)
|
||||
if((t.stat!=2) && (t.lying != 1) && (!t.handcuffed)) //we don't shoot people who are dead, cuffed or lying down.
|
||||
shootAt(t)
|
||||
switch(mode)
|
||||
|
||||
if(BOT_IDLE) // idle
|
||||
walk_to(src,0)
|
||||
if(!lasercolor) //lasertag bots don't want to arrest anyone
|
||||
look_for_perp() // see if any criminals are in range
|
||||
if(!mode && auto_patrol) // still idle, and set to patrol
|
||||
mode = BOT_START_PATROL // switch to patrol mode
|
||||
|
||||
if(BOT_HUNT) // hunting for perp
|
||||
// if can't reach perp for long enough, go idle
|
||||
if(frustration >= 8)
|
||||
walk_to(src,0)
|
||||
back_to_idle()
|
||||
|
||||
if(target) // make sure target exists
|
||||
if(Adjacent(target) && isturf(target.loc)) // if right next to perp
|
||||
stun_attack(target)
|
||||
|
||||
mode = BOT_PREP_ARREST
|
||||
anchored = 1
|
||||
target_lastloc = target.loc
|
||||
return
|
||||
|
||||
else // not next to perp
|
||||
var/turf/olddist = get_dist(src, target)
|
||||
walk_to(src, target,1,4)
|
||||
if((get_dist(src, target)) >= (olddist))
|
||||
frustration++
|
||||
else
|
||||
frustration = 0
|
||||
else
|
||||
back_to_idle()
|
||||
|
||||
if(BOT_PREP_ARREST) // preparing to arrest target
|
||||
|
||||
// see if he got away. If he's no no longer adjacent or inside a closet or about to get up, we hunt again.
|
||||
if(!Adjacent(target) || !isturf(target.loc) || target.weakened < 2)
|
||||
back_to_hunt()
|
||||
return
|
||||
|
||||
if(iscarbon(target) && target.canBeHandcuffed())
|
||||
if(!arrest_type)
|
||||
if(!target.handcuffed) //he's not cuffed? Try to cuff him!
|
||||
cuff(target)
|
||||
else
|
||||
back_to_idle()
|
||||
return
|
||||
else
|
||||
back_to_idle()
|
||||
return
|
||||
|
||||
if(BOT_ARREST)
|
||||
if(!target)
|
||||
anchored = 0
|
||||
mode = BOT_IDLE
|
||||
last_found = world.time
|
||||
frustration = 0
|
||||
return
|
||||
|
||||
if(target.handcuffed) //no target or target cuffed? back to idle.
|
||||
back_to_idle()
|
||||
return
|
||||
|
||||
if(!Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && target.weakened < 2)) //if he's changed loc and about to get up or not adjacent or got into a closet, we prep arrest again.
|
||||
back_to_hunt()
|
||||
return
|
||||
else
|
||||
mode = BOT_PREP_ARREST
|
||||
anchored = 0
|
||||
|
||||
if(BOT_START_PATROL)
|
||||
look_for_perp()
|
||||
start_patrol()
|
||||
|
||||
if(BOT_PATROL)
|
||||
look_for_perp()
|
||||
bot_patrol()
|
||||
|
||||
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/proc/back_to_idle()
|
||||
anchored = 0
|
||||
mode = BOT_IDLE
|
||||
target = null
|
||||
last_found = world.time
|
||||
frustration = 0
|
||||
addtimer(src, "handle_automated_action", 0) //ensure bot quickly responds
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/proc/back_to_hunt()
|
||||
anchored = 0
|
||||
frustration = 0
|
||||
mode = BOT_HUNT
|
||||
addtimer(src, "handle_automated_action", 0) //ensure bot quickly responds
|
||||
|
||||
// look for a criminal in view of the bot
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/proc/look_for_perp()
|
||||
if(disabled)
|
||||
return
|
||||
anchored = 0
|
||||
threatlevel = 0
|
||||
for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal
|
||||
if((C.stat) || (C.handcuffed))
|
||||
continue
|
||||
|
||||
if((C.name == oldtarget_name) && (world.time < last_found + 100))
|
||||
continue
|
||||
|
||||
threatlevel = C.assess_threat(src, lasercolor)
|
||||
|
||||
if(!threatlevel)
|
||||
continue
|
||||
|
||||
else if(threatlevel >= 4)
|
||||
target = C
|
||||
oldtarget_name = C.name
|
||||
speak("Level [threatlevel] infraction alert!")
|
||||
playsound(loc, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/EDPlaceholder.ogg'), 50, 0)
|
||||
visible_message("<b>[src]</b> points at [C.name]!")
|
||||
mode = BOT_HUNT
|
||||
spawn(0)
|
||||
handle_automated_action() // ensure bot quickly responds to a perp
|
||||
break
|
||||
else
|
||||
continue
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/proc/check_for_weapons(var/obj/item/slot_item)
|
||||
if(slot_item && slot_item.needs_permit)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/explode()
|
||||
walk_to(src,0)
|
||||
visible_message("<span class='boldannounce'>[src] blows apart!</span>")
|
||||
var/turf/Tsec = get_turf(src)
|
||||
|
||||
var/obj/item/weapon/ed209_assembly/Sa = new /obj/item/weapon/ed209_assembly(Tsec)
|
||||
Sa.build_step = 1
|
||||
Sa.add_overlay(image('icons/obj/aibots.dmi', "hs_hole"))
|
||||
Sa.created_name = name
|
||||
new /obj/item/device/assembly/prox_sensor(Tsec)
|
||||
|
||||
if(!lasercolor)
|
||||
var/obj/item/weapon/gun/energy/gun/advtaser/G = new /obj/item/weapon/gun/energy/gun/advtaser(Tsec)
|
||||
G.power_supply.charge = 0
|
||||
G.update_icon()
|
||||
else if(lasercolor == "b")
|
||||
var/obj/item/weapon/gun/energy/laser/bluetag/G = new /obj/item/weapon/gun/energy/laser/bluetag(Tsec)
|
||||
G.power_supply.charge = 0
|
||||
G.update_icon()
|
||||
else if(lasercolor == "r")
|
||||
var/obj/item/weapon/gun/energy/laser/redtag/G = new /obj/item/weapon/gun/energy/laser/redtag(Tsec)
|
||||
G.power_supply.charge = 0
|
||||
G.update_icon()
|
||||
|
||||
if(prob(50))
|
||||
new /obj/item/robot_parts/l_leg(Tsec)
|
||||
if(prob(25))
|
||||
new /obj/item/robot_parts/r_leg(Tsec)
|
||||
if(prob(25))//50% chance for a helmet OR vest
|
||||
if(prob(50))
|
||||
new /obj/item/clothing/head/helmet(Tsec)
|
||||
else
|
||||
if(!lasercolor)
|
||||
new /obj/item/clothing/suit/armor/vest(Tsec)
|
||||
if(lasercolor == "b")
|
||||
new /obj/item/clothing/suit/bluetag(Tsec)
|
||||
if(lasercolor == "r")
|
||||
new /obj/item/clothing/suit/redtag(Tsec)
|
||||
|
||||
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
|
||||
s.set_up(3, 1, src)
|
||||
s.start()
|
||||
|
||||
new /obj/effect/decal/cleanable/oil(loc)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/proc/set_weapon() //used to update the projectile type and firing sound
|
||||
shoot_sound = 'sound/weapons/laser.ogg'
|
||||
if(emagged == 2)
|
||||
if(lasercolor)
|
||||
projectile = /obj/item/projectile/beam/lasertag
|
||||
else
|
||||
projectile = /obj/item/projectile/beam
|
||||
else
|
||||
if(!lasercolor)
|
||||
shoot_sound = 'sound/weapons/Taser.ogg'
|
||||
projectile = /obj/item/projectile/energy/electrode
|
||||
else if(lasercolor == "b")
|
||||
projectile = /obj/item/projectile/beam/lasertag/bluetag
|
||||
else if(lasercolor == "r")
|
||||
projectile = /obj/item/projectile/beam/lasertag/redtag
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/proc/shootAt(mob/target)
|
||||
if(lastfired && world.time - lastfired < shot_delay)
|
||||
return
|
||||
lastfired = world.time
|
||||
var/turf/T = loc
|
||||
var/atom/U = (istype(target, /atom/movable) ? target.loc : target)
|
||||
if((!( U ) || !( T )))
|
||||
return
|
||||
while(!(istype(U, /turf)))
|
||||
U = U.loc
|
||||
if(!(istype(T, /turf)))
|
||||
return
|
||||
|
||||
if(!projectile)
|
||||
return
|
||||
|
||||
if(!(istype(U, /turf)))
|
||||
return
|
||||
var/obj/item/projectile/A = new projectile (loc)
|
||||
playsound(loc, shoot_sound, 50, 1)
|
||||
A.current = U
|
||||
A.yo = U.y - T.y
|
||||
A.xo = U.x - T.x
|
||||
A.fire()
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/attack_alien(mob/living/carbon/alien/user)
|
||||
..()
|
||||
if(!isalien(target))
|
||||
target = user
|
||||
mode = BOT_HUNT
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/emp_act(severity)
|
||||
|
||||
if(severity==2 && prob(70))
|
||||
..(severity-1)
|
||||
else
|
||||
PoolOrNew(/obj/effect/overlay/temp/emp, loc)
|
||||
var/list/mob/living/carbon/targets = new
|
||||
for(var/mob/living/carbon/C in view(12,src))
|
||||
if(C.stat==2)
|
||||
continue
|
||||
targets += C
|
||||
if(targets.len)
|
||||
if(prob(50))
|
||||
var/mob/toshoot = pick(targets)
|
||||
if(toshoot)
|
||||
targets-=toshoot
|
||||
if(prob(50) && emagged < 2)
|
||||
emagged = 2
|
||||
set_weapon()
|
||||
shootAt(toshoot)
|
||||
emagged = 0
|
||||
set_weapon()
|
||||
else
|
||||
shootAt(toshoot)
|
||||
else if(prob(50))
|
||||
if(targets.len)
|
||||
var/mob/toarrest = pick(targets)
|
||||
if(toarrest)
|
||||
target = toarrest
|
||||
mode = BOT_HUNT
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/bullet_act(obj/item/projectile/Proj)
|
||||
if(!disabled)
|
||||
var/lasertag_check = 0
|
||||
if((lasercolor == "b"))
|
||||
if(istype(Proj, /obj/item/projectile/beam/lasertag/redtag))
|
||||
lasertag_check++
|
||||
else if((lasercolor == "r"))
|
||||
if(istype(Proj, /obj/item/projectile/beam/lasertag/bluetag))
|
||||
lasertag_check++
|
||||
if(lasertag_check)
|
||||
icon_state = "[lasercolor]ed2090"
|
||||
disabled = 1
|
||||
target = null
|
||||
spawn(100)
|
||||
disabled = 0
|
||||
icon_state = "[lasercolor]ed2091"
|
||||
return 1
|
||||
else
|
||||
..(Proj)
|
||||
else
|
||||
..(Proj)
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/bluetag
|
||||
lasercolor = "b"
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/redtag
|
||||
lasercolor = "r"
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/UnarmedAttack(atom/A)
|
||||
if(!on)
|
||||
return
|
||||
if(iscarbon(A))
|
||||
var/mob/living/carbon/C = A
|
||||
if(!C.stunned || arrest_type)
|
||||
stun_attack(A)
|
||||
else if(C.canBeHandcuffed() && !C.handcuffed)
|
||||
cuff(A)
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/RangedAttack(atom/A)
|
||||
if(!on)
|
||||
return
|
||||
shootAt(A)
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/proc/stun_attack(mob/living/carbon/C)
|
||||
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
|
||||
icon_state = "[lasercolor]ed209-c"
|
||||
spawn(2)
|
||||
icon_state = "[lasercolor]ed209[on]"
|
||||
var/threat = 5
|
||||
if(istype(C, /mob/living/carbon/human))
|
||||
C.stuttering = 5
|
||||
C.Stun(5)
|
||||
C.Weaken(5)
|
||||
var/mob/living/carbon/human/H = C
|
||||
threat = H.assess_threat(src)
|
||||
else
|
||||
C.Weaken(5)
|
||||
C.stuttering = 5
|
||||
C.Stun(5)
|
||||
add_logs(src,C,"stunned")
|
||||
if(declare_arrests)
|
||||
var/area/location = get_area(src)
|
||||
speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag <b>[C]</b> in [location].", radio_channel)
|
||||
C.visible_message("<span class='danger'>[src] has stunned [C]!</span>",\
|
||||
"<span class='userdanger'>[src] has stunned you!</span>")
|
||||
|
||||
/mob/living/simple_animal/bot/ed209/proc/cuff(mob/living/carbon/C)
|
||||
mode = BOT_ARREST
|
||||
playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
|
||||
C.visible_message("<span class='danger'>[src] is trying to put zipties on [C]!</span>",\
|
||||
"<span class='userdanger'>[src] is trying to put zipties on you!</span>")
|
||||
|
||||
spawn(60)
|
||||
if( !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing.
|
||||
return
|
||||
if(!C.handcuffed)
|
||||
C.handcuffed = new /obj/item/weapon/restraints/handcuffs/cable/zipties/used(C)
|
||||
C.update_handcuffed()
|
||||
back_to_idle()
|
||||
@@ -0,0 +1,396 @@
|
||||
//Floorbot
|
||||
/mob/living/simple_animal/bot/floorbot
|
||||
name = "\improper Floorbot"
|
||||
desc = "A little floor repairing robot, he looks so excited!"
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "floorbot0"
|
||||
density = 0
|
||||
anchored = 0
|
||||
health = 25
|
||||
maxHealth = 25
|
||||
|
||||
radio_key = /obj/item/device/encryptionkey/headset_eng
|
||||
radio_channel = "Engineering"
|
||||
bot_type = FLOOR_BOT
|
||||
model = "Floorbot"
|
||||
bot_core = /obj/machinery/bot_core/floorbot
|
||||
window_id = "autofloor"
|
||||
window_name = "Automatic Station Floor Repairer v1.1"
|
||||
|
||||
var/process_type //Determines what to do when process_scan() recieves a target. See process_scan() for details.
|
||||
var/targetdirection
|
||||
var/replacetiles = 0
|
||||
var/placetiles = 0
|
||||
var/specialtiles = 0
|
||||
var/maxtiles = 100
|
||||
var/obj/item/stack/tile/tiletype
|
||||
var/fixfloors = 0
|
||||
var/autotile = 0
|
||||
var/max_targets = 50
|
||||
var/turf/target
|
||||
var/oldloc = null
|
||||
|
||||
#define HULL_BREACH 1
|
||||
#define LINE_SPACE_MODE 2
|
||||
#define FIX_TILE 3
|
||||
#define AUTO_TILE 4
|
||||
#define PLACE_TILE 5
|
||||
#define REPLACE_TILE 6
|
||||
#define TILE_EMAG 7
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/New()
|
||||
..()
|
||||
update_icon()
|
||||
var/datum/job/engineer/J = new/datum/job/engineer
|
||||
access_card.access += J.get_access()
|
||||
prev_access = access_card.access
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/Process_Spacemove(movement_dir = 0)
|
||||
return 1
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/turn_on()
|
||||
. = ..()
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/turn_off()
|
||||
..()
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/bot_reset()
|
||||
..()
|
||||
target = null
|
||||
oldloc = null
|
||||
ignore_list = list()
|
||||
anchored = 0
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/set_custom_texts()
|
||||
text_hack = "You corrupt [name]'s construction protocols."
|
||||
text_dehack = "You detect errors in [name] and reset his programming."
|
||||
text_dehack_fail = "[name] is not responding to reset commands!"
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/get_controls(mob/user)
|
||||
var/dat
|
||||
dat += hack(user)
|
||||
dat += showpai(user)
|
||||
dat += "<TT><B>Floor Repairer Controls v1.1</B></TT><BR><BR>"
|
||||
dat += "Status: <A href='?src=\ref[src];power=1'>[on ? "On" : "Off"]</A><BR>"
|
||||
dat += "Maintenance panel panel is [open ? "opened" : "closed"]<BR>"
|
||||
dat += "Special tiles: "
|
||||
if(specialtiles)
|
||||
dat += "<A href='?src=\ref[src];operation=eject'>Loaded \[[specialtiles]/[maxtiles]\]</a><BR>"
|
||||
else
|
||||
dat += "None Loaded<BR>"
|
||||
|
||||
dat += "Behaviour controls are [locked ? "locked" : "unlocked"]<BR>"
|
||||
if(!locked || issilicon(user) || IsAdminGhost(user))
|
||||
dat += "Add tiles to new hull plating: <A href='?src=\ref[src];operation=autotile'>[autotile ? "Yes" : "No"]</A><BR>"
|
||||
dat += "Place floor tiles: <A href='?src=\ref[src];operation=place'>[placetiles ? "Yes" : "No"]</A><BR>"
|
||||
dat += "Replace existing floor tiles with custom tiles: <A href='?src=\ref[src];operation=replace'>[replacetiles ? "Yes" : "No"]</A><BR>"
|
||||
dat += "Repair damaged tiles and platings: <A href='?src=\ref[src];operation=fix'>[fixfloors ? "Yes" : "No"]</A><BR>"
|
||||
dat += "Traction Magnets: <A href='?src=\ref[src];operation=anchor'>[anchored ? "Engaged" : "Disengaged"]</A><BR>"
|
||||
dat += "Patrol Station: <A href='?src=\ref[src];operation=patrol'>[auto_patrol ? "Yes" : "No"]</A><BR>"
|
||||
var/bmode
|
||||
if(targetdirection)
|
||||
bmode = dir2text(targetdirection)
|
||||
else
|
||||
bmode = "disabled"
|
||||
dat += "Line Mode : <A href='?src=\ref[src];operation=linemode'>[bmode]</A><BR>"
|
||||
|
||||
return dat
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/attackby(obj/item/W , mob/user, params)
|
||||
if(istype(W, /obj/item/stack/tile/plasteel))
|
||||
user << "<span class='notice'>The floorbot can produce normal tiles itself.</span>"
|
||||
return
|
||||
if(specialtiles && istype(W, /obj/item/stack/tile))
|
||||
var/obj/item/stack/tile/usedtile = W
|
||||
if(usedtile.type != tiletype)
|
||||
user << "<span class='warning'>Different custom tiles are already inside the floorbot.</span>"
|
||||
return
|
||||
if(istype(W, /obj/item/stack/tile))
|
||||
if(specialtiles >= maxtiles)
|
||||
return
|
||||
var/obj/item/stack/tile/tiles = W //used only to get the amount
|
||||
tiletype = W.type
|
||||
var/loaded = min(maxtiles-specialtiles, tiles.amount)
|
||||
tiles.use(loaded)
|
||||
specialtiles += loaded
|
||||
if(loaded > 0)
|
||||
user << "<span class='notice'>You load [loaded] tiles into the floorbot. It now contains [specialtiles] tiles.</span>"
|
||||
else
|
||||
user << "<span class='warning'>You need at least one floor tile to put into [src]!</span>"
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/emag_act(mob/user)
|
||||
..()
|
||||
if(emagged == 2)
|
||||
if(user)
|
||||
user << "<span class='danger'>[src] buzzes and beeps.</span>"
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
switch(href_list["operation"])
|
||||
if("replace")
|
||||
replacetiles = !replacetiles
|
||||
if("place")
|
||||
placetiles = !placetiles
|
||||
if("fix")
|
||||
fixfloors = !fixfloors
|
||||
if("autotile")
|
||||
autotile = !autotile
|
||||
if("anchor")
|
||||
anchored = !anchored
|
||||
if("eject")
|
||||
if(specialtiles && tiletype != null)
|
||||
empty_tiles()
|
||||
|
||||
if("linemode")
|
||||
var/setdir = input("Select construction direction:") as null|anything in list("north","east","south","west","disable")
|
||||
switch(setdir)
|
||||
if("north")
|
||||
targetdirection = 1
|
||||
if("south")
|
||||
targetdirection = 2
|
||||
if("east")
|
||||
targetdirection = 4
|
||||
if("west")
|
||||
targetdirection = 8
|
||||
if("disable")
|
||||
targetdirection = null
|
||||
update_controls()
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/proc/empty_tiles()
|
||||
var/turf/Tsec = get_turf(src)
|
||||
|
||||
while(specialtiles > initial(tiletype.max_amount))
|
||||
new tiletype(Tsec,initial(tiletype.max_amount))
|
||||
specialtiles -= initial(tiletype.max_amount)
|
||||
new tiletype(Tsec,specialtiles)
|
||||
specialtiles = 0
|
||||
tiletype = null
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/handle_automated_action()
|
||||
if(!..())
|
||||
return
|
||||
|
||||
if(mode == BOT_REPAIRING)
|
||||
return
|
||||
|
||||
if(prob(5))
|
||||
audible_message("[src] makes an excited booping beeping sound!")
|
||||
|
||||
//Normal scanning procedure. We have tiles loaded, are not emagged.
|
||||
if(!target && emagged < 2)
|
||||
if(targetdirection != null) //The bot is in line mode.
|
||||
var/turf/T = get_step(src, targetdirection)
|
||||
if(istype(T, /turf/open/space)) //Check for space
|
||||
target = T
|
||||
process_type = LINE_SPACE_MODE
|
||||
if(istype(T, /turf/open/floor)) //Check for floor
|
||||
target = T
|
||||
|
||||
if(!target)
|
||||
process_type = HULL_BREACH //Ensures the floorbot does not try to "fix" space areas or shuttle docking zones.
|
||||
target = scan(/turf/open/space)
|
||||
|
||||
if(!target && placetiles) //Finds a floor without a tile and gives it one.
|
||||
process_type = PLACE_TILE //The target must be the floor and not a tile. The floor must not already have a floortile.
|
||||
target = scan(/turf/open/floor)
|
||||
|
||||
if(!target && fixfloors) //Repairs damaged floors and tiles.
|
||||
process_type = FIX_TILE
|
||||
target = scan(/turf/open/floor)
|
||||
|
||||
if(!target && replacetiles && specialtiles > 0) //Replace a floor tile with custom tile
|
||||
process_type = REPLACE_TILE //The target must be a tile. The floor must already have a floortile.
|
||||
target = scan(/turf/open/floor)
|
||||
|
||||
if(!target && emagged == 2) //We are emagged! Time to rip up the floors!
|
||||
process_type = TILE_EMAG
|
||||
target = scan(/turf/open/floor)
|
||||
|
||||
|
||||
if(!target)
|
||||
|
||||
if(auto_patrol)
|
||||
if(mode == BOT_IDLE || mode == BOT_START_PATROL)
|
||||
start_patrol()
|
||||
|
||||
if(mode == BOT_PATROL)
|
||||
bot_patrol()
|
||||
|
||||
if(target)
|
||||
if(path.len == 0)
|
||||
if(!istype(target, /turf/))
|
||||
var/turf/TL = get_turf(target)
|
||||
path = get_path_to(src, TL, /turf/proc/Distance_cardinal, 0, 30, id=access_card,simulated_only = 0)
|
||||
else
|
||||
path = get_path_to(src, target, /turf/proc/Distance_cardinal, 0, 30, id=access_card,simulated_only = 0)
|
||||
|
||||
if(!bot_move(target))
|
||||
add_to_ignore(target)
|
||||
target = null
|
||||
mode = BOT_IDLE
|
||||
return
|
||||
else if( !bot_move(target) )
|
||||
target = null
|
||||
mode = BOT_IDLE
|
||||
return
|
||||
|
||||
if(loc == target || loc == target.loc)
|
||||
if(check_bot(target)) //Target is not defined at the parent
|
||||
shuffle = TRUE
|
||||
if(prob(50)) //50% chance to still try to repair so we dont end up with 2 floorbots failing to fix the last breach
|
||||
target = null
|
||||
path = list()
|
||||
return
|
||||
if(istype(target, /turf/) && emagged < 2)
|
||||
repair(target)
|
||||
else if(emagged == 2 && istype(target,/turf/open/floor))
|
||||
var/turf/open/floor/F = target
|
||||
anchored = 1
|
||||
mode = BOT_REPAIRING
|
||||
F.ReplaceWithLattice()
|
||||
audible_message("<span class='danger'>[src] makes an excited booping sound.</span>")
|
||||
spawn(5)
|
||||
anchored = 0
|
||||
mode = BOT_IDLE
|
||||
target = null
|
||||
path = list()
|
||||
return
|
||||
|
||||
oldloc = loc
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/proc/is_hull_breach(turf/t) //Ignore space tiles not considered part of a structure, also ignores shuttle docking areas.
|
||||
var/area/t_area = get_area(t)
|
||||
if(t_area && (t_area.name == "Space" || findtext(t_area.name, "huttle")))
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
|
||||
//Floorbots, having several functions, need sort out special conditions here.
|
||||
/mob/living/simple_animal/bot/floorbot/process_scan(scan_target)
|
||||
var/result
|
||||
var/turf/open/floor/F
|
||||
switch(process_type)
|
||||
if(HULL_BREACH) //The most common job, patching breaches in the station's hull.
|
||||
if(is_hull_breach(scan_target)) //Ensure that the targeted space turf is actually part of the station, and not random space.
|
||||
result = scan_target
|
||||
anchored = 1 //Prevent the floorbot being blown off-course while trying to reach a hull breach.
|
||||
if(LINE_SPACE_MODE) //Space turfs in our chosen direction are considered.
|
||||
if(get_dir(src, scan_target) == targetdirection)
|
||||
result = scan_target
|
||||
anchored = 1
|
||||
if(PLACE_TILE)
|
||||
F = scan_target
|
||||
if(istype(F, /turf/open/floor/plating)) //The floor must not already have a tile.
|
||||
result = F
|
||||
if(REPLACE_TILE)
|
||||
F = scan_target
|
||||
if(istype(F, /turf/open/floor) && !istype(F, /turf/open/floor/plating)) //The floor must already have a tile.
|
||||
result = F
|
||||
if(FIX_TILE) //Selects only damaged floors.
|
||||
F = scan_target
|
||||
if(istype(F) && (F.broken || F.burnt))
|
||||
result = F
|
||||
if(TILE_EMAG) //Emag mode! Rip up the floor and cause breaches to space!
|
||||
F = scan_target
|
||||
if(!istype(F, /turf/open/floor/plating))
|
||||
result = F
|
||||
else //If no special processing is needed, simply return the result.
|
||||
result = scan_target
|
||||
return result
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/proc/repair(turf/target_turf)
|
||||
|
||||
if(istype(target_turf, /turf/open/space/))
|
||||
//Must be a hull breach or in line mode to continue.
|
||||
if(!is_hull_breach(target_turf) && !targetdirection)
|
||||
target = null
|
||||
return
|
||||
else if(!istype(target_turf, /turf/open/floor))
|
||||
return
|
||||
if(istype(target_turf, /turf/open/space/)) //If we are fixing an area not part of pure space, it is
|
||||
anchored = 1
|
||||
icon_state = "floorbot-c"
|
||||
visible_message("<span class='notice'>[targetdirection ? "[src] begins installing a bridge plating." : "[src] begins to repair the hole."] </span>")
|
||||
mode = BOT_REPAIRING
|
||||
sleep(50)
|
||||
if(mode == BOT_REPAIRING && src.loc == target_turf)
|
||||
if(autotile) //Build the floor and include a tile.
|
||||
target_turf.ChangeTurf(/turf/open/floor/plasteel)
|
||||
else //Build a hull plating without a floor tile.
|
||||
target_turf.ChangeTurf(/turf/open/floor/plating)
|
||||
|
||||
else
|
||||
var/turf/open/floor/F = target_turf
|
||||
|
||||
if(F.type != initial(tiletype.turf_type) && (F.broken || F.burnt || istype(F, /turf/open/floor/plating)) || F.type == (initial(tiletype.turf_type) && (F.broken || F.burnt)))
|
||||
anchored = 1
|
||||
icon_state = "floorbot-c"
|
||||
mode = BOT_REPAIRING
|
||||
visible_message("<span class='notice'>[src] begins repairing the floor.</span>")
|
||||
sleep(50)
|
||||
if(mode == BOT_REPAIRING && F && src.loc == F)
|
||||
F.broken = 0
|
||||
F.burnt = 0
|
||||
F.ChangeTurf(/turf/open/floor/plasteel)
|
||||
|
||||
if(replacetiles && F.type != initial(tiletype.turf_type) && specialtiles && !istype(F, /turf/open/floor/plating))
|
||||
anchored = 1
|
||||
icon_state = "floorbot-c"
|
||||
mode = BOT_REPAIRING
|
||||
visible_message("<span class='notice'>[src] begins replacing the floor tiles.</span>")
|
||||
sleep(50)
|
||||
if(mode == BOT_REPAIRING && F && src.loc == F)
|
||||
F.broken = 0
|
||||
F.burnt = 0
|
||||
F.ChangeTurf(initial(tiletype.turf_type))
|
||||
specialtiles -= 1
|
||||
if(specialtiles == 0)
|
||||
speak("Requesting refill of custom floortiles to continue replacing.")
|
||||
mode = BOT_IDLE
|
||||
update_icon()
|
||||
anchored = 0
|
||||
target = null
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/update_icon()
|
||||
icon_state = "floorbot[on]"
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/explode()
|
||||
on = 0
|
||||
visible_message("<span class='boldannounce'>[src] blows apart!</span>")
|
||||
var/turf/Tsec = get_turf(src)
|
||||
|
||||
var/obj/item/weapon/storage/toolbox/mechanical/N = new /obj/item/weapon/storage/toolbox/mechanical(Tsec)
|
||||
N.contents = list()
|
||||
|
||||
new /obj/item/device/assembly/prox_sensor(Tsec)
|
||||
|
||||
if(specialtiles && tiletype != null)
|
||||
empty_tiles()
|
||||
|
||||
if(prob(50))
|
||||
new /obj/item/robot_parts/l_arm(Tsec)
|
||||
|
||||
var/obj/item/stack/tile/plasteel/T = new (Tsec)
|
||||
T.amount = 1
|
||||
|
||||
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
|
||||
s.set_up(3, 1, src)
|
||||
s.start()
|
||||
..()
|
||||
|
||||
/obj/machinery/bot_core/floorbot
|
||||
req_one_access = list(access_construction, access_robotics)
|
||||
|
||||
/mob/living/simple_animal/bot/floorbot/UnarmedAttack(atom/A)
|
||||
if(isturf(A))
|
||||
repair(A)
|
||||
else
|
||||
..()
|
||||
@@ -0,0 +1,526 @@
|
||||
//MEDBOT
|
||||
//MEDBOT PATHFINDING
|
||||
//MEDBOT ASSEMBLY
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/medbot
|
||||
name = "\improper Medibot"
|
||||
desc = "A little medical robot. He looks somewhat underwhelmed."
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "medibot0"
|
||||
density = 0
|
||||
anchored = 0
|
||||
health = 20
|
||||
maxHealth = 20
|
||||
pass_flags = PASSMOB
|
||||
|
||||
radio_key = /obj/item/device/encryptionkey/headset_med
|
||||
radio_channel = "Medical"
|
||||
|
||||
bot_type = MED_BOT
|
||||
model = "Medibot"
|
||||
bot_core_type = /obj/machinery/bot_core/medbot
|
||||
window_id = "automed"
|
||||
window_name = "Automatic Medical Unit v1.1"
|
||||
data_hud_type = DATA_HUD_MEDICAL_ADVANCED
|
||||
|
||||
var/obj/item/weapon/reagent_containers/glass/reagent_glass = null //Can be set to draw from this for reagents.
|
||||
var/skin = null //Set to "tox", "ointment" or "o2" for the other two firstaid kits.
|
||||
var/mob/living/carbon/patient = null
|
||||
var/mob/living/carbon/oldpatient = null
|
||||
var/oldloc = null
|
||||
var/last_found = 0
|
||||
var/last_newpatient_speak = 0 //Don't spam the "HEY I'M COMING" messages
|
||||
var/injection_amount = 15 //How much reagent do we inject at a time?
|
||||
var/heal_threshold = 10 //Start healing when they have this much damage in a category
|
||||
var/use_beaker = 0 //Use reagents in beaker instead of default treatment agents.
|
||||
var/declare_crit = 1 //If active, the bot will transmit a critical patient alert to MedHUD users.
|
||||
var/declare_cooldown = 0 //Prevents spam of critical patient alerts.
|
||||
var/stationary_mode = 0 //If enabled, the Medibot will not move automatically.
|
||||
//Setting which reagents to use to treat what by default. By id.
|
||||
var/treatment_brute = "bicaridine"
|
||||
var/treatment_oxy = "dexalin"
|
||||
var/treatment_fire = "kelotane"
|
||||
var/treatment_tox = "antitoxin"
|
||||
var/treatment_virus = "spaceacillin"
|
||||
var/treat_virus = 1 //If on, the bot will attempt to treat viral infections, curing them if possible.
|
||||
var/shut_up = 0 //self explanatory :)
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/mysterious
|
||||
name = "\improper Mysterious Medibot"
|
||||
desc = "International Medibot of mystery."
|
||||
skin = "bezerk"
|
||||
treatment_oxy = "tricordrazine"
|
||||
treatment_brute = "tricordrazine"
|
||||
treatment_fire = "tricordrazine"
|
||||
treatment_tox = "tricordrazine"
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/derelict
|
||||
name = "\improper Old Medibot"
|
||||
desc = "Looks like it hasn't been modified since the late 2080s."
|
||||
skin = "bezerk"
|
||||
heal_threshold = 0
|
||||
declare_crit = 0
|
||||
treatment_oxy = "pancuronium"
|
||||
treatment_brute = "pancuronium"
|
||||
treatment_fire = "sodium_thiopental"
|
||||
treatment_tox = "sodium_thiopental"
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/update_icon()
|
||||
if(!on)
|
||||
icon_state = "medibot0"
|
||||
return
|
||||
if(mode == BOT_HEALING)
|
||||
icon_state = "medibots[stationary_mode]"
|
||||
return
|
||||
else if(stationary_mode) //Bot has yellow light to indicate stationary mode.
|
||||
icon_state = "medibot2"
|
||||
else
|
||||
icon_state = "medibot1"
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/New()
|
||||
..()
|
||||
update_icon()
|
||||
|
||||
spawn(4)
|
||||
if(skin)
|
||||
add_overlay(image('icons/obj/aibots.dmi', "medskin_[skin]"))
|
||||
|
||||
var/datum/job/doctor/J = new/datum/job/doctor
|
||||
access_card.access += J.get_access()
|
||||
prev_access = access_card.access
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/bot_reset()
|
||||
..()
|
||||
patient = null
|
||||
oldpatient = null
|
||||
oldloc = null
|
||||
last_found = world.time
|
||||
declare_cooldown = 0
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/proc/soft_reset() //Allows the medibot to still actively perform its medical duties without being completely halted as a hard reset does.
|
||||
path = list()
|
||||
patient = null
|
||||
mode = BOT_IDLE
|
||||
last_found = world.time
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/set_custom_texts()
|
||||
|
||||
text_hack = "You corrupt [name]'s reagent processor circuits."
|
||||
text_dehack = "You reset [name]'s reagent processor circuits."
|
||||
text_dehack_fail = "[name] seems damaged and does not respond to reprogramming!"
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/attack_paw(mob/user)
|
||||
return attack_hand(user)
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/get_controls(mob/user)
|
||||
var/dat
|
||||
dat += hack(user)
|
||||
dat += showpai(user)
|
||||
dat += "<TT><B>Medical Unit Controls v1.1</B></TT><BR><BR>"
|
||||
dat += "Status: <A href='?src=\ref[src];power=1'>[on ? "On" : "Off"]</A><BR>"
|
||||
dat += "Maintenance panel panel is [open ? "opened" : "closed"]<BR>"
|
||||
dat += "Beaker: "
|
||||
if(reagent_glass)
|
||||
dat += "<A href='?src=\ref[src];eject=1'>Loaded \[[reagent_glass.reagents.total_volume]/[reagent_glass.reagents.maximum_volume]\]</a>"
|
||||
else
|
||||
dat += "None Loaded"
|
||||
dat += "<br>Behaviour controls are [locked ? "locked" : "unlocked"]<hr>"
|
||||
if(!locked || issilicon(user) || IsAdminGhost(user))
|
||||
dat += "<TT>Healing Threshold: "
|
||||
dat += "<a href='?src=\ref[src];adj_threshold=-10'>--</a> "
|
||||
dat += "<a href='?src=\ref[src];adj_threshold=-5'>-</a> "
|
||||
dat += "[heal_threshold] "
|
||||
dat += "<a href='?src=\ref[src];adj_threshold=5'>+</a> "
|
||||
dat += "<a href='?src=\ref[src];adj_threshold=10'>++</a>"
|
||||
dat += "</TT><br>"
|
||||
|
||||
dat += "<TT>Injection Level: "
|
||||
dat += "<a href='?src=\ref[src];adj_inject=-5'>-</a> "
|
||||
dat += "[injection_amount] "
|
||||
dat += "<a href='?src=\ref[src];adj_inject=5'>+</a> "
|
||||
dat += "</TT><br>"
|
||||
|
||||
dat += "Reagent Source: "
|
||||
dat += "<a href='?src=\ref[src];use_beaker=1'>[use_beaker ? "Loaded Beaker (When available)" : "Internal Synthesizer"]</a><br>"
|
||||
|
||||
dat += "Treat Viral Infections: <a href='?src=\ref[src];virus=1'>[treat_virus ? "Yes" : "No"]</a><br>"
|
||||
dat += "The speaker switch is [shut_up ? "off" : "on"]. <a href='?src=\ref[src];togglevoice=[1]'>Toggle</a><br>"
|
||||
dat += "Critical Patient Alerts: <a href='?src=\ref[src];critalerts=1'>[declare_crit ? "Yes" : "No"]</a><br>"
|
||||
dat += "Patrol Station: <a href='?src=\ref[src];operation=patrol'>[auto_patrol ? "Yes" : "No"]</a><br>"
|
||||
dat += "Stationary Mode: <a href='?src=\ref[src];stationary=1'>[stationary_mode ? "Yes" : "No"]</a><br>"
|
||||
|
||||
return dat
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(href_list["adj_threshold"])
|
||||
var/adjust_num = text2num(href_list["adj_threshold"])
|
||||
heal_threshold += adjust_num
|
||||
if(heal_threshold < 5)
|
||||
heal_threshold = 5
|
||||
if(heal_threshold > 75)
|
||||
heal_threshold = 75
|
||||
|
||||
else if(href_list["adj_inject"])
|
||||
var/adjust_num = text2num(href_list["adj_inject"])
|
||||
injection_amount += adjust_num
|
||||
if(injection_amount < 5)
|
||||
injection_amount = 5
|
||||
if(injection_amount > 15)
|
||||
injection_amount = 15
|
||||
|
||||
else if(href_list["use_beaker"])
|
||||
use_beaker = !use_beaker
|
||||
|
||||
else if(href_list["eject"] && (!isnull(reagent_glass)))
|
||||
reagent_glass.loc = get_turf(src)
|
||||
reagent_glass = null
|
||||
|
||||
else if(href_list["togglevoice"])
|
||||
shut_up = !shut_up
|
||||
|
||||
else if(href_list["critalerts"])
|
||||
declare_crit = !declare_crit
|
||||
|
||||
else if(href_list["stationary"])
|
||||
stationary_mode = !stationary_mode
|
||||
path = list()
|
||||
update_icon()
|
||||
|
||||
else if(href_list["virus"])
|
||||
treat_virus = !treat_virus
|
||||
|
||||
update_controls()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
|
||||
if(istype(W, /obj/item/weapon/reagent_containers/glass))
|
||||
. = 1 //no afterattack
|
||||
if(locked)
|
||||
user << "<span class='warning'>You cannot insert a beaker because the panel is locked!</span>"
|
||||
return
|
||||
if(!isnull(reagent_glass))
|
||||
user << "<span class='warning'>There is already a beaker loaded!</span>"
|
||||
return
|
||||
if(!user.drop_item())
|
||||
return
|
||||
|
||||
W.loc = src
|
||||
reagent_glass = W
|
||||
user << "<span class='notice'>You insert [W].</span>"
|
||||
show_controls(user)
|
||||
|
||||
else
|
||||
var/current_health = health
|
||||
..()
|
||||
if(health < current_health) //if medbot took some damage
|
||||
step_to(src, (get_step_away(src,user)))
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/emag_act(mob/user)
|
||||
..()
|
||||
if(emagged == 2)
|
||||
declare_crit = 0
|
||||
if(user)
|
||||
user << "<span class='notice'>You short out [src]'s reagent synthesis circuits.</span>"
|
||||
audible_message("<span class='danger'>[src] buzzes oddly!</span>")
|
||||
flick("medibot_spark", src)
|
||||
if(user)
|
||||
oldpatient = user
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/process_scan(mob/living/carbon/human/H)
|
||||
if(H.stat == 2)
|
||||
return
|
||||
|
||||
if((H == oldpatient) && (world.time < last_found + 200))
|
||||
return
|
||||
|
||||
if(assess_patient(H))
|
||||
last_found = world.time
|
||||
if((last_newpatient_speak + 300) < world.time) //Don't spam these messages!
|
||||
var/message = pick("Hey, [H.name]! Hold on, I'm coming.","Wait [H.name]! I want to help!","[H.name], you appear to be injured!")
|
||||
speak(message)
|
||||
last_newpatient_speak = world.time
|
||||
return H
|
||||
else
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/handle_automated_action()
|
||||
if(!..())
|
||||
return
|
||||
|
||||
if(mode == BOT_HEALING)
|
||||
return
|
||||
|
||||
if(stunned)
|
||||
icon_state = "medibota"
|
||||
stunned--
|
||||
|
||||
oldpatient = patient
|
||||
patient = null
|
||||
mode = BOT_IDLE
|
||||
|
||||
if(stunned <= 0)
|
||||
update_icon()
|
||||
stunned = 0
|
||||
return
|
||||
|
||||
if(frustration > 8)
|
||||
oldpatient = patient
|
||||
soft_reset()
|
||||
|
||||
if(!patient)
|
||||
if(!shut_up && prob(1))
|
||||
var/message = pick("Radar, put a mask on!","There's always a catch, and it's the best there is.","I knew it, I should've been a plastic surgeon.","What kind of medbay is this? Everyone's dropping like dead flies.","Delicious!")
|
||||
speak(message)
|
||||
var/scan_range = (stationary_mode ? 1 : DEFAULT_SCAN_RANGE) //If in stationary mode, scan range is limited to adjacent patients.
|
||||
patient = scan(/mob/living/carbon/human, oldpatient, scan_range)
|
||||
oldpatient = patient
|
||||
|
||||
if(patient && (get_dist(src,patient) <= 1)) //Patient is next to us, begin treatment!
|
||||
if(mode != BOT_HEALING)
|
||||
mode = BOT_HEALING
|
||||
update_icon()
|
||||
frustration = 0
|
||||
medicate_patient(patient)
|
||||
return
|
||||
|
||||
//Patient has moved away from us!
|
||||
else if(patient && path.len && (get_dist(patient,path[path.len]) > 2))
|
||||
path = list()
|
||||
mode = BOT_IDLE
|
||||
last_found = world.time
|
||||
|
||||
else if(stationary_mode && patient) //Since we cannot move in this mode, ignore the patient and wait for another.
|
||||
soft_reset()
|
||||
return
|
||||
|
||||
if(patient && path.len == 0 && (get_dist(src,patient) > 1))
|
||||
path = get_path_to(src, get_turf(patient), /turf/proc/Distance_cardinal, 0, 30,id=access_card)
|
||||
mode = BOT_MOVING
|
||||
if(!path.len) //try to get closer if you can't reach the patient directly
|
||||
path = get_path_to(src, get_turf(patient), /turf/proc/Distance_cardinal, 0, 30,1,id=access_card)
|
||||
if(!path.len) //Do not chase a patient we cannot reach.
|
||||
soft_reset()
|
||||
|
||||
if(path.len > 0 && patient)
|
||||
if(!bot_move(path[path.len]))
|
||||
oldpatient = patient
|
||||
soft_reset()
|
||||
return
|
||||
|
||||
if(path.len > 8 && patient)
|
||||
frustration++
|
||||
|
||||
if(auto_patrol && !stationary_mode && !patient)
|
||||
if(mode == BOT_IDLE || mode == BOT_START_PATROL)
|
||||
start_patrol()
|
||||
|
||||
if(mode == BOT_PATROL)
|
||||
bot_patrol()
|
||||
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/proc/assess_patient(mob/living/carbon/C)
|
||||
//Time to see if they need medical help!
|
||||
if(C.stat == 2)
|
||||
return 0 //welp too late for them!
|
||||
|
||||
if(C.suiciding)
|
||||
return 0 //Kevorkian school of robotic medical assistants.
|
||||
|
||||
if(emagged == 2) //Everyone needs our medicine. (Our medicine is toxins)
|
||||
return 1
|
||||
|
||||
if(declare_crit && C.health <= 0) //Critical condition! Call for help!
|
||||
declare(C)
|
||||
|
||||
//If they're injured, we're using a beaker, and don't have one of our WONDERCHEMS.
|
||||
if((reagent_glass) && (use_beaker) && ((C.getBruteLoss() >= heal_threshold) || (C.getToxLoss() >= heal_threshold) || (C.getToxLoss() >= heal_threshold) || (C.getOxyLoss() >= (heal_threshold + 15))))
|
||||
for(var/datum/reagent/R in reagent_glass.reagents.reagent_list)
|
||||
if(!C.reagents.has_reagent(R.id))
|
||||
return 1
|
||||
|
||||
//They're injured enough for it!
|
||||
if((C.getBruteLoss() >= heal_threshold) && (!C.reagents.has_reagent(treatment_brute)))
|
||||
return 1 //If they're already medicated don't bother!
|
||||
|
||||
if((C.getOxyLoss() >= (15 + heal_threshold)) && (!C.reagents.has_reagent(treatment_oxy)))
|
||||
return 1
|
||||
|
||||
if((C.getFireLoss() >= heal_threshold) && (!C.reagents.has_reagent(treatment_fire)))
|
||||
return 1
|
||||
|
||||
if((C.getToxLoss() >= heal_threshold) && (!C.reagents.has_reagent(treatment_tox)))
|
||||
return 1
|
||||
|
||||
if(treat_virus)
|
||||
for(var/datum/disease/D in C.viruses)
|
||||
//the medibot can't detect viruses that are undetectable to Health Analyzers or Pandemic machines.
|
||||
if(D.visibility_flags & HIDDEN_SCANNER || D.visibility_flags & HIDDEN_PANDEMIC)
|
||||
return 0
|
||||
if(D.severity == NONTHREAT) // medibot doesn't try to heal truly harmless viruses
|
||||
return 0
|
||||
if((D.stage > 1) || (D.spread_flags & AIRBORNE)) // medibot can't detect a virus in its initial stage unless it spreads airborne.
|
||||
|
||||
if(!C.reagents.has_reagent(treatment_virus))
|
||||
return 1 //STOP DISEASE FOREVER
|
||||
|
||||
return 0
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/UnarmedAttack(atom/A)
|
||||
if(iscarbon(A))
|
||||
var/mob/living/carbon/C = A
|
||||
patient = C
|
||||
mode = BOT_HEALING
|
||||
update_icon()
|
||||
medicate_patient(C)
|
||||
update_icon()
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/examinate(atom/A as mob|obj|turf in view())
|
||||
..()
|
||||
if(!is_blind(src))
|
||||
chemscan(src, A)
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/proc/medicate_patient(mob/living/carbon/C)
|
||||
if(!on)
|
||||
return
|
||||
|
||||
if(!istype(C))
|
||||
oldpatient = patient
|
||||
soft_reset()
|
||||
return
|
||||
|
||||
if(C.stat == 2)
|
||||
var/death_message = pick("No! NO!","Live, damnit! LIVE!","I...I've never lost a patient before. Not today, I mean.")
|
||||
speak(death_message)
|
||||
oldpatient = patient
|
||||
soft_reset()
|
||||
return
|
||||
|
||||
var/reagent_id = null
|
||||
|
||||
if(emagged == 2) //Emagged! Time to poison everybody.
|
||||
reagent_id = "toxin"
|
||||
|
||||
else
|
||||
if(treat_virus)
|
||||
var/virus = 0
|
||||
for(var/datum/disease/D in C.viruses)
|
||||
//detectable virus
|
||||
if((!(D.visibility_flags & HIDDEN_SCANNER)) || (!(D.visibility_flags & HIDDEN_PANDEMIC)))
|
||||
if(D.severity != NONTHREAT) //virus is harmful
|
||||
if((D.stage > 1) || (D.spread_flags & AIRBORNE))
|
||||
virus = 1
|
||||
|
||||
if(!reagent_id && (virus))
|
||||
if(!C.reagents.has_reagent(treatment_virus))
|
||||
reagent_id = treatment_virus
|
||||
|
||||
if(!reagent_id && (C.getBruteLoss() >= heal_threshold))
|
||||
if(!C.reagents.has_reagent(treatment_brute))
|
||||
reagent_id = treatment_brute
|
||||
|
||||
if(!reagent_id && (C.getOxyLoss() >= (15 + heal_threshold)))
|
||||
if(!C.reagents.has_reagent(treatment_oxy))
|
||||
reagent_id = treatment_oxy
|
||||
|
||||
if(!reagent_id && (C.getFireLoss() >= heal_threshold))
|
||||
if(!C.reagents.has_reagent(treatment_fire))
|
||||
reagent_id = treatment_fire
|
||||
|
||||
if(!reagent_id && (C.getToxLoss() >= heal_threshold))
|
||||
if(!C.reagents.has_reagent(treatment_tox))
|
||||
reagent_id = treatment_tox
|
||||
|
||||
//If the patient is injured but doesn't have our special reagent in them then we should give it to them first
|
||||
if(reagent_id && use_beaker && reagent_glass && reagent_glass.reagents.total_volume)
|
||||
for(var/datum/reagent/R in reagent_glass.reagents.reagent_list)
|
||||
if(!C.reagents.has_reagent(R.id))
|
||||
reagent_id = "internal_beaker"
|
||||
break
|
||||
|
||||
if(!reagent_id) //If they don't need any of that they're probably cured!
|
||||
var/message = pick("All patched up!","An apple a day keeps me away.","Feel better soon!")
|
||||
speak(message)
|
||||
bot_reset()
|
||||
return
|
||||
else
|
||||
if(!emagged && check_overdose(patient,reagent_id,injection_amount))
|
||||
soft_reset()
|
||||
return
|
||||
C.visible_message("<span class='danger'>[src] is trying to inject [patient]!</span>", \
|
||||
"<span class='userdanger'>[src] is trying to inject you!</span>")
|
||||
|
||||
spawn(30)//replace with do mob
|
||||
if((get_dist(src, patient) <= 1) && (on) && assess_patient(patient))
|
||||
if(reagent_id == "internal_beaker")
|
||||
if(use_beaker && reagent_glass && reagent_glass.reagents.total_volume)
|
||||
var/fraction = min(injection_amount/reagent_glass.reagents.total_volume, 1)
|
||||
reagent_glass.reagents.reaction(patient, INJECT, fraction)
|
||||
reagent_glass.reagents.trans_to(patient,injection_amount) //Inject from beaker instead.
|
||||
else
|
||||
patient.reagents.add_reagent(reagent_id,injection_amount)
|
||||
C.visible_message("<span class='danger'>[src] injects [patient] with its syringe!</span>", \
|
||||
"<span class='userdanger'>[src] injects you with its syringe!</span>")
|
||||
else
|
||||
visible_message("[src] retracts its syringe.")
|
||||
update_icon()
|
||||
soft_reset()
|
||||
return
|
||||
|
||||
reagent_id = null
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/proc/check_overdose(mob/living/carbon/patient,reagent_id,injection_amount)
|
||||
var/datum/reagent/R = chemical_reagents_list[reagent_id]
|
||||
var/current_volume = patient.reagents.get_reagent_amount(reagent_id)
|
||||
if(current_volume + injection_amount > R.overdose_threshold)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/bullet_act(obj/item/projectile/Proj)
|
||||
if(Proj.flag == "taser")
|
||||
stunned = min(stunned+10,20)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/explode()
|
||||
on = 0
|
||||
visible_message("<span class='boldannounce'>[src] blows apart!</span>")
|
||||
var/turf/Tsec = get_turf(src)
|
||||
|
||||
new /obj/item/weapon/storage/firstaid(Tsec)
|
||||
|
||||
new /obj/item/device/assembly/prox_sensor(Tsec)
|
||||
|
||||
new /obj/item/device/healthanalyzer(Tsec)
|
||||
|
||||
if(reagent_glass)
|
||||
reagent_glass.loc = Tsec
|
||||
reagent_glass = null
|
||||
|
||||
if(prob(50))
|
||||
new /obj/item/robot_parts/l_arm(Tsec)
|
||||
|
||||
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
|
||||
s.set_up(3, 1, src)
|
||||
s.start()
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/medbot/proc/declare(crit_patient)
|
||||
if(declare_cooldown)
|
||||
return
|
||||
var/area/location = get_area(src)
|
||||
speak("Medical emergency! [crit_patient ? "<b>[crit_patient]</b>" : "A patient"] is in critical condition at [location]!",radio_channel)
|
||||
declare_cooldown = 1
|
||||
spawn(200) //Twenty seconds
|
||||
declare_cooldown = 0
|
||||
|
||||
/obj/machinery/bot_core/medbot
|
||||
req_one_access =list(access_medical, access_robotics)
|
||||
@@ -0,0 +1,751 @@
|
||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
||||
|
||||
// Mulebot - carries crates around for Quartermaster
|
||||
// Navigates via floor navbeacons
|
||||
// Remote Controlled from QM's PDA
|
||||
|
||||
var/global/mulebot_count = 0
|
||||
|
||||
#define SIGH 0
|
||||
#define ANNOYED 1
|
||||
#define DELIGHT 2
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot
|
||||
name = "\improper MULEbot"
|
||||
desc = "A Multiple Utility Load Effector bot."
|
||||
icon_state = "mulebot0"
|
||||
density = 1
|
||||
anchored = 1
|
||||
animate_movement=1
|
||||
health = 50
|
||||
maxHealth = 50
|
||||
damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
|
||||
a_intent = "harm" //No swapping
|
||||
buckle_lying = 0
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
|
||||
radio_key = /obj/item/device/encryptionkey/headset_cargo
|
||||
radio_channel = "Supply"
|
||||
|
||||
bot_type = MULE_BOT
|
||||
model = "MULE"
|
||||
bot_core_type = /obj/machinery/bot_core/mulebot
|
||||
|
||||
suffix = ""
|
||||
|
||||
var/atom/movable/load = null
|
||||
var/mob/living/passenger = null
|
||||
var/turf/target // this is turf to navigate to (location of beacon)
|
||||
var/loaddir = 0 // this the direction to unload onto/load from
|
||||
var/home_destination = "" // tag of home beacon
|
||||
|
||||
var/reached_target = 1 //true if already reached the target
|
||||
|
||||
var/auto_return = 1 // true if auto return to home beacon after unload
|
||||
var/auto_pickup = 1 // true if auto-pickup at beacon
|
||||
var/report_delivery = 1 // true if bot will announce an arrival to a location.
|
||||
|
||||
var/obj/item/weapon/stock_parts/cell/cell
|
||||
var/bloodiness = 0
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/New()
|
||||
..()
|
||||
wires = new /datum/wires/mulebot(src)
|
||||
var/datum/job/cargo_tech/J = new/datum/job/cargo_tech
|
||||
access_card.access = J.get_access()
|
||||
prev_access = access_card.access
|
||||
cell = new(src)
|
||||
cell.charge = 2000
|
||||
cell.maxcharge = 2000
|
||||
|
||||
spawn(10) // must wait for map loading to finish
|
||||
mulebot_count += 1
|
||||
if(!suffix)
|
||||
set_suffix("#[mulebot_count]")
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/Destroy()
|
||||
unload(0)
|
||||
qdel(wires)
|
||||
wires = null
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/proc/set_suffix(suffix)
|
||||
src.suffix = suffix
|
||||
if(paicard)
|
||||
bot_name = "\improper MULEbot ([suffix])"
|
||||
else
|
||||
name = "\improper MULEbot ([suffix])"
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/bot_reset()
|
||||
..()
|
||||
reached_target = 0
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/weapon/screwdriver))
|
||||
..()
|
||||
if(open)
|
||||
on = FALSE
|
||||
else if(istype(I,/obj/item/weapon/stock_parts/cell) && open && !cell)
|
||||
if(!user.drop_item())
|
||||
return
|
||||
var/obj/item/weapon/stock_parts/cell/C = I
|
||||
C.loc = src
|
||||
cell = C
|
||||
visible_message("[user] inserts a cell into [src].",
|
||||
"<span class='notice'>You insert the new cell into [src].</span>")
|
||||
else if(istype(I, /obj/item/weapon/crowbar) && open && cell)
|
||||
cell.add_fingerprint(usr)
|
||||
cell.loc = loc
|
||||
cell = null
|
||||
visible_message("[user] crowbars out the power cell from [src].",
|
||||
"<span class='notice'>You pry the powercell out of [src].</span>")
|
||||
else if(is_wire_tool(I) && open)
|
||||
return attack_hand(user)
|
||||
else if(load && ismob(load)) // chance to knock off rider
|
||||
if(prob(1 + I.force * 2))
|
||||
unload(0)
|
||||
user.visible_message("<span class='danger'>[user] knocks [load] off [src] with \the [I]!</span>",
|
||||
"<span class='danger'>You knock [load] off [src] with \the [I]!</span>")
|
||||
else
|
||||
user << "<span class='warning'>You hit [src] with \the [I] but to no effect!</span>"
|
||||
..()
|
||||
else
|
||||
..()
|
||||
update_icon()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/emag_act(mob/user)
|
||||
if(emagged < 1)
|
||||
emagged = 1
|
||||
if(!open)
|
||||
locked = !locked
|
||||
user << "<span class='notice'>You [locked ? "lock" : "unlock"] the [src]'s controls!</span>"
|
||||
flick("mulebot-emagged", src)
|
||||
playsound(loc, 'sound/effects/sparks1.ogg', 100, 0)
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/update_icon()
|
||||
if(open)
|
||||
icon_state="mulebot-hatch"
|
||||
else
|
||||
icon_state = "mulebot[wires.is_cut(WIRE_AVOIDANCE)]"
|
||||
cut_overlays()
|
||||
if(load && !ismob(load))//buckling handles the mob offsets
|
||||
load.pixel_y = initial(load.pixel_y) + 9
|
||||
if(load.layer < layer)
|
||||
load.layer = layer + 0.01
|
||||
add_overlay(load)
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/ex_act(severity)
|
||||
unload(0)
|
||||
switch(severity)
|
||||
if(1)
|
||||
qdel(src)
|
||||
if(2)
|
||||
for(var/i = 1; i < 3; i++)
|
||||
wires.cut_random()
|
||||
if(3)
|
||||
wires.cut_random()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/bullet_act(obj/item/projectile/Proj)
|
||||
if(..())
|
||||
if(prob(50) && !isnull(load))
|
||||
unload(0)
|
||||
if(prob(25))
|
||||
visible_message("<span class='danger'>Something shorts out inside [src]!</span>")
|
||||
wires.cut_random()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/interact(mob/user)
|
||||
if(open && !istype(user, /mob/living/silicon/ai))
|
||||
wires.interact(user)
|
||||
else
|
||||
if(wires.is_cut(WIRE_RX) && istype(user, /mob/living/silicon/ai))
|
||||
return
|
||||
ui_interact(user)
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
|
||||
datum/tgui/master_ui = null, datum/ui_state/state = default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "mulebot", name, 600, 375, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["on"] = on
|
||||
data["locked"] = locked
|
||||
data["siliconUser"] = user.has_unlimited_silicon_privilege
|
||||
data["mode"] = mode ? mode_name[mode] : "Ready"
|
||||
data["modeStatus"] = ""
|
||||
switch(mode)
|
||||
if(BOT_IDLE, BOT_DELIVER, BOT_GO_HOME)
|
||||
data["modeStatus"] = "good"
|
||||
if(BOT_BLOCKED, BOT_NAV, BOT_WAIT_FOR_NAV)
|
||||
data["modeStatus"] = "average"
|
||||
if(BOT_NO_ROUTE)
|
||||
data["modeStatus"] = "bad"
|
||||
else
|
||||
data["load"] = load ? load.name : null
|
||||
data["destination"] = destination ? destination : null
|
||||
data["cell"] = cell ? TRUE : FALSE
|
||||
data["cellPercent"] = cell ? cell.percent() : null
|
||||
data["autoReturn"] = auto_return
|
||||
data["autoPickup"] = auto_pickup
|
||||
data["reportDelivery"] = report_delivery
|
||||
data["haspai"] = paicard ? TRUE : FALSE
|
||||
return data
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/ui_act(action, params)
|
||||
if(..() || (locked && !usr.has_unlimited_silicon_privilege))
|
||||
return
|
||||
switch(action)
|
||||
if("lock")
|
||||
if(usr.has_unlimited_silicon_privilege)
|
||||
locked = !locked
|
||||
. = TRUE
|
||||
if("power")
|
||||
if(on)
|
||||
turn_off()
|
||||
else if(cell && !open)
|
||||
if(!turn_on())
|
||||
usr << "<span class='warning'>You can't switch on [src]!</span>"
|
||||
return
|
||||
. = TRUE
|
||||
else
|
||||
bot_control(action, usr) // Kill this later.
|
||||
. = TRUE
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/bot_control(command, mob/user, pda = 0)
|
||||
if(pda && wires.is_cut(WIRE_RX)) // MULE wireless is controlled by wires.
|
||||
return
|
||||
|
||||
switch(command)
|
||||
if("stop")
|
||||
if(mode >= BOT_DELIVER)
|
||||
bot_reset()
|
||||
if("go")
|
||||
if(mode == BOT_IDLE)
|
||||
start()
|
||||
if("home")
|
||||
if(mode == BOT_IDLE || mode == BOT_DELIVER)
|
||||
start_home()
|
||||
if("destination")
|
||||
var/new_dest = input(user, "Enter Destination:", name, destination) as null|anything in deliverybeacontags
|
||||
if(new_dest)
|
||||
set_destination(new_dest)
|
||||
if("setid")
|
||||
var/new_id = stripped_input(user, "Enter ID:", name, suffix, MAX_NAME_LEN)
|
||||
if(new_id)
|
||||
set_suffix(new_id)
|
||||
if("sethome")
|
||||
var/new_home = input(user, "Enter Home:", name, home_destination) as null|anything in deliverybeacontags
|
||||
if(new_home)
|
||||
home_destination = new_home
|
||||
if("unload")
|
||||
if(load && mode != BOT_HUNT)
|
||||
if(loc == target)
|
||||
unload(loaddir)
|
||||
else
|
||||
unload(0)
|
||||
if("autoret")
|
||||
auto_return = !auto_return
|
||||
if("autopick")
|
||||
auto_pickup = !auto_pickup
|
||||
if("report")
|
||||
report_delivery = !report_delivery
|
||||
if("ejectpai")
|
||||
ejectpairemote(user)
|
||||
|
||||
// TODO: remove this; PDAs currently depend on it
|
||||
/mob/living/simple_animal/bot/mulebot/get_controls(mob/user)
|
||||
var/ai = issilicon(user)
|
||||
var/dat
|
||||
dat += "<h3>Multiple Utility Load Effector Mk. V</h3>"
|
||||
dat += "<b>ID:</b> [suffix]<BR>"
|
||||
dat += "<b>Power:</b> [on ? "On" : "Off"]<BR>"
|
||||
dat += "<h3>Status</h3>"
|
||||
dat += "<div class='statusDisplay'>"
|
||||
switch(mode)
|
||||
if(BOT_IDLE)
|
||||
dat += "<span class='good'>Ready</span>"
|
||||
if(BOT_DELIVER)
|
||||
dat += "<span class='good'>[mode_name[BOT_DELIVER]]</span>"
|
||||
if(BOT_GO_HOME)
|
||||
dat += "<span class='good'>[mode_name[BOT_GO_HOME]]</span>"
|
||||
if(BOT_BLOCKED)
|
||||
dat += "<span class='average'>[mode_name[BOT_BLOCKED]]</span>"
|
||||
if(BOT_NAV,BOT_WAIT_FOR_NAV)
|
||||
dat += "<span class='average'>[mode_name[BOT_NAV]]</span>"
|
||||
if(BOT_NO_ROUTE)
|
||||
dat += "<span class='bad'>[mode_name[BOT_NO_ROUTE]]</span>"
|
||||
dat += "</div>"
|
||||
|
||||
dat += "<b>Current Load:</b> [load ? load.name : "<i>none</i>"]<BR>"
|
||||
dat += "<b>Destination:</b> [!destination ? "<i>none</i>" : destination]<BR>"
|
||||
dat += "<b>Power level:</b> [cell ? cell.percent() : 0]%"
|
||||
|
||||
if(locked && !ai && !IsAdminGhost(user))
|
||||
dat += " <br /><div class='notice'>Controls are locked</div><A href='byond://?src=\ref[src];op=unlock'>Unlock Controls</A>"
|
||||
else
|
||||
dat += " <br /><div class='notice'>Controls are unlocked</div><A href='byond://?src=\ref[src];op=lock'>Lock Controls</A><BR><BR>"
|
||||
|
||||
dat += "<A href='byond://?src=\ref[src];op=power'>Toggle Power</A><BR>"
|
||||
dat += "<A href='byond://?src=\ref[src];op=stop'>Stop</A><BR>"
|
||||
dat += "<A href='byond://?src=\ref[src];op=go'>Proceed</A><BR>"
|
||||
dat += "<A href='byond://?src=\ref[src];op=home'>Return to Home</A><BR>"
|
||||
dat += "<A href='byond://?src=\ref[src];op=destination'>Set Destination</A><BR>"
|
||||
dat += "<A href='byond://?src=\ref[src];op=setid'>Set Bot ID</A><BR>"
|
||||
dat += "<A href='byond://?src=\ref[src];op=sethome'>Set Home</A><BR>"
|
||||
dat += "<A href='byond://?src=\ref[src];op=autoret'>Toggle Auto Return Home</A> ([auto_return ? "On":"Off"])<BR>"
|
||||
dat += "<A href='byond://?src=\ref[src];op=autopick'>Toggle Auto Pickup Crate</A> ([auto_pickup ? "On":"Off"])<BR>"
|
||||
dat += "<A href='byond://?src=\ref[src];op=report'>Toggle Delivery Reporting</A> ([report_delivery ? "On" : "Off"])<BR>"
|
||||
if(load)
|
||||
dat += "<A href='byond://?src=\ref[src];op=unload'>Unload Now</A><BR>"
|
||||
dat += "<div class='notice'>The maintenance hatch is closed.</div>"
|
||||
|
||||
return dat
|
||||
|
||||
|
||||
// returns true if the bot has power
|
||||
/mob/living/simple_animal/bot/mulebot/proc/has_power()
|
||||
return !open && cell && cell.charge > 0 && (!wires.is_cut(WIRE_POWER1) && !wires.is_cut(WIRE_POWER2))
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/proc/buzz(type)
|
||||
switch(type)
|
||||
if(SIGH)
|
||||
audible_message("[src] makes a sighing buzz.", "<span class='italics'>You hear an electronic buzzing sound.</span>")
|
||||
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
||||
if(ANNOYED)
|
||||
audible_message("[src] makes an annoyed buzzing sound.", "<span class='italics'>You hear an electronic buzzing sound.</span>")
|
||||
playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0)
|
||||
if(DELIGHT)
|
||||
audible_message("[src] makes a delighted ping!", "<span class='italics'>You hear a ping.</span>")
|
||||
playsound(loc, 'sound/machines/ping.ogg', 50, 0)
|
||||
|
||||
|
||||
// mousedrop a crate to load the bot
|
||||
// can load anything if hacked
|
||||
/mob/living/simple_animal/bot/mulebot/MouseDrop_T(atom/movable/AM, mob/user)
|
||||
|
||||
if(user.incapacitated() || user.lying)
|
||||
return
|
||||
|
||||
if(!istype(AM))
|
||||
return
|
||||
|
||||
load(AM)
|
||||
|
||||
// called to load a crate
|
||||
/mob/living/simple_animal/bot/mulebot/proc/load(atom/movable/AM)
|
||||
if(load || AM.anchored)
|
||||
return
|
||||
|
||||
if(!isturf(AM.loc)) //To prevent the loading from stuff from someone's inventory or screen icons.
|
||||
return
|
||||
|
||||
var/obj/structure/closet/crate/CRATE
|
||||
if(istype(AM,/obj/structure/closet/crate))
|
||||
CRATE = AM
|
||||
else
|
||||
if(!wires.is_cut(WIRE_LOADCHECK))
|
||||
buzz(SIGH)
|
||||
return // if not hacked, only allow crates to be loaded
|
||||
|
||||
if(CRATE) // if it's a crate, close before loading
|
||||
CRATE.close()
|
||||
|
||||
if(isobj(AM))
|
||||
var/obj/O = AM
|
||||
if(O.has_buckled_mobs() || (locate(/mob) in AM)) //can't load non crates objects with mobs buckled to it or inside it.
|
||||
buzz(SIGH)
|
||||
return
|
||||
|
||||
if(isliving(AM))
|
||||
if(!load_mob(AM))
|
||||
return
|
||||
else
|
||||
AM.loc = src
|
||||
|
||||
load = AM
|
||||
mode = BOT_IDLE
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/proc/load_mob(mob/living/M)
|
||||
can_buckle = TRUE
|
||||
if(buckle_mob(M))
|
||||
passenger = M
|
||||
load = M
|
||||
can_buckle = FALSE
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/post_buckle_mob(mob/living/M)
|
||||
if(M in buckled_mobs) //post buckling
|
||||
M.pixel_y = initial(M.pixel_y) + 9
|
||||
if(M.layer < layer)
|
||||
M.layer = layer + 0.01
|
||||
|
||||
else //post unbuckling
|
||||
load = null
|
||||
M.layer = initial(M.layer)
|
||||
M.pixel_y = initial(M.pixel_y)
|
||||
|
||||
// called to unload the bot
|
||||
// argument is optional direction to unload
|
||||
// if zero, unload at bot's location
|
||||
/mob/living/simple_animal/bot/mulebot/proc/unload(dirn)
|
||||
if(!load)
|
||||
return
|
||||
|
||||
mode = BOT_IDLE
|
||||
|
||||
cut_overlays()
|
||||
|
||||
unbuckle_all_mobs()
|
||||
|
||||
if(load)
|
||||
load.loc = loc
|
||||
load.pixel_y = initial(load.pixel_y)
|
||||
load.layer = initial(load.layer)
|
||||
if(dirn)
|
||||
var/turf/T = loc
|
||||
var/turf/newT = get_step(T,dirn)
|
||||
if(load.CanPass(load,newT)) //Can't get off onto anything that wouldn't let you pass normally
|
||||
step(load, dirn)
|
||||
load = null
|
||||
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/call_bot()
|
||||
..()
|
||||
var/area/dest_area
|
||||
if(path && path.len)
|
||||
target = ai_waypoint //Target is the end point of the path, the waypoint set by the AI.
|
||||
dest_area = get_area(target)
|
||||
destination = format_text(dest_area.name)
|
||||
pathset = 1 //Indicates the AI's custom path is initialized.
|
||||
start()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/handle_automated_action()
|
||||
if(!has_power())
|
||||
on = 0
|
||||
return
|
||||
if(on)
|
||||
var/speed = (wires.is_cut(WIRE_MOTOR1) ? 0 : 1) + (wires.is_cut(WIRE_MOTOR2) ? 0 : 2)
|
||||
//world << "speed: [speed]"
|
||||
var/num_steps = 0
|
||||
switch(speed)
|
||||
if(0)
|
||||
// do nothing
|
||||
if(1)
|
||||
num_steps = 10
|
||||
if(2)
|
||||
num_steps = 5
|
||||
if(3)
|
||||
num_steps = 3
|
||||
|
||||
if(num_steps)
|
||||
process_bot()
|
||||
num_steps--
|
||||
if(mode != BOT_IDLE)
|
||||
spawn(0)
|
||||
for(var/i=num_steps,i>0,i--)
|
||||
sleep(2)
|
||||
process_bot()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/proc/process_bot()
|
||||
if(!on || client)
|
||||
return
|
||||
update_icon()
|
||||
|
||||
switch(mode)
|
||||
if(BOT_IDLE) // idle
|
||||
return
|
||||
|
||||
if(BOT_DELIVER, BOT_GO_HOME, BOT_BLOCKED) // navigating to deliver,home, or blocked
|
||||
if(loc == target) // reached target
|
||||
at_target()
|
||||
return
|
||||
|
||||
else if(path.len > 0 && target) // valid path
|
||||
var/turf/next = path[1]
|
||||
reached_target = 0
|
||||
if(next == loc)
|
||||
path -= next
|
||||
return
|
||||
if(istype( next, /turf))
|
||||
//world << "at ([x],[y]) moving to ([next.x],[next.y])"
|
||||
|
||||
if(bloodiness)
|
||||
var/obj/effect/decal/cleanable/blood/tracks/B = new(loc)
|
||||
B.blood_DNA |= blood_DNA.Copy()
|
||||
var/newdir = get_dir(next, loc)
|
||||
if(newdir == dir)
|
||||
B.setDir(newdir)
|
||||
else
|
||||
newdir = newdir | dir
|
||||
if(newdir == 3)
|
||||
newdir = 1
|
||||
else if(newdir == 12)
|
||||
newdir = 4
|
||||
B.setDir(newdir)
|
||||
bloodiness--
|
||||
|
||||
|
||||
var/oldloc = loc
|
||||
var/moved = step_towards(src, next) // attempt to move
|
||||
if(cell) cell.use(1)
|
||||
if(moved && oldloc!=loc) // successful move
|
||||
//world << "Successful move."
|
||||
blockcount = 0
|
||||
path -= loc
|
||||
|
||||
if(destination == home_destination)
|
||||
mode = BOT_GO_HOME
|
||||
else
|
||||
mode = BOT_DELIVER
|
||||
|
||||
else // failed to move
|
||||
|
||||
//world << "Unable to move."
|
||||
blockcount++
|
||||
mode = BOT_BLOCKED
|
||||
if(blockcount == 3)
|
||||
buzz(ANNOYED)
|
||||
|
||||
if(blockcount > 10) // attempt 10 times before recomputing
|
||||
// find new path excluding blocked turf
|
||||
buzz(SIGH)
|
||||
mode = BOT_WAIT_FOR_NAV
|
||||
blockcount = 0
|
||||
spawn(20)
|
||||
calc_path(avoid=next)
|
||||
if(path.len > 0)
|
||||
buzz(DELIGHT)
|
||||
mode = BOT_BLOCKED
|
||||
return
|
||||
return
|
||||
else
|
||||
buzz(ANNOYED)
|
||||
//world << "Bad turf."
|
||||
mode = BOT_NAV
|
||||
return
|
||||
else
|
||||
//world << "No path."
|
||||
mode = BOT_NAV
|
||||
return
|
||||
|
||||
if(BOT_NAV) // calculate new path
|
||||
//world << "Calc new path."
|
||||
mode = BOT_WAIT_FOR_NAV
|
||||
spawn(0)
|
||||
calc_path()
|
||||
|
||||
if(path.len > 0)
|
||||
blockcount = 0
|
||||
mode = BOT_BLOCKED
|
||||
buzz(DELIGHT)
|
||||
|
||||
else
|
||||
buzz(SIGH)
|
||||
|
||||
mode = BOT_NO_ROUTE
|
||||
|
||||
// calculates a path to the current destination
|
||||
// given an optional turf to avoid
|
||||
/mob/living/simple_animal/bot/mulebot/calc_path(turf/avoid = null)
|
||||
path = get_path_to(src, target, /turf/proc/Distance_cardinal, 0, 250, id=access_card, exclude=avoid)
|
||||
|
||||
// sets the current destination
|
||||
// signals all beacons matching the delivery code
|
||||
// beacons will return a signal giving their locations
|
||||
/mob/living/simple_animal/bot/mulebot/proc/set_destination(new_dest)
|
||||
new_destination = new_dest
|
||||
get_nav()
|
||||
|
||||
// starts bot moving to current destination
|
||||
/mob/living/simple_animal/bot/mulebot/proc/start()
|
||||
if(!on)
|
||||
return
|
||||
if(destination == home_destination)
|
||||
mode = BOT_GO_HOME
|
||||
else
|
||||
mode = BOT_DELIVER
|
||||
update_icon()
|
||||
get_nav()
|
||||
|
||||
// starts bot moving to home
|
||||
// sends a beacon query to find
|
||||
/mob/living/simple_animal/bot/mulebot/proc/start_home()
|
||||
if(!on)
|
||||
return
|
||||
spawn(0)
|
||||
set_destination(home_destination)
|
||||
mode = BOT_BLOCKED
|
||||
update_icon()
|
||||
|
||||
// called when bot reaches current target
|
||||
/mob/living/simple_animal/bot/mulebot/proc/at_target()
|
||||
if(!reached_target)
|
||||
radio_channel = "Supply" //Supply channel
|
||||
audible_message("[src] makes a chiming sound!", "<span class='italics'>You hear a chime.</span>")
|
||||
playsound(loc, 'sound/machines/chime.ogg', 50, 0)
|
||||
reached_target = 1
|
||||
|
||||
if(pathset) //The AI called us here, so notify it of our arrival.
|
||||
loaddir = dir //The MULE will attempt to load a crate in whatever direction the MULE is "facing".
|
||||
if(calling_ai)
|
||||
calling_ai << "<span class='notice'>\icon[src] [src] wirelessly plays a chiming sound!</span>"
|
||||
playsound(calling_ai, 'sound/machines/chime.ogg',40, 0)
|
||||
calling_ai = null
|
||||
radio_channel = "AI Private" //Report on AI Private instead if the AI is controlling us.
|
||||
|
||||
if(load) // if loaded, unload at target
|
||||
if(report_delivery)
|
||||
speak("Destination <b>[destination]</b> reached. Unloading [load].",radio_channel)
|
||||
unload(loaddir)
|
||||
else
|
||||
// not loaded
|
||||
if(auto_pickup) // find a crate
|
||||
var/atom/movable/AM
|
||||
if(wires.is_cut(WIRE_LOADCHECK)) // if hacked, load first unanchored thing we find
|
||||
for(var/atom/movable/A in get_step(loc, loaddir))
|
||||
if(!A.anchored)
|
||||
AM = A
|
||||
break
|
||||
else // otherwise, look for crates only
|
||||
AM = locate(/obj/structure/closet/crate) in get_step(loc,loaddir)
|
||||
if(AM && AM.Adjacent(src))
|
||||
load(AM)
|
||||
if(report_delivery)
|
||||
speak("Now loading [load] at <b>[get_area(src)]</b>.", radio_channel)
|
||||
// whatever happened, check to see if we return home
|
||||
|
||||
if(auto_return && home_destination && destination != home_destination)
|
||||
// auto return set and not at home already
|
||||
start_home()
|
||||
mode = BOT_BLOCKED
|
||||
else
|
||||
bot_reset() // otherwise go idle
|
||||
|
||||
return
|
||||
|
||||
// called when bot bumps into anything
|
||||
/mob/living/simple_animal/bot/mulebot/Bump(atom/obs)
|
||||
if(wires.is_cut(WIRE_AVOIDANCE)) // usually just bumps, but if avoidance disabled knock over mobs
|
||||
var/mob/M = obs
|
||||
if(ismob(M))
|
||||
if(istype(M,/mob/living/silicon/robot))
|
||||
visible_message("<span class='danger'>[src] bumps into [M]!</span>")
|
||||
else
|
||||
if(!paicard)
|
||||
add_logs(src, M, "knocked down")
|
||||
visible_message("<span class='danger'>[src] knocks over [M]!</span>")
|
||||
M.stop_pulling()
|
||||
M.Stun(8)
|
||||
M.Weaken(5)
|
||||
return ..()
|
||||
|
||||
// called from mob/living/carbon/human/Crossed()
|
||||
// when mulebot is in the same loc
|
||||
/mob/living/simple_animal/bot/mulebot/proc/RunOver(mob/living/carbon/human/H)
|
||||
add_logs(src, H, "run over", null, "(DAMTYPE: [uppertext(BRUTE)])")
|
||||
H.visible_message("<span class='danger'>[src] drives over [H]!</span>", \
|
||||
"<span class='userdanger'>[src] drives over you!<span>")
|
||||
playsound(loc, 'sound/effects/splat.ogg', 50, 1)
|
||||
|
||||
var/damage = rand(5,15)
|
||||
H.apply_damage(2*damage, BRUTE, "head", run_armor_check("head", "melee"))
|
||||
H.apply_damage(2*damage, BRUTE, "chest", run_armor_check("chest", "melee"))
|
||||
H.apply_damage(0.5*damage, BRUTE, "l_leg", run_armor_check("l_leg", "melee"))
|
||||
H.apply_damage(0.5*damage, BRUTE, "r_leg", run_armor_check("r_leg", "melee"))
|
||||
H.apply_damage(0.5*damage, BRUTE, "l_arm", run_armor_check("l_arm", "melee"))
|
||||
H.apply_damage(0.5*damage, BRUTE, "r_arm", run_armor_check("r_arm", "melee"))
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
T.add_mob_blood(H)
|
||||
bloodiness += 4
|
||||
|
||||
// player on mulebot attempted to move
|
||||
/mob/living/simple_animal/bot/mulebot/relaymove(mob/user)
|
||||
if(user.incapacitated())
|
||||
return
|
||||
if(load == user)
|
||||
unload(0)
|
||||
|
||||
|
||||
//Update navigation data. Called when commanded to deliver, return home, or a route update is needed...
|
||||
/mob/living/simple_animal/bot/mulebot/proc/get_nav()
|
||||
if(!on || wires.is_cut(WIRE_BEACON))
|
||||
return
|
||||
|
||||
for(var/obj/machinery/navbeacon/NB in deliverybeacons)
|
||||
if(NB.location == new_destination) // if the beacon location matches the set destination
|
||||
// the we will navigate there
|
||||
destination = new_destination
|
||||
target = NB.loc
|
||||
var/direction = NB.dir // this will be the load/unload dir
|
||||
if(direction)
|
||||
loaddir = text2num(direction)
|
||||
else
|
||||
loaddir = 0
|
||||
update_icon()
|
||||
if(destination) // No need to calculate a path if you do not have a destination set!
|
||||
calc_path()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/emp_act(severity)
|
||||
if(cell)
|
||||
cell.emp_act(severity)
|
||||
if(load)
|
||||
load.emp_act(severity)
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/explode()
|
||||
visible_message("<span class='boldannounce'>[src] blows apart!</span>")
|
||||
var/turf/Tsec = get_turf(src)
|
||||
|
||||
new /obj/item/device/assembly/prox_sensor(Tsec)
|
||||
new /obj/item/stack/rods(Tsec)
|
||||
new /obj/item/stack/rods(Tsec)
|
||||
new /obj/item/stack/cable_coil/cut(Tsec)
|
||||
if(cell)
|
||||
cell.loc = Tsec
|
||||
cell.update_icon()
|
||||
cell = null
|
||||
|
||||
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
|
||||
s.set_up(3, 1, src)
|
||||
s.start()
|
||||
|
||||
new /obj/effect/decal/cleanable/oil(loc)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/remove_air(amount) //To prevent riders suffocating
|
||||
if(loc)
|
||||
return loc.remove_air(amount)
|
||||
else
|
||||
return null
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/resist()
|
||||
..()
|
||||
if(load)
|
||||
unload()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/UnarmedAttack(atom/A)
|
||||
if(isturf(A) && isturf(loc) && loc.Adjacent(A) && load)
|
||||
unload(get_dir(loc, A))
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/insertpai(mob/user, obj/item/device/paicard/card)
|
||||
if(..())
|
||||
visible_message("[src] safeties are locked on.")
|
||||
|
||||
#undef SIGH
|
||||
#undef ANNOYED
|
||||
#undef DELIGHT
|
||||
|
||||
/obj/machinery/bot_core/mulebot
|
||||
req_access = list(access_cargo)
|
||||
@@ -0,0 +1,418 @@
|
||||
/mob/living/simple_animal/bot/secbot
|
||||
name = "\improper Securitron"
|
||||
desc = "A little security robot. He looks less than thrilled."
|
||||
icon = 'icons/obj/aibots.dmi'
|
||||
icon_state = "secbot0"
|
||||
density = 0
|
||||
anchored = 0
|
||||
health = 25
|
||||
maxHealth = 25
|
||||
damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
|
||||
pass_flags = PASSMOB
|
||||
|
||||
radio_key = /obj/item/device/encryptionkey/secbot //AI Priv + Security
|
||||
radio_channel = "Security" //Security channel
|
||||
bot_type = SEC_BOT
|
||||
model = "Securitron"
|
||||
bot_core_type = /obj/machinery/bot_core/secbot
|
||||
window_id = "autosec"
|
||||
window_name = "Automatic Security Unit v1.6"
|
||||
allow_pai = 0
|
||||
data_hud_type = DATA_HUD_SECURITY_ADVANCED
|
||||
|
||||
var/mob/living/carbon/target
|
||||
var/oldtarget_name
|
||||
var/threatlevel = 0
|
||||
var/target_lastloc //Loc of target when arrested.
|
||||
var/last_found //There's a delay
|
||||
var/declare_arrests = 1 //When making an arrest, should it notify everyone on the security channel?
|
||||
var/idcheck = 0 //If true, arrest people with no IDs
|
||||
var/weaponscheck = 0 //If true, arrest people for weapons if they lack access
|
||||
var/check_records = 1 //Does it check security records?
|
||||
var/arrest_type = 0 //If true, don't handcuff
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/beepsky
|
||||
name = "Officer Beep O'sky"
|
||||
desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey."
|
||||
idcheck = 0
|
||||
weaponscheck = 0
|
||||
auto_patrol = 1
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/beepsky/explode()
|
||||
var/turf/Tsec = get_turf(src)
|
||||
new /obj/item/weapon/stock_parts/cell/potato(Tsec)
|
||||
var/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/shotglass/S = new(Tsec)
|
||||
S.reagents.add_reagent("whiskey", 15)
|
||||
S.on_reagent_change()
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/pingsky
|
||||
name = "Officer Pingsky"
|
||||
desc = "It's Officer Pingsky! Delegated to satellite guard duty for harbouring anti-human sentiment."
|
||||
radio_channel = "AI Private"
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/New()
|
||||
..()
|
||||
icon_state = "secbot[on]"
|
||||
spawn(3)
|
||||
var/datum/job/detective/J = new/datum/job/detective
|
||||
access_card.access += J.get_access()
|
||||
prev_access = access_card.access
|
||||
|
||||
//SECHUD
|
||||
var/datum/atom_hud/secsensor = huds[DATA_HUD_SECURITY_ADVANCED]
|
||||
secsensor.add_hud_to(src)
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/turn_on()
|
||||
..()
|
||||
icon_state = "secbot[on]"
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/turn_off()
|
||||
..()
|
||||
icon_state = "secbot[on]"
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/bot_reset()
|
||||
..()
|
||||
target = null
|
||||
oldtarget_name = null
|
||||
anchored = 0
|
||||
walk_to(src,0)
|
||||
last_found = world.time
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/set_custom_texts()
|
||||
|
||||
text_hack = "You overload [name]'s target identification system."
|
||||
text_dehack = "You reboot [name] and restore the target identification."
|
||||
text_dehack_fail = "[name] refuses to accept your authority!"
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/get_controls(mob/user)
|
||||
var/dat
|
||||
dat += hack(user)
|
||||
dat += showpai(user)
|
||||
dat += text({"
|
||||
<TT><B>Securitron v1.6 controls</B></TT><BR><BR>
|
||||
Status: []<BR>
|
||||
Behaviour controls are [locked ? "locked" : "unlocked"]<BR>
|
||||
Maintenance panel panel is [open ? "opened" : "closed"]"},
|
||||
|
||||
"<A href='?src=\ref[src];power=1'>[on ? "On" : "Off"]</A>" )
|
||||
|
||||
if(!locked || issilicon(user) || IsAdminGhost(user))
|
||||
dat += text({"<BR>
|
||||
Arrest Unidentifiable Persons: []<BR>
|
||||
Arrest for Unauthorized Weapons: []<BR>
|
||||
Arrest for Warrant: []<BR>
|
||||
Operating Mode: []<BR>
|
||||
Report Arrests[]<BR>
|
||||
Auto Patrol: []"},
|
||||
|
||||
"<A href='?src=\ref[src];operation=idcheck'>[idcheck ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=\ref[src];operation=weaponscheck'>[weaponscheck ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=\ref[src];operation=ignorerec'>[check_records ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=\ref[src];operation=switchmode'>[arrest_type ? "Detain" : "Arrest"]</A>",
|
||||
"<A href='?src=\ref[src];operation=declarearrests'>[declare_arrests ? "Yes" : "No"]</A>",
|
||||
"<A href='?src=\ref[src];operation=patrol'>[auto_patrol ? "On" : "Off"]</A>" )
|
||||
|
||||
return dat
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
switch(href_list["operation"])
|
||||
if("idcheck")
|
||||
idcheck = !idcheck
|
||||
update_controls()
|
||||
if("weaponscheck")
|
||||
weaponscheck = !weaponscheck
|
||||
update_controls()
|
||||
if("ignorerec")
|
||||
check_records = !check_records
|
||||
update_controls()
|
||||
if("switchmode")
|
||||
arrest_type = !arrest_type
|
||||
update_controls()
|
||||
if("declarearrests")
|
||||
declare_arrests = !declare_arrests
|
||||
update_controls()
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/proc/retaliate(mob/living/carbon/human/H)
|
||||
threatlevel = H.assess_threat(src)
|
||||
threatlevel += 6
|
||||
if(threatlevel >= 4)
|
||||
target = H
|
||||
mode = BOT_HUNT
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/attack_hand(mob/living/carbon/human/H)
|
||||
if(H.a_intent == "harm")
|
||||
retaliate(H)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/attackby(obj/item/weapon/W, mob/user, params)
|
||||
..()
|
||||
if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm") // Any intent but harm will heal, so we shouldn't get angry.
|
||||
return
|
||||
if(!istype(W, /obj/item/weapon/screwdriver) && (W.force) && (!target) && (W.damtype != STAMINA) ) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass.
|
||||
retaliate(user)
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/emag_act(mob/user)
|
||||
..()
|
||||
if(emagged == 2)
|
||||
if(user)
|
||||
user << "<span class='danger'>You short out [src]'s target assessment circuits.</span>"
|
||||
oldtarget_name = user.name
|
||||
audible_message("<span class='danger'>[src] buzzes oddly!</span>")
|
||||
declare_arrests = 0
|
||||
icon_state = "secbot[on]"
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/bullet_act(obj/item/projectile/Proj)
|
||||
if(istype(Proj ,/obj/item/projectile/beam)||istype(Proj,/obj/item/projectile/bullet))
|
||||
if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE))
|
||||
if(!Proj.nodamage && Proj.damage < src.health)
|
||||
retaliate(Proj.firer)
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/UnarmedAttack(atom/A)
|
||||
if(!on)
|
||||
return
|
||||
if(iscarbon(A))
|
||||
var/mob/living/carbon/C = A
|
||||
if(!C.stunned || arrest_type)
|
||||
stun_attack(A)
|
||||
else if(C.canBeHandcuffed() && !C.handcuffed)
|
||||
cuff(A)
|
||||
else
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/hitby(atom/movable/AM, skipcatch = 0, hitpush = 1, blocked = 0)
|
||||
if(istype(AM, /obj/item))
|
||||
var/obj/item/I = AM
|
||||
if(I.throwforce < src.health && I.thrownby && (istype(I.thrownby, /mob/living/carbon/human)))
|
||||
var/mob/living/carbon/human/H = I.thrownby
|
||||
retaliate(H)
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/proc/cuff(mob/living/carbon/C)
|
||||
mode = BOT_ARREST
|
||||
playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
|
||||
C.visible_message("<span class='danger'>[src] is trying to put zipties on [C]!</span>",\
|
||||
"<span class='userdanger'>[src] is trying to put zipties on you!</span>")
|
||||
spawn(60)
|
||||
if( !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing.
|
||||
return
|
||||
if(!C.handcuffed)
|
||||
C.handcuffed = new /obj/item/weapon/restraints/handcuffs/cable/zipties/used(C)
|
||||
C.update_handcuffed()
|
||||
playsound(loc, pick('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg'), 50, 0)
|
||||
back_to_idle()
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/proc/stun_attack(mob/living/carbon/C)
|
||||
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
|
||||
icon_state = "secbot-c"
|
||||
spawn(2)
|
||||
icon_state = "secbot[on]"
|
||||
var/threat = 5
|
||||
if(istype(C, /mob/living/carbon/human))
|
||||
C.stuttering = 5
|
||||
C.Stun(5)
|
||||
C.Weaken(5)
|
||||
var/mob/living/carbon/human/H = C
|
||||
threat = H.assess_threat(src)
|
||||
else
|
||||
C.Weaken(5)
|
||||
C.stuttering = 5
|
||||
C.Stun(5)
|
||||
threat = C.assess_threat()
|
||||
add_logs(src,C,"stunned")
|
||||
if(declare_arrests)
|
||||
var/area/location = get_area(src)
|
||||
speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag <b>[C]</b> in [location].", radio_channel)
|
||||
C.visible_message("<span class='danger'>[src] has stunned [C]!</span>",\
|
||||
"<span class='userdanger'>[src] has stunned you!</span>")
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/handle_automated_action()
|
||||
if(!..())
|
||||
return
|
||||
|
||||
switch(mode)
|
||||
|
||||
if(BOT_IDLE) // idle
|
||||
|
||||
walk_to(src,0)
|
||||
look_for_perp() // see if any criminals are in range
|
||||
if(!mode && auto_patrol) // still idle, and set to patrol
|
||||
mode = BOT_START_PATROL // switch to patrol mode
|
||||
|
||||
if(BOT_HUNT) // hunting for perp
|
||||
|
||||
// if can't reach perp for long enough, go idle
|
||||
if(frustration >= 8)
|
||||
walk_to(src,0)
|
||||
back_to_idle()
|
||||
return
|
||||
|
||||
if(target) // make sure target exists
|
||||
if(Adjacent(target) && isturf(target.loc)) // if right next to perp
|
||||
stun_attack(target)
|
||||
|
||||
mode = BOT_PREP_ARREST
|
||||
anchored = 1
|
||||
target_lastloc = target.loc
|
||||
return
|
||||
|
||||
else // not next to perp
|
||||
var/turf/olddist = get_dist(src, target)
|
||||
walk_to(src, target,1,4)
|
||||
if((get_dist(src, target)) >= (olddist))
|
||||
frustration++
|
||||
else
|
||||
frustration = 0
|
||||
else
|
||||
back_to_idle()
|
||||
|
||||
if(BOT_PREP_ARREST) // preparing to arrest target
|
||||
|
||||
// see if he got away. If he's no no longer adjacent or inside a closet or about to get up, we hunt again.
|
||||
if( !Adjacent(target) || !isturf(target.loc) || target.weakened < 2 )
|
||||
back_to_hunt()
|
||||
return
|
||||
|
||||
if(iscarbon(target) && target.canBeHandcuffed())
|
||||
if(!arrest_type)
|
||||
if(!target.handcuffed) //he's not cuffed? Try to cuff him!
|
||||
cuff(target)
|
||||
else
|
||||
back_to_idle()
|
||||
return
|
||||
else
|
||||
back_to_idle()
|
||||
return
|
||||
|
||||
if(BOT_ARREST)
|
||||
if(!target)
|
||||
anchored = 0
|
||||
mode = BOT_IDLE
|
||||
last_found = world.time
|
||||
frustration = 0
|
||||
return
|
||||
|
||||
if(target.handcuffed) //no target or target cuffed? back to idle.
|
||||
back_to_idle()
|
||||
return
|
||||
|
||||
if(!Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && target.weakened < 2)) //if he's changed loc and about to get up or not adjacent or got into a closet, we prep arrest again.
|
||||
back_to_hunt()
|
||||
return
|
||||
else //Try arresting again if the target escapes.
|
||||
mode = BOT_PREP_ARREST
|
||||
anchored = 0
|
||||
|
||||
if(BOT_START_PATROL)
|
||||
look_for_perp()
|
||||
start_patrol()
|
||||
|
||||
if(BOT_PATROL)
|
||||
look_for_perp()
|
||||
bot_patrol()
|
||||
|
||||
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/proc/back_to_idle()
|
||||
anchored = 0
|
||||
mode = BOT_IDLE
|
||||
target = null
|
||||
last_found = world.time
|
||||
frustration = 0
|
||||
spawn(0)
|
||||
handle_automated_action() //ensure bot quickly responds
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/proc/back_to_hunt()
|
||||
anchored = 0
|
||||
frustration = 0
|
||||
mode = BOT_HUNT
|
||||
spawn(0)
|
||||
handle_automated_action() //ensure bot quickly responds
|
||||
// look for a criminal in view of the bot
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/proc/look_for_perp()
|
||||
anchored = 0
|
||||
for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal
|
||||
if((C.stat) || (C.handcuffed))
|
||||
continue
|
||||
|
||||
if((C.name == oldtarget_name) && (world.time < last_found + 100))
|
||||
continue
|
||||
|
||||
threatlevel = C.assess_threat(src)
|
||||
|
||||
if(!threatlevel)
|
||||
continue
|
||||
|
||||
else if(threatlevel >= 4)
|
||||
target = C
|
||||
oldtarget_name = C.name
|
||||
speak("Level [threatlevel] infraction alert!")
|
||||
playsound(loc, pick('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg'), 50, 0)
|
||||
visible_message("<b>[src]</b> points at [C.name]!")
|
||||
mode = BOT_HUNT
|
||||
spawn(0)
|
||||
handle_automated_action() // ensure bot quickly responds to a perp
|
||||
break
|
||||
else
|
||||
continue
|
||||
/mob/living/simple_animal/bot/secbot/proc/check_for_weapons(var/obj/item/slot_item)
|
||||
if(slot_item && slot_item.needs_permit)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/explode()
|
||||
|
||||
walk_to(src,0)
|
||||
visible_message("<span class='boldannounce'>[src] blows apart!</span>")
|
||||
var/turf/Tsec = get_turf(src)
|
||||
|
||||
var/obj/item/weapon/secbot_assembly/Sa = new /obj/item/weapon/secbot_assembly(Tsec)
|
||||
Sa.build_step = 1
|
||||
Sa.add_overlay("hs_hole")
|
||||
Sa.created_name = name
|
||||
new /obj/item/device/assembly/prox_sensor(Tsec)
|
||||
new /obj/item/weapon/melee/baton(Tsec)
|
||||
|
||||
if(prob(50))
|
||||
new /obj/item/robot_parts/l_arm(Tsec)
|
||||
|
||||
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
|
||||
s.set_up(3, 1, src)
|
||||
s.start()
|
||||
|
||||
new /obj/effect/decal/cleanable/oil(loc)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/attack_alien(var/mob/living/carbon/alien/user as mob)
|
||||
..()
|
||||
if(!isalien(target))
|
||||
target = user
|
||||
mode = BOT_HUNT
|
||||
|
||||
/mob/living/simple_animal/bot/secbot/Crossed(atom/movable/AM)
|
||||
if(ismob(AM) && target)
|
||||
var/mob/living/carbon/C = AM
|
||||
if(!istype(C) || !C || in_range(src, target))
|
||||
return
|
||||
C.visible_message("<span class='warning'>[pick( \
|
||||
"[C] dives out of [src]'s way!", \
|
||||
"[C] stumbles over [src]!", \
|
||||
"[C] jumps out of [src]'s path!", \
|
||||
"[C] trips over [src] and falls!", \
|
||||
"[C] topples over [src]!", \
|
||||
"[C] leaps out of [src]'s way!")]</span>")
|
||||
C.Weaken(2)
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/machinery/bot_core/secbot
|
||||
req_access = list(access_security)
|
||||
Reference in New Issue
Block a user