mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-26 21:49:11 +01:00
@@ -6,6 +6,7 @@
|
||||
layer = MOB_LAYER
|
||||
universal_speak = 1
|
||||
density = 0
|
||||
|
||||
var/obj/item/weapon/card/id/botcard = null
|
||||
var/list/botcard_access = list()
|
||||
var/on = 1
|
||||
@@ -13,11 +14,30 @@
|
||||
var/locked = 1
|
||||
var/emagged = 0
|
||||
var/light_strength = 3
|
||||
var/busy = 0
|
||||
|
||||
var/obj/access_scanner = null
|
||||
var/list/req_access = list()
|
||||
var/list/req_one_access = list()
|
||||
|
||||
var/atom/target = null
|
||||
var/list/ignore_list = list()
|
||||
var/list/patrol_path = list()
|
||||
var/list/target_path = list()
|
||||
var/turf/obstacle = null
|
||||
|
||||
var/wait_if_pulled = 0 // Only applies to moving to the target
|
||||
var/will_patrol = 0 // Not a setting - whether or no this type of bots patrols at all
|
||||
var/patrol_speed = 1 // How many times per tick we move when patrolling
|
||||
var/target_speed = 2 // Ditto for chasing the target
|
||||
var/min_target_dist = 1 // How close we try to get to the target
|
||||
var/max_target_dist = 50 // How far we are willing to go
|
||||
var/max_patrol_dist = 250
|
||||
|
||||
var/target_patience = 5
|
||||
var/frustration = 0
|
||||
var/max_frustration = 0
|
||||
|
||||
/mob/living/bot/New()
|
||||
..()
|
||||
update_icons()
|
||||
@@ -40,6 +60,9 @@
|
||||
stunned = 0
|
||||
paralysis = 0
|
||||
|
||||
if(on && !client && !busy)
|
||||
handleAI()
|
||||
|
||||
/mob/living/bot/updatehealth()
|
||||
if(status_flags & GODMODE)
|
||||
health = maxHealth
|
||||
@@ -109,6 +132,123 @@
|
||||
/mob/living/bot/emag_act(var/remaining_charges, var/mob/user)
|
||||
return 0
|
||||
|
||||
/mob/living/bot/proc/handleAI()
|
||||
if(ignore_list.len)
|
||||
for(var/atom/A in ignore_list)
|
||||
if(!A || !A.loc || prob(1))
|
||||
ignore_list -= A
|
||||
handleRegular()
|
||||
if(target && confirmTarget(target))
|
||||
if(Adjacent(target))
|
||||
handleAdjacentTarget()
|
||||
else
|
||||
handleRangedTarget()
|
||||
if(!wait_if_pulled || !pulledby)
|
||||
for(var/i = 1 to target_speed)
|
||||
stepToTarget()
|
||||
if(i < target_speed)
|
||||
sleep(20 / target_speed)
|
||||
if(max_frustration && frustration > max_frustration * target_speed)
|
||||
handleFrustrated(1)
|
||||
else
|
||||
resetTarget()
|
||||
lookForTargets()
|
||||
if(will_patrol && !pulledby && !target)
|
||||
if(patrol_path.len)
|
||||
for(var/i = 1 to patrol_speed)
|
||||
handlePatrol()
|
||||
if(i < patrol_speed)
|
||||
sleep(20 / patrol_speed)
|
||||
if(max_frustration && frustration > max_frustration * patrol_speed)
|
||||
handleFrustrated(0)
|
||||
else
|
||||
startPatrol()
|
||||
else
|
||||
handleIdle()
|
||||
|
||||
/mob/living/bot/proc/handleRegular()
|
||||
return
|
||||
|
||||
/mob/living/bot/proc/handleAdjacentTarget()
|
||||
return
|
||||
|
||||
/mob/living/bot/proc/handleRangedTarget()
|
||||
return
|
||||
|
||||
/mob/living/bot/proc/stepToTarget()
|
||||
if(!target || !target.loc)
|
||||
return
|
||||
if(get_dist(src, target) > min_target_dist)
|
||||
if(!target_path.len || get_turf(target) != target_path[target_path.len])
|
||||
calcTargetPath()
|
||||
if(makeStep(target_path))
|
||||
frustration = 0
|
||||
else if(max_frustration)
|
||||
++frustration
|
||||
return
|
||||
|
||||
/mob/living/bot/proc/handleFrustrated(var/targ)
|
||||
obstacle = targ ? target_path[1] : patrol_path[1]
|
||||
target_path = list()
|
||||
patrol_path = list()
|
||||
return
|
||||
|
||||
/mob/living/bot/proc/lookForTargets()
|
||||
return
|
||||
|
||||
/mob/living/bot/proc/confirmTarget(var/atom/A)
|
||||
if(A.invisibility >= INVISIBILITY_LEVEL_ONE)
|
||||
return 0
|
||||
if(A in ignore_list)
|
||||
return 0
|
||||
if(!A.loc)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/mob/living/bot/proc/handlePatrol()
|
||||
makeStep(patrol_path)
|
||||
return
|
||||
|
||||
/mob/living/bot/proc/startPatrol()
|
||||
var/turf/T = getPatrolTurf()
|
||||
if(T)
|
||||
patrol_path = AStar(get_turf(loc), T, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, max_patrol_dist, id = botcard, exclude = obstacle)
|
||||
if(!patrol_path)
|
||||
patrol_path = list()
|
||||
obstacle = null
|
||||
return
|
||||
|
||||
/mob/living/bot/proc/getPatrolTurf()
|
||||
return null
|
||||
|
||||
/mob/living/bot/proc/handleIdle()
|
||||
return
|
||||
|
||||
/mob/living/bot/proc/calcTargetPath()
|
||||
target_path = AStar(get_turf(loc), get_turf(target), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, max_target_dist, id = botcard, exclude = obstacle)
|
||||
if(!target_path)
|
||||
if(target && target.loc)
|
||||
ignore_list |= target
|
||||
resetTarget()
|
||||
obstacle = null
|
||||
return
|
||||
|
||||
/mob/living/bot/proc/makeStep(var/list/path)
|
||||
if(!path.len)
|
||||
return 0
|
||||
var/turf/T = path[1]
|
||||
if(get_turf(src) == T)
|
||||
path -= T
|
||||
return makeStep(path)
|
||||
|
||||
return step_towards(src, T)
|
||||
|
||||
/mob/living/bot/proc/resetTarget()
|
||||
target = null
|
||||
target_path = list()
|
||||
frustration = 0
|
||||
obstacle = null
|
||||
|
||||
/mob/living/bot/proc/turn_on()
|
||||
if(stat)
|
||||
return 0
|
||||
@@ -121,6 +261,9 @@
|
||||
on = 0
|
||||
set_light(0)
|
||||
update_icons()
|
||||
resetTarget()
|
||||
patrol_path = list()
|
||||
ignore_list = list()
|
||||
|
||||
/mob/living/bot/proc/explode()
|
||||
qdel(src)
|
||||
@@ -141,7 +284,7 @@
|
||||
// for(var/turf/simulated/t in oview(src,1))
|
||||
|
||||
for(var/d in cardinal)
|
||||
var/turf/simulated/T = get_step(src, d)
|
||||
var/turf/T = get_step(src, d)
|
||||
if(istype(T) && !T.density)
|
||||
if(!LinkBlockedWithAccess(src, T, ID))
|
||||
L.Add(T)
|
||||
|
||||
@@ -6,18 +6,8 @@
|
||||
botcard_access = list(access_janitor, access_maint_tunnels)
|
||||
|
||||
locked = 0 // Start unlocked so roboticist can set them to patrol.
|
||||
|
||||
var/obj/effect/decal/cleanable/target
|
||||
var/list/path = list()
|
||||
var/list/patrol_path = list()
|
||||
var/list/ignorelist = list()
|
||||
|
||||
var/obj/cleanbot_listener/listener = null
|
||||
var/beacon_freq = 1445 // navigation beacon frequency
|
||||
var/signal_sent = 0
|
||||
var/closest_dist
|
||||
var/next_dest
|
||||
var/next_dest_loc
|
||||
wait_if_pulled = 1
|
||||
min_target_dist = 0
|
||||
|
||||
var/cleaning = 0
|
||||
var/screwloose = 0
|
||||
@@ -26,48 +16,11 @@
|
||||
var/blood = 1
|
||||
var/list/target_types = list()
|
||||
|
||||
var/maximum_search_range = 7
|
||||
|
||||
/mob/living/bot/cleanbot/New()
|
||||
..()
|
||||
get_targets()
|
||||
|
||||
listener = new /obj/cleanbot_listener(src)
|
||||
listener.cleanbot = src
|
||||
|
||||
if(radio_controller)
|
||||
radio_controller.add_object(listener, beacon_freq, filter = RADIO_NAVBEACONS)
|
||||
|
||||
/mob/living/bot/cleanbot/proc/handle_target()
|
||||
if(loc == target.loc)
|
||||
if(!cleaning)
|
||||
UnarmedAttack(target)
|
||||
return 1
|
||||
if(!path.len)
|
||||
// spawn(0)
|
||||
path = AStar(loc, target.loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id = botcard)
|
||||
if(!path)
|
||||
custom_emote(2, "[src] can't reach the target and is giving up.")
|
||||
target = null
|
||||
path = list()
|
||||
return
|
||||
if(path.len)
|
||||
step_to(src, path[1])
|
||||
path -= path[1]
|
||||
return 1
|
||||
return
|
||||
|
||||
/mob/living/bot/cleanbot/Life()
|
||||
..()
|
||||
|
||||
if(!on)
|
||||
return
|
||||
|
||||
if(client)
|
||||
return
|
||||
if(cleaning)
|
||||
return
|
||||
|
||||
/mob/living/bot/cleanbot/handleIdle()
|
||||
if(!screwloose && !oddbutton && prob(5))
|
||||
custom_emote(2, "makes an excited beeping booping sound!")
|
||||
|
||||
@@ -79,68 +32,27 @@
|
||||
if(oddbutton && prob(5)) // Make a big mess
|
||||
visible_message("Something flies out of [src]. He seems to be acting oddly.")
|
||||
var/obj/effect/decal/cleanable/blood/gibs/gib = new /obj/effect/decal/cleanable/blood/gibs(loc)
|
||||
ignorelist += gib
|
||||
ignore_list += gib
|
||||
spawn(600)
|
||||
ignorelist -= gib
|
||||
ignore_list -= gib
|
||||
|
||||
// Find a target
|
||||
|
||||
if(pulledby) // Don't wiggle if someone pulls you
|
||||
patrol_path = list()
|
||||
return
|
||||
|
||||
var/found_spot
|
||||
search_loop:
|
||||
for(var/i=0, i <= maximum_search_range, i++)
|
||||
for(var/obj/effect/decal/cleanable/D in view(i, src))
|
||||
if(D in ignorelist)
|
||||
continue
|
||||
for(var/T in target_types)
|
||||
if(istype(D, T))
|
||||
patrol_path = list()
|
||||
target = D
|
||||
found_spot = handle_target()
|
||||
if (found_spot)
|
||||
break search_loop
|
||||
else
|
||||
target = null
|
||||
continue // no need to check the other types
|
||||
|
||||
|
||||
if(!found_spot && !target) // No targets in range
|
||||
if(!patrol_path || !patrol_path.len)
|
||||
if(!signal_sent || signal_sent > world.time + 200) // Waited enough or didn't send yet
|
||||
var/datum/radio_frequency/frequency = radio_controller.return_frequency(beacon_freq)
|
||||
if(!frequency)
|
||||
return
|
||||
|
||||
closest_dist = 9999
|
||||
next_dest = null
|
||||
next_dest_loc = null
|
||||
|
||||
var/datum/signal/signal = new()
|
||||
signal.source = src
|
||||
signal.transmission_method = 1
|
||||
signal.data = list("findbeakon" = "patrol")
|
||||
frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS)
|
||||
signal_sent = world.time
|
||||
else
|
||||
if(next_dest)
|
||||
next_dest_loc = listener.memorized[next_dest]
|
||||
if(next_dest_loc)
|
||||
patrol_path = AStar(loc, next_dest_loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 120, id = botcard, exclude = null)
|
||||
signal_sent = 0
|
||||
else
|
||||
if(pulledby) // Don't wiggle if someone pulls you
|
||||
patrol_path = list()
|
||||
return
|
||||
if(patrol_path[1] == loc)
|
||||
patrol_path -= patrol_path[1]
|
||||
var/moved = step_towards(src, patrol_path[1])
|
||||
if(moved)
|
||||
patrol_path -= patrol_path[1]
|
||||
/mob/living/bot/cleanbot/lookForTargets()
|
||||
for(var/obj/effect/decal/cleanable/D in view(world.view, src)) // There was some odd code to make it start with nearest decals, it's unnecessary, this works
|
||||
if(confirmTarget(D))
|
||||
target = D
|
||||
return
|
||||
|
||||
/mob/living/bot/cleanbot/confirmTarget(var/obj/effect/decal/cleanable/D)
|
||||
if(!..())
|
||||
return 0
|
||||
for(var/T in target_types)
|
||||
if(istype(D, T))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/bot/cleanbot/handleAdjacentTarget()
|
||||
if(get_turf(target) == src.loc)
|
||||
UnarmedAttack(target)
|
||||
|
||||
/mob/living/bot/cleanbot/UnarmedAttack(var/obj/effect/decal/cleanable/D, var/proximity)
|
||||
if(!..())
|
||||
@@ -152,7 +64,7 @@
|
||||
if(D.loc != loc)
|
||||
return
|
||||
|
||||
cleaning = 1
|
||||
busy = 1
|
||||
custom_emote(2, "begins to clean up \the [D]")
|
||||
update_icons()
|
||||
var/cleantime = istype(D, /obj/effect/decal/cleanable/dirt) ? 10 : 50
|
||||
@@ -165,7 +77,7 @@
|
||||
qdel(D)
|
||||
if(D == target)
|
||||
target = null
|
||||
cleaning = 0
|
||||
busy = 0
|
||||
update_icons()
|
||||
|
||||
/mob/living/bot/cleanbot/explode()
|
||||
@@ -185,17 +97,11 @@
|
||||
return
|
||||
|
||||
/mob/living/bot/cleanbot/update_icons()
|
||||
if(cleaning)
|
||||
if(busy)
|
||||
icon_state = "cleanbot-c"
|
||||
else
|
||||
icon_state = "cleanbot[on]"
|
||||
|
||||
/mob/living/bot/cleanbot/turn_off()
|
||||
..()
|
||||
target = null
|
||||
path = list()
|
||||
patrol_path = list()
|
||||
|
||||
/mob/living/bot/cleanbot/attack_hand(var/mob/user)
|
||||
var/dat
|
||||
dat += "<TT><B>Automatic Station Cleaner v1.0</B></TT><BR><BR>"
|
||||
@@ -230,10 +136,6 @@
|
||||
if("patrol")
|
||||
should_patrol = !should_patrol
|
||||
patrol_path = null
|
||||
if("freq")
|
||||
var/freq = text2num(input("Select frequency for navigation beacons", "Frequnecy", num2text(beacon_freq / 10))) * 10
|
||||
if (freq > 0)
|
||||
beacon_freq = freq
|
||||
if("screw")
|
||||
screwloose = !screwloose
|
||||
usr << "<span class='notice'>You twiddle the screw.</span>"
|
||||
@@ -264,25 +166,6 @@
|
||||
if(blood)
|
||||
target_types += /obj/effect/decal/cleanable/blood
|
||||
|
||||
/* Radio object that listens to signals */
|
||||
|
||||
/obj/cleanbot_listener
|
||||
var/mob/living/bot/cleanbot/cleanbot = null
|
||||
var/list/memorized = list()
|
||||
|
||||
/obj/cleanbot_listener/receive_signal(var/datum/signal/signal)
|
||||
var/recv = signal.data["beacon"]
|
||||
var/valid = signal.data["patrol"]
|
||||
if(!recv || !valid || !cleanbot)
|
||||
return
|
||||
|
||||
var/dist = get_dist(cleanbot, signal.source.loc)
|
||||
memorized[recv] = signal.source.loc
|
||||
|
||||
if(dist < cleanbot.closest_dist) // We check all signals, choosing the closest beakon; then we move to the NEXT one after the closest one
|
||||
cleanbot.closest_dist = dist
|
||||
cleanbot.next_dest = signal.data["next_patrol"]
|
||||
|
||||
/* Assembly */
|
||||
|
||||
/obj/item/weapon/bucket_sensor
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
health = 100
|
||||
maxHealth = 100
|
||||
|
||||
bot_version = "2.5"
|
||||
is_ranged = 1
|
||||
preparing_arrest_sounds = new()
|
||||
|
||||
@@ -20,7 +19,7 @@
|
||||
var/last_shot = 0
|
||||
|
||||
/mob/living/bot/secbot/ed209/update_icons()
|
||||
if(on && is_attacking)
|
||||
if(on && busy)
|
||||
icon_state = "ed209-c"
|
||||
else
|
||||
icon_state = "ed209[on]"
|
||||
@@ -50,6 +49,9 @@
|
||||
new /obj/effect/decal/cleanable/blood/oil(Tsec)
|
||||
qdel(src)
|
||||
|
||||
/mob/living/bot/secbot/ed209/handleRangedTarget()
|
||||
RangedAttack(target)
|
||||
|
||||
/mob/living/bot/secbot/ed209/RangedAttack(var/atom/A)
|
||||
if(last_shot + shot_delay > world.time)
|
||||
src << "You are not ready to fire yet!"
|
||||
|
||||
@@ -22,17 +22,12 @@
|
||||
|
||||
var/obj/structure/reagent_dispensers/watertank/tank
|
||||
|
||||
var/attacking = 0
|
||||
var/list/path = list()
|
||||
var/atom/target
|
||||
var/frustration = 0
|
||||
|
||||
/mob/living/bot/farmbot/New()
|
||||
..()
|
||||
spawn(5)
|
||||
tank = locate() in contents
|
||||
if(!tank)
|
||||
tank = new /obj/structure/reagent_dispensers/watertank(src)
|
||||
/mob/living/bot/farmbot/New(var/newloc, var/newTank)
|
||||
..(newloc)
|
||||
if(!newTank)
|
||||
newTank = new /obj/structure/reagent_dispensers/watertank(src)
|
||||
tank = newTank
|
||||
tank.forceMove(src)
|
||||
|
||||
/mob/living/bot/farmbot/attack_hand(var/mob/user as mob)
|
||||
. = ..()
|
||||
@@ -110,63 +105,55 @@
|
||||
else
|
||||
icon_state = "farmbot[on]"
|
||||
|
||||
/mob/living/bot/farmbot/Life()
|
||||
..()
|
||||
if(!on)
|
||||
return
|
||||
/mob/living/bot/farmbot/handleRegular()
|
||||
if(emagged && prob(1))
|
||||
flick("farmbot_broke", src)
|
||||
if(client)
|
||||
return
|
||||
|
||||
if(target)
|
||||
if(Adjacent(target))
|
||||
UnarmedAttack(target)
|
||||
path = list()
|
||||
target = null
|
||||
else
|
||||
if(path.len && frustration < 5)
|
||||
if(path[1] == loc)
|
||||
path -= path[1]
|
||||
var/t = step_towards(src, path[1])
|
||||
if(t)
|
||||
path -= path[1]
|
||||
else
|
||||
++frustration
|
||||
else
|
||||
path = list()
|
||||
target = null
|
||||
/mob/living/bot/farmbot/handleAdjacentTarget()
|
||||
UnarmedAttack(target)
|
||||
|
||||
/mob/living/bot/farmbot/lookForTargets()
|
||||
if(emagged)
|
||||
for(var/mob/living/carbon/human/H in view(7, src))
|
||||
target = H
|
||||
return
|
||||
else
|
||||
if(emagged)
|
||||
for(var/mob/living/carbon/human/H in view(7, src))
|
||||
target = H
|
||||
break
|
||||
else
|
||||
for(var/obj/machinery/portable_atmospherics/hydroponics/tray in view(7, src))
|
||||
if(process_tray(tray))
|
||||
target = tray
|
||||
frustration = 0
|
||||
break
|
||||
if(!target && refills_water && tank && tank.reagents.total_volume < tank.reagents.maximum_volume)
|
||||
for(var/obj/structure/sink/source in view(7, src))
|
||||
target = source
|
||||
frustration = 0
|
||||
break
|
||||
if(target)
|
||||
var/t = get_dir(target, src) // Turf with the tray is impassable, so a* can't navigate directly to it
|
||||
path = AStar(loc, get_step(target, t), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id = botcard)
|
||||
if(!path)
|
||||
path = list()
|
||||
for(var/obj/machinery/portable_atmospherics/hydroponics/tray in view(7, src))
|
||||
if(confirmTarget(tray))
|
||||
target = tray
|
||||
return
|
||||
if(!target && refills_water && tank && tank.reagents.total_volume < tank.reagents.maximum_volume)
|
||||
for(var/obj/structure/sink/source in view(7, src))
|
||||
target = source
|
||||
return
|
||||
|
||||
/mob/living/bot/farmbot/calcTargetPath() // We need to land NEXT to the tray, because the tray itself is impassable
|
||||
for(var/trayDir in list(NORTH, SOUTH, EAST, WEST))
|
||||
target_path = AStar(get_turf(loc), get_step(get_turf(target), trayDir), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, max_target_dist, id = botcard)
|
||||
if(target_path)
|
||||
break
|
||||
if(!target_path)
|
||||
ignore_list |= target
|
||||
target = null
|
||||
target_path = list()
|
||||
return
|
||||
|
||||
/mob/living/bot/farmbot/stepToTarget() // Same reason
|
||||
var/turf/T = get_turf(target)
|
||||
if(!target_path.len || !T.Adjacent(target_path[target_path.len]))
|
||||
calcTargetPath()
|
||||
makeStep(target_path)
|
||||
return
|
||||
|
||||
/mob/living/bot/farmbot/UnarmedAttack(var/atom/A, var/proximity)
|
||||
if(!..())
|
||||
return
|
||||
if(attacking)
|
||||
if(busy)
|
||||
return
|
||||
|
||||
if(istype(A, /obj/machinery/portable_atmospherics/hydroponics))
|
||||
var/obj/machinery/portable_atmospherics/hydroponics/T = A
|
||||
var/t = process_tray(T)
|
||||
var/t = confirmTarget(T)
|
||||
switch(t)
|
||||
if(0)
|
||||
return
|
||||
@@ -174,7 +161,7 @@
|
||||
action = "water" // Needs a better one
|
||||
update_icons()
|
||||
visible_message("<span class='notice'>[src] starts [T.dead? "removing the plant from" : "harvesting"] \the [A].</span>")
|
||||
attacking = 1
|
||||
busy = 1
|
||||
if(do_after(src, 30))
|
||||
visible_message("<span class='notice'>[src] [T.dead? "removes the plant from" : "harvests"] \the [A].</span>")
|
||||
T.attack_hand(src)
|
||||
@@ -182,7 +169,7 @@
|
||||
action = "water"
|
||||
update_icons()
|
||||
visible_message("<span class='notice'>[src] starts watering \the [A].</span>")
|
||||
attacking = 1
|
||||
busy = 1
|
||||
if(do_after(src, 30))
|
||||
playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
|
||||
visible_message("<span class='notice'>[src] waters \the [A].</span>")
|
||||
@@ -191,7 +178,7 @@
|
||||
action = "hoe"
|
||||
update_icons()
|
||||
visible_message("<span class='notice'>[src] starts uprooting the weeds in \the [A].</span>")
|
||||
attacking = 1
|
||||
busy = 1
|
||||
if(do_after(src, 30))
|
||||
visible_message("<span class='notice'>[src] uproots the weeds in \the [A].</span>")
|
||||
T.weedlevel = 0
|
||||
@@ -199,11 +186,11 @@
|
||||
action = "fertile"
|
||||
update_icons()
|
||||
visible_message("<span class='notice'>[src] starts fertilizing \the [A].</span>")
|
||||
attacking = 1
|
||||
busy = 1
|
||||
if(do_after(src, 30))
|
||||
visible_message("<span class='notice'>[src] waters \the [A].</span>")
|
||||
visible_message("<span class='notice'>[src] fertilizes \the [A].</span>")
|
||||
T.reagents.add_reagent("ammonia", 10)
|
||||
attacking = 0
|
||||
busy = 0
|
||||
action = ""
|
||||
update_icons()
|
||||
T.update_icon()
|
||||
@@ -213,20 +200,20 @@
|
||||
action = "water"
|
||||
update_icons()
|
||||
visible_message("<span class='notice'>[src] starts refilling its tank from \the [A].</span>")
|
||||
attacking = 1
|
||||
busy = 1
|
||||
while(do_after(src, 10) && tank.reagents.total_volume < tank.reagents.maximum_volume)
|
||||
tank.reagents.add_reagent("water", 10)
|
||||
if(prob(5))
|
||||
playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
|
||||
attacking = 0
|
||||
busy = 0
|
||||
action = ""
|
||||
update_icons()
|
||||
visible_message("<span class='notice'>[src] finishes refilling its tank.</span>")
|
||||
else if(emagged && ishuman(A))
|
||||
var/action = pick("weed", "water")
|
||||
attacking = 1
|
||||
busy = 1
|
||||
spawn(50) // Some delay
|
||||
attacking = 0
|
||||
busy = 0
|
||||
switch(action)
|
||||
if("weed")
|
||||
flick("farmbot_hoe", src)
|
||||
@@ -238,7 +225,8 @@
|
||||
A.attack_generic(src, 5, t)
|
||||
if("water")
|
||||
flick("farmbot_water", src)
|
||||
visible_message("<span class='danger'>[src] splashes [A] with water!</span>") // That's it. RP effect.
|
||||
visible_message("<span class='danger'>[src] splashes [A] with water!</span>")
|
||||
tank.reagents.splash(A, 100)
|
||||
|
||||
/mob/living/bot/farmbot/explode()
|
||||
visible_message("<span class='danger'>[src] blows apart!</span>")
|
||||
@@ -261,8 +249,22 @@
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
/mob/living/bot/farmbot/proc/process_tray(var/obj/machinery/portable_atmospherics/hydroponics/tray)
|
||||
if(!tray || !istype(tray))
|
||||
/mob/living/bot/farmbot/confirmTarget(var/atom/targ)
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
if(emagged && ishuman(targ))
|
||||
if(targ in view(world.view, src))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
if(istype(targ, /obj/structure/sink))
|
||||
if(!tank || tank.reagents.total_volume >= tank.reagents.maximum_volume)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
var/obj/machinery/portable_atmospherics/hydroponics/tray = targ
|
||||
if(!istype(tray))
|
||||
return 0
|
||||
|
||||
if(tray.closed_system || !tray.seed)
|
||||
@@ -291,28 +293,28 @@
|
||||
icon_state = "water_arm"
|
||||
var/build_step = 0
|
||||
var/created_name = "Farmbot"
|
||||
var/obj/tank
|
||||
w_class = 3.0
|
||||
|
||||
New()
|
||||
..()
|
||||
spawn(4) // If an admin spawned it, it won't have a watertank it, so lets make one for em!
|
||||
var tank = locate(/obj/structure/reagent_dispensers/watertank) in contents
|
||||
if(!tank)
|
||||
new /obj/structure/reagent_dispensers/watertank(src)
|
||||
|
||||
/obj/item/weapon/farmbot_arm_assembly/New(var/newloc, var/theTank)
|
||||
..(newloc)
|
||||
if(!theTank) // If an admin spawned it, it won't have a watertank it, so lets make one for em!
|
||||
tank = new /obj/structure/reagent_dispensers/watertank(src)
|
||||
else
|
||||
tank = theTank
|
||||
tank.forceMove(src)
|
||||
|
||||
/obj/structure/reagent_dispensers/watertank/attackby(var/obj/item/robot_parts/S, mob/user as mob)
|
||||
if ((!istype(S, /obj/item/robot_parts/l_arm)) && (!istype(S, /obj/item/robot_parts/r_arm)))
|
||||
..()
|
||||
return
|
||||
|
||||
var/obj/item/weapon/farmbot_arm_assembly/A = new /obj/item/weapon/farmbot_arm_assembly(loc)
|
||||
|
||||
user << "You add the robot arm to [src]."
|
||||
loc = A //Place the water tank into the assembly, it will be needed for the finished bot
|
||||
user.drop_from_inventory(S)
|
||||
qdel(S)
|
||||
|
||||
new /obj/item/weapon/farmbot_arm_assembly(loc, src)
|
||||
|
||||
/obj/item/weapon/farmbot_arm_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob)
|
||||
..()
|
||||
if((istype(W, /obj/item/device/analyzer/plant_analyzer)) && (build_step == 0))
|
||||
@@ -339,10 +341,7 @@
|
||||
else if((isprox(W)) && (build_step == 3))
|
||||
build_step++
|
||||
user << "You complete the Farmbot! Beep boop."
|
||||
var/mob/living/bot/farmbot/S = new /mob/living/bot/farmbot(get_turf(src))
|
||||
for(var/obj/structure/reagent_dispensers/watertank/wTank in contents)
|
||||
wTank.loc = S
|
||||
S.tank = wTank
|
||||
var/mob/living/bot/farmbot/S = new /mob/living/bot/farmbot(get_turf(src), tank)
|
||||
S.name = created_name
|
||||
user.remove_from_mob(W)
|
||||
qdel(W)
|
||||
|
||||
@@ -3,22 +3,20 @@
|
||||
desc = "A little floor repairing robot, he looks so excited!"
|
||||
icon_state = "floorbot0"
|
||||
req_access = list(access_construction)
|
||||
wait_if_pulled = 1
|
||||
min_target_dist = 0
|
||||
|
||||
var/amount = 10 // 1 for tile, 2 for lattice
|
||||
var/maxAmount = 60
|
||||
var/tilemake = 0 // When it reaches 100, bot makes a tile
|
||||
var/repairing = 0
|
||||
var/improvefloors = 0
|
||||
var/eattiles = 0
|
||||
var/maketiles = 0
|
||||
var/targetdirection = null
|
||||
var/list/path = list()
|
||||
var/list/ignorelist = list()
|
||||
var/turf/target
|
||||
var/floor_build_type = /decl/flooring/tiling // Basic steel floor.
|
||||
|
||||
/mob/living/bot/floorbot/update_icons()
|
||||
if(repairing)
|
||||
if(busy)
|
||||
icon_state = "floorbot-c"
|
||||
else if(amount > 0)
|
||||
icon_state = "floorbot[on]"
|
||||
@@ -31,7 +29,7 @@
|
||||
dat += "<TT><B>Automatic Station Floor Repairer v1.0</B></TT><BR><BR>"
|
||||
dat += "Status: <A href='?src=\ref[src];operation=start'>[src.on ? "On" : "Off"]</A><BR>"
|
||||
dat += "Maintenance panel is [open ? "opened" : "closed"]<BR>"
|
||||
//dat += "Tiles left: [amount]<BR>"
|
||||
dat += "Tiles left: [amount]<BR>"
|
||||
dat += "Behvaiour controls are [locked ? "locked" : "unlocked"]<BR>"
|
||||
if(!locked || issilicon(user))
|
||||
dat += "Improves floors: <A href='?src=\ref[src];operation=improve'>[improvefloors ? "Yes" : "No"]</A><BR>"
|
||||
@@ -89,100 +87,90 @@
|
||||
targetdirection = null
|
||||
attack_hand(usr)
|
||||
|
||||
/mob/living/bot/floorbot/turn_off()
|
||||
..()
|
||||
target = null
|
||||
path = list()
|
||||
ignorelist = list()
|
||||
|
||||
/mob/living/bot/floorbot/Life()
|
||||
..()
|
||||
|
||||
if(!on)
|
||||
return
|
||||
|
||||
/mob/living/bot/floorbot/handleRegular()
|
||||
++tilemake
|
||||
if(tilemake >= 100)
|
||||
tilemake = 0
|
||||
addTiles(1)
|
||||
|
||||
if(client)
|
||||
return
|
||||
|
||||
if(prob(5))
|
||||
if(prob(1))
|
||||
custom_emote(2, "makes an excited booping beeping sound!")
|
||||
|
||||
if(ignorelist.len) // Don't stick forever
|
||||
for(var/T in ignorelist)
|
||||
if(prob(1))
|
||||
ignorelist -= T
|
||||
|
||||
if(amount && !emagged)
|
||||
if(!target && targetdirection) // Building a bridge
|
||||
var/turf/T = get_step(src, targetdirection)
|
||||
while(T in range(src))
|
||||
if(istype(T, /turf/space))
|
||||
target = T
|
||||
break
|
||||
T = get_step(T, targetdirection)
|
||||
|
||||
if(!target) // Fixing floors
|
||||
for(var/turf/T in view(src))
|
||||
if(T.loc.name == "Space")
|
||||
continue
|
||||
if(T in ignorelist)
|
||||
continue
|
||||
if(istype(T, /turf/space))
|
||||
if(get_turf(T) == loc || prob(40)) // So they target the same tile all the time
|
||||
target = T
|
||||
if(improvefloors && istype(T, /turf/simulated/floor))
|
||||
var/turf/simulated/floor/F = T
|
||||
if(!F.flooring && (get_turf(T) == loc || prob(40)))
|
||||
target = T
|
||||
|
||||
if(emagged) // Time to griff
|
||||
for(var/turf/simulated/floor/D in view(src))
|
||||
if(D.loc.name == "Space")
|
||||
continue
|
||||
if(D in ignorelist)
|
||||
continue
|
||||
target = D
|
||||
break
|
||||
|
||||
if(!target && amount < maxAmount && eattiles || maketiles) // Eat tiles
|
||||
if(eattiles)
|
||||
for(var/obj/item/stack/tile/floor/T in view(src))
|
||||
if(T in ignorelist)
|
||||
continue
|
||||
target = T
|
||||
break
|
||||
if(maketiles && !target)
|
||||
for(var/obj/item/stack/material/steel/T in view(src))
|
||||
if(T in ignorelist)
|
||||
continue
|
||||
target = T
|
||||
break
|
||||
|
||||
if(target && get_turf(target) == loc)
|
||||
/mob/living/bot/floorbot/handleAdjacentTarget()
|
||||
if(get_turf(target) == src.loc)
|
||||
UnarmedAttack(target)
|
||||
|
||||
if(target && get_turf(target) != loc && !path.len)
|
||||
spawn(0)
|
||||
path = AStar(loc, get_turf(target), /turf/proc/AdjacentTurfsSpace, /turf/proc/Distance, 0, 30, id = botcard)
|
||||
if(!path)
|
||||
path = list()
|
||||
ignorelist += target
|
||||
target = null
|
||||
/mob/living/bot/floorbot/lookForTargets()
|
||||
if(emagged) // Time to griff
|
||||
for(var/turf/simulated/floor/D in view(src))
|
||||
if(confirmTarget(D))
|
||||
target = D
|
||||
return
|
||||
|
||||
if(path.len)
|
||||
step_to(src, path[1])
|
||||
path -= path[1]
|
||||
else if(amount)
|
||||
if(targetdirection) // Building a bridge
|
||||
var/turf/T = get_step(src, targetdirection)
|
||||
while(T in range(world.view, src))
|
||||
if(confirmTarget(T))
|
||||
target = T
|
||||
return
|
||||
T = get_step(T, targetdirection)
|
||||
|
||||
else // Fixing floors
|
||||
for(var/turf/space/T in view(src)) // Breaches are of higher priority
|
||||
if(confirmTarget(T))
|
||||
target = T
|
||||
return
|
||||
|
||||
for(var/turf/simulated/mineral/floor/T in view(src)) // Asteroids are of smaller priority
|
||||
if(confirmTarget(T))
|
||||
target = T
|
||||
return
|
||||
|
||||
if(improvefloors)
|
||||
for(var/turf/simulated/floor/T in view(src))
|
||||
if(confirmTarget(T))
|
||||
target = T
|
||||
return
|
||||
|
||||
if(amount < maxAmount && (eattiles || maketiles))
|
||||
for(var/obj/item/stack/S in view(src))
|
||||
if(confirmTarget(S))
|
||||
target = S
|
||||
return
|
||||
|
||||
/mob/living/bot/floorbot/confirmTarget(var/atom/A) // The fact that we do some checks twice may seem confusing but remember that the bot's settings may be toggled while it's moving and we want them to stop in that case
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
if(istype(A, /obj/item/stack/tile/floor))
|
||||
return (amount < maxAmount && eattiles)
|
||||
if(istype(A, /obj/item/stack/material/steel))
|
||||
return (amount < maxAmount && maketiles)
|
||||
|
||||
if(A.loc.name == "Space")
|
||||
return 0
|
||||
|
||||
if(emagged)
|
||||
return (istype(A, /turf/simulated/floor))
|
||||
|
||||
if(!amount)
|
||||
return 0
|
||||
|
||||
if(istype(A, /turf/space))
|
||||
return 1
|
||||
|
||||
if(istype(A, /turf/simulated/mineral/floor))
|
||||
return 1
|
||||
|
||||
var/turf/simulated/floor/T = A
|
||||
return (istype(T) && improvefloors && !T.flooring && (get_turf(T) == loc || prob(40)))
|
||||
|
||||
/mob/living/bot/floorbot/UnarmedAttack(var/atom/A, var/proximity)
|
||||
if(!..())
|
||||
return
|
||||
|
||||
if(repairing)
|
||||
if(busy)
|
||||
return
|
||||
|
||||
if(get_turf(A) != loc)
|
||||
@@ -190,9 +178,9 @@
|
||||
|
||||
if(emagged && istype(A, /turf/simulated/floor))
|
||||
var/turf/simulated/floor/F = A
|
||||
repairing = 1
|
||||
busy = 1
|
||||
update_icons()
|
||||
if(F.is_plating())
|
||||
if(F.flooring)
|
||||
visible_message("<span class='warning'>[src] begins to tear the floor tile from the floor!</span>")
|
||||
if(do_after(src, 50))
|
||||
F.break_tile_to_plating()
|
||||
@@ -203,15 +191,15 @@
|
||||
F.ReplaceWithLattice()
|
||||
addTiles(1)
|
||||
target = null
|
||||
repairing = 0
|
||||
busy = 0
|
||||
update_icons()
|
||||
else if(istype(A, /turf/space))
|
||||
else if(istype(A, /turf/space) || istype(A, /turf/simulated/mineral/floor))
|
||||
var/building = 2
|
||||
if(locate(/obj/structure/lattice, A))
|
||||
building = 1
|
||||
if(amount < building)
|
||||
return
|
||||
repairing = 1
|
||||
busy = 1
|
||||
update_icons()
|
||||
visible_message("<span class='notice'>[src] begins to repair the hole.</span>")
|
||||
if(do_after(src, 50))
|
||||
@@ -223,12 +211,12 @@
|
||||
I = PoolOrNew(/obj/item/stack/rods, src)
|
||||
A.attackby(I, src)
|
||||
target = null
|
||||
repairing = 0
|
||||
busy = 0
|
||||
update_icons()
|
||||
else if(istype(A, /turf/simulated/floor))
|
||||
var/turf/simulated/floor/F = A
|
||||
if(!F.flooring && amount)
|
||||
repairing = 1
|
||||
busy = 1
|
||||
update_icons()
|
||||
visible_message("<span class='notice'>[src] begins to improve the floor.</span>")
|
||||
if(do_after(src, 50))
|
||||
@@ -236,12 +224,12 @@
|
||||
F.set_flooring(get_flooring_data(floor_build_type))
|
||||
addTiles(-1)
|
||||
target = null
|
||||
repairing = 0
|
||||
busy = 0
|
||||
update_icons()
|
||||
else if(istype(A, /obj/item/stack/tile/floor) && amount < maxAmount)
|
||||
var/obj/item/stack/tile/floor/T = A
|
||||
visible_message("<span class='notice'>[src] begins to collect tiles.</span>")
|
||||
repairing = 1
|
||||
busy = 1
|
||||
update_icons()
|
||||
if(do_after(src, 20))
|
||||
if(T)
|
||||
@@ -249,13 +237,13 @@
|
||||
T.use(eaten)
|
||||
addTiles(eaten)
|
||||
target = null
|
||||
repairing = 0
|
||||
busy = 0
|
||||
update_icons()
|
||||
else if(istype(A, /obj/item/stack/material) && amount + 4 <= maxAmount)
|
||||
var/obj/item/stack/material/M = A
|
||||
if(M.get_material_name() == DEFAULT_WALL_MATERIAL)
|
||||
visible_message("<span class='notice'>[src] begins to make tiles.</span>")
|
||||
repairing = 1
|
||||
busy = 1
|
||||
update_icons()
|
||||
if(do_after(50))
|
||||
if(M)
|
||||
|
||||
@@ -3,21 +3,16 @@
|
||||
desc = "A little medical robot. He looks somewhat underwhelmed."
|
||||
icon_state = "medibot0"
|
||||
req_access = list(access_medical)
|
||||
|
||||
var/skin = null //Set to "tox", "ointment" or "o2" for the other two firstaid kits.
|
||||
botcard_access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics)
|
||||
|
||||
var/skin = null //Set to "tox", "ointment" or "o2" for the other two firstaid kits.
|
||||
|
||||
//AI vars
|
||||
var/frustration = 0
|
||||
var/list/path = list()
|
||||
var/mob/living/carbon/human/patient = null
|
||||
var/mob/ignored = list() // Used by emag
|
||||
var/last_newpatient_speak = 0
|
||||
var/vocal = 1
|
||||
|
||||
//Healing vars
|
||||
var/obj/item/weapon/reagent_containers/glass/reagent_glass = null //Can be set to draw from this for reagents.
|
||||
var/currently_healing = 0
|
||||
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.
|
||||
@@ -29,49 +24,26 @@
|
||||
var/treatment_emag = "toxin"
|
||||
var/declare_treatment = 0 //When attempting to treat a patient, should it notify everyone wearing medhuds?
|
||||
|
||||
/mob/living/bot/medbot/Life()
|
||||
..()
|
||||
/mob/living/bot/medbot/handleIdle()
|
||||
if(vocal && 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!")
|
||||
say(message)
|
||||
|
||||
if(!on)
|
||||
return
|
||||
/mob/living/bot/medbot/handleAdjacentTarget()
|
||||
UnarmedAttack(target)
|
||||
|
||||
if(!client)
|
||||
/mob/living/bot/medbot/lookForTargets()
|
||||
for(var/mob/living/carbon/human/H in view(7, src)) // Time to find a patient!
|
||||
if(confirmTarget(H))
|
||||
target = H
|
||||
if(last_newpatient_speak + 300 < world.time)
|
||||
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!")
|
||||
say(message)
|
||||
custom_emote(1, "points at [H.name].")
|
||||
last_newpatient_speak = world.time
|
||||
break
|
||||
|
||||
if(vocal && 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!")
|
||||
say(message)
|
||||
|
||||
if(patient)
|
||||
if(Adjacent(patient))
|
||||
if(!currently_healing)
|
||||
UnarmedAttack(patient)
|
||||
else
|
||||
if(path.len && (get_dist(patient, path[path.len]) > 2)) // We have a path, but it's off
|
||||
path = list()
|
||||
if(!path.len && (get_dist(src, patient) > 1))
|
||||
spawn(0)
|
||||
path = AStar(loc, get_turf(patient), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id = botcard)
|
||||
if(!path)
|
||||
path = list()
|
||||
if(path.len)
|
||||
step_to(src, path[1])
|
||||
path -= path[1]
|
||||
++frustration
|
||||
if(get_dist(src, patient) > 7 || frustration > 8)
|
||||
patient = null
|
||||
else
|
||||
for(var/mob/living/carbon/human/H in view(7, src)) // Time to find a patient!
|
||||
if(valid_healing_target(H))
|
||||
patient = H
|
||||
frustration = 0
|
||||
if(last_newpatient_speak + 300 < world.time)
|
||||
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!")
|
||||
say(message)
|
||||
custom_emote(1, "points at [H.name].")
|
||||
last_newpatient_speak = world.time
|
||||
break
|
||||
|
||||
/mob/living/bot/medbot/UnarmedAttack(var/mob/living/carbon/human/H, var/proximity)
|
||||
/mob/living/bot/medbot/UnarmedAttack(var/mob/living/carbon/human/H)
|
||||
if(!..())
|
||||
return
|
||||
|
||||
@@ -81,25 +53,27 @@
|
||||
if(!istype(H))
|
||||
return
|
||||
|
||||
if(busy)
|
||||
return
|
||||
|
||||
if(H.stat == DEAD)
|
||||
var/death_message = pick("No! NO!", "Live, damnit! LIVE!", "I... I've never lost a patient before. Not today, I mean.")
|
||||
say(death_message)
|
||||
patient = null
|
||||
target = null
|
||||
return
|
||||
|
||||
var/t = valid_healing_target(H)
|
||||
var/t = confirmTarget(H)
|
||||
if(!t)
|
||||
var/message = pick("All patched up!", "An apple a day keeps me away.", "Feel better soon!")
|
||||
say(message)
|
||||
patient = null
|
||||
target = null
|
||||
return
|
||||
|
||||
icon_state = "medibots"
|
||||
visible_message("<span class='warning'>[src] is trying to inject [H]!</span>")
|
||||
if(declare_treatment)
|
||||
var/area/location = get_area(src)
|
||||
broadcast_medical_hud_message("[src] is treating <b>[H]</b> in <b>[location]</b>", src)
|
||||
currently_healing = 1
|
||||
busy = 1
|
||||
update_icons()
|
||||
if(do_mob(src, H, 30))
|
||||
if(t == 1)
|
||||
@@ -107,14 +81,14 @@
|
||||
else
|
||||
H.reagents.add_reagent(t, injection_amount)
|
||||
visible_message("<span class='warning'>[src] injects [H] with the syringe!</span>")
|
||||
currently_healing = 0
|
||||
busy = 0
|
||||
update_icons()
|
||||
|
||||
/mob/living/bot/medbot/update_icons()
|
||||
overlays.Cut()
|
||||
if(skin)
|
||||
overlays += image('icons/obj/aibots.dmi', "medskin_[skin]")
|
||||
if(currently_healing)
|
||||
if(busy)
|
||||
icon_state = "medibots"
|
||||
else
|
||||
icon_state = "medibot[on]"
|
||||
@@ -226,13 +200,13 @@
|
||||
user << "<span class='warning'>You short out [src]'s reagent synthesis circuits.</span>"
|
||||
visible_message("<span class='warning'>[src] buzzes oddly!</span>")
|
||||
flick("medibot_spark", src)
|
||||
patient = null
|
||||
currently_healing = 0
|
||||
target = null
|
||||
busy = 0
|
||||
emagged = 1
|
||||
on = 1
|
||||
update_icons()
|
||||
. = 1
|
||||
ignored |= user
|
||||
ignore_list |= user
|
||||
|
||||
/mob/living/bot/medbot/explode()
|
||||
on = 0
|
||||
@@ -255,15 +229,15 @@
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
/mob/living/bot/medbot/proc/valid_healing_target(var/mob/living/carbon/human/H)
|
||||
/mob/living/bot/medbot/confirmTarget(var/mob/living/carbon/human/H)
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
if(H.stat == DEAD) // He's dead, Jim
|
||||
return null
|
||||
return 0
|
||||
|
||||
if(H.suiciding)
|
||||
return null
|
||||
|
||||
if(H in ignored)
|
||||
return null
|
||||
return 0
|
||||
|
||||
if(emagged)
|
||||
return treatment_emag
|
||||
|
||||
@@ -17,23 +17,22 @@
|
||||
maxHealth = 150
|
||||
mob_bump_flag = HEAVY
|
||||
|
||||
min_target_dist = 0
|
||||
max_target_dist = 250
|
||||
target_speed = 3
|
||||
max_frustration = 5
|
||||
botcard_access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station)
|
||||
|
||||
var/busy = 0
|
||||
var/mode = MULE_IDLE
|
||||
var/atom/movable/load
|
||||
|
||||
var/paused = 0
|
||||
var/crates_only = 1
|
||||
var/auto_return = 1
|
||||
var/safety = 1
|
||||
|
||||
var/list/path = list()
|
||||
var/frustration = 0
|
||||
var/turf/target
|
||||
var/targetName
|
||||
var/turf/home
|
||||
var/homeName
|
||||
var/turf/obstacle
|
||||
|
||||
var/global/amount = 0
|
||||
|
||||
@@ -71,19 +70,6 @@
|
||||
dat += "Power: [on ? "On" : "Off"]<BR>"
|
||||
|
||||
if(!open)
|
||||
dat += "Status: "
|
||||
switch(mode)
|
||||
if(MULE_IDLE)
|
||||
dat += "Ready"
|
||||
if(MULE_MOVING, MULE_UNLOAD, MULE_PATH_DONE)
|
||||
dat += "Navigating"
|
||||
if(MULE_UNLOAD)
|
||||
dat += "Unloading"
|
||||
if(MULE_LOST)
|
||||
dat += "Processing commands"
|
||||
if(MULE_CALC_MIN to MULE_CALC_MAX)
|
||||
dat += "Calculating navigation path"
|
||||
|
||||
dat += "<BR>Current Load: [load ? load.name : "<i>none</i>"]<BR>"
|
||||
|
||||
if(locked)
|
||||
@@ -177,10 +163,9 @@
|
||||
/mob/living/bot/mulebot/proc/obeyCommand(var/command)
|
||||
switch(command)
|
||||
if("Home")
|
||||
mode = MULE_IDLE
|
||||
resetTarget()
|
||||
target = home
|
||||
targetName = "Home"
|
||||
mode = MULE_LOST
|
||||
if("SetD")
|
||||
var/new_dest
|
||||
var/list/beaconlist = new()
|
||||
@@ -192,13 +177,13 @@
|
||||
else
|
||||
alert("No destination beacons available.")
|
||||
if(new_dest)
|
||||
resetTarget()
|
||||
target = get_turf(beaconlist[new_dest])
|
||||
targetName = new_dest
|
||||
if("GoTD")
|
||||
if(mode == MULE_IDLE)
|
||||
mode = MULE_LOST
|
||||
paused = 0
|
||||
if("Stop")
|
||||
mode = MULE_IDLE
|
||||
paused = 1
|
||||
|
||||
/mob/living/bot/mulebot/emag_act(var/remaining_charges, var/user)
|
||||
locked = !locked
|
||||
@@ -211,89 +196,49 @@
|
||||
if(open)
|
||||
icon_state = "mulebot-hatch"
|
||||
return
|
||||
if(mode == MULE_MOVING || mode == MULE_UNLOAD)
|
||||
if(target_path.len && !paused)
|
||||
icon_state = "mulebot1"
|
||||
return
|
||||
icon_state = "mulebot0"
|
||||
|
||||
/mob/living/bot/mulebot/Life()
|
||||
..()
|
||||
|
||||
if(busy)
|
||||
return
|
||||
|
||||
/mob/living/bot/mulebot/handleRegular()
|
||||
if(!safety && prob(1))
|
||||
flick("mulebot-emagged", src)
|
||||
update_icons()
|
||||
|
||||
switch(mode)
|
||||
if(MULE_IDLE) // Idle
|
||||
return
|
||||
if(MULE_MOVING) // Moving to target
|
||||
if(!target) // Return home
|
||||
if(auto_return && home)
|
||||
target = home
|
||||
targetName = "Home"
|
||||
mode = MULE_LOST
|
||||
else
|
||||
mode = MULE_IDLE
|
||||
update_icons()
|
||||
return
|
||||
if(loc == target) // Unload or stop
|
||||
custom_emote(2, "makes a chiming sound.")
|
||||
playsound(loc, 'sound/machines/chime.ogg', 50, 0)
|
||||
mode = MULE_UNLOAD
|
||||
update_icons()
|
||||
return
|
||||
if(path.len) // Move
|
||||
makeStep()
|
||||
sleep(10)
|
||||
if(path.len)
|
||||
makeStep()
|
||||
else
|
||||
mode = MULE_LOST
|
||||
/mob/living/bot/mulebot/handleFrustrated()
|
||||
custom_emote(2, "makes a sighing buzz.")
|
||||
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
||||
..()
|
||||
|
||||
update_icons()
|
||||
return
|
||||
if(MULE_UNLOAD)
|
||||
unload(dir)
|
||||
/mob/living/bot/mulebot/handleAdjacentTarget()
|
||||
if(target == src.loc)
|
||||
custom_emote(2, "makes a chiming sound.")
|
||||
playsound(loc, 'sound/machines/chime.ogg', 50, 0)
|
||||
UnarmedAttack(target)
|
||||
resetTarget()
|
||||
if(auto_return && home && (loc != home))
|
||||
target = home
|
||||
targetName = "Home"
|
||||
|
||||
if(auto_return && home && (loc != home))
|
||||
target = home
|
||||
targetName = "Home"
|
||||
mode = MULE_LOST
|
||||
else
|
||||
mode = MULE_IDLE
|
||||
update_icons()
|
||||
return
|
||||
if(MULE_LOST) // Lost my way
|
||||
if(target)
|
||||
spawn(0)
|
||||
calc_path(obstacle)
|
||||
mode = MULE_CALC_MIN
|
||||
else
|
||||
mode = MULE_IDLE
|
||||
update_icons()
|
||||
return
|
||||
if(MULE_CALC_MIN to MULE_CALC_MAX) // Calcing path
|
||||
if(path.len)
|
||||
mode = MULE_PATH_DONE
|
||||
update_icons()
|
||||
else
|
||||
++mode
|
||||
return
|
||||
if(MULE_PATH_DONE) // Done with path
|
||||
obstacle = null
|
||||
if(path.len)
|
||||
frustration = 0
|
||||
mode = MULE_MOVING
|
||||
else
|
||||
if(home)
|
||||
target = home
|
||||
targetName = "Home"
|
||||
mode = MULE_LOST
|
||||
else
|
||||
mode = MULE_IDLE
|
||||
update_icons()
|
||||
/mob/living/bot/mulebot/confirmTarget()
|
||||
return 1
|
||||
|
||||
/mob/living/bot/mulebot/calcTargetPath()
|
||||
..()
|
||||
if(!target_path.len && target != home) // I presume that target is not null
|
||||
resetTarget()
|
||||
target = home
|
||||
targetName = "Home"
|
||||
|
||||
/mob/living/bot/mulebot/stepToTarget()
|
||||
if(paused)
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/bot/mulebot/UnarmedAttack(var/turf/T)
|
||||
if(T == src.loc)
|
||||
unload(dir)
|
||||
|
||||
/mob/living/bot/mulebot/Bump(var/mob/living/M)
|
||||
if(!safety && istype(M))
|
||||
@@ -302,28 +247,6 @@
|
||||
M.Weaken(5)
|
||||
..()
|
||||
|
||||
/mob/living/bot/mulebot/proc/makeStep()
|
||||
var/turf/next = path[1]
|
||||
if(next == loc)
|
||||
path -= next
|
||||
return
|
||||
|
||||
var/moved = step_towards(src, next)
|
||||
if(moved)
|
||||
frustration = 0
|
||||
path -= next
|
||||
else if(frustration < 6)
|
||||
if(frustration == 3)
|
||||
custom_emote(2, "makes an annoyed buzzing sound")
|
||||
playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0)
|
||||
++frustration
|
||||
else
|
||||
custom_emote(2, "makes a sighing buzz.")
|
||||
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
||||
obstacle = next
|
||||
|
||||
mode = MULE_LOST
|
||||
|
||||
/mob/living/bot/mulebot/proc/runOver(var/mob/living/carbon/human/H)
|
||||
if(istype(H)) // No safety checks - WILL run over lying humans. Stop ERPing in the maint!
|
||||
visible_message("<span class='warning'>[src] drives over [H]!</span>")
|
||||
@@ -362,11 +285,6 @@
|
||||
new /obj/effect/decal/cleanable/blood/oil(Tsec)
|
||||
..()
|
||||
|
||||
/mob/living/bot/mulebot/proc/calc_path(var/turf/avoid = null)
|
||||
path = AStar(loc, target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 250, id = botcard, exclude = avoid)
|
||||
if(!path)
|
||||
path = list()
|
||||
|
||||
/mob/living/bot/mulebot/proc/load(var/atom/movable/C)
|
||||
if(busy || load || get_dist(C, src) > 1 || !isturf(C.loc))
|
||||
return
|
||||
@@ -384,10 +302,6 @@
|
||||
if(istype(crate))
|
||||
crate.close()
|
||||
|
||||
//I'm sure someone will come along and ask why this is here... well people were dragging screen items onto the mule, and that was not cool.
|
||||
//So this is a simple fix that only allows a selection of item types to be considered. Further narrowing-down is below.
|
||||
//if(!istype(C,/obj/item) && !istype(C,/obj/machinery) && !istype(C,/obj/structure) && !ismob(C))
|
||||
// return
|
||||
busy = 1
|
||||
|
||||
C.loc = loc
|
||||
@@ -428,11 +342,3 @@
|
||||
AM.layer = initial(AM.layer)
|
||||
AM.pixel_y = initial(AM.pixel_y)
|
||||
busy = 0
|
||||
|
||||
#undef MULE_IDLE
|
||||
#undef MULE_MOVING
|
||||
#undef MULE_UNLOAD
|
||||
#undef MULE_LOST
|
||||
#undef MULE_CALC_MIN
|
||||
#undef MULE_CALC_MAX
|
||||
#undef MULE_PATH_DONE
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
health = 50
|
||||
req_one_access = list(access_security, access_forensics_lockers)
|
||||
botcard_access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels)
|
||||
|
||||
var/mob/target
|
||||
patrol_speed = 2
|
||||
target_speed = 3
|
||||
|
||||
var/idcheck = 0 // If true, arrests for having weapons without authorization.
|
||||
var/check_records = 0 // If true, arrests people without a record.
|
||||
@@ -16,30 +16,9 @@
|
||||
var/declare_arrests = 0 // If true, announces arrests over sechuds.
|
||||
var/auto_patrol = 0 // If true, patrols on its own
|
||||
|
||||
var/mode = 0
|
||||
#define SECBOT_IDLE 0 // idle
|
||||
#define SECBOT_HUNT 1 // found target, hunting
|
||||
#define SECBOT_ARREST 2 // arresting target
|
||||
#define SECBOT_START_PATROL 3 // start patrol
|
||||
#define SECBOT_WAIT_PATROL 4 // waiting for signals
|
||||
#define SECBOT_PATROL 5 // patrolling
|
||||
#define SECBOT_SUMMON 6 // summoned by PDA
|
||||
var/is_attacking = 0
|
||||
var/is_ranged = 0
|
||||
var/awaiting_surrender = 0
|
||||
|
||||
var/obj/secbot_listener/listener = null
|
||||
var/beacon_freq = 1445 // Navigation beacon frequency
|
||||
var/control_freq = BOT_FREQ // Bot control frequency
|
||||
var/list/path = list()
|
||||
var/frustration = 0
|
||||
var/turf/patrol_target = null // This is where we are headed
|
||||
var/closest_dist // Used to find the closest beakon
|
||||
var/destination = "__nearest__" // This is the current beacon's ID
|
||||
var/next_destination = "__nearest__" // This is the next beacon's ID
|
||||
var/nearest_beacon // Tag of the beakon that we assume to be the closest one
|
||||
|
||||
var/bot_version = 1.3
|
||||
var/list/threat_found_sounds = new('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg')
|
||||
var/list/preparing_arrest_sounds = new('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg')
|
||||
|
||||
@@ -48,24 +27,8 @@
|
||||
desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey."
|
||||
auto_patrol = 1
|
||||
|
||||
/mob/living/bot/secbot/New()
|
||||
..()
|
||||
listener = new /obj/secbot_listener(src)
|
||||
listener.secbot = src
|
||||
|
||||
spawn(5) // Since beepsky is made on the start... this delay is necessary
|
||||
if(radio_controller)
|
||||
radio_controller.add_object(listener, control_freq, filter = RADIO_SECBOT)
|
||||
radio_controller.add_object(listener, beacon_freq, filter = RADIO_NAVBEACONS)
|
||||
|
||||
/mob/living/bot/secbot/turn_off()
|
||||
..()
|
||||
target = null
|
||||
frustration = 0
|
||||
mode = SECBOT_IDLE
|
||||
|
||||
/mob/living/bot/secbot/update_icons()
|
||||
if(on && is_attacking)
|
||||
if(on && busy)
|
||||
icon_state = "secbot-c"
|
||||
else
|
||||
icon_state = "secbot[on]"
|
||||
@@ -78,7 +41,7 @@
|
||||
/mob/living/bot/secbot/attack_hand(var/mob/user)
|
||||
user.set_machine(src)
|
||||
var/dat
|
||||
dat += "<TT><B>Automatic Security Unit v[bot_version]</B></TT><BR><BR>"
|
||||
dat += "<TT><B>Automatic Security Unit</B></TT><BR><BR>"
|
||||
dat += "Status: <A href='?src=\ref[src];power=1'>[on ? "On" : "Off"]</A><BR>"
|
||||
dat += "Behaviour controls are [locked ? "locked" : "unlocked"]<BR>"
|
||||
dat += "Maintenance panel is [open ? "opened" : "closed"]"
|
||||
@@ -89,7 +52,7 @@
|
||||
dat += "Operating Mode: <A href='?src=\ref[src];operation=switchmode'>[arrest_type ? "Detain" : "Arrest"]</A><BR>"
|
||||
dat += "Report Arrests: <A href='?src=\ref[src];operation=declarearrests'>[declare_arrests ? "Yes" : "No"]</A><BR>"
|
||||
dat += "Auto Patrol: <A href='?src=\ref[src];operation=patrol'>[auto_patrol ? "On" : "Off"]</A>"
|
||||
user << browse("<HEAD><TITLE>Securitron v[bot_version] controls</TITLE></HEAD>[dat]", "window=autosec")
|
||||
user << browse("<HEAD><TITLE>Securitron controls</TITLE></HEAD>[dat]", "window=autosec")
|
||||
onclose(user, "autosec")
|
||||
return
|
||||
|
||||
@@ -118,7 +81,6 @@
|
||||
arrest_type = !arrest_type
|
||||
if("patrol")
|
||||
auto_patrol = !auto_patrol
|
||||
mode = SECBOT_IDLE
|
||||
if("declarearrests")
|
||||
declare_arrests = !declare_arrests
|
||||
attack_hand(usr)
|
||||
@@ -129,127 +91,47 @@
|
||||
if(health < curhealth)
|
||||
target = user
|
||||
awaiting_surrender = 5
|
||||
mode = SECBOT_HUNT
|
||||
|
||||
/mob/living/bot/secbot/Life()
|
||||
/mob/living/bot/secbot/startPatrol()
|
||||
if(!locked) // Stop running away when we set you up
|
||||
return
|
||||
..()
|
||||
if(!on)
|
||||
return
|
||||
if(client)
|
||||
return
|
||||
|
||||
if(!target)
|
||||
scan_view()
|
||||
/mob/living/bot/secbot/confirmTarget(var/atom/A)
|
||||
if(!..())
|
||||
return 0
|
||||
|
||||
if(!locked && (mode == SECBOT_START_PATROL || mode == SECBOT_PATROL)) // Stop running away when we set you up
|
||||
mode = SECBOT_IDLE
|
||||
return (check_threat(A) > 3)
|
||||
|
||||
switch(mode)
|
||||
if(SECBOT_IDLE)
|
||||
if(auto_patrol && locked)
|
||||
mode = SECBOT_START_PATROL
|
||||
/mob/living/bot/secbot/lookForTargets()
|
||||
for(var/mob/living/M in view(src))
|
||||
if(M.stat == DEAD)
|
||||
continue
|
||||
if(confirmTarget(M))
|
||||
var/threat = check_threat(M)
|
||||
target = M
|
||||
awaiting_surrender = -1
|
||||
say("Level [threat] infraction alert!")
|
||||
custom_emote(1, "points at [M.name]!")
|
||||
return
|
||||
|
||||
if(SECBOT_HUNT) // Target is in the view or has been recently - chase it
|
||||
if(frustration > 7)
|
||||
target = null
|
||||
frustration = 0
|
||||
awaiting_surrender = 0
|
||||
mode = SECBOT_IDLE
|
||||
return
|
||||
if(target)
|
||||
var/threat = check_threat(target)
|
||||
if(threat < 4) // Re-evaluate in case they dropped the weapon or something
|
||||
target = null
|
||||
frustration = 0
|
||||
awaiting_surrender = 0
|
||||
mode = SECBOT_IDLE
|
||||
return
|
||||
if(!(target in view(7, src)))
|
||||
++frustration
|
||||
if(Adjacent(target))
|
||||
mode = SECBOT_ARREST
|
||||
return
|
||||
else
|
||||
if(is_ranged)
|
||||
RangedAttack(target)
|
||||
else
|
||||
step_towards(src, target) // Melee bots chase a bit faster
|
||||
spawn(8)
|
||||
if(!Adjacent(target))
|
||||
step_towards(src, target)
|
||||
spawn(16)
|
||||
if(!Adjacent(target))
|
||||
step_towards(src, target)
|
||||
/mob/living/bot/secbot/calcTargetPath()
|
||||
..()
|
||||
if(awaiting_surrender != -1)
|
||||
awaiting_surrender = 5 // This implies that a) we have already approached the target and b) it has moved after the warning
|
||||
|
||||
if(SECBOT_ARREST) // Target is next to us - attack it
|
||||
if(!target)
|
||||
mode = SECBOT_IDLE
|
||||
if(!Adjacent(target))
|
||||
awaiting_surrender = 5 // I'm done playing nice
|
||||
mode = SECBOT_HUNT
|
||||
return
|
||||
var/threat = check_threat(target)
|
||||
if(threat < 4)
|
||||
target = null
|
||||
awaiting_surrender = 0
|
||||
frustration = 0
|
||||
mode = SECBOT_IDLE
|
||||
return
|
||||
if(awaiting_surrender < 5 && ishuman(target) && !target.lying)
|
||||
if(awaiting_surrender == 0)
|
||||
say("Down on the floor, [target]! You have five seconds to comply.")
|
||||
++awaiting_surrender
|
||||
else
|
||||
UnarmedAttack(target)
|
||||
if(ishuman(target) && declare_arrests)
|
||||
var/area/location = get_area(src)
|
||||
broadcast_security_hud_message("[src] is [arrest_type ? "detaining" : "arresting"] a level [check_threat(target)] suspect <b>[target]</b> in <b>[location]</b>.", src)
|
||||
return
|
||||
|
||||
if(SECBOT_START_PATROL)
|
||||
if(path.len && patrol_target)
|
||||
mode = SECBOT_PATROL
|
||||
return
|
||||
else if(patrol_target)
|
||||
spawn(0)
|
||||
calc_path()
|
||||
if(!path.len)
|
||||
patrol_target = null
|
||||
mode = SECBOT_IDLE
|
||||
else
|
||||
mode = SECBOT_PATROL
|
||||
if(!patrol_target)
|
||||
if(next_destination)
|
||||
find_next_target()
|
||||
else
|
||||
find_patrol_target()
|
||||
say("Engaging patrol mode.")
|
||||
mode = SECBOT_WAIT_PATROL
|
||||
return
|
||||
|
||||
if(SECBOT_WAIT_PATROL)
|
||||
if(patrol_target)
|
||||
mode = SECBOT_START_PATROL
|
||||
else
|
||||
++frustration
|
||||
if(frustration > 120)
|
||||
frustration = 0
|
||||
mode = SECBOT_IDLE
|
||||
|
||||
if(SECBOT_PATROL)
|
||||
patrol_step()
|
||||
spawn(10)
|
||||
patrol_step()
|
||||
return
|
||||
|
||||
if(SECBOT_SUMMON)
|
||||
patrol_step()
|
||||
spawn(8)
|
||||
patrol_step()
|
||||
spawn(16)
|
||||
patrol_step()
|
||||
return
|
||||
/mob/living/bot/secbot/handleAdjacentTarget()
|
||||
if(awaiting_surrender < 5 && ishuman(target) && !target:lying)
|
||||
if(awaiting_surrender == -1)
|
||||
say("Down on the floor, [target]! You have five seconds to comply.")
|
||||
++awaiting_surrender
|
||||
else
|
||||
UnarmedAttack(target)
|
||||
if(ishuman(target) && declare_arrests)
|
||||
var/area/location = get_area(src)
|
||||
broadcast_security_hud_message("[src] is [arrest_type ? "detaining" : "arresting"] a level [check_threat(target)] suspect <b>[target]</b> in <b>[location]</b>.", src)
|
||||
|
||||
// say("Engaging patrol mode.")
|
||||
|
||||
/mob/living/bot/secbot/UnarmedAttack(var/mob/M, var/proximity)
|
||||
if(!..())
|
||||
@@ -271,31 +153,33 @@
|
||||
C.stun_effect_act(0, 60, null)
|
||||
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
|
||||
do_attack_animation(C)
|
||||
is_attacking = 1
|
||||
busy = 1
|
||||
update_icons()
|
||||
spawn(2)
|
||||
is_attacking = 0
|
||||
busy = 0
|
||||
update_icons()
|
||||
visible_message("<span class='warning'>[C] was prodded by [src] with a stun baton!</span>")
|
||||
else
|
||||
playsound(loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2)
|
||||
visible_message("<span class='warning'>[src] is trying to put handcuffs on [C]!</span>")
|
||||
busy = 1
|
||||
if(do_mob(src, C, 60))
|
||||
if(!C.handcuffed)
|
||||
C.handcuffed = new /obj/item/weapon/handcuffs(C)
|
||||
C.update_inv_handcuffed()
|
||||
if(preparing_arrest_sounds.len)
|
||||
playsound(loc, pick(preparing_arrest_sounds), 50, 0)
|
||||
busy = 0
|
||||
else if(istype(M, /mob/living/simple_animal))
|
||||
var/mob/living/simple_animal/S = M
|
||||
S.AdjustStunned(10)
|
||||
S.adjustBruteLoss(15)
|
||||
do_attack_animation(M)
|
||||
playsound(loc, "swing_hit", 50, 1, -1)
|
||||
is_attacking = 1
|
||||
busy = 1
|
||||
update_icons()
|
||||
spawn(2)
|
||||
is_attacking = 0
|
||||
busy = 0
|
||||
update_icons()
|
||||
visible_message("<span class='warning'>[M] was beaten by [src] with a stun baton!</span>")
|
||||
|
||||
@@ -319,30 +203,8 @@
|
||||
new /obj/effect/decal/cleanable/blood/oil(Tsec)
|
||||
qdel(src)
|
||||
|
||||
/mob/living/bot/secbot/proc/scan_view()
|
||||
for(var/mob/living/M in view(7, src))
|
||||
if(M.invisibility >= INVISIBILITY_LEVEL_ONE)
|
||||
continue
|
||||
if(M.stat)
|
||||
continue
|
||||
|
||||
var/threat = check_threat(M)
|
||||
|
||||
if(threat >= 4)
|
||||
target = M
|
||||
say("Level [threat] infraction alert!")
|
||||
custom_emote(1, "points at [M.name]!")
|
||||
mode = SECBOT_HUNT
|
||||
break
|
||||
return
|
||||
|
||||
/mob/living/bot/secbot/proc/calc_path(var/turf/avoid = null)
|
||||
path = AStar(loc, patrol_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 120, id=botcard, exclude=avoid)
|
||||
if(!path)
|
||||
path = list()
|
||||
|
||||
/mob/living/bot/secbot/proc/check_threat(var/mob/living/M)
|
||||
if(!M || !istype(M) || M.stat || src == M)
|
||||
if(!M || !istype(M) || M.stat == DEAD || src == M)
|
||||
return 0
|
||||
|
||||
if(emagged)
|
||||
@@ -350,131 +212,6 @@
|
||||
|
||||
return M.assess_perp(access_scanner, 0, idcheck, check_records, check_arrest)
|
||||
|
||||
/mob/living/bot/secbot/proc/patrol_step()
|
||||
if(loc == patrol_target)
|
||||
patrol_target = null
|
||||
path = list()
|
||||
mode = SECBOT_IDLE
|
||||
return
|
||||
|
||||
if(path.len && patrol_target)
|
||||
var/turf/next = path[1]
|
||||
if(loc == next)
|
||||
path -= next
|
||||
return
|
||||
var/moved = step_towards(src, next)
|
||||
if(moved)
|
||||
path -= next
|
||||
frustration = 0
|
||||
else
|
||||
++frustration
|
||||
if(frustration > 5) // Make a new path
|
||||
mode = SECBOT_START_PATROL
|
||||
return
|
||||
else
|
||||
mode = SECBOT_START_PATROL
|
||||
|
||||
/mob/living/bot/secbot/proc/find_patrol_target()
|
||||
send_status()
|
||||
nearest_beacon = null
|
||||
next_destination = "__nearest__"
|
||||
listener.post_signal(beacon_freq, "findbeacon", "patrol")
|
||||
|
||||
/mob/living/bot/secbot/proc/find_next_target()
|
||||
send_status()
|
||||
nearest_beacon = null
|
||||
listener.post_signal(beacon_freq, "findbeacon", "patrol")
|
||||
|
||||
/mob/living/bot/secbot/proc/send_status()
|
||||
var/list/kv = list(
|
||||
"type" = "secbot",
|
||||
"name" = name,
|
||||
"loca" = get_area(loc),
|
||||
"mode" = mode
|
||||
)
|
||||
listener.post_signal_multiple(control_freq, kv)
|
||||
|
||||
/obj/secbot_listener
|
||||
var/mob/living/bot/secbot/secbot = null
|
||||
|
||||
/obj/secbot_listener/proc/post_signal(var/freq, var/key, var/value) // send a radio signal with a single data key/value pair
|
||||
post_signal_multiple(freq, list("[key]" = value))
|
||||
|
||||
/obj/secbot_listener/proc/post_signal_multiple(var/freq, var/list/keyval) // send a radio signal with multiple data key/values
|
||||
var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq)
|
||||
if(!frequency)
|
||||
return
|
||||
|
||||
var/datum/signal/signal = new()
|
||||
signal.source = secbot
|
||||
signal.transmission_method = 1
|
||||
signal.data = keyval.Copy()
|
||||
|
||||
if(signal.data["findbeacon"])
|
||||
frequency.post_signal(secbot, signal, filter = RADIO_NAVBEACONS)
|
||||
else if(signal.data["type"] == "secbot")
|
||||
frequency.post_signal(secbot, signal, filter = RADIO_SECBOT)
|
||||
else
|
||||
frequency.post_signal(secbot, signal)
|
||||
|
||||
/obj/secbot_listener/receive_signal(datum/signal/signal)
|
||||
if(!secbot || !secbot.on)
|
||||
return
|
||||
|
||||
var/recv = signal.data["command"]
|
||||
if(recv == "bot_status")
|
||||
secbot.send_status()
|
||||
return
|
||||
|
||||
if(signal.data["active"] == secbot)
|
||||
switch(recv)
|
||||
if("stop")
|
||||
secbot.mode = SECBOT_IDLE
|
||||
secbot.auto_patrol = 0
|
||||
return
|
||||
|
||||
if("go")
|
||||
secbot.mode = SECBOT_IDLE
|
||||
secbot.auto_patrol = 1
|
||||
return
|
||||
|
||||
if("summon")
|
||||
secbot.patrol_target = signal.data["target"]
|
||||
secbot.next_destination = secbot.destination
|
||||
secbot.destination = null
|
||||
//secbot.awaiting_beacon = 0
|
||||
secbot.mode = SECBOT_SUMMON
|
||||
secbot.calc_path()
|
||||
secbot.say("Responding.")
|
||||
return
|
||||
|
||||
recv = signal.data["beacon"]
|
||||
var/valid = signal.data["patrol"]
|
||||
if(!recv || !valid)
|
||||
return
|
||||
|
||||
if(recv == secbot.next_destination) // This beacon is our target
|
||||
secbot.destination = secbot.next_destination
|
||||
secbot.patrol_target = signal.source.loc
|
||||
secbot.next_destination = signal.data["next_patrol"]
|
||||
else if(secbot.next_destination == "__nearest__")
|
||||
var/dist = get_dist(secbot, signal.source.loc)
|
||||
if(dist <= 1)
|
||||
return
|
||||
|
||||
if(secbot.nearest_beacon)
|
||||
if(dist < secbot.closest_dist)
|
||||
secbot.nearest_beacon = recv
|
||||
secbot.patrol_target = secbot.nearest_beacon
|
||||
secbot.next_destination = signal.data["next_patrol"]
|
||||
secbot.closest_dist = dist
|
||||
return
|
||||
else
|
||||
secbot.nearest_beacon = recv
|
||||
secbot.patrol_target = secbot.nearest_beacon
|
||||
secbot.next_destination = signal.data["next_patrol"]
|
||||
secbot.closest_dist = dist
|
||||
|
||||
//Secbot Construction
|
||||
|
||||
/obj/item/clothing/head/helmet/attackby(var/obj/item/device/assembly/signaler/S, mob/user as mob)
|
||||
|
||||
Reference in New Issue
Block a user