diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 04ce1bbe36..5f1ae13336 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -181,4 +181,4 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list( /obj/item/stack/sheet/plasmaglass, /obj/item/stack/sheet/plasmarglass))) -#define is_glass_sheet(O) (is_type_in_typecache(O, GLOB.glass_sheet_types)) +#define is_glass_sheet(O) (is_type_in_typecache(O, GLOB.glass_sheet_types)) \ No newline at end of file diff --git a/code/__DEFINES/robots.dm b/code/__DEFINES/robots.dm index 38dc9e48cc..e08cdb8816 100644 --- a/code/__DEFINES/robots.dm +++ b/code/__DEFINES/robots.dm @@ -34,6 +34,7 @@ #define FLOOR_BOT 4 // Floorbots #define CLEAN_BOT 8 // Cleanbots #define MED_BOT 16 // Medibots +#define HONK_BOT 32 // Honkbots & ED-Honks //AI notification defines #define NEW_BORG 1 @@ -41,3 +42,7 @@ #define RENAME 3 #define AI_SHELL 4 #define DISCONNECT 5 + +//Assembly defines +#define ASSEMBLY_FIRST_STEP 0 +#define ASSEMBLY_SECOND_STEP 1 diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 5f254d759d..5eb85379b7 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -256,6 +256,18 @@ GLOBAL_LIST_INIT(cardboard_recipes, list ( \ /obj/item/stack/sheet/cardboard/fifty amount = 50 +/obj/item/stack/sheet/cardboard/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stamp/clown) && !istype(loc, /obj/item/storage)) + var/atom/droploc = drop_location() + if(use(1)) + playsound(I, 'sound/items/bikehorn.ogg', 50, 1, -1) + to_chat(user, "You stamp the cardboard! Its a clown box! Honk!") + if (amount >= 0) + new/obj/item/storage/box/clown(droploc) //bugfix + else + . = ..() + + /* * Runed Metal */ diff --git a/code/game/objects/items/storage/boxes.dm b/code/game/objects/items/storage/boxes.dm index 7cd28e3bc9..26ea13cb04 100644 --- a/code/game/objects/items/storage/boxes.dm +++ b/code/game/objects/items/storage/boxes.dm @@ -607,6 +607,29 @@ playsound(loc, "rustle", 50, 1, -5) user.visible_message("[user] hugs \the [src].","You hug \the [src].") +/////clown box & honkbot assembly +obj/item/storage/box/clown + name = "clown box" + desc = "A colorful cardboard box for the clown" + icon_state = "clownbox" + illustration = null + +/obj/item/storage/box/clown/attackby(obj/item/I, mob/user, params) + if((istype(I, /obj/item/bodypart/l_arm/robot)) || (istype(I, /obj/item/bodypart/r_arm/robot))) + if(contents.len) //prevent accidently deleting contents + to_chat(user, "You need to empty [src] out first!") + return + if(!user.temporarilyRemoveItemFromInventory(I)) + return + qdel(I) + to_chat(user, "You add some wheels to the [src]! You've got an honkbot assembly now! Honk!") + var/obj/item/honkbot_assembly/A = new + qdel(src) + user.put_in_hands(A) + else + return ..() + +////// /obj/item/storage/box/hug/medical/PopulateContents() new /obj/item/stack/medical/bruise_pack(src) new /obj/item/stack/medical/ointment(src) @@ -730,7 +753,7 @@ /obj/item/storage/box/ingredients //This box is for the randomely chosen version the chef spawns with, it shouldn't actually exist. name = "ingredients box" illustration = "fruit" - var/theme_name + var/theme_name /obj/item/storage/box/ingredients/Initialize() . = ..() diff --git a/code/game/sound.dm b/code/game/sound.dm index 13a3343e06..7ed7822888 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -180,4 +180,6 @@ soundin = pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg') if("law") soundin = 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') + if("honkbot_e") + soundin = pick('sound/items/bikehorn.ogg', 'sound/items/AirHorn2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/items/AirHorn.ogg', 'sound/effects/reee.ogg', 'sound/items/WEEOO1.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bcreep.ogg','sound/magic/Fireball.ogg' ,'sound/effects/pray.ogg', 'sound/voice/hiss1.ogg','sound/machines/buzz-sigh.ogg', 'sound/machines/ping.ogg', 'sound/weapons/flashbang.ogg', 'sound/weapons/bladeslice.ogg') return soundin diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm index c53925e076..9d0a623b35 100644 --- a/code/modules/mob/living/simple_animal/bot/construction.dm +++ b/code/modules/mob/living/simple_animal/bot/construction.dm @@ -347,6 +347,51 @@ S.name = created_name qdel(src) + +//Honkbot Assembly +/obj/item/honkbot_assembly + name = "incomplete honkbot assembly" + desc = "The clown's up to no good once more" + icon = 'icons/mob/aibots.dmi' + icon_state = "honkbot_arm" + var/build_step = ASSEMBLY_FIRST_STEP + var/created_name = "Honkbot" + +/obj/item/honkbot_assembly/attackby(obj/item/I, mob/user, params) + + if(isprox(I) && (build_step == ASSEMBLY_FIRST_STEP)) + if(!user.temporarilyRemoveItemFromInventory(I)) + return + build_step++ + to_chat(user, "You add the [I] to [src]!") + icon_state = "honkbot_proxy" + name = "incomplete Honkbot assembly" + qdel(I) + + else if(istype(I, /obj/item/bikehorn) && (build_step == ASSEMBLY_SECOND_STEP)) + if(istype(loc, /obj/item/storage/backpack)) //don't build them in your backpacks! + return + if(!user.temporarilyRemoveItemFromInventory(I)) + return + to_chat(user, "You add the [I] to [src]! Honk!") + var/T = get_turf(loc) //important to spawn on turf. + var/mob/living/simple_animal/bot/honkbot/S = new(drop_location(T)) + S.name = created_name + S.spam_flag = TRUE // only long enough to hear the first ping. + addtimer(CALLBACK (S, .mob/living/simple_animal/bot/honkbot/proc/react_ping), 5) + qdel(I) + qdel(src) + + else if(istype(I, /obj/item/pen)) + var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + created_name = t + + else return ..() + //Secbot Assembly /obj/item/secbot_assembly name = "incomplete securitron assembly" diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm new file mode 100644 index 0000000000..be41ba791d --- /dev/null +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -0,0 +1,371 @@ +/mob/living/simple_animal/bot/honkbot + name = "\improper honkbot" + desc = "A little robot. It looks happy with its bike horn." + icon = 'icons/mob/aibots.dmi' + icon_state = "honkbot" + density = FALSE + anchored = FALSE + health = 25 + maxHealth = 25 + damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) + pass_flags = PASSMOB + + radio_key = /obj/item/device/encryptionkey/headset_service //doesn't have security key + radio_channel = "Service" //Doesn't even use the radio anyway. + bot_type = HONK_BOT + model = "Honkbot" + bot_core_type = /obj/machinery/bot_core/honkbot + window_id = "autohonk" + window_name = "Honkomatic Bike Horn Unit v1.0.7" + data_hud_type = DATA_HUD_SECURITY_BASIC // show jobs + + var/honksound = 'sound/items/bikehorn.ogg' //customizable sound + var/spam_flag = FALSE + var/cooldowntime = 30 + var/cooldowntimehorn = 10 + 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/declare_arrests = FALSE // speak, you shall not, unless to Honk + var/idcheck = TRUE + var/fcheck = TRUE + var/check_records = TRUE + var/arrest_type = FALSE + var/weaponscheck = TRUE + +/mob/living/simple_animal/bot/honkbot/Initialize() + . = ..() + icon_state = "honkbot[on]" + auto_patrol = TRUE + var/datum/job/clown/J = new/datum/job/clown + 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/blink_end() //used for addtimer + icon_state = "honkbot[on]" + +/mob/living/simple_animal/bot/honkbot/proc/sensor_blink() + icon_state = "honkbot-c" + addtimer(CALLBACK(src, .proc/blink_end), 5) + +//honkbots react with sounds. +/mob/living/simple_animal/bot/honkbot/proc/react_ping() + playsound(src, 'sound/machines/ping.ogg', 50, 1, -1) //the first sound upon creation! + spam_flag = TRUE + sensor_blink() + addtimer(CALLBACK(src, .proc/spam_flag_false), 18) // calibrates before starting the honk + +/mob/living/simple_animal/bot/honkbot/proc/react_buzz() + playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 1, -1) + sensor_blink() + +/mob/living/simple_animal/bot/honkbot/bot_reset() + ..() + target = null + oldtarget_name = null + anchored = FALSE + walk_to(src,0) + last_found = world.time + spam_flag = FALSE + +/mob/living/simple_animal/bot/honkbot/set_custom_texts() + + text_hack = "You overload [name]'s sound control system" + text_dehack = "You reboot [name] and restore the sound control system." + text_dehack_fail = "[name] refuses to accept your authority!" + +/mob/living/simple_animal/bot/honkbot/get_controls(mob/user) + var/dat + dat += hack(user) + dat += showpai(user) + dat += text({" +Honkomatic Bike Horn Unit v1.0.7 controls

+Status: []
+Behaviour controls are [locked ? "locked" : "unlocked"]
+Maintenance panel panel is [open ? "opened" : "closed"]"}, + +"[on ? "On" : "Off"]" ) + + if(!locked || issilicon(user) || IsAdminGhost(user)) + dat += text({"
Auto Patrol: []"}, + +"[auto_patrol ? "On" : "Off"]" ) + return dat + +/mob/living/simple_animal/bot/honkbot/proc/judgement_criteria() + var/final = NONE + if(check_records) + final = final|JUDGE_RECORDCHECK + if(emagged) + final = final|JUDGE_EMAGGED + return final + +/mob/living/simple_animal/bot/honkbot/proc/retaliate(mob/living/carbon/human/H) + var/judgement_criteria = judgement_criteria() + threatlevel = H.assess_threat(judgement_criteria) + threatlevel += 6 + if(threatlevel >= 4) + target = H + mode = BOT_HUNT + +/mob/living/simple_animal/bot/honkbot/attack_hand(mob/living/carbon/human/H) + if(H.a_intent == "harm") + retaliate(H) + addtimer(CALLBACK(src, .proc/react_buzz), 5) + return ..() + + +/mob/living/simple_animal/bot/honkbot/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/weldingtool) && user.a_intent != INTENT_HARM). + return + if(!istype(W, /obj/item/screwdriver) && (W.force) && (!target) && (W.damtype != STAMINA) ) // Check for welding tool to fix #2432. + retaliate(user) + addtimer(CALLBACK(src, .proc/react_buzz), 5) + ..() + +/mob/living/simple_animal/bot/honkbot/emag_act(mob/user) + ..() + if(emagged == 2) + if(user) + user << "You short out [src]'s sound control system. It gives out an evil laugh!!" + oldtarget_name = user.name + audible_message("[src] gives out an evil laugh!") + playsound(src, 'sound/machines/honkbot_evil_laugh.ogg', 75, 1, -1) // evil laughter + icon_state = "honkbot[on]" + +/mob/living/simple_animal/bot/honkbot/bullet_act(obj/item/projectile/Proj) + if((istype(Proj,/obj/item/projectile/beam)) || (istype(Proj,/obj/item/projectile/bullet) && (Proj.damage_type == BURN))||(Proj.damage_type == BRUTE) && (!Proj.nodamage && Proj.damage < health)) + retaliate(Proj.firer) + ..() + +/mob/living/simple_animal/bot/honkbot/UnarmedAttack(atom/A) + if(!on) + return + if(iscarbon(A)) + var/mob/living/carbon/C = A + if (emagged <= 1) + honk_attack(A) + else + if(!C.IsStun() || arrest_type) + stun_attack(A) + ..() + else if (!spam_flag) //honking at the ground + bike_horn(A) + + +/mob/living/simple_animal/bot/honkbot/hitby(atom/movable/AM, skipcatch = 0, hitpush = 1, blocked = 0) + if(istype(AM, /obj/item)) + playsound(src, honksound, 50, 1, -1) + var/obj/item/I = AM + if(I.throwforce < health && I.thrownby && (istype(I.thrownby, /mob/living/carbon/human))) + var/mob/living/carbon/human/H = I.thrownby + retaliate(H) + ..() + +/mob/living/simple_animal/bot/honkbot/proc/bike_horn() //use bike_horn + if (emagged <= 1) + if (!spam_flag) + playsound(src, honksound, 50, 1, -1) + spam_flag = TRUE //prevent spam + sensor_blink() + addtimer(CALLBACK(src, .proc/spam_flag_false), cooldowntimehorn) + else if (emagged == 2) //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, .proc/blink_end), 30) + addtimer(CALLBACK(src, .proc/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, 1, -1) + spam_flag = TRUE // prevent spam + sensor_blink() + addtimer(CALLBACK(src, .proc/spam_flag_false), cooldowntimehorn) + +/mob/living/simple_animal/bot/honkbot/proc/stun_attack(mob/living/carbon/C) // airhorn stun + if(!spam_flag) + playsound(loc, 'sound/items/AirHorn.ogg', 100, 1, -1) //HEEEEEEEEEEEENK!! + sensor_blink() + if(spam_flag == 0) + if(ishuman(C)) + C.stuttering = 20 + C.adjustEarDamage(0, 5) //far less damage than the H.O.N.K. + C.Jitter(50) + C.Knockdown(60) + var/mob/living/carbon/human/H = C + if(client) //prevent spam from players.. + spam_flag = TRUE + if (emagged <= 1) //HONK once, then leave + var/judgement_criteria = judgement_criteria() + threatlevel = H.assess_threat(judgement_criteria) + threatlevel -= 6 + 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/spam_flag_false), cooldowntime) + + add_logs(src,C,"honked") + + C.visible_message("[src] has honked [C]!",\ + "[src] has honked you!") + else + C.stuttering = 20 + C.Knockdown(80) + addtimer(CALLBACK(src, .proc/spam_flag_false), cooldowntime) + + +/mob/living/simple_animal/bot/honkbot/handle_automated_action() + if(!..()) + return + + switch(mode) + + if(BOT_IDLE) // idle + + walk_to(src,0) + look_for_perp() + if(!mode && auto_patrol) + mode = BOT_START_PATROL + + if(BOT_HUNT) + + // if can't reach perp for long enough, go idle + if(frustration >= 5) //gives up easier than beepsky + walk_to(src,0) + 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 + back_to_idle() + + + if(BOT_START_PATROL) + look_for_perp() + start_patrol() + + if(BOT_PATROL) + look_for_perp() + bot_patrol() + + return + +/mob/living/simple_animal/bot/honkbot/proc/back_to_idle() + anchored = FALSE + mode = BOT_IDLE + target = null + last_found = world.time + frustration = 0 + INVOKE_ASYNC(src, .proc/handle_automated_action) //responds quickly + +/mob/living/simple_animal/bot/honkbot/proc/back_to_hunt() + anchored = FALSE + frustration = 0 + mode = BOT_HUNT + INVOKE_ASYNC(src, .proc/handle_automated_action) // responds quickly + +/mob/living/simple_animal/bot/honkbot/proc/look_for_perp() + anchored = FALSE + for (var/mob/living/carbon/C in view(7,src)) + if((C.stat) || (C.handcuffed)) + continue + + if((C.name == oldtarget_name) && (world.time < last_found + 100)) + continue + + var/judgement_criteria = judgement_criteria() + threatlevel = C.assess_threat(judgement_criteria) + + if(threatlevel <= 3) + if(C in view(4,src)) //keep the range short for patrolling + if(!spam_flag) + bike_horn() + + else if(threatlevel >= 10) + bike_horn() //just spam the shit outta this + + else if(threatlevel >= 4) + if(!spam_flag) + target = C + oldtarget_name = C.name + bike_horn() + speak("Honk!") + visible_message("[src] starts chasing [C.name]!") + mode = BOT_HUNT + INVOKE_ASYNC(src, .proc/handle_automated_action) + break + else + continue + +/mob/living/simple_animal/bot/honkbot/explode() + + walk_to(src,0) + visible_message("[src] blows apart!") + var/turf/Tsec = get_turf(src) + //doesn't drop cardboard nor its assembly, since its a very frail material. + if(prob(50)) + new /obj/item/bodypart/l_arm/robot/(Tsec) + new /obj/item/bikehorn(Tsec) + new /obj/item/device/assembly/prox_sensor(Tsec) + + var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread + s.set_up(3, 1, src) + s.start() + + new /obj/effect/decal/cleanable/oil(loc) + ..() + +/mob/living/simple_animal/bot/honkbot/attack_alien(var/mob/living/carbon/alien/user as mob) + ..() + if(!isalien(target)) + target = user + mode = BOT_HUNT + +/mob/living/simple_animal/bot/honkbot/Crossed(atom/movable/AM) + if(ismob(AM)) + if(prob(30)) //you're far more likely to trip on a honkbot + 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.Knockdown(10) + playsound(loc, 'sound/misc/sadtrombone.ogg', 50, 1, -1) + speak("Honk!") + sensor_blink() + return + ..() + +/obj/machinery/bot_core/honkbot + req_one_access = list(ACCESS_THEATRE, ACCESS_ROBOTICS) \ No newline at end of file diff --git a/icons/mob/aibots.dmi b/icons/mob/aibots.dmi index 860f76001b..c6ee573913 100644 Binary files a/icons/mob/aibots.dmi and b/icons/mob/aibots.dmi differ diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi index 6fdb531956..024a00b067 100644 Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ diff --git a/sound/machines/honkbot_evil_laugh.ogg b/sound/machines/honkbot_evil_laugh.ogg new file mode 100644 index 0000000000..4101d82742 Binary files /dev/null and b/sound/machines/honkbot_evil_laugh.ogg differ diff --git a/tgstation.dme b/tgstation.dme index c2ee860831..49ef73f89a 100755 --- a/tgstation.dme +++ b/tgstation.dme @@ -1787,6 +1787,7 @@ #include "code\modules\mob\living\simple_animal\bot\construction.dm" #include "code\modules\mob\living\simple_animal\bot\ed209bot.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\medbot.dm" #include "code\modules\mob\living\simple_animal\bot\mulebot.dm" #include "code\modules\mob\living\simple_animal\bot\secbot.dm"