diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm index 5af7e94582..1c24593dbb 100644 --- a/code/modules/mob/living/bot/secbot.dm +++ b/code/modules/mob/living/bot/secbot.dm @@ -1,4 +1,4 @@ -#define SECBOT_WAIT_TIME 5 //number of in-game seconds to wait for someone to surrender +#define SECBOT_WAIT_TIME 3 //Around number*2 real seconds to surrender. #define SECBOT_THREAT_ARREST 4 //threat level at which we decide to arrest someone #define SECBOT_THREAT_ATTACK 8 //threat level at which was assume immediate danger and attack right away @@ -9,18 +9,20 @@ maxHealth = 100 health = 100 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) + botcard_access = list(access_security, access_sec_doors, access_forensics_lockers, access_maint_tunnels) patrol_speed = 2 target_speed = 3 var/default_icon_state = "secbot" - var/idcheck = 0 // If true, arrests for having weapons without authorization. - var/check_records = 0 // If true, arrests people without a record. - var/check_arrest = 1 // If true, arrests people who are set to arrest. - var/arrest_type = 0 // If true, doesn't handcuff. You monster. - var/declare_arrests = 0 // If true, announces arrests over sechuds. + var/idcheck = FALSE // If true, arrests for having weapons without authorization. + var/check_records = FALSE // If true, arrests people without a record. + var/check_arrest = TRUE // If true, arrests people who are set to arrest. + var/arrest_type = FALSE // If true, doesn't handcuff. You monster. + var/declare_arrests = FALSE // If true, announces arrests over sechuds. + var/threat = 0 // How much of a threat something is. Set upon acquiring a target. + var/attacked = FALSE // If true, gives the bot enough threat assessment to attack immediately. - var/is_ranged = 0 + var/is_ranged = FALSE var/awaiting_surrender = 0 var/can_next_insult = 0 // Uses world.time var/stun_strength = 60 // For humans. @@ -45,7 +47,7 @@ /mob/living/bot/secbot/beepsky name = "Officer Beepsky" desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey." - will_patrol = 1 + will_patrol = TRUE /mob/living/bot/secbot/slime name = "Slime Securitron" @@ -126,18 +128,18 @@ . = ..() if(!emagged) if(user) - user << "\The [src] buzzes and beeps." - emagged = 1 + to_chat(user, "\The [src] buzzes and beeps.") + emagged = TRUE patrol_speed = 3 target_speed = 4 - return 1 + return TRUE else - user << "\The [src] is already corrupt." + to_chat(user, "\The [src] is already corrupt.") /mob/living/bot/secbot/attackby(var/obj/item/O, var/mob/user) var/curhealth = health . = ..() - if(health < curhealth && on == 1) + if(health < curhealth && on == TRUE) react_to_attack(user) /mob/living/bot/secbot/bullet_act(var/obj/item/projectile/P) @@ -154,18 +156,21 @@ ..() /mob/living/bot/secbot/proc/react_to_attack(mob/attacker) + if(!on) // We don't want it to react if it's off + return + if(!target) playsound(src.loc, pick(threat_found_sounds), 50) global_announcer.autosay("[src] was attacked by a hostile [target_name(attacker)] in [get_area(src)].", "[src]", "Security") target = attacker - awaiting_surrender = INFINITY // Don't try and wait for surrender + attacked = TRUE // Say "freeze!" and demand surrender /mob/living/bot/secbot/proc/demand_surrender(mob/target, var/threat) var/suspect_name = target_name(target) if(declare_arrests) global_announcer.autosay("[src] is [arrest_type ? "detaining" : "arresting"] a level [threat] suspect [suspect_name] in [get_area(src)].", "[src]", "Security") - say("Down on the floor, [suspect_name]! You have [SECBOT_WAIT_TIME] seconds to comply.") + say("Down on the floor, [suspect_name]! You have [SECBOT_WAIT_TIME*2] seconds to comply.") playsound(src.loc, pick(preparing_arrest_sounds), 50) // Register to be told when the target moves GLOB.moved_event.register(target, src, /mob/living/bot/secbot/proc/target_moved) @@ -179,7 +184,8 @@ /mob/living/bot/secbot/resetTarget() ..() GLOB.moved_event.unregister(target, src) - awaiting_surrender = -1 + awaiting_surrender = 0 + attacked = FALSE walk_to(src, 0) /mob/living/bot/secbot/startPatrol() @@ -189,17 +195,18 @@ /mob/living/bot/secbot/confirmTarget(var/atom/A) if(!..()) - return 0 - return (check_threat(A) >= SECBOT_THREAT_ARREST) + return FALSE + check_threat(A) + if(threat >= SECBOT_THREAT_ARREST) + return TRUE /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 + awaiting_surrender = 0 say("Level [threat] infraction alert!") custom_emote(1, "points at [M.name]!") playsound(src.loc, pick(threat_found_sounds), 50) @@ -207,15 +214,15 @@ /mob/living/bot/secbot/handleAdjacentTarget() var/mob/living/carbon/human/H = target - var/threat = check_threat(target) + check_threat(target) if(awaiting_surrender < SECBOT_WAIT_TIME && istype(H) && !H.lying && threat < SECBOT_THREAT_ATTACK) - if(awaiting_surrender == -1) // On first tick of awaiting... + if(awaiting_surrender == 0) // On first tick of awaiting... demand_surrender(target, threat) ++awaiting_surrender else if(declare_arrests) var/action = arrest_type ? "detaining" : "arresting" - if(istype(target, /mob/living/simple_mob)) + if(!ishuman(target)) action = "fighting" global_announcer.autosay("[src] is [action] a level [threat] [action != "fighting" ? "suspect" : "threat"] [target_name(target)] in [get_area(src)].", "[src]", "Security") UnarmedAttack(target) @@ -224,7 +231,6 @@ /mob/living/bot/secbot/proc/insult(var/mob/living/L) if(can_next_insult > world.time) return - var/threat = check_threat(L) if(threat >= 10) playsound(src.loc, 'sound/voice/binsult.ogg', 75) can_next_insult = world.time + 20 SECONDS @@ -240,47 +246,47 @@ if(!istype(M)) return - if(istype(M, /mob/living/carbon)) - var/mob/living/carbon/C = M - var/cuff = 1 - if(istype(C, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = C - if(istype(H.back, /obj/item/weapon/rig) && istype(H.gloves,/obj/item/clothing/gloves/gauntlets/rig)) - cuff = 0 - if(!C.lying || C.handcuffed || arrest_type) - cuff = 0 + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/cuff = TRUE + + if(!H.lying || H.handcuffed || arrest_type) + cuff = FALSE if(!cuff) - C.stun_effect_act(0, stun_strength, null) + H.stun_effect_act(0, stun_strength, null) playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) - do_attack_animation(C) - busy = 1 + do_attack_animation(H) + busy = TRUE update_icons() spawn(2) - busy = 0 + busy = FALSE update_icons() - visible_message("\The [C] was prodded by \the [src] with a stun baton!") - insult(C) + visible_message("\The [H] was prodded by \the [src] with a stun baton!") + insult(H) else playsound(loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2) - visible_message("\The [src] is trying to put handcuffs on \the [C]!") - busy = 1 - if(do_mob(src, C, 60)) - if(!C.handcuffed) - C.handcuffed = new /obj/item/weapon/handcuffs(C) - C.update_inv_handcuffed() - busy = 0 - else if(istype(M, /mob/living/simple_mob)) - var/mob/living/simple_mob/S = M - S.adjustBruteLoss(xeno_harm_strength) + visible_message("\The [src] is trying to put handcuffs on \the [H]!") + busy = TRUE + if(do_mob(src, H, 60)) + if(!H.handcuffed) + if(istype(H.back, /obj/item/weapon/rig) && istype(H.gloves,/obj/item/clothing/gloves/gauntlets/rig)) + H.handcuffed = new /obj/item/weapon/handcuffs/cable(H) // Better to be cable cuffed than stun-locked + else + H.handcuffed = new /obj/item/weapon/handcuffs(H) + H.update_inv_handcuffed() + busy = FALSE + else if(istype(M, /mob/living)) + var/mob/living/L = M + L.adjustBruteLoss(xeno_harm_strength) do_attack_animation(M) playsound(loc, "swing_hit", 50, 1, -1) - busy = 1 + busy = TRUE update_icons() spawn(2) - busy = 0 + busy = FALSE update_icons() visible_message("\The [M] was beaten by \the [src] with a stun baton!") - insult(S) + insult(L) /mob/living/bot/secbot/slime/UnarmedAttack(var/mob/living/L, var/proximity) ..() @@ -317,12 +323,15 @@ /mob/living/bot/secbot/proc/check_threat(var/mob/living/M) if(!M || !istype(M) || M.stat == DEAD || src == M) - return 0 + threat = 0 - if(emagged && !M.incapacitated()) //check incapacitated so emagged secbots don't keep attacking the same target forever - return 10 + else if(emagged && !M.incapacitated()) //check incapacitated so emagged secbots don't keep attacking the same target forever + threat = 10 - return M.assess_perp(access_scanner, 0, idcheck, check_records, check_arrest) + else + threat = M.assess_perp(access_scanner, 0, idcheck, check_records, check_arrest) // Set base threat level + if(attacked) + threat += SECBOT_THREAT_ATTACK // Increase enough so we can attack immediately in return //Secbot Construction @@ -365,12 +374,12 @@ if(WT.remove_fuel(0, user)) build_step = 1 overlays += image('icons/obj/aibots.dmi', "hs_hole") - user << "You weld a hole in \the [src]." + to_chat(user, "You weld a hole in \the [src].") else if(isprox(W) && (build_step == 1)) user.drop_item() build_step = 2 - user << "You add \the [W] to [src]." + to_chat(user, "You add \the [W] to [src].") overlays += image('icons/obj/aibots.dmi', "hs_eye") name = "helmet/signaler/prox sensor assembly" qdel(W) @@ -378,14 +387,14 @@ else if((istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm) || (istype(W, /obj/item/organ/external/arm) && ((W.name == "robotic right arm") || (W.name == "robotic left arm")))) && build_step == 2) user.drop_item() build_step = 3 - user << "You add \the [W] to [src]." + to_chat(user, "You add \the [W] to [src].") name = "helmet/signaler/prox sensor/robot arm assembly" overlays += image('icons/obj/aibots.dmi', "hs_arm") qdel(W) else if(istype(W, /obj/item/weapon/melee/baton) && build_step == 3) user.drop_item() - user << "You complete the Securitron! Beep boop." + to_chat(user, "You complete the Securitron! Beep boop.") if(istype(W, /obj/item/weapon/melee/baton/slime)) var/mob/living/bot/secbot/slime/S = new /mob/living/bot/secbot/slime(get_turf(src)) S.name = created_name @@ -399,6 +408,6 @@ var/t = sanitizeSafe(input(user, "Enter new robot name", name, created_name), MAX_NAME_LEN) if(!t) return - if(!in_range(src, usr) && loc != usr) + if(!in_range(src, user) && loc != user) return - created_name = t + created_name = t \ No newline at end of file diff --git a/html/changelogs/Nalarac - Sec Bot.yml b/html/changelogs/Nalarac - Sec Bot.yml new file mode 100644 index 0000000000..af6cfc3e48 --- /dev/null +++ b/html/changelogs/Nalarac - Sec Bot.yml @@ -0,0 +1,38 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: Nalarac + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - bugfix: "Security bots will smack attackers once again." + - tweak: "Security bots will cable cuff people with hardsuit gauntlets instead of stun-locking." + - tweak: "Cut real time for security bots demanding surrender in half (6 seconds)." \ No newline at end of file