diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index ab1bf3d3e91..03cfae8693e 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -281,6 +281,11 @@
return TRUE
..()
+/// baton used for security bots
+/obj/item/melee/baton/infinite_cell
+ hitcost = 0
+ turned_on = TRUE
+
//Makeshift stun baton. Replacement for stun gloves.
/obj/item/melee/baton/cattleprod
name = "stunprod"
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index 4286bf48dfb..08fc714f25d 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -96,6 +96,11 @@
hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD)//Diagnostic HUD views
+ /// storing last chased target known location
+ var/turf/last_target_location
+ /// will be true if we lost target we were chasing
+ var/lost_target = FALSE
+
/obj/item/radio/headset/bot
requires_tcomms = FALSE
canhear_range = 0
@@ -110,6 +115,36 @@
B.radio_config["[B.radio_channel]"] = 1
config(B.radio_config)
+/mob/living/simple_animal/bot/proc/try_chasing_target(mob/target)
+ if(target in view(12, src))
+ if(lost_target)
+ frustration = 0
+ lost_target = FALSE
+ last_target_location = get_turf(target)
+ var/dist = get_dist(src, target)
+ walk_to(src, target, 1, 4)
+ if(get_dist(src, target) >= dist)
+ frustration++
+ return
+
+ if(!lost_target)
+ walk_to(src, 0)
+ lost_target = TRUE
+ frustration = 0
+
+ if(get_turf(src) == last_target_location)
+ frustration += 2
+ return
+
+ if(!bot_move(last_target_location, move_speed = 6))
+ var/last_target_pos_path = get_path_to(src, last_target_location, id = access_card, skip_first = TRUE)
+ if(length(last_target_pos_path) == 0)
+ frustration = 10
+ return
+ set_path(last_target_pos_path)
+ bot_move(last_target_location, move_speed = 6)
+ frustration++
+
/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)
@@ -605,6 +640,8 @@ Pass a positive integer as an argument to override a bot's default speed.
deltimer(reset_access_timer_id)
reset_access_timer_id = null
set_path(null)
+ last_target_location = null
+ lost_target = FALSE
summon_target = null
pathset = FALSE
access_card.access = prev_access
diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
index 0e9270984f0..9b7c3e74d4c 100644
--- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
@@ -40,14 +40,16 @@
var/idcheck = FALSE //If true, arrest people with no IDs
var/weapons_check = TRUE //If true, arrest people for weapons if they don't have access
var/check_records = TRUE //Does it check security records?
- var/arrest_type = FALSE //If true, don't handcuff
+ var/no_handcuffs = FALSE //If true, don't handcuff
var/projectile = /obj/item/projectile/beam/disabler //Holder for projectile type
var/shoot_sound = 'sound/weapons/taser.ogg'
var/baton_delayed = FALSE
-
+ var/obj/item/melee/baton/infinite_cell/baton = null // stunbaton bot uses to melee attack
+ var/currently_cuffing = FALSE // TRUE if we're cuffing someone right now
/mob/living/simple_animal/bot/ed209/Initialize(mapload, created_name, created_lasercolor)
. = ..()
+ baton = new(src)
if(created_name)
name = created_name
if(created_lasercolor)
@@ -59,7 +61,7 @@
if(lasercolor)
shot_delay = 6 //Longer shot delay because JESUS CHRIST
check_records = FALSE //Don't actively target people set to arrest
- arrest_type = TRUE //Don't even try to cuff
+ no_handcuffs = TRUE //Don't even try to cuff
declare_arrests = FALSE // Don't spam sec
req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE, ACCESS_ROBOTICS)
@@ -69,6 +71,10 @@
else if(lasercolor == "r")
name = pick("RED RAMPAGE","RED ROVER","RED KILLDEATH MURDERBOT")
+/mob/living/simple_animal/bot/ed209/Destroy()
+ QDEL_NULL(baton)
+ return ..()
+
/mob/living/simple_animal/bot/ed209/proc/setup_access()
if(access_card)
var/datum/job/detective/J = new/datum/job/detective
@@ -89,6 +95,7 @@
target = null
oldtarget_name = null
anchored = FALSE
+ currently_cuffing = FALSE
walk_to(src,0)
set_path(null)
last_found = world.time
@@ -116,7 +123,7 @@
data["check_id"] = idcheck
data["check_weapons"] = weapons_check
data["check_warrant"] = check_records
- data["arrest_mode"] = arrest_type // detain or arrest
+ data["arrest_mode"] = no_handcuffs // detain or arrest
data["arrest_declare"] = declare_arrests // announce arrests on radio
return data
@@ -148,7 +155,7 @@
if("authwarrant")
check_records = !check_records
if("arrtype")
- arrest_type = !arrest_type
+ no_handcuffs = !no_handcuffs
if("arrdeclare")
declare_arrests = !declare_arrests
if("ejectpai")
@@ -240,40 +247,36 @@
walk_to(src,0)
set_path(null)
if(!lasercolor) //lasertag bots don't want to arrest anyone
- look_for_perp() // see if any criminals are in range
+ if(find_new_target())
+ return // 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)
+ walk_to(src, 0)
set_path(null)
back_to_idle()
+ return
- if(target) // make sure target exists
- if(Adjacent(target) && isturf(target.loc) && !baton_delayed) // if right next to perp
- stun_attack(target)
- if(!lasercolor)
- mode = BOT_PREP_ARREST
- anchored = TRUE
- target_lastloc = target.loc
- return
- else
- mode = BOT_HUNT
- target = null
- target_lastloc = null
- return
-
- else if(!disabled) // 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
+ if(!target) // make sure target exists
back_to_idle()
+ return
+
+ if(Adjacent(target) && isturf(target.loc) && !baton_delayed) // if right next to perp
+ stun_attack(target)
+ if(!lasercolor)
+ mode = BOT_PREP_ARREST
+ anchored = TRUE
+ target_lastloc = target.loc
+ return
+ mode = BOT_HUNT
+ target = null
+ target_lastloc = null
+ return
+
+ try_chasing_target(target)
if(BOT_PREP_ARREST) // preparing to arrest target
@@ -282,42 +285,43 @@
back_to_hunt()
return
- if(iscarbon(target) && target.canBeHandcuffed())
- if(!arrest_type)
- if(!target.handcuffed) //he's not cuffed? Try to cuff him!
- start_cuffing(target)
- else
- back_to_idle()
- return
- else
+ if(no_handcuffs) // should we not cuff?
back_to_idle()
return
- if(BOT_ARREST)
- if(!target)
- anchored = FALSE
- mode = BOT_IDLE
- last_found = world.time
- frustration = 0
+ if(!(iscarbon(target) && target.canBeHandcuffed()))
+ back_to_idle()
return
- if(target.handcuffed) //no target or target cuffed? back to idle.
+ if(currently_cuffing)
+ return
+
+ if(!target.handcuffed)
+ cuff(target)
+ return
+
+ back_to_idle()
+
+ if(BOT_ARREST)
+ if(!target || target.handcuffed)
back_to_idle()
return
if(!Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && world.time - target.stam_regen_start_time < 4 SECONDS && target.getStaminaLoss() <= 100)) //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 = FALSE
+
+ mode = BOT_PREP_ARREST
+ anchored = FALSE
if(BOT_START_PATROL)
- look_for_perp()
+ if(find_new_target())
+ return
start_patrol()
if(BOT_PATROL)
- look_for_perp()
+ if(find_new_target())
+ return
bot_patrol()
@@ -339,9 +343,9 @@
// look for a criminal in view of the bot
-/mob/living/simple_animal/bot/ed209/proc/look_for_perp()
+/mob/living/simple_animal/bot/ed209/proc/find_new_target()
if(disabled)
- return
+ return FALSE
anchored = FALSE
threatlevel = 0
for(var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal
@@ -353,20 +357,18 @@
threatlevel = C.assess_threat(src, lasercolor)
- if(!threatlevel)
+ if(!threatlevel || threatlevel < 4)
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("[src] points at [C.name]!")
- mode = BOT_HUNT
- INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
- break
- else
- continue
+ target = C
+ oldtarget_name = C.name
+ speak("Level [threatlevel] infraction alert!")
+ playsound(loc, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/edplaceholder.ogg'), 50, FALSE)
+ visible_message("[src] points at [C.name]!")
+ mode = BOT_HUNT
+ INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
+ return TRUE
+ return FALSE
/mob/living/simple_animal/bot/ed209/proc/check_for_weapons(obj/item/slot_item)
if(slot_item && slot_item.needs_permit)
@@ -541,10 +543,10 @@
return
if(iscarbon(A))
var/mob/living/carbon/C = A
- if(!C.IsStunned() || arrest_type && !baton_delayed)
+ if(!C.IsStunned() || no_handcuffs && !baton_delayed)
stun_attack(A)
else if(C.canBeHandcuffed() && !C.handcuffed)
- start_cuffing(A)
+ cuff(A)
else
..()
@@ -562,37 +564,33 @@
shootAt(A)
/mob/living/simple_animal/bot/ed209/proc/stun_attack(mob/living/carbon/C)
- playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
+ var/threat = C.assess_threat(src)
+ var/prev_intent = a_intent
+ a_intent = INTENT_HELP
+ baton.attack(C, src)
+ a_intent = prev_intent
+ baton_delayed = TRUE
+ addtimer(VARSET_CALLBACK(src, baton_delayed, FALSE), BATON_COOLDOWN)
icon_state = "[lasercolor]ed209-c"
addtimer(VARSET_CALLBACK(src, icon_state, "[lasercolor]ed209[on]"), 2)
- var/threat = C.assess_threat(src)
- C.SetStuttering(10 SECONDS)
- C.apply_damage(60, STAMINA)
- baton_delayed = TRUE
- C.apply_status_effect(STATUS_EFFECT_DELAYED, 2.5 SECONDS, CALLBACK(C, TYPE_PROC_REF(/mob/living/, KnockDown), 10 SECONDS), COMSIG_LIVING_CLEAR_STUNS)
- addtimer(VARSET_CALLBACK(src, baton_delayed, FALSE), BATON_COOLDOWN)
- add_attack_logs(src, C, "batoned")
if(declare_arrests)
var/area/location = get_area(src)
- speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag [C] in [location].", radio_channel)
- C.visible_message("[src] has stunned [C]!",\
- "[src] has stunned you!")
+ speak("[no_handcuffs ? "Detaining" : "Arresting"] level [threat] scumbag [C] in [location].", radio_channel)
-/mob/living/simple_animal/bot/ed209/proc/start_cuffing(mob/living/carbon/C)
+/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("[src] is trying to put zipties on [C]!",\
"[src] is trying to put zipties on you!")
- addtimer(CALLBACK(src, PROC_REF(cuff_target), C), 6 SECONDS)
+ INVOKE_ASYNC(src, PROC_REF(cuff_callback), C)
-/mob/living/simple_animal/bot/ed209/proc/cuff_target(mob/living/carbon/C)
- if(!Adjacent(C)|| !isturf(C.loc)) //if he's in a closet or not adjacent, we cancel cuffing.
+/mob/living/simple_animal/bot/ed209/proc/cuff_callback(mob/living/carbon/C)
+ if(!do_after(src, 6 SECONDS, target = C))
return
-
- if(!C.handcuffed)
+ if(!C.handcuffed && on)
C.handcuffed = new /obj/item/restraints/handcuffs/cable/zipties/used(C)
C.update_handcuffed()
- back_to_idle()
+ back_to_idle()
#undef BATON_COOLDOWN
diff --git a/code/modules/mob/living/simple_animal/bot/griefsky.dm b/code/modules/mob/living/simple_animal/bot/griefsky.dm
index 2f6977e6066..2530633daa3 100644
--- a/code/modules/mob/living/simple_animal/bot/griefsky.dm
+++ b/code/modules/mob/living/simple_animal/bot/griefsky.dm
@@ -110,7 +110,8 @@
icon_state = "griefsky1"
walk_to(src,0)
set_path(null)
- look_for_perp() // see if any criminals are in range
+ if(find_new_target())
+ return // 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
@@ -121,38 +122,38 @@
set_path(null)
back_to_idle()
return
- if(target) // make sure target exists
- if(target.stat == !DEAD)
- if(Adjacent(target) && isturf(target.loc)) // if right next to perp
- target_lastloc = target.loc
- sword_attack(target)
- anchored = TRUE
- frustration++
- return
- else // not next to perp
- var/turf/olddist = get_dist(src, target)
- walk_to(src, target,1,3) //he's a fast fucker
- if((get_dist(src, target)) >= (olddist))
- frustration++
- else
- frustration = 0
- else
- back_to_idle()
- speak("You fool")
- else
+
+ if(!target) // make sure target exists
back_to_idle()
+ speak("You fool")
+ return
+
+ if(target.stat == DEAD)
+ back_to_idle()
+ return
+
+ if(Adjacent(target) && isturf(target.loc)) // if right next to perp
+ target_lastloc = target.loc
+ sword_attack(target)
+ anchored = TRUE
+ frustration++
+ return // not next to perp
+
+ try_chasing_target(target)
if(BOT_START_PATROL)
- look_for_perp()
+ if(find_new_target())
+ return
start_patrol()
if(BOT_PATROL)
icon_state = "griefsky1"
- look_for_perp()
+ if(find_new_target())
+ return
bot_patrol()
return
-/mob/living/simple_animal/bot/secbot/griefsky/look_for_perp()
+/mob/living/simple_animal/bot/secbot/griefsky/find_new_target()
anchored = FALSE
for(var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal
if((C.stat) || (C.handcuffed))
@@ -163,22 +164,20 @@
threatlevel = C.assess_threat(src)
- if(!threatlevel)
+ if(!threatlevel || threatlevel < 4)
continue
- else if(threatlevel >= 4)
- target = C
- oldtarget_name = C.name
- speak("You are a bold one")
- playsound(src,'sound/weapons/saberon.ogg',50,TRUE,-1)
- visible_message("[src] ignites his energy swords!")
- icon_state = "griefsky-c"
- visible_message("[src] points at [C.name]!")
- mode = BOT_HUNT
- INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
- break
- else
- continue
+ target = C
+ oldtarget_name = C.name
+ speak("You are a bold one")
+ playsound(src,'sound/weapons/saberon.ogg', 50, TRUE, -1)
+ visible_message("[src] ignites his energy swords!")
+ icon_state = "griefsky-c"
+ visible_message("[src] points at [C.name]!")
+ mode = BOT_HUNT
+ INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
+ return TRUE
+ return FALSE
/mob/living/simple_animal/bot/secbot/griefsky/explode()
walk_to(src,0)
diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm
index 85b6e7a0fff..093d9ccb1da 100644
--- a/code/modules/mob/living/simple_animal/bot/honkbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm
@@ -20,14 +20,14 @@
var/honksound = 'sound/items/bikehorn.ogg' //customizable sound
var/spam_flag = FALSE
- var/cooldowntime = 30
- var/cooldowntimehorn = 10
+ var/cooldowntime = 3 SECONDS
+ var/cooldowntimehorn = 1 SECONDS
var/mob/living/carbon/target
var/oldtarget_name
var/target_lastloc = FALSE //Loc of target when arrested.
var/last_found = FALSE //There's a delay
var/threatlevel = FALSE
- var/arrest_type = FALSE
+ var/no_handcuffs = FALSE
/mob/living/simple_animal/bot/honkbot/Initialize(mapload)
. = ..()
@@ -37,9 +37,6 @@
access_card.access += J.get_access()
prev_access = access_card.access
-/mob/living/simple_animal/bot/honkbot/proc/spam_flag_false() //used for addtimer
- spam_flag = FALSE
-
/mob/living/simple_animal/bot/honkbot/proc/sensor_blink()
icon_state = "honkbot-c"
addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_icon)), 5, TIMER_OVERRIDE|TIMER_UNIQUE)
@@ -49,7 +46,7 @@
playsound(src, 'sound/machines/ping.ogg', 50, TRUE, -1) //the first sound upon creation!
spam_flag = TRUE
sensor_blink()
- addtimer(CALLBACK(src, PROC_REF(spam_flag_false)), 18) // calibrates before starting the honk
+ addtimer(VARSET_CALLBACK(src, spam_flag, FALSE), 1.8 SECONDS)
/mob/living/simple_animal/bot/honkbot/proc/react_buzz()
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE, -1)
@@ -146,7 +143,7 @@
if(!emagged)
honk_attack(A)
else
- if(!C.IsStunned() || arrest_type)
+ if(!C.IsStunned() || no_handcuffs)
stun_attack(A)
..()
else if(!spam_flag) //honking at the ground
@@ -167,21 +164,21 @@
playsound(src, honksound, 50, TRUE, -1)
spam_flag = TRUE //prevent spam
sensor_blink()
- addtimer(CALLBACK(src, PROC_REF(spam_flag_false)), cooldowntimehorn)
- else if(emagged) //emagged honkbots will spam short and memorable sounds.
+ addtimer(VARSET_CALLBACK(src, spam_flag, FALSE), cooldowntimehorn)
+ else //emagged honkbots will spam short and memorable sounds.
if(!spam_flag)
playsound(src, "honkbot_e", 50, 0)
spam_flag = TRUE // prevent spam
icon_state = "honkbot-e"
addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, update_icon)), 30, TIMER_OVERRIDE|TIMER_UNIQUE)
- addtimer(CALLBACK(src, PROC_REF(spam_flag_false)), cooldowntimehorn)
+ addtimer(VARSET_CALLBACK(src, spam_flag, FALSE), cooldowntimehorn)
/mob/living/simple_animal/bot/honkbot/proc/honk_attack(mob/living/carbon/C) // horn attack
if(!spam_flag)
playsound(loc, honksound, 50, TRUE, -1)
spam_flag = TRUE // prevent spam
sensor_blink()
- addtimer(CALLBACK(src, PROC_REF(spam_flag_false)), cooldowntimehorn)
+ addtimer(VARSET_CALLBACK(src, spam_flag, FALSE), cooldowntimehorn)
/mob/living/simple_animal/bot/honkbot/proc/stun_attack(mob/living/carbon/C) // airhorn stun
if(!spam_flag)
@@ -203,14 +200,14 @@
target = oldtarget_name
else // you really don't want to hit an emagged honkbot
threatlevel = 6 // will never let you go
- addtimer(CALLBACK(src, PROC_REF(spam_flag_false)), cooldowntime)
+ addtimer(VARSET_CALLBACK(src, spam_flag, FALSE), cooldowntimehorn)
add_attack_logs(src, C, "honked by [src]")
C.visible_message("[src] has honked [C]!",\
"[src] has honked you!")
else
C.Stuttering(40 SECONDS)
C.Stun(20 SECONDS)
- addtimer(CALLBACK(src, PROC_REF(spam_flag_false)), cooldowntime)
+ addtimer(VARSET_CALLBACK(src, spam_flag, FALSE), cooldowntimehorn)
/mob/living/simple_animal/bot/honkbot/handle_automated_action()
@@ -219,7 +216,8 @@
switch(mode)
if(BOT_IDLE) // idle
walk_to(src, 0)
- look_for_perp()
+ if(find_new_target())
+ return
if(!mode && auto_patrol)
mode = BOT_START_PATROL
if(BOT_HUNT)
@@ -229,33 +227,31 @@
playsound(loc, 'sound/misc/sadtrombone.ogg', 25, 1, -1)
back_to_idle()
return
- if(target) // make sure target exists
- if(Adjacent(target) && isturf(target.loc))
- if(threatlevel <= 4)
- honk_attack(target)
- else
- if(threatlevel >= 6)
- set waitfor = 0
- stun_attack(target)
- anchored = FALSE
- 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
+
+ if(!target) // make sure target exists
back_to_idle()
+ return
+
+ if(Adjacent(target) && isturf(target.loc))
+ if(threatlevel <= 4)
+ honk_attack(target)
+ else
+ if(threatlevel >= 6)
+ stun_attack(target)
+ anchored = FALSE
+ target_lastloc = target.loc
+ return
+
+ try_chasing_target(target)
if(BOT_START_PATROL)
- look_for_perp()
+ if(find_new_target())
+ return
start_patrol()
if(BOT_PATROL)
- look_for_perp()
+ if(find_new_target())
+ return
bot_patrol()
return
@@ -273,7 +269,7 @@
mode = BOT_HUNT
INVOKE_ASYNC(src, PROC_REF(handle_automated_action)) // responds quickly
-/mob/living/simple_animal/bot/honkbot/proc/look_for_perp()
+/mob/living/simple_animal/bot/honkbot/proc/find_new_target()
anchored = FALSE
for(var/mob/living/carbon/C in view(7, src))
if((C.stat) || (C.handcuffed))
@@ -282,24 +278,26 @@
if((C.name == oldtarget_name) && (world.time < last_found + 100))
continue
- if(threatlevel <= 3 && !emagged)
- if(C in view(4, src)) //keep the range short for patrolling
- if(!spam_flag)
- bike_horn()
- else if(threatlevel >= 4)
- if(!spam_flag || emagged)
- target = C
- oldtarget_name = C.name
+ if(threatlevel < 4)
+ if(emagged) // actually emagged
bike_horn()
- speak("Honk!")
- visible_message("[src] starts chasing [C.name]!")
- mode = BOT_HUNT
- INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
- break
else
- continue
- else if(emagged)
- bike_horn() //just spam the shit outta this
+ if(C in view(4, src) && !spam_flag) //keep the range short for patrolling
+ bike_horn()
+ continue
+
+ if(spam_flag && !emagged)
+ continue
+
+ target = C
+ oldtarget_name = C.name
+ bike_horn()
+ speak("Honk!")
+ visible_message("[src] starts chasing [C.name]!")
+ mode = BOT_HUNT
+ INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
+ return TRUE
+ return FALSE
/mob/living/simple_animal/bot/honkbot/explode() //doesn't drop cardboard nor its assembly, since its a very frail material.
walk_to(src, 0)
diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm
index b94fa3835fc..2b4821ddc54 100644
--- a/code/modules/mob/living/simple_animal/bot/secbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/secbot.dm
@@ -32,12 +32,27 @@
var/idcheck = FALSE //If true, arrest people with no IDs
var/weapons_check = FALSE //If true, arrest people for weapons if they lack access
var/check_records = TRUE //Does it check security records?
- var/arrest_type = FALSE //If true, don't handcuff
+ var/no_handcuffs = FALSE //If true, don't handcuff
var/harmbaton = FALSE //If true, beat instead of stun
var/flashing_lights = FALSE //If true, flash lights
var/baton_delayed = FALSE
var/prev_flashing_lights = FALSE
allow_pai = FALSE
+ var/obj/item/melee/baton/infinite_cell/baton = null // stunbaton bot uses to melee attack
+ var/currently_cuffing = FALSE // TRUE if we're cuffing someone right now
+ var/played_sound_this_hunt = FALSE // used to make beepsky beep when it lost its target
+
+/mob/living/simple_animal/bot/secbot/Initialize(mapload)
+ . = ..()
+ baton = new(src)
+ icon_state = "[base_icon][on]"
+ var/datum/job/detective/J = new/datum/job/detective
+ access_card.access += J.get_access()
+ prev_access = access_card.access
+
+/mob/living/simple_animal/bot/secbot/Destroy()
+ QDEL_NULL(baton)
+ return ..()
/mob/living/simple_animal/bot/secbot/beepsky
name = "Officer Beepsky"
@@ -72,7 +87,7 @@
base_icon = "rustbot"
icon_state = "rustbot0"
declare_arrests = FALSE
- arrest_type = TRUE
+ no_handcuffs = TRUE
harmbaton = TRUE
emagged = TRUE
@@ -81,16 +96,9 @@
health = 100
maxHealth = 100
idcheck = TRUE
- arrest_type = TRUE
+ no_handcuffs = TRUE
weapons_check = TRUE
-/mob/living/simple_animal/bot/secbot/Initialize(mapload)
- . = ..()
- icon_state = "[base_icon][on]"
-
- var/datum/job/detective/J = new/datum/job/detective
- access_card.access += J.get_access()
- prev_access = access_card.access
/mob/living/simple_animal/bot/secbot/turn_on()
..()
@@ -102,6 +110,8 @@
/mob/living/simple_animal/bot/secbot/bot_reset()
..()
+ currently_cuffing = FALSE
+ played_sound_this_hunt = FALSE
target = null
oldtarget_name = null
anchored = FALSE
@@ -131,7 +141,7 @@
data["check_id"] = idcheck
data["check_weapons"] = weapons_check
data["check_warrant"] = check_records
- data["arrest_mode"] = arrest_type // detain or arrest
+ data["arrest_mode"] = no_handcuffs // detain or arrest
data["arrest_declare"] = declare_arrests // announce arrests on radio
return data
@@ -163,7 +173,7 @@
if("authwarrant")
check_records = !check_records
if("arrtype")
- arrest_type = !arrest_type
+ no_handcuffs = !no_handcuffs
if("arrdeclare")
declare_arrests = !declare_arrests
if("ejectpai")
@@ -214,7 +224,7 @@
return
if(iscarbon(A))
var/mob/living/carbon/C = A
- if((!C.IsWeakened() || arrest_type) && !baton_delayed)
+ if((!C.IsWeakened() || no_handcuffs) && !baton_delayed)
stun_attack(A)
else if(C.canBeHandcuffed() && !C.handcuffed)
cuff(A)
@@ -239,34 +249,30 @@
INVOKE_ASYNC(src, PROC_REF(cuff_callback), C)
/mob/living/simple_animal/bot/secbot/proc/cuff_callback(mob/living/carbon/C)
- if(do_after(src, 60, target = C))
- if(!C.handcuffed && on)
- C.handcuffed = new /obj/item/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()
+ currently_cuffing = TRUE
+ if(!do_after(src, 6 SECONDS, target = C))
+ currently_cuffing = FALSE
+ return
+ currently_cuffing = FALSE
+ if(!C.handcuffed && on)
+ C.handcuffed = new /obj/item/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, FALSE)
+ 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)
- if(harmbaton)
- playsound(loc, 'sound/weapons/genhit1.ogg', 50, 1, -1)
- do_attack_animation(C)
+ var/threat = C.assess_threat(src)
+ var/prev_intent = a_intent
+ a_intent = harmbaton ? INTENT_HARM : INTENT_HELP
+ baton.attack(C, src)
+ a_intent = prev_intent
+ baton_delayed = TRUE
+ addtimer(VARSET_CALLBACK(src, baton_delayed, FALSE), BATON_COOLDOWN)
icon_state = "[base_icon]-c"
addtimer(VARSET_CALLBACK(src, icon_state, "[base_icon][on]"), 2)
- var/threat = C.assess_threat(src)
- if(ishuman(C) && harmbaton) // Bots with harmbaton enabled become shitcurity. - Dave
- C.apply_damage(10, BRUTE)
- C.SetStuttering(10 SECONDS)
- C.apply_damage(60, STAMINA)
- baton_delayed = TRUE
- C.apply_status_effect(STATUS_EFFECT_DELAYED, 2.5 SECONDS, CALLBACK(C, TYPE_PROC_REF(/mob/living/, KnockDown), 10 SECONDS), COMSIG_LIVING_CLEAR_STUNS)
- addtimer(VARSET_CALLBACK(src, baton_delayed, FALSE), BATON_COOLDOWN)
- add_attack_logs(src, C, "batoned")
if(declare_arrests)
var/area/location = get_area(src)
- speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag [C] in [location].", radio_channel)
- C.visible_message("[src] has [harmbaton ? "beaten" : "stunned"] [C]!",\
- "[src] has [harmbaton ? "beaten" : "stunned"] you!")
+ speak("[no_handcuffs ? "Detaining" : "Arresting"] level [threat] scumbag [C] in [location].", radio_channel)
/mob/living/simple_animal/bot/secbot/Life(seconds, times_fired)
. = ..()
@@ -289,6 +295,15 @@
flashing_lights = !flashing_lights
+/mob/living/simple_animal/bot/secbot/try_chasing_target()
+ . = ..()
+ if(lost_target && !played_sound_this_hunt && frustration > 2)
+ playsound(loc, 'sound/machines/synth_no.ogg', 50, FALSE)
+ played_sound_this_hunt = TRUE
+ if(!lost_target && played_sound_this_hunt)
+ playsound(loc, 'sound/machines/synth_yes.ogg', 50, FALSE)
+ played_sound_this_hunt = FALSE
+
/mob/living/simple_animal/bot/secbot/handle_automated_action()
if(!..())
return
@@ -297,84 +312,80 @@
switch(mode)
if(BOT_IDLE) // idle
- walk_to(src,0)
+ walk_to(src, 0)
set_path(null)
- look_for_perp() // see if any criminals are in range
+ if(find_new_target()) // see if any criminals are in range
+ return
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)
+ playsound(loc, 'sound/machines/buzz-two.ogg', 25, FALSE)
+ walk_to(src, 0)
set_path(null)
back_to_idle()
return
- if(target) // make sure target exists
- if(Adjacent(target) && isturf(target.loc) && !baton_delayed) // if right next to perp
- stun_attack(target)
-
- mode = BOT_PREP_ARREST
- anchored = TRUE
- 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
+ if(!target) // make sure target exists
back_to_idle()
+ return
+
+ if(Adjacent(target) && isturf(target.loc) && !baton_delayed) // if right next to perp
+ stun_attack(target)
+ mode = BOT_PREP_ARREST
+ anchored = TRUE
+ target_lastloc = target.loc
+ return
+
+ try_chasing_target(target)
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) || world.time - target.stam_regen_start_time < 4 SECONDS && target.getStaminaLoss() <= 100)
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
+ // target is stunned and nearby
+ if(no_handcuffs) // should we not cuff?
back_to_idle()
return
- if(BOT_ARREST)
- if(!target)
- anchored = FALSE
- mode = BOT_IDLE
- last_found = world.time
- frustration = 0
+ if(!(iscarbon(target) && target.canBeHandcuffed()))
+ back_to_idle()
return
- if(target.handcuffed) //no target or target cuffed? back to idle.
+ if(currently_cuffing)
+ return
+
+ if(!target.handcuffed)
+ cuff(target)
+ return
+
+ back_to_idle()
+
+ if(BOT_ARREST)
+ if(!target || target.handcuffed)
back_to_idle()
return
if(!Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && target.AmountWeakened() < 4 SECONDS)) //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 = FALSE
+ //Try arresting again if the target escapes.
+ mode = BOT_PREP_ARREST
+ anchored = FALSE
if(BOT_START_PATROL)
- look_for_perp()
+ if(find_new_target())
+ return
start_patrol()
if(BOT_PATROL)
- look_for_perp()
+ if(find_new_target())
+ return
bot_patrol()
-
return
/mob/living/simple_animal/bot/secbot/proc/back_to_idle()
@@ -392,7 +403,7 @@
INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
// look for a criminal in view of the bot
-/mob/living/simple_animal/bot/secbot/proc/look_for_perp()
+/mob/living/simple_animal/bot/secbot/proc/find_new_target()
anchored = FALSE
for(var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal
if((C.stat) || (C.handcuffed))
@@ -403,20 +414,19 @@
threatlevel = C.assess_threat(src)
- if(!threatlevel)
+ if(!threatlevel || threatlevel < 4)
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("[src] points at [C.name]!")
- mode = BOT_HUNT
- INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
- break
- else
- continue
+ 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, FALSE)
+ visible_message("[src] points at [C.name]!")
+ mode = BOT_HUNT
+ INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
+ return TRUE
+ return FALSE
+
/mob/living/simple_animal/bot/secbot/proc/check_for_weapons(obj/item/slot_item)
if(slot_item && slot_item.needs_permit)
return 1
diff --git a/code/modules/mob/living/simple_animal/bot/syndicate_bots.dm b/code/modules/mob/living/simple_animal/bot/syndicate_bots.dm
index 94bd9bf579a..291690dda33 100644
--- a/code/modules/mob/living/simple_animal/bot/syndicate_bots.dm
+++ b/code/modules/mob/living/simple_animal/bot/syndicate_bots.dm
@@ -10,7 +10,7 @@
maxHealth = 300
declare_arrests = FALSE
idcheck = TRUE
- arrest_type = TRUE
+ no_handcuffs = TRUE
auto_patrol = TRUE
emagged = TRUE
faction = list("syndicate")
@@ -72,6 +72,11 @@
/mob/living/simple_animal/bot/ed209/syndicate/emag_act(mob/user)
to_chat(user, "[src] has no card reader slot!")
+/mob/living/simple_animal/bot/ed209/syndicate/try_chasing_target()
+ . = ..()
+ if(!lost_target)
+ shootAt(target)
+
/mob/living/simple_animal/bot/ed209/syndicate/ed209_ai()
var/turf/current_turf = get_turf(src)
if(saved_turf && current_turf != saved_turf)
@@ -86,39 +91,42 @@
if(BOT_IDLE)
walk_to(src,0)
set_path(null)
- look_for_perp()
+ if(find_new_target())
+ return
if(!mode && auto_patrol)
mode = BOT_START_PATROL
+
if(BOT_HUNT)
if(frustration >= 8)
walk_to(src,0)
set_path(null)
back_to_idle()
- if(target)
- if(isliving(target))
- if(target.stat == DEAD)
- back_to_idle()
- return
- shootAt(target)
- var/turf/olddist = get_dist(src, target)
- walk_to(src, target,1,4)
- if((get_dist(src, target)) >= (olddist))
- frustration++
- else
- frustration = 0
- else
+ return
+
+ if(!target)
back_to_idle()
+ return
+
+ if(isliving(target) && target.stat == DEAD)
+ back_to_idle()
+ return
+
+ try_chasing_target(target)
+
if(BOT_START_PATROL)
- look_for_perp()
+ if(find_new_target())
+ return
start_patrol()
+
if(BOT_PATROL)
- look_for_perp()
+ if(find_new_target())
+ return
bot_patrol()
else
back_to_idle()
return
-/mob/living/simple_animal/bot/ed209/syndicate/look_for_perp()
+/mob/living/simple_animal/bot/ed209/syndicate/find_new_target()
if(disabled)
return
for(var/mob/M in view(7, src))
@@ -133,9 +141,9 @@
target = M
oldtarget_name = M.name
mode = BOT_HUNT
- spawn(0)
- handle_automated_action()
- break
+ INVOKE_ASYNC(src, PROC_REF(handle_automated_action))
+ return TRUE
+ return FALSE
/mob/living/simple_animal/bot/ed209/syndicate/shootAt(atom/target)
@@ -178,7 +186,7 @@
return
shootAt(A)
-/mob/living/simple_animal/bot/ed209/syndicate/start_cuffing(mob/living/carbon/C)
+/mob/living/simple_animal/bot/ed209/syndicate/cuff(mob/living/carbon/C)
shootAt(C)
/mob/living/simple_animal/bot/ed209/syndicate/stun_attack(mob/living/carbon/C)