#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 /mob/living/bot/secbot name = "Securitron" desc = "A little security robot. He looks less than thrilled." icon_state = "secbot0" 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_maint_tunnels) patrol_speed = 2 target_speed = 3 max_frustration = 7 //CHOMPEdit density = TRUE var/default_icon_state = "secbot" 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/retaliates = TRUE //If this type of secbot should retaliate at all - so that slime securitrons don't go ballistic the second they get glomped. var/is_ranged = FALSE var/awaiting_surrender = 0 var/can_next_insult = 0 // Uses world.time var/stun_strength = 60 // For humans. var/xeno_harm_strength = 15 // How hard to hit simple_mobs. var/baton_glow = "#FF6A00" var/used_weapon = /obj/item/melee/baton //Weapon used by the bot var/list/threat_found_sounds = list('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg') var/list/preparing_arrest_sounds = list('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/bcreep.ogg') var/list/fighting_sounds = list('sound/voice/biamthelaw.ogg', 'sound/voice/bradio.ogg', 'sound/voice/bjustice.ogg') //VOREStation Add - They don't like being pulled. This is going to fuck with slimesky, but meh. //Screw you. Just screw you and your 'meh' /mob/living/bot/secbot/Life() ..() if(health > 0 && on && pulledby) if(isliving(pulledby)) var/pull_allowed = FALSE for(var/A in req_one_access) if(A in pulledby.GetAccess()) pull_allowed = TRUE if(!pull_allowed) var/mob/living/L = pulledby UnarmedAttack(L) say("Do not interfere with active law enforcement routines!") global_announcer.autosay("[src] was interfered with in [get_area(src)], activating defense routines.", "[src]", "Security") //VOREStation Add End /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 = TRUE maxHealth = 130 health = 130 /mob/living/bot/secbot/slime name = "Slime Securitron" desc = "A little security robot, with a slime baton subsituted for the regular one." default_icon_state = "slimesecbot" stun_strength = 10 // Slimebatons aren't meant for humans. retaliates = FALSE // No, you're not allowed to beat the slimes to death just because they scratched you. xeno_harm_strength = 9 // Weaker than regular slimesky but they can stun. baton_glow = "#33CCFF" req_one_access = list(access_research, access_robotics) botcard_access = list(access_research, access_robotics, access_xenobiology, access_xenoarch, access_tox, access_tox_storage, access_maint_tunnels) used_weapon = /obj/item/melee/baton/slime var/xeno_stun_strength = 5 // How hard to slimebatoned()'d naughty slimes. 5 works out to 2 discipline and 5 weaken. /mob/living/bot/secbot/slime/slimesky name = "Doctor Slimesky" desc = "An old friend of Officer Beep O'sky. He prescribes beatings to rowdy slimes so that real doctors don't need to treat the xenobiologists." maxHealth = 130 health = 130 /mob/living/bot/secbot/update_icons() if(on && busy) icon_state = "[default_icon_state]-c" else icon_state = "[default_icon_state][on]" if(on) set_light(2, 1, baton_glow) else set_light(0) /mob/living/bot/secbot/tgui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "Secbot", name) ui.open() /mob/living/bot/secbot/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) var/list/data = ..() data["on"] = on data["open"] = open data["locked"] = locked data["idcheck"] = null data["check_records"] = null data["check_arrest"] = null data["arrest_type"] = null data["declare_arrests"] = null data["bot_patrolling"] = null data["will_patrol"] = null if(!locked || issilicon(user)) data["idcheck"] = idcheck data["check_records"] = check_records data["check_arrest"] = check_arrest data["arrest_type"] = arrest_type data["declare_arrests"] = declare_arrests data["bot_patrolling"] = using_map.bot_patrolling data["patrol"] = will_patrol return data /mob/living/bot/secbot/attack_hand(var/mob/user) tgui_interact(user) /mob/living/bot/secbot/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) return add_fingerprint(ui.user) switch(action) if("power") if(!access_scanner.allowed(ui.user)) return FALSE if(on) turn_off() else turn_on() . = TRUE if(locked && !issilicon(ui.user)) return TRUE switch(action) if("idcheck") idcheck = !idcheck . = TRUE if("ignorerec") check_records = !check_records . = TRUE if("ignorearr") check_arrest = !check_arrest . = TRUE if("switchmode") arrest_type = !arrest_type . = TRUE if("patrol") will_patrol = !will_patrol . = TRUE if("declarearrests") declare_arrests = !declare_arrests . = TRUE /mob/living/bot/secbot/emag_act(var/remaining_uses, var/mob/user) . = ..() if(!emagged) if(user) to_chat(user, span_notice("\The [src] buzzes and beeps.")) emagged = TRUE patrol_speed = 3 target_speed = 4 return TRUE else to_chat(user, span_notice("\The [src] is already corrupt.")) /mob/living/bot/secbot/attackby(var/obj/item/O, var/mob/user) var/curhealth = health . = ..() if(health < curhealth && on == TRUE) react_to_attack(user) /mob/living/bot/secbot/bullet_act(var/obj/item/projectile/P) var/curhealth = health var/mob/shooter = P.firer . = ..() //if we already have a target just ignore to avoid lots of checking if(!target && health < curhealth && shooter && (shooter in view(world.view, src))) react_to_attack(shooter) /mob/living/bot/secbot/attack_generic(var/mob/attacker) if(attacker) react_to_attack(attacker) ..() /mob/living/bot/secbot/proc/react_to_attack(mob/attacker) if(!on || !retaliates) // We don't want it to react if it's off or doesn't care return if(!target) playsound(src, 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 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*2] seconds to comply.") playsound(src, pick(preparing_arrest_sounds), 50) // Register to be told when the target moves target.AddComponent(/datum/component/recursive_move) RegisterSignal(target, COMSIG_OBSERVER_MOVED, /mob/living/bot/secbot/proc/target_moved) // Callback invoked if the registered target moves /mob/living/bot/secbot/proc/target_moved(atom/movable/moving_instance, atom/old_loc, atom/new_loc) if(get_dist(get_turf(src), get_turf(target)) >= 1) awaiting_surrender = INFINITY // Done waiting! UnregisterSignal(moving_instance, COMSIG_OBSERVER_MOVED) /mob/living/bot/secbot/resetTarget() ..() if(target) UnregisterSignal(target, COMSIG_OBSERVER_MOVED) awaiting_surrender = 0 attacked = FALSE walk_to(src, 0) /mob/living/bot/secbot/startPatrol() if(!locked) // Stop running away when we set you up return ..() /mob/living/bot/secbot/confirmTarget(var/atom/A) if(!..()) 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)) target = M awaiting_surrender = 0 say("Level [threat] infraction alert!") custom_emote(1, "points at [M.name]!") playsound(src, pick(threat_found_sounds), 50) return /mob/living/bot/secbot/handleAdjacentTarget() var/mob/living/carbon/human/H = target check_threat(target) if(awaiting_surrender < SECBOT_WAIT_TIME && istype(H) && !H.lying && threat < SECBOT_THREAT_ATTACK) 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(!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) /mob/living/bot/secbot/handlePanic() // Speed modification based on alert level. . = 0 switch(get_security_level()) if("green") . = 0 if("yellow") . = 0 if("violet") . = 0 if("orange") . = 0 if("blue") . = 1 if("red") . = 2 if("delta") . = 2 return . // So Beepsky talks while beating up simple mobs. /mob/living/bot/secbot/proc/insult(var/mob/living/L) if(can_next_insult > world.time) return if(threat >= 10) playsound(src, 'sound/voice/binsult.ogg', 75) can_next_insult = world.time + 20 SECONDS else playsound(src, pick(fighting_sounds), 75) can_next_insult = world.time + 5 SECONDS /mob/living/bot/secbot/UnarmedAttack(var/mob/M, var/proximity) if(!..()) return if(!istype(M)) return if(ishuman(M)) var/mob/living/carbon/human/H = M var/cuff = TRUE if(!H.lying || H.handcuffed || arrest_type) cuff = FALSE if(!cuff) H.stun_effect_act(0, stun_strength, null) playsound(src, 'sound/weapons/Egloves.ogg', 50, 1, -1) do_attack_animation(H) busy = TRUE update_icons() spawn(2) busy = FALSE update_icons() visible_message(span_warning("\The [H] was prodded by \the [src] with a stun baton!")) insult(H) else playsound(src, 'sound/weapons/handcuffs.ogg', 30, 1, -2) visible_message(span_warning("\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/rig) && istype(H.gloves,/obj/item/clothing/gloves/gauntlets/rig)) H.handcuffed = new /obj/item/handcuffs/cable(H) // Better to be cable cuffed than stun-locked else H.handcuffed = new /obj/item/handcuffs(H) H.update_handcuffed() busy = FALSE else if(isliving(M)) var/mob/living/L = M L.adjustBruteLoss(xeno_harm_strength) do_attack_animation(M) playsound(src, "swing_hit", 50, 1, -1) busy = TRUE update_icons() spawn(2) busy = FALSE update_icons() visible_message(span_warning("\The [M] was beaten by \the [src] with a stun baton!")) insult(L) /mob/living/bot/secbot/slime/UnarmedAttack(var/mob/living/L, var/proximity) ..() if(istype(L, /mob/living/simple_mob/slime/xenobio)) var/mob/living/simple_mob/slime/xenobio/S = L S.slimebatoned(src, xeno_stun_strength) /mob/living/bot/secbot/explode() visible_message(span_warning("[src] blows apart!")) var/turf/Tsec = get_turf(src) var/obj/item/secbot_assembly/Sa = new /obj/item/secbot_assembly(Tsec) Sa.build_step = 1 Sa.add_overlay("hs_hole") Sa.created_name = name new /obj/item/assembly/prox_sensor(Tsec) new used_weapon(Tsec) if(prob(50)) new /obj/item/robot_parts/l_arm(Tsec) var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(3, 1, src) s.start() new /obj/effect/decal/cleanable/blood/oil(Tsec) //qdel(src) return ..() /mob/living/bot/secbot/proc/target_name(mob/living/T) if(ishuman(T)) var/mob/living/carbon/human/H = T return H.get_id_name("unidentified person") return "unidentified lifeform" /mob/living/bot/secbot/proc/check_threat(var/mob/living/M) if(!M || !istype(M) || M.stat == DEAD || src == M) threat = 0 else if(emagged && !M.incapacitated()) //check incapacitated so emagged secbots don't keep attacking the same target forever threat = 10 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 /obj/item/clothing/head/helmet/attackby(var/obj/item/assembly/signaler/S, mob/user as mob) ..() if(!issignaler(S)) ..() return if(type != /obj/item/clothing/head/helmet) //Eh, but we don't want people making secbots out of space helmets. return if(S.secured) qdel(S) var/obj/item/secbot_assembly/A = new /obj/item/secbot_assembly user.put_in_hands(A) to_chat(user, "You add the signaler to the helmet.") user.drop_from_inventory(src) qdel(src) else return /obj/item/secbot_assembly name = "helmet/signaler assembly" desc = "Some sort of bizarre assembly." icon = 'icons/obj/aibots.dmi' icon_state = "helmet_signaler" item_icons = list( slot_l_hand_str = 'icons/mob/items/lefthand_hats.dmi', slot_r_hand_str = 'icons/mob/items/righthand_hats.dmi', ) item_state = "helmet" var/build_step = 0 var/created_name = "Securitron" /obj/item/secbot_assembly/attackby(var/obj/item/W, var/mob/user) ..() if(W.has_tool_quality(TOOL_WELDER) && !build_step) var/obj/item/weldingtool/WT = W.get_welder() if(WT.remove_fuel(0, user)) build_step = 1 add_overlay("hs_hole") to_chat(user, "You weld a hole in \the [src].") else if(isprox(W) && (build_step == 1)) user.drop_item() build_step = 2 to_chat(user, "You add \the [W] to [src].") add_overlay("hs_eye") name = "helmet/signaler/prox sensor assembly" qdel(W) 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 to_chat(user, "You add \the [W] to [src].") name = "helmet/signaler/prox sensor/robot arm assembly" add_overlay("hs_arm") qdel(W) else if(istype(W, /obj/item/melee/baton) && build_step == 3) user.drop_item() to_chat(user, "You complete the Securitron! Beep boop.") if(istype(W, /obj/item/melee/baton/slime)) var/mob/living/bot/secbot/slime/S = new /mob/living/bot/secbot/slime(get_turf(src)) S.name = created_name else var/mob/living/bot/secbot/S = new /mob/living/bot/secbot(get_turf(src)) S.name = created_name qdel(W) qdel(src) else if(istype(W, /obj/item/pen)) var/t = sanitizeSafe(tgui_input_text(user, "Enter new robot name", name, created_name, MAX_NAME_LEN), MAX_NAME_LEN) if(!t) return if(!in_range(src, user) && loc != user) return created_name = t #undef SECBOT_WAIT_TIME #undef SECBOT_THREAT_ARREST #undef SECBOT_THREAT_ATTACK