Files
Paradise/code/game/machinery/bots/bots.dm
ZomgPonies f22ae3acbe Merge pull request #998 from tigercat2000/LightShit
Baystation12's Lighting Overhaul
2015-05-11 17:06:30 -04:00

795 lines
27 KiB
Plaintext

// AI (i.e. game AI, not the AI player) controlled bots
/obj/machinery/bot
icon = 'icons/obj/aibots.dmi'
layer = MOB_LAYER
light_range = 3
use_power = 0
var/obj/item/weapon/card/id/botcard // the ID card that the bot "holds"
var/list/prev_access = list()
var/on = 1
var/health = 0 //do not forget to set health for your bot!
var/maxhealth = 0
var/fire_dam_coeff = 1.0
var/brute_dam_coeff = 1.0
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/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 = 0 //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_name = "Common"
//var/emagged = 0 //Urist: Moving that var to the general /bot tree as it's used by most bots
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/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/control_freq = 1447 // bot control frequency
var/bot_filter // The radio filter the bot uses to identify itself on the network.
var/bot_type = 0 //The type of bot it is, for radio control.
#define SEC_BOT 1 // Secutritrons (Beepsky) and ED-209s
#define MULE_BOT 2 // MULEbots
#define FLOOR_BOT 3 // Floorbots
#define CLEAN_BOT 4 // Cleanbots
#define MED_BOT 5 // Medibots
var/bot_type_name = "Bot"
#define DEFAULT_SCAN_RANGE 7 //default view range for finding targets.
//Mode defines
#define BOT_IDLE 0 // idle
#define BOT_HUNT 1 // found target, hunting
#define BOT_PREP_ARREST 2 // at target, preparing to arrest
#define BOT_ARREST 3 // arresting target
#define BOT_START_PATROL 4 // start patrol
#define BOT_PATROL 5 // patrolling
#define BOT_SUMMON 6 // summoned by PDA
#define BOT_CLEANING 7 // cleaning (cleanbots)
#define BOT_REPAIRING 8 // repairing hull breaches (floorbots)
#define BOT_MOVING 9 // for clean/floor bots, when moving.
#define BOT_HEALING 10 // healing people (medbots)
#define BOT_RESPONDING 11 // responding to a call from the AI
#define BOT_LOADING 12 // loading/unloading
#define BOT_DELIVER 13 // moving to deliver
#define BOT_GO_HOME 14 // returning to home
#define BOT_BLOCKED 15 // blocked
#define BOT_NAV 16 // computing navigation
#define BOT_WAIT_FOR_NAV 17 // waiting for nav computation
#define BOT_NO_ROUTE 18 // no destination beacon found (or no route)
var/list/mode_name = list("In Pursuit","Preparing to Arrest","Arresting","Beginning Patrol","Patrolling","Summoned by PDA", \
"Cleaning", "Repairing", "Proceeding to work site","Healing","Responding","Loading/Unloading","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 AI's bot control interface.
/obj/machinery/bot/proc/turn_on()
if(stat) return 0
on = 1
set_light(initial(light_range))
return 1
/obj/machinery/bot/proc/turn_off()
on = 0
set_light(0)
bot_reset() //Resets an AI's call, should it exist.
/obj/machinery/bot/New()
..()
aibots += src //Global bot list
botcard = new /obj/item/weapon/card/id(src)
set_custom_texts()
Radio = new /obj/item/device/radio(src)
Radio.listening = 0 //Makes bot radios transmit only so no one hears things while adjacent to one.
Radio.config(list("[radio_name]" = 0))
Radio.follow_target = src
/obj/machinery/bot/proc/add_to_beacons(bot_filter) //Master filter control for bots. Must be placed in the bot's local New() to support map spawned bots.
spawn(20)
if(radio_controller)
radio_controller.add_object(src, beacon_freq, RADIO_NAVBEACONS)
if(bot_filter)
radio_controller.add_object(src, control_freq, bot_filter)
/obj/machinery/bot/proc/explode()
aibots -= src
qdel(src)
/obj/machinery/bot/proc/healthcheck()
if (health <= 0)
explode()
/obj/machinery/bot/proc/Emag(mob/user as mob) //Master Emag proc. Ensure this is called in your bot before setting unique functions.
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='warning'>You bypass [src]'s controls.</span>"
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.
else //Bot is unlocked, but the maint panel has not been opened with a screwdriver yet.
user << "<span class='notice'>You need to open maintenance panel first.</span>"
/obj/machinery/bot/examine(mob/user)
..()
if (health < maxhealth)
if (health > maxhealth/3)
user << "<span class='danger'>[src]'s parts look loose.</span>"
else
user << "<span class='danger'>[src]'s parts look very loose.</span>"
else
user << "[src] is in pristine condition."
/obj/machinery/bot/attack_alien(var/mob/living/carbon/alien/user as mob)
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(src)
health -= rand(15,30)*brute_dam_coeff
visible_message("<span class='userdanger'>[user] has slashed [src]!</span>")
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
if(prob(10))
new /obj/effect/decal/cleanable/blood/oil(loc)
healthcheck()
/obj/machinery/bot/attack_animal(var/mob/living/simple_animal/M as mob)
M.do_attack_animation(src)
if(M.melee_damage_upper == 0)
return
health -= M.melee_damage_upper
visible_message("<span class='userdanger'>[M] has [M.attacktext] [src]!</span>")
add_logs(M, src, "attacked", admin=0)
if(prob(10))
new /obj/effect/decal/cleanable/blood/oil(loc)
healthcheck()
/obj/machinery/bot/Topic(href, href_list) //Master Topic to handle common functions.
. = ..()
if (.)
return
if(topic_denied(usr))
usr << "<span class='warning'>[src]'s interface is not responding!</span>"
href_list = list()
return
usr.set_machine(src)
add_fingerprint(usr)
if((href_list["power"]) && (allowed(usr) || !locked))
if (on)
turn_off()
else
turn_on()
switch(href_list["operation"])
if("patrol")
auto_patrol = !auto_patrol
mode = BOT_IDLE
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='userdanger'>[text_dehack_fail]</span>"
else
emagged = 0
hacked = 0
usr << "<span class='notice'>[text_dehack]</span>"
bot_reset()
updateUsrDialog()
/obj/machinery/bot/proc/topic_denied(mob/user) //Access check proc for bot topics! Remember to place in a bot's individual Topic if desired.
// 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)) //Bot is hacked, so only silicons are allowed access.
return 1
else
return 0
/obj/machinery/bot/proc/bot_process() //Master process which handles code common across most bots.
set background = BACKGROUND_ENABLED
if(!on)
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.
/obj/machinery/bot/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/weapon/screwdriver))
if(!locked)
open = !open
user << "<span class='notice'>Maintenance panel is now [open ? "opened" : "closed"].</span>"
else
user << "<span class='warning'>Maintenance panel is locked.</span>"
else
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))
health = min(maxhealth, health+10)
user.visible_message("<span class='notice'>[user] repairs [src]!</span>","<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/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(5, 1, src)
switch(W.damtype)
if("fire")
health -= W.force * fire_dam_coeff
s.start()
if("brute")
health -= W.force * brute_dam_coeff
s.start()
..()
healthcheck()
/obj/machinery/bot/emag_act(user as mob)
if (emagged < 2)
Emag(user)
/obj/machinery/bot/bullet_act(var/obj/item/projectile/Proj)
if((Proj.damage_type == BRUTE || Proj.damage_type == BURN))
health -= Proj.damage
if(prob(75) && Proj.damage > 0)
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(5, 1, src)
s.start()
..()
healthcheck()
return
/obj/machinery/bot/blob_act()
health -= rand(20,40)*fire_dam_coeff
healthcheck()
return
/obj/machinery/bot/ex_act(severity)
switch(severity)
if(1.0)
explode()
return
if(2.0)
health -= rand(5,10)*fire_dam_coeff
health -= rand(10,20)*brute_dam_coeff
healthcheck()
return
if(3.0)
if (prob(50))
health -= rand(1,5)*fire_dam_coeff
health -= rand(1,5)*brute_dam_coeff
healthcheck()
return
return
/obj/machinery/bot/emp_act(severity)
var/was_on = on
stat |= EMPED
var/obj/effect/overlay/pulse2 = new/obj/effect/overlay ( loc )
pulse2.icon = 'icons/effects/effects.dmi'
pulse2.icon_state = "empdisable"
pulse2.name = "emp sparks"
pulse2.anchored = 1
pulse2.dir = pick(cardinal)
spawn(10)
pulse2.delete()
if (on)
turn_off()
spawn(severity*300)
stat &= ~EMPED
if (was_on)
turn_on()
/obj/machinery/bot/proc/hack(mob/user)
var/hack
if(issilicon(user)) //Allows silicons 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><BR>"
return hack
/obj/machinery/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]."
/obj/machinery/bot/attack_ai(mob/user as mob)
attack_hand(user)
/obj/machinery/bot/proc/speak(var/message, var/freqname = null) //Pass a message to have the bot say() it. Pass a frequency to say it on the radio.
if((!on) || (!message))
return
if(freqname)
Radio.autosay(message, src.name, freqname, list(src.z))
else
for(var/mob/O in hearers(src, null))
O.show_message("<span class='game say'><span class='name'>[src]</span> beeps, \"[message]\"</span>",2)
return
//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.
*/
obj/machinery/bot/proc/scan(var/scan_type, var/old_target, var/scan_range)
var/final_result
for (var/scan in view (scan_range ? scan_range : DEFAULT_SCAN_RANGE, src) ) //Search for something in range!
if(!istype(scan, scan_type)) //Check that the thing we found is the type we want!
continue //If not, keep searching!
if( !(scan in ignore_list) && !(scan in old_target) ) //Filter for blacklisted elements, usually unreachable or previously processed oness
var/scan_result = process_scan(scan) //Some bots may require additional processing when a result is selected.
if( scan_result )
final_result = scan_result
else
continue //The current element failed assessment, move on to the next.
else
continue
return final_result
//When the scan finds a target, run bot specific processing to select it for the next step. Empty by default.
obj/machinery/bot/proc/process_scan(var/scan_target)
return scan_target
/obj/machinery/bot/proc/add_to_ignore(var/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.
*/
obj/machinery/bot/proc/bot_move(var/dest, var/move_speed)
if(!dest || !path || path.len == 0) //A-star failed or a path/destination was not set.
path = list()
return 0
if(get_turf(src) == get_turf(dest)) //We have arrived, no need to move again.
return 1
var/success
var/step_count = move_speed ? move_speed : speed //If a value is passed into move_speed, use that instead of the default speed var.
if(step_count >= 1 && tries < 4)
for(step_count, step_count >= 1,step_count--)
success = bot_step(dest)
if (success)
tries = 0
else
tries++
break
sleep(4)
else
return 0
return 1
obj/machinery/bot/proc/bot_step(var/dest)
if(path && path.len > 1)
step_towards(src, path[1])
if(get_turf(src) == path[1]) //Successful move
path -= path[1]
else
return 0
else if(path.len == 1)
step_towards(src, dest)
path = list()
return 1
/obj/machinery/bot/proc/check_bot_access()
if(mode != BOT_SUMMON && mode != BOT_RESPONDING)
botcard.access = prev_access
/obj/machinery/bot/proc/call_bot(var/caller, var/turf/waypoint)
bot_reset() //Reset a bot before setting it to call mode.
var/area/end_area = get_area(waypoint)
//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 = AStar(src, waypoint, /turf/proc/CardinalTurfsWithAccess, /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.
botcard = all_access //Give the bot all-access while under the AI's command.
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
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()
/obj/machinery/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()
obj/machinery/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
botcard.access = prev_access
tries = 0
mode = BOT_IDLE
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Patrol and summon code!
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/obj/machinery/bot/proc/bot_patrol()
patrol_step()
spawn(5)
if(mode == BOT_PATROL)
patrol_step()
return
obj/machinery/bot/proc/start_patrol()
if(tries >= 4) //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 = 0
return
mode = BOT_PATROL
else if(!awaiting_beacon) // no patrol target, so need a new one
find_patrol_target()
speak("Engaging patrol mode.")
tries++
return
// perform a single patrol step
/obj/machinery/bot/proc/patrol_step()
if(loc == patrol_target) // reached target
at_patrol_target()
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) // successful move
blockcount = 0
else // failed to move
blockcount++
if(blockcount > 5) // attempt 5 times before recomputing
// find new path excluding blocked turf
spawn(2)
calc_path(next)
if(path.len == 0)
find_patrol_target()
else
blockcount = 0
tries = 0
return
return
else // no path, so calculate new one
mode = BOT_START_PATROL
return
// finds a new patrol target
/obj/machinery/bot/proc/find_patrol_target()
send_status()
if(awaiting_beacon) // awaiting beacon response
awaiting_beacon++
if(awaiting_beacon > 40) // wait 40 secs for beacon response
find_nearest_beacon() // then go to nearest instead
return
if(next_destination)
set_destination(next_destination)
else
find_nearest_beacon()
return
// finds the nearest beacon to self
// signals all beacons matching the patrol code
/obj/machinery/bot/proc/find_nearest_beacon()
nearest_beacon = null
new_destination = "__nearest__"
post_signal(beacon_freq, "findbeacon", "patrol")
awaiting_beacon = 1
spawn(200)
awaiting_beacon = 0
if(nearest_beacon)
set_destination(nearest_beacon)
tries = 0
else
auto_patrol = 0
mode = BOT_IDLE
speak("Disengaging patrol mode.")
send_status()
/obj/machinery/bot/proc/at_patrol_target()
find_patrol_target()
return
// sets the current destination
// signals all beacons matching the patrol code
// beacons will return a signal giving their locations
/obj/machinery/bot/proc/set_destination(var/new_dest)
new_destination = new_dest
post_signal(beacon_freq, "findbeacon", "patrol")
awaiting_beacon = 1
// receive a radio signal
// used for beacon reception
/obj/machinery/bot/receive_signal(datum/signal/signal)
//log_admin("DEBUG \[[// world.timeofday]\]: /obj/machinery/bot/receive_signal([signal.debug_print()])")
if(!on)
return
/*
if(!signal.data["beacon"])
for(var/x in signal.data)
world << "* [x] = [signal.data[x]]"
*/
var/recv = signal.data["command"]
// process all-bot input
if(recv=="bot_status")
send_status()
// check to see if we are the commanded bot
if(signal.data["active"] == src)
if(emagged == 2 || remote_disabled) //Emagged bots do not respect anyone's authority! Bots with their remote controls off cannot get commands.
return
// process control input
switch(recv)
if("stop")
bot_reset() //HOLD IT!!
auto_patrol = 0
return
if("go")
auto_patrol = 1
return
if("summon")
bot_reset()
var/list/user_access = signal.data["useraccess"]
summon_target = signal.data["target"] //Location of the user
if(user_access.len != 0)
botcard.access = user_access + prev_access //Adds the user's access, if any.
mode = BOT_SUMMON
calc_summon_path()
speak("Responding.", radio_name)
return
// receive response from beacon
recv = signal.data["beacon"]
var/valid = signal.data["patrol"]
if(!recv || !valid)
return
if(recv == new_destination) // if the recvd beacon location matches the set destination
// the we will navigate there
destination = new_destination
patrol_target = signal.source.loc
next_destination = signal.data["next_patrol"]
awaiting_beacon = 0
// if looking for nearest beacon
else if(new_destination == "__nearest__")
var/dist = get_dist(src,signal.source.loc)
if(nearest_beacon)
// note we ignore the beacon we are located at
if(dist>1 && dist<get_dist(src,nearest_beacon_loc))
nearest_beacon = recv
nearest_beacon_loc = signal.source.loc
return
else
return
else if(dist > 1)
nearest_beacon = recv
nearest_beacon_loc = signal.source.loc
return
// send a radio signal with a single data key/value pair
/obj/machinery/bot/proc/post_signal(var/freq, var/key, var/value)
post_signal_multiple(freq, list("[key]" = value) )
// send a radio signal with multiple data key/values
/obj/machinery/bot/proc/post_signal_multiple(var/freq, var/list/keyval)
if(!z || z != 1) //Bot control will only work on station.
return
var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq)
if(!frequency) return
var/datum/signal/signal = new()
signal.source = src
signal.transmission_method = 1
// for(var/key in keyval)
// signal.data[key] = keyval[key]
signal.data = keyval
// world << "sent [key],[keyval[key]] on [freq]"
if(signal.data["findbeacon"])
frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS)
else if(signal.data["type"] == bot_type)
frequency.post_signal(src, signal, filter = bot_filter)
else
frequency.post_signal(src, signal)
// signals bot status etc. to controller
/obj/machinery/bot/proc/send_status()
if(remote_disabled || emagged == 2)
return
var/list/kv = list(
"type" = bot_type,
"name" = name,
"loca" = get_area(src), // area
"mode" = mode
)
post_signal_multiple(control_freq, kv)
obj/machinery/bot/proc/bot_summon()
// summoned to PDA
summon_step()
return
// calculates a path to the current destination
// given an optional turf to avoid
/obj/machinery/bot/proc/calc_path(var/turf/avoid)
check_bot_access()
path = AStar(loc, patrol_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 120, id=botcard, exclude=avoid)
if(!path)
path = list()
/obj/machinery/bot/proc/calc_summon_path(var/turf/avoid)
check_bot_access()
path = AStar(loc, summon_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 150, id=botcard, exclude=avoid)
if(!path || tries >= 5) //Cannot reach target. Give up and announce the issue.
speak("Summon command failed, destination unreachable.", radio_name)
bot_reset()
/obj/machinery/bot/proc/summon_step()
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)
blockcount = 0
else // failed to move
blockcount++
if(blockcount > 5) // attempt 5 times before recomputing
// find new path excluding blocked turf
spawn(2)
calc_summon_path(next)
tries++
return
return
else // no path, so calculate new one
calc_summon_path()
return
/obj/machinery/bot/Bump(M as mob|obj) //Leave no door unopened!
if((istype(M, /obj/machinery/door/airlock) || istype(M, /obj/machinery/door/window)) && (!isnull(botcard)))
var/obj/machinery/door/D = M
if(D.check_access(botcard))
D.open()
frustration = 0
else if((istype(M, /mob/living/)) && (!anchored))
var/mob/living/Mb = M
loc = Mb.loc
frustration = 0
return