diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index 617a0af3d7d..ec3be384bad 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -30,3 +30,4 @@ var/global/list/beacons = list() var/global/list/shuttle_caller_list = list() //list of all communication consoles and AIs, for automatic shuttle calls when there are none. var/global/list/tracked_implants = list() //list of all current implants that are tracked to work out what sort of trek everyone is on. Sadly not on lavaworld not implemented... var/global/list/abductor_equipment = list() //list of all abductor equipment +var/global/list/global_intercoms = list() //list of all intercomms, across all z-levels \ No newline at end of file diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm index cc3c176ec17..38f711500ff 100644 --- a/code/datums/uplink_item.dm +++ b/code/datums/uplink_item.dm @@ -235,6 +235,16 @@ var/list/uplink_items = list() cost = 5 job = list("Barber") +//Botanist + +/datum/uplink_item/jobspecific/bee_briefcase + name = "Briefcase Full of Bees" + desc = "A seemingly innocent briefcase full of not-so-innocent Syndicate-bred bees. Inject the case with blood to train the bees to ignore the donor(s). It also wirelessly taps into station intercomms to broadcast a message of TERROR." + reference = "BEE" + item = /obj/item/weapon/bee_briefcase + cost = 10 + job = list("Botanist") + //Engineer /datum/uplink_item/jobspecific/powergloves diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index fba22decc42..f1eb48e06d7 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -56,6 +56,7 @@ dir=ndir b_stat=1 on = 0 + global_intercoms.Add(src) update_icon() /obj/item/device/radio/intercom/department/medbay/New() @@ -104,6 +105,7 @@ /obj/item/device/radio/intercom/Destroy() processing_objects.Remove(src) + global_intercoms.Remove(src) return ..() /obj/item/device/radio/intercom/attack_ai(mob/user as mob) diff --git a/code/game/objects/items/weapons/bee_briefcase.dm b/code/game/objects/items/weapons/bee_briefcase.dm new file mode 100644 index 00000000000..5c3e9afff5d --- /dev/null +++ b/code/game/objects/items/weapons/bee_briefcase.dm @@ -0,0 +1,79 @@ + +/obj/item/weapon/bee_briefcase + name = "briefcase" + desc = "This briefcase has easy-release clasps and smells vaguely of honey and blood..." + icon = 'icons/obj/storage.dmi' + icon_state = "briefcase" + item_state = "briefcase" + flags = CONDUCT + hitsound = "swing_hit" + force = 10 + throw_speed = 2 + throw_range = 4 + w_class = 4 + attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") + var/bees_left = 10 + var/list/blood_list = list() + var/sound_file = 'sound/misc/briefcase_bees.ogg' + var/next_sound = 0 + +/obj/item/weapon/bee_briefcase/examine(mob/user) + ..() + if(loc == user) + if(bees_left) + to_chat(user, "There are [bees_left] bees still inside in briefcase!") + else + to_chat(user, "The bees are gone... Colony collapse disorder?") + +/obj/item/weapon/bee_briefcase/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/weapon/reagent_containers/syringe)) + + var/obj/item/weapon/reagent_containers/syringe/S = I + if(!bees_left) + to_chat(user, "The briefcase is empty, so there is no point in injecting something into it.") + return + if(S.reagents && S.reagents.total_volume) + to_chat(user, "You inject [src] with [S].") + for(var/datum/reagent/A in S.reagents.reagent_list) + if(A.id == "blood") + if(!(A.data["donor"] in blood_list)) + blood_list += A.data["donor"] + if(A.id == "strange_reagent") //RELOAD THE BEES (1 bee per 1 unit, max 15 bees) + if(bees_left < 15) + bees_left = min(15, round((bees_left + A.volume), 1)) //No partial bees, max 15 bees in case at any given time + to_chat(user, "The buzzing inside the briefcase intensifies as new bees form inside.") + else + to_chat(user, "The buzzing inside the briefcase swells momentarily, then returns to normal. Guess it was too cramped...") + S.reagents.clear_reagents() + S.update_icon() + + else if(istype(I, /obj/item/weapon/plantspray)) + var/obj/item/weapon/plantspray/PS = I + user.drop_item(PS) + bees_left = max(0, (bees_left - PS.pest_kill_str)) + to_chat(user, "You spray [PS] into \the [src].") + playsound(loc, 'sound/effects/spray3.ogg', 50, 1, -6) + qdel(PS) + +/obj/item/weapon/bee_briefcase/attack_self(mob/user as mob) + if(!bees_left) + to_chat(user, "The lack of all and any bees at this event has been somewhat of a let-down...") + return + else + if(world.time >= next_sound) //This cooldown doesn't prevent us from releasing bees, just stops the sound + next_sound = world.time + 90 + //Play sound through the station intercomms, so everyone knows the doom you have wrought. + for(var/O in global_intercoms) + var/obj/item/device/radio/intercom/I = O + if(I.z != ZLEVEL_STATION) //Only broadcast to the station intercoms + continue + if(!I.on) //Only broadcast to active intercoms (powered, switched on) + continue + playsound(I, sound_file, 35) + + //Release up to 5 bees per use. Without using strange reagent, that means two uses. WITH strange reagent, you can get more if you don't release the last bee + for(var/bee = min(5, bees_left), bee > 0, bee--) + var/mob/living/simple_animal/hostile/poison/bees/syndi/B = new /mob/living/simple_animal/hostile/poison/bees/syndi(null) + B.master_and_friends = blood_list //Doesn't automatically add the person who opens the case, so the bees will attack the user unless they gave their blood + B.forceMove(get_turf(user)) //RELEASE THE BEES! + bees_left -= 5 \ No newline at end of file diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index 25b0ed54140..47c7da15724 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -52,7 +52,7 @@ var/list/sounds_cache = list() set desc = "Plays a sound at every intercomm on the station z level. Works best with small sounds." if(!check_rights(R_SOUNDS)) return - var/A = alert("This will play a sound at every intercomm on the station Z, are you sure you want to continue? This works best with short sounds, beware.","Warning","Yep","Nope") + var/A = alert("This will play a sound at every intercomm, are you sure you want to continue? This works best with short sounds, beware.","Warning","Yep","Nope") if(A != "Yep") return var/list/sounds = file2list("sound/serversound_list.txt"); @@ -67,15 +67,25 @@ var/list/sounds_cache = list() if(inputvol && inputvol >= 1 && inputvol <= 70) cvol = inputvol - var/list/intercomms = list() + //Allows for override to utilize intercomms on all z-levels + var/B = alert("Do you want to play through intercomms on ALL Z-levels, or just the station?", "Override", "All", "Station") + var/ignore_z = 0 + if(B == "All") + ignore_z = 1 - for(var/obj/item/device/radio/intercom/I in world) - if(I.z != ZLEVEL_STATION) continue - intercomms += I + //Allows for override to utilize incomplete and unpowered intercomms + var/C = alert("Do you want to play through unpowered / incomplete intercomms, so the crew can't silence it?", "Override", "Yep", "Nope") + var/ignore_power = 0 + if(C == "Yep") + ignore_power = 1 - if(intercomms.len) - for(var/obj/item/device/radio/intercom/I in intercomms) - playsound(I, melody, cvol) + for(var/O in global_intercoms) + var/obj/item/device/radio/intercom/I = O + if(I.z != ZLEVEL_STATION && !ignore_z) + continue + if(!I.on && !ignore_power) + continue + playsound(I, melody, cvol) /* /client/proc/cuban_pete() diff --git a/code/modules/hydroponics/beekeeping/beebox.dm b/code/modules/hydroponics/beekeeping/beebox.dm index d3d9cb0f3dc..150528d4c66 100644 --- a/code/modules/hydroponics/beekeeping/beebox.dm +++ b/code/modules/hydroponics/beekeeping/beebox.dm @@ -109,7 +109,7 @@ if((bee_resources >= BEE_RESOURCE_NEW_BEE_COST && prob(BEE_PROB_NEW_BEE)) || freebee) if(!freebee) bee_resources = max(bee_resources - BEE_RESOURCE_NEW_BEE_COST, 0) - var/mob/living/simple_animal/hostile/poison/bees/B = new(src) + var/mob/living/simple_animal/hostile/poison/bees/worker/B = new(src) B.beehome = src B.assign_reagent(queen_bee.beegent) bees += B @@ -180,7 +180,7 @@ visible_message("[user] sets [qb] down inside the apiary, making it their new home.") var/relocated = 0 for(var/b in bees) - var/mob/living/simple_animal/hostile/poison/bees/B = b + var/mob/living/simple_animal/hostile/poison/bees/worker/B = b if(B.reagent_incompatible(queen_bee)) bees -= B B.beehome = null diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm index 5a2ef732eb5..54b0d10608a 100644 --- a/code/modules/mob/living/simple_animal/hostile/bees.dm +++ b/code/modules/mob/living/simple_animal/hostile/bees.dm @@ -42,33 +42,23 @@ var/datum/reagent/beegent = null //hehe, beegent var/obj/structure/beebox/beehome = null - var/idle = 0 var/isqueen = FALSE + var/idle = 0 var/icon_base = "bee" var/static/list/bee_icons = list() - /mob/living/simple_animal/hostile/poison/bees/Process_Spacemove(movement_dir = 0) return 1 - /mob/living/simple_animal/hostile/poison/bees/New() ..() generate_bee_visuals() - /mob/living/simple_animal/hostile/poison/bees/Destroy() - if(beehome) - beehome.bees.Remove(src) - beehome = null beegent = null return ..() - /mob/living/simple_animal/hostile/poison/bees/death(gibbed) - if(beehome) - beehome.bees.Remove(src) - beehome = null beegent = null ..() ghostize() @@ -77,10 +67,6 @@ /mob/living/simple_animal/hostile/poison/bees/examine(mob/user) ..() - if(!beehome) - to_chat(user, "This bee is homeless!") - - /mob/living/simple_animal/hostile/poison/bees/proc/generate_bee_visuals() overlays.Cut() @@ -107,31 +93,87 @@ wings = bee_icons["[icon_base]_wings"] overlays += wings - //We don't attack beekeepers/people dressed as bees//Todo: bee costume /mob/living/simple_animal/hostile/poison/bees/CanAttack(atom/the_target) . = ..() - if(!.) - return 0 - if(ishuman(the_target)) - var/mob/living/carbon/human/H = the_target - return !H.bee_friendly() - + return . /mob/living/simple_animal/hostile/poison/bees/Found(atom/A) + if(isliving(A)) + var/mob/living/L = A + return !L.bee_friendly() + return 0 + +/mob/living/simple_animal/hostile/poison/bees/AttackingTarget() + if(beegent && isliving(target)) + var/mob/living/L = target + if(!isnull(target.reagents)) + beegent.reaction_mob(L, INGEST) + L.reagents.add_reagent(beegent.id, rand(1,5)) + target.attack_animal(src) + +/mob/living/simple_animal/hostile/poison/bees/proc/assign_reagent(datum/reagent/R) + if(istype(R)) + beegent = R + name = "[initial(name)] ([R.name])" + generate_bee_visuals() + +/mob/living/simple_animal/hostile/poison/bees/handle_automated_action() + . = ..() + if(!.) + return + +/mob/living/simple_animal/hostile/poison/bees/proc/reagent_incompatible(mob/living/simple_animal/hostile/poison/bees/B) + if(!B) + return 0 + if(B.beegent && beegent && B.beegent.id != beegent.id || B.beegent && !beegent || !B.beegent && beegent) + return 1 + return 0 + + + + +//Botany Worker Bees +/mob/living/simple_animal/hostile/poison/bees/worker + //Blank type define in case we need to give them special stuff later, plus organization (currently they are same as base type bee) + + +/mob/living/simple_animal/hostile/poison/bees/worker/Destroy() + if(beehome) + beehome.bees.Remove(src) + beehome = null + ..() + +/mob/living/simple_animal/hostile/poison/bees/worker/death(gibbed) + if(beehome) + beehome.bees.Remove(src) + beehome = null + ..() + +/mob/living/simple_animal/hostile/poison/bees/worker/examine(mob/user) + ..() + + if(!beehome) + to_chat(user, "This bee is homeless!") + +/mob/living/simple_animal/hostile/poison/bees/worker/Found(atom/A) if(istype(A, /obj/machinery/portable_atmospherics/hydroponics)) var/obj/machinery/portable_atmospherics/hydroponics/Hydro = A if(Hydro.seed && !Hydro.dead && !Hydro.recent_bee_visit) wanted_objects |= /obj/machinery/portable_atmospherics/hydroponics //so we only hunt them while they're alive/seeded/not visisted return 1 - if(ishuman(A)) - var/mob/living/carbon/human/H = A - return !H.bee_friendly() - return 0 + ..() +/mob/living/simple_animal/hostile/poison/bees/worker/CanAttack(atom/the_target) + . = ..() + if(!.) + return 0 + if(isliving(the_target)) //Should ignore ghosts and camera mobs already, but just in case + var/mob/living/L = the_target + return !L.bee_friendly() -/mob/living/simple_animal/hostile/poison/bees/AttackingTarget() - //Pollinate +/mob/living/simple_animal/hostile/poison/bees/worker/AttackingTarget() + //Pollinate if(istype(target, /obj/machinery/portable_atmospherics/hydroponics)) var/obj/machinery/portable_atmospherics/hydroponics/Hydro = target pollinate(Hydro) @@ -141,22 +183,9 @@ target = null wanted_objects.Remove(/obj/structure/beebox) //so we don't attack beeboxes when not going home else - if(beegent && isliving(target)) - var/mob/living/L = target - if(!isnull(target.reagents)) - beegent.reaction_mob(L, INGEST) - L.reagents.add_reagent(beegent.id, rand(1,5)) - target.attack_animal(src) + ..() - -/mob/living/simple_animal/hostile/poison/bees/proc/assign_reagent(datum/reagent/R) - if(istype(R)) - beegent = R - name = "[initial(name)] ([R.name])" - generate_bee_visuals() - - -/mob/living/simple_animal/hostile/poison/bees/proc/pollinate(obj/machinery/portable_atmospherics/hydroponics/Hydro) +/mob/living/simple_animal/hostile/poison/bees/worker/proc/pollinate(obj/machinery/portable_atmospherics/hydroponics/Hydro) if(!istype(Hydro) || !Hydro.seed || Hydro.dead || Hydro.recent_bee_visit) target = null return @@ -187,12 +216,10 @@ if(beehome) beehome.bee_resources = min(beehome.bee_resources + growth, 100) - -/mob/living/simple_animal/hostile/poison/bees/handle_automated_action() +/mob/living/simple_animal/hostile/poison/bees/worker/handle_automated_action() . = ..() if(!.) return - if(!isqueen) if(loc == beehome) idle = min(100, ++idle) @@ -212,7 +239,9 @@ beehome = BB - /mob/living/simple_animal/hostile/poison/bees/queen + +//Botany Queen Bee +/mob/living/simple_animal/hostile/poison/bees/queen name = "queen bee" desc = "she's the queen of bees, BZZ BZZ" icon_base = "queen" @@ -223,28 +252,13 @@ /mob/living/simple_animal/hostile/poison/bees/queen/Found(atom/A) return 0 - -//leave pollination for the peasent bees -/mob/living/simple_animal/hostile/poison/bees/queen/AttackingTarget() - if(beegent && isliving(target)) - var/mob/living/L = target - beegent.reaction_mob(L, TOUCH) - L.reagents.add_reagent(beegent.id, rand(1,5)) - target.attack_animal(src) - - -//PEASENT BEES -/mob/living/simple_animal/hostile/poison/bees/queen/pollinate() - return - - -/mob/living/simple_animal/hostile/poison/bees/proc/reagent_incompatible(mob/living/simple_animal/hostile/poison/bees/B) - if(!B) +/mob/living/simple_animal/hostile/poison/bees/queen/CanAttack(atom/the_target) + . = ..() + if(!.) return 0 - if(B.beegent && beegent && B.beegent.id != beegent.id || B.beegent && !beegent || !B.beegent && beegent) - return 1 - return 0 - + if(isliving(the_target)) //Should ignore ghosts and camera mobs already, but just in case + var/mob/living/L = the_target + return !L.bee_friendly() /obj/item/queen_bee name = "queen bee" @@ -254,7 +268,6 @@ icon = 'icons/mob/bees.dmi' var/mob/living/simple_animal/hostile/poison/bees/queen/queen - /obj/item/queen_bee/attackby(obj/item/I, mob/user, params) if(istype(I,/obj/item/weapon/reagent_containers/syringe)) var/obj/item/weapon/reagent_containers/syringe/S = I @@ -280,7 +293,6 @@ to_chat(user, "You don't have enough units of that chemical to modify the bee's DNA!") ..() - /obj/item/queen_bee/bought/New() ..() queen = new(src) @@ -288,3 +300,43 @@ /obj/item/queen_bee/Destroy() qdel(queen) return ..() + + + +//Syndicate Bees +/mob/living/simple_animal/hostile/poison/bees/syndi + name = "syndi-bee" + desc = "The result of a large influx of BEES!" + melee_damage_lower = 5 + melee_damage_upper = 5 + maxHealth = 25 + health = 25 + faction = list("hostile", "syndicate") + search_objects = 0 //these bees don't care about trivial things like plants, especially when there is havoc to sow + beegent = new /datum/reagent/facid() //prepare to die + var/list/master_and_friends = list() + +/mob/living/simple_animal/hostile/poison/bees/syndi/assign_reagent(datum/reagent/R) + return + +/mob/living/simple_animal/hostile/poison/bees/syndi/Found(atom/A) + return CanAttack(A) + +/mob/living/simple_animal/hostile/poison/bees/syndi/CanAttack(atom/the_target) + . = ..() + if(!.) + return 0 + if(isliving(the_target)) + var/mob/living/L = the_target + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H in master_and_friends) + return 0 + return 1 + +/mob/living/simple_animal/hostile/poison/bees/syndi/AttackingTarget() + ..() + if(target && isliving(target)) + var/mob/living/L = target + if(L.stat) + LoseTarget() diff --git a/paradise.dme b/paradise.dme index c50ca178c7f..6e79b17f2d0 100644 --- a/paradise.dme +++ b/paradise.dme @@ -774,6 +774,7 @@ #include "code\game\objects\items\stacks\tiles\tile_types.dm" #include "code\game\objects\items\weapons\AI_modules.dm" #include "code\game\objects\items\weapons\alien_specific.dm" +#include "code\game\objects\items\weapons\bee_briefcase.dm" #include "code\game\objects\items\weapons\cards_ids.dm" #include "code\game\objects\items\weapons\cash.dm" #include "code\game\objects\items\weapons\caution.dm" diff --git a/sound/misc/briefcase_bees.ogg b/sound/misc/briefcase_bees.ogg new file mode 100644 index 00000000000..5483d9d5f91 Binary files /dev/null and b/sound/misc/briefcase_bees.ogg differ