diff --git a/code/modules/mob/living/simple_animal/bot/griefsky.dm b/code/modules/mob/living/simple_animal/bot/griefsky.dm new file mode 100644 index 00000000000..0709c2158ae --- /dev/null +++ b/code/modules/mob/living/simple_animal/bot/griefsky.dm @@ -0,0 +1,562 @@ +/mob/living/simple_animal/bot/griefsky + name = "\improper General Griefsky" + desc = "A little security robot. He looks less than thrilled." + icon = 'icons/obj/aibots.dmi' + icon_state = "griefsky0" + density = 0 + anchored = 0 + health = 25 + maxHealth = 25 + damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) + pass_flags = PASSMOB + + radio_channel = "Security" //Security channel + bot_type = SEC_BOT + bot_filter = RADIO_SECBOT + model = "Securitron" + bot_purpose = "seek out criminals, handcuff them, and report their location to security" + bot_core_type = /obj/machinery/bot_core/secbot + window_id = "autosec" + window_name = "Automatic Security Unit v9.0" + path_image_color = "#FF0000" + data_hud_type = DATA_HUD_SECURITY_ADVANCED + allow_pai = 0 + + var/base_icon = "griefsky" + var/mob/living/carbon/target + var/oldtarget_name + var/threatlevel = 0 + var/target_lastloc //Loc of target when arrested. + var/last_found //There's a delay + var/declare_arrests = 1 //When making an arrest, should it notify everyone on the security channel? + //var/idcheck = 0 //If true, arrest people with no IDs + var/weaponscheck = 0 //If true, arrest people for weapons if they lack access + var/check_records = 1 //Does it check security records? + var/arrest_type = 0 //If true, don't handcuff + var/harmbaton = 1 //If true, beat instead of stun + var/flashing_lights = 0 //If true, flash lights + var/prev_flashing_lights = 0 //REMOVE VARS WHEN DONE!!!!!!!!!!!!!PLEASE SACALAS >:/, ya estna en beepsky + var/onetime = 0 + + var/baton_type = /obj/item/melee/baton + +/mob/living/simple_animal/bot/secbot/grievous/Initialize() +. = ..() +weapon = new baton_type(src) +weapon.attack_self(src) + +/mob/living/simple_animal/bot/secbot/grievous/stun_attack(mob/living/carbon/C) //Criminals don't deserve to live + weapon.attack(C, src) + playsound(src, 'sound/weapons/blade1.ogg', 50, TRUE, -1) + if(C.stat == DEAD) + addtimer(CALLBACK(src, .proc/update_icon), 2) + back_to_idle() + + + + + + + + + + + + + + +/mob/living/simple_animal/bot/griefsky/explode() + var/turf/Tsec = get_turf(src) + new /obj/item/stock_parts/cell/potato(Tsec) + var/obj/item/reagent_containers/food/drinks/drinkingglass/S = new(Tsec) + S.reagents.add_reagent("whiskey", 15) + S.on_reagent_change() + ..() + +/mob/living/simple_animal/bot/griefsky/New() + ..() + icon_state = "[base_icon][on]" + spawn(3) + var/datum/job/detective/J = new/datum/job/detective + access_card.access += J.get_access() + prev_access = access_card.access + + //SECHUD + var/datum/atom_hud/secsensor = huds[DATA_HUD_SECURITY_ADVANCED] + secsensor.add_hud_to(src) + permanent_huds |= secsensor + +/mob/living/simple_animal/bot/griefsky/turn_on() + ..() + icon_state = "[base_icon][on]" + +/mob/living/simple_animal/bot/griefsky/turn_off() + ..() + icon_state = "[base_icon][on]" + +/mob/living/simple_animal/bot/griefsky/bot_reset() + ..() + target = null + oldtarget_name = null + anchored = 0 + walk_to(src,0) + last_found = world.time + +/mob/living/simple_animal/bot/griefsky/set_custom_texts() + + text_hack = "You overload [name]'s target identification system." + text_dehack = "You reboot [name] and restore the target identification." + text_dehack_fail = "[name] refuses to accept your authority!" + +/mob/living/simple_animal/bot/griefsky/get_controls(mob/user) + var/dat + dat += hack(user) + dat += showpai(user) + dat += text({" +Securitron v1.6 controls

+Status: []
+Behaviour controls are [locked ? "locked" : "unlocked"]
+Maintenance panel panel is [open ? "opened" : "closed"]"}, + +"[on ? "On" : "Off"]" ) + + if(!locked || issilicon(user) || user.can_admin_interact()) + dat += text({"
+Arrest Unidentifiable Persons: []
+Arrest for Unauthorized Weapons: []
+Arrest for Warrant: []
+Operating Mode: []
+Report Arrests[]
+Auto Patrol: []"}, + +"[idcheck ? "Yes" : "No"]", +"[weaponscheck ? "Yes" : "No"]", +"[check_records ? "Yes" : "No"]", +"[arrest_type ? "Detain" : "Arrest"]", +"[declare_arrests ? "Yes" : "No"]", +"[auto_patrol ? "On" : "Off"]" ) + + return dat + +/mob/living/simple_animal/bot/griefsky/Topic(href, href_list) + if(..()) + return 1 + + switch(href_list["operation"]) + if("idcheck") + idcheck = !idcheck + update_controls() + if("weaponscheck") + weaponscheck = !weaponscheck + update_controls() + if("ignorerec") + check_records = !check_records + update_controls() + if("switchmode") + arrest_type = !arrest_type + update_controls() + if("declarearrests") + declare_arrests = !declare_arrests + update_controls() + + /* + +/mob/living/simple_animal/bot/griefsky/proc/retaliate(mob/living/carbon/human/H) + threatlevel = H.assess_threat(src) + threatlevel += 6 + if(threatlevel >= 4) + target = H + mode = BOT_HUNT + +/mob/living/simple_animal/bot/griefsky/attack_hand(mob/living/carbon/human/H) + if(H.a_intent == INTENT_HARM || H.a_intent == INTENT_DISARM) + retaliate(H) + return ..() + +/mob/living/simple_animal/bot/griefsky/attackby(obj/item/W, mob/user, params) + ..() + if(istype(W, /obj/item/weldingtool) && user.a_intent != INTENT_HARM) // Any intent but harm will heal, so we shouldn't get angry. + return + if(!istype(W, /obj/item/screwdriver) && (W.force) && (!target) && (W.damtype != STAMINA) ) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass. + retaliate(user) + +/mob/living/simple_animal/bot/griefsky/emag_act(mob/user) + ..() + if(emagged == 2) + if(user) + to_chat(user, "You short out [src]'s target assessment circuits.") + oldtarget_name = user.name + audible_message("[src] buzzes oddly!") + declare_arrests = 0 + icon_state = "[base_icon][on]" + +/mob/living/simple_animal/bot/griefsky/bullet_act(obj/item/projectile/Proj) + if(istype(Proj ,/obj/item/projectile/beam)||istype(Proj,/obj/item/projectile/bullet)) + if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE)) + if(!Proj.nodamage && Proj.damage < src.health) + retaliate(Proj.firer) + ..() + + +/mob/living/simple_animal/bot/griefsky/UnarmedAttack(atom/A) + if(!on) + return + if(iscarbon(A)) + var/mob/living/carbon/C = A + if(!C.stunned || arrest_type) + stun_attack(A) + else if(C.canBeHandcuffed() && !C.handcuffed) + cuff(A) + else + ..() + + +/mob/living/simple_animal/bot/griefsky/hitby(atom/movable/AM, skipcatch = 0, hitpush = 1, blocked = 0) + if(istype(AM, /obj/item)) + var/obj/item/I = AM + if(I.throwforce < src.health && I.thrownby && ishuman(I.thrownby)) + var/mob/living/carbon/human/H = I.thrownby + retaliate(H) + ..() + + +/mob/living/simple_animal/bot/griefsky/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!") + spawn(60) + if( !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. + return + if(!C.handcuffed) + 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() + +/mob/living/simple_animal/bot/griefsky/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) + spawn(2) + icon_state = "[base_icon][on]" + var/threat = C.assess_threat(src) + if(ishuman(C) && harmbaton) // Bots with harmbaton enabled become shitcurity. - Dave + C.apply_damage(10, BRUTE) + C.SetStuttering(5) + C.Stun(5) + C.Weaken(5) + add_attack_logs(src, C, "stunned") + 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!") + +/mob/living/simple_animal/bot/griefsky/Life(seconds, times_fired) + . = ..() + if(flashing_lights) + switch(light_color) + if(LIGHT_COLOR_PURE_RED) + light_color = LIGHT_COLOR_PURE_BLUE + if(LIGHT_COLOR_PURE_BLUE) + light_color = LIGHT_COLOR_PURE_RED + update_light() + else if(prev_flashing_lights) + light_color = LIGHT_COLOR_PURE_RED + update_light() + + prev_flashing_lights = flashing_lights + +/mob/living/simple_animal/bot/griefsky/verb/toggle_flashing_lights() + set name = "Toggle Flashing Lights" + set category = "Object" + set src = usr + + flashing_lights = !flashing_lights + +/mob/living/simple_animal/bot/griefsky/handle_automated_action() + if(!..()) + return + + flashing_lights = mode == BOT_HUNT + + switch(mode) + if(BOT_IDLE) // idle + walk_to(src,0) + look_for_perp() // 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) + back_to_idle() + return + + if(target) // make sure target exists + if(Adjacent(target) && isturf(target.loc)) // if right next to perp + stun_attack(target) + + mode = BOT_PREP_ARREST + anchored = 1 + 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 + back_to_idle() + + if(BOT_PREP_ARREST) // preparing to arrest target + icon_state = "griefsky-c" + // 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) || target.weakened < 2 ) + 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 + back_to_idle() + return + + if(BOT_ARREST) + if(!target) + anchored = 0 + mode = BOT_IDLE + last_found = world.time + frustration = 0 + return + + if(target.handcuffed) //no target or target cuffed? back to idle. + back_to_idle() + return + + if(!Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && target.weakened < 2)) //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 = 0 + + if(BOT_START_PATROL) + look_for_perp() + start_patrol() + + if(BOT_PATROL) + look_for_perp() + bot_patrol() + + + return + +/mob/living/simple_animal/bot/griefsky/proc/back_to_idle() + anchored = 0 + mode = BOT_IDLE + target = null + last_found = world.time + frustration = 0 + spawn(0) + handle_automated_action() //ensure bot quickly responds + +/mob/living/simple_animal/bot/griefsky/proc/back_to_hunt() + anchored = 0 + frustration = 0 + mode = BOT_HUNT + spawn(0) + handle_automated_action() //ensure bot quickly responds +// look for a criminal in view of the bot + +/mob/living/simple_animal/bot/griefsky/proc/look_for_perp() + anchored = 0 + for(var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal + if((C.stat) || (C.handcuffed)) + continue + + if((C.name == oldtarget_name) && (world.time < last_found + 100)) + continue + + threatlevel = C.assess_threat(src) + + if(!threatlevel) + 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 + icon_state = "griefsky-c" + spawn(0) + handle_automated_action() // ensure bot quickly responds to a perp + break + else + continue +/mob/living/simple_animal/bot/griefsky/proc/check_for_weapons(var/obj/item/slot_item) + if(slot_item && slot_item.needs_permit) + return 1 + return 0 + +/*/mob/living/simple_animal/bot/griefsky/explode() + + walk_to(src,0) + visible_message("[src] blows apart!") + var/turf/Tsec = get_turf(src) + + var/obj/item/griefsky_assembly/Sa = new /obj/item/griefsky_assembly(Tsec) + Sa.build_step = 1 + Sa.overlays += "hs_hole" + Sa.created_name = name + new /obj/item/assembly/prox_sensor(Tsec) + new /obj/item/melee/baton(Tsec) + + if(prob(50)) + new /obj/item/robot_parts/l_arm(Tsec) + + do_sparks(3, 1, src) + + new /obj/effect/decal/cleanable/blood/oil(loc) + ..()*/ + +/mob/living/simple_animal/bot/griefsky/attack_alien(var/mob/living/carbon/alien/user as mob) + ..() + if(!isalien(target)) + target = user + mode = BOT_HUNT + +/mob/living/simple_animal/bot/griefsky/Crossed(atom/movable/AM) + if(ismob(AM) && target) + var/mob/living/carbon/C = AM + if(!istype(C) || !C || in_range(src, target)) + return + C.visible_message("[pick( \ + "[C] dives out of [src]'s way!", \ + "[C] stumbles over [src]!", \ + "[C] jumps out of [src]'s path!", \ + "[C] trips over [src] and falls!", \ + "[C] topples over [src]!", \ + "[C] leaps out of [src]'s way!")]") + C.Weaken(2) + return + ..() */ + + /mob/living/simple_animal/bot/secbot/griefsky/Crossed(atom/movable/AM) + ..() + if(ismob(AM) && AM == target) + visible_message("[src] flails his swords and cuts [AM]!") + playsound(src,'sound/effects/beepskyspinsabre.ogg',100,TRUE,-1) + stun_attack(AM) + +/mob/living/simple_animal/bot/secbot/griefsky/Initialize() + . = ..() + weapon = new baton_type(src) + weapon.attack_self(src) + +/mob/living/simple_animal/bot/secbot/griefsky/Destroy() + QDEL_NULL(weapon) + return ..() + +/mob/living/simple_animal/bot/secbot/griefsky/special_retaliate_after_attack(mob/user) + if(mode != BOT_HUNT) + return + if(prob(block_chance)) + visible_message("[src] deflects [user]'s attack with his energy swords!") + playsound(src, 'sound/weapons/blade1.ogg', 50, TRUE, -1) + return TRUE + +/mob/living/simple_animal/bot/secbot/griefsky/stun_attack(mob/living/carbon/C) //Criminals don't deserve to live + weapon.attack(C, src) + playsound(src, 'sound/weapons/blade1.ogg', 50, TRUE, -1) + if(C.stat == DEAD) + addtimer(CALLBACK(src, .proc/update_icon), 2) + back_to_idle() + + +/mob/living/simple_animal/bot/secbot/griefsky/handle_automated_action() + if(!on) + return + switch(mode) + if(BOT_IDLE) // idle + update_icon() + walk_to(src,0) + look_for_perp() // 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 + update_icon() + playsound(src,'sound/effects/beepskyspinsabre.ogg',100,TRUE,-1) + // general beepsky doesn't give up so easily, jedi scum + if(frustration >= 20) + walk_to(src,0) + back_to_idle() + return + if(target) // make sure target exists + if(Adjacent(target) && isturf(target.loc)) // if right next to perp + target_lastloc = target.loc //stun_attack() can clear the target if they're dead, so this needs to be set first + stun_attack(target) + anchored = TRUE + 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 + back_to_idle() + + if(BOT_START_PATROL) + look_for_perp() + start_patrol() + + if(BOT_PATROL) + look_for_perp() + bot_patrol() + +/mob/living/simple_animal/bot/secbot/griefsky/look_for_perp() + anchored = FALSE + var/judgement_criteria = judgement_criteria() + for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal + if((C.stat) || (C.handcuffed)) + continue + + if((C.name == oldtarget_name) && (world.time < last_found + 100)) + continue + + threatlevel = C.assess_threat(judgement_criteria, weaponcheck=CALLBACK(src, .proc/check_for_weapons)) + + if(!threatlevel) + continue + + else if(threatlevel >= 4) + target = C + oldtarget_name = C.name + speak("Level [threatlevel] infraction alert!") + playsound(src, pick('sound/voice/beepsky/criminal.ogg', 'sound/voice/beepsky/justice.ogg', 'sound/voice/beepsky/freeze.ogg'), 50, FALSE) + 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/handle_automated_action) + break + else + continue + +/obj/machinery/bot_core/secbot + req_access = list(access_security) \ No newline at end of file diff --git a/icons/obj/aibots.dmi b/icons/obj/aibots.dmi index 14ac2d95ac9..8b062c0bb58 100644 Binary files a/icons/obj/aibots.dmi and b/icons/obj/aibots.dmi differ diff --git a/paradise.dme b/paradise.dme index 9e36bf7b5af..a45839b77a4 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1843,6 +1843,7 @@ #include "code\modules\mob\living\simple_animal\bot\emote.dm" #include "code\modules\mob\living\simple_animal\bot\floorbot.dm" #include "code\modules\mob\living\simple_animal\bot\honkbot.dm" +#include "code\modules\mob\living\simple_animal\bot\griefsky.dm" #include "code\modules\mob\living\simple_animal\bot\medbot.dm" #include "code\modules\mob\living\simple_animal\bot\mulebot.dm" #include "code\modules\mob\living\simple_animal\bot\secbot.dm"