From ba98eb4d165457673ad973908a0f5acf7e61999d Mon Sep 17 00:00:00 2001 From: HMBGERDO <61080616+HMBGERDO@users.noreply.github.com> Date: Thu, 22 Feb 2024 00:01:48 +0100 Subject: [PATCH] Bots refactor (#23550) * ugh... bots refactor * everyone uses pathfind and eyes * most hunt logic to proc * Update code/modules/mob/living/simple_animal/bot/ed209bot.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Update code/modules/mob/living/simple_animal/bot/ed209bot.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Sirryan review * Update code/modules/mob/living/simple_animal/bot/honkbot.dm Co-authored-by: warriorstar-orion * better var naming i guess? * Update code/modules/mob/living/simple_animal/bot/ed209bot.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Update code/modules/mob/living/simple_animal/bot/griefsky.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Update code/modules/mob/living/simple_animal/bot/secbot.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Update code/modules/mob/living/simple_animal/bot/secbot.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Update code/modules/mob/living/simple_animal/bot/secbot.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Update code/modules/mob/living/simple_animal/bot/secbot.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Update code/modules/mob/living/simple_animal/bot/secbot.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * Update code/modules/mob/living/simple_animal/bot/secbot.dm Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> * review * uh oh * Update code/modules/mob/living/simple_animal/bot/secbot.dm Co-authored-by: Deniz <66401072+Oyu07@users.noreply.github.com> * Update code/modules/mob/living/simple_animal/bot/secbot.dm Co-authored-by: Deniz <66401072+Oyu07@users.noreply.github.com> * return FALSE --------- Co-authored-by: Ryan <80364400+Sirryan2002@users.noreply.github.com> Co-authored-by: warriorstar-orion Co-authored-by: Deniz <66401072+Oyu07@users.noreply.github.com> --- code/game/objects/items/weapons/stunbaton.dm | 5 + .../mob/living/simple_animal/bot/bot.dm | 37 ++++ .../mob/living/simple_animal/bot/ed209bot.dm | 164 ++++++++------- .../mob/living/simple_animal/bot/griefsky.dm | 73 ++++--- .../mob/living/simple_animal/bot/honkbot.dm | 104 +++++----- .../mob/living/simple_animal/bot/secbot.dm | 190 +++++++++--------- .../simple_animal/bot/syndicate_bots.dm | 52 +++-- 7 files changed, 340 insertions(+), 285 deletions(-) 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)