diff --git a/SQL/feedback_conversion_2017-11-12.py b/SQL/feedback_conversion_2017-11-12.py index 8a60e10b7e1c..718eabd10b55 100644 --- a/SQL/feedback_conversion_2017-11-12.py +++ b/SQL/feedback_conversion_2017-11-12.py @@ -456,7 +456,7 @@ def pick_parsing(var_name, var_value, details, multirows_completed): raise Exception("Python must be at least version 3 for this script.") text_keys = ["religion_book", "religion_deity", "religion_name", "shuttle_fasttravel", "shuttle_manipulator", "shuttle_purchase", "shuttle_reason", "station_renames"] amount_keys = ["admin_cookies_spawned", "cyborg_ais_created", "cyborg_frames_built", "cyborg_mmis_filled", "newscaster_newspapers_printed", "newscaster_stories", "nuclear_challenge_mode"] -simple_tallies = ["admin_secrets_fun_used", "admin_verb", "assembly_made", "brother_success", "cell_used", "changeling_power_purchase", "changeling_success", "chaplain_weapon", "chemical_reaction", "circuit_printed", "clockcult_scripture_recited", "contamination", "cult_runes_scribed", "engine_started", "event_admin_cancelled", "event_ran", "food_harvested", "food_made", "gun_fired", "handcuffs", "item_deconstructed", "item_printed", "jaunter", "lazarus_injector", "megafauna_kills", "mining_voucher_redeemed", "mobs_killed_mining", "object_crafted", "ore_mined", "pick_used_mining", "slime_cores_used", "surgeries_completed", "time_dilation_current", "traitor_random_uplink_items_gotten", "traitor_success", "voice_of_god", "warp_cube", "wisp_lantern", "wizard_spell_learned", "wizard_success", "zone_targeted"] +simple_tallies = ["admin_secrets_fun_used", "admin_verb", "assembly_made", "brother_success", "cell_used", "changeling_power_purchase", "changeling_success", "chaplain_weapon", "chemical_reaction", "circuit_printed", "clockcult_scripture_recited", "contamination", "cult_runes_scribed", "engine_started", "event_admin_cancelled", "event_ran", "food_harvested", "food_made", "gun_fired", "handcuffs", "item_deconstructed", "item_printed", "jaunter", "lazarus_injector", "megafauna_kills", "mining_voucher_redeemed", "mobs_killed_mining", "object_crafted", "ore_mined", "pick_used_mining", "slime_cores_used", "surgeries_completed", "time_dilation_current", "traitor_random_uplink_items_gotten", "traitor_success", "voice_of_god", "warp_cube", "wisp_lantern", "wizard_spell_learned", "wizard_success", "zone_targeted", "emote_used"] nested_tallies = ["admin_toggle", "cargo_imports", "changeling_objective", "changeling_powers", "cult_objective", "export_sold_cost", "hivelord_core", "item_used_for_combat", "job_preferences", "mining_equipment_bought", "played_url", "preferences_verb", "testmerged_prs", "traitor_objective", "traitor_uplink_items_bought", "vending_machine_usage", "wizard_objective", "wizard_spell_improved"] associatives = ["colonies_dropped", "commendation", "high_research_level"] special_cases = ["immortality_talisman", "newscaster_channels", "radio_usage", "shuttle_gib", "slime_babies_born", "slime_core_harvested"] diff --git a/_maps/RandomRuins/IceRuins/icemoon_surface_bughabitat.dmm b/_maps/RandomRuins/IceRuins/icemoon_surface_bughabitat.dmm index baa64e12c7c3..6392388b1096 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_surface_bughabitat.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_surface_bughabitat.dmm @@ -289,7 +289,7 @@ /turf/open/floor/carpet/neon/simple/lime, /area/ruin/bughabitat) "IX" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/plastic, /area/ruin/bughabitat) "Jr" = ( diff --git a/_maps/RandomRuins/IceRuins/icemoon_surface_smoking_room.dmm b/_maps/RandomRuins/IceRuins/icemoon_surface_smoking_room.dmm index 297685b06af7..7914006c65ee 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_surface_smoking_room.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_surface_smoking_room.dmm @@ -116,7 +116,7 @@ /turf/open/floor/wood, /area/ruin/smoking_room/house) "w" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/nospawn) "x" = ( diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_homestead.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_homestead.dmm index f8fd8f41c92a..5ab59bf79dab 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_homestead.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_homestead.dmm @@ -1,6 +1,6 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "bf" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/nospawn) "cP" = ( @@ -44,7 +44,7 @@ color = "#8a7453"; dir = 8 }, -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/plating/snowed/smoothed/icemoon, /area/icemoon/surface/outdoors/nospawn) "ng" = ( @@ -120,7 +120,7 @@ color = "#8a7453"; dir = 10 }, -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/plating/snowed/smoothed/icemoon, /area/icemoon/surface/outdoors/nospawn) "vI" = ( @@ -209,7 +209,7 @@ /obj/structure/railing{ color = "#8a7453" }, -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/plating/snowed/smoothed/icemoon, /area/icemoon/surface/outdoors/nospawn) "Fg" = ( @@ -328,18 +328,18 @@ color = "#8a7453"; dir = 8 }, -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/nospawn) "Tf" = ( /obj/structure/railing{ color = "#8a7453" }, -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/nospawn) "TP" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/plating/snowed/smoothed/icemoon, /area/icemoon/surface/outdoors/nospawn) "Vf" = ( diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_hermit.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_hermit.dmm index 786b4130df5f..eb4f49bdba7c 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_hermit.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_hermit.dmm @@ -78,7 +78,7 @@ /turf/open/floor/plating, /area/ruin/powered/hermit) "sj" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/machinery/light/directional/north, /turf/open/floor/grass/fairy, /area/ruin/powered/hermit) @@ -87,7 +87,7 @@ /turf/open/floor/wood, /area/ruin/powered/hermit) "uw" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/grass/fairy, /area/ruin/powered/hermit) "wf" = ( @@ -157,7 +157,7 @@ "Yi" = ( /obj/item/seeds/plump, /obj/item/seeds/plump, -/obj/item/seeds/tower, +/obj/item/seeds/tree, /obj/item/seeds/reishi, /obj/structure/table/wood, /obj/item/food/grown/mushroom/glowshroom, diff --git a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm index ee999a468bfb..d980033f1c94 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm @@ -582,7 +582,7 @@ /turf/open/floor/wood, /area/ruin/powered/beach) "sV" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/iron/grimy, /area/ruin/powered/beach) "ta" = ( @@ -853,14 +853,14 @@ /turf/closed/wall/mineral/sandstone, /area/ruin/powered/beach) "CZ" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer4{ dir = 4 }, /turf/open/floor/iron/grimy, /area/ruin/powered/beach) "Dg" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden{ dir = 4 }, diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm index 47fe3152a8b5..1423580debe1 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm @@ -711,7 +711,7 @@ /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) "ie" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/structure/stone_tile/block/cracked{ dir = 1 }, @@ -1112,8 +1112,8 @@ /obj/item/seeds/glowshroom, /obj/item/seeds/lavaland/porcini, /obj/item/seeds/lavaland/porcini, -/obj/item/seeds/tower, -/obj/item/seeds/tower, +/obj/item/seeds/tree, +/obj/item/seeds/tree, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/ruin/unpowered/ash_walkers) "GW" = ( @@ -1131,7 +1131,7 @@ /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) "Ic" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/structure/stone_tile/surrounding_tile/cracked{ dir = 1 }, @@ -1301,7 +1301,7 @@ /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) "Tr" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/structure/stone_tile/block{ dir = 8 }, diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_gaia.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_gaia.dmm index b34c93c82104..f59641d4bb49 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_gaia.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_gaia.dmm @@ -30,9 +30,7 @@ /turf/open/misc/grass/lavaland, /area/ruin/unpowered/gaia) "i" = ( -/obj/machinery/hydroponics/soil{ - self_sustaining = 1 - }, +/obj/machinery/growing/soil, /turf/open/misc/grass/lavaland, /area/ruin/unpowered/gaia) "j" = ( diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_hermit.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_hermit.dmm index 49e241256578..d363d32a2d5c 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_hermit.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_hermit.dmm @@ -56,11 +56,11 @@ /obj/item/seeds/plump, /obj/item/seeds/plump, /obj/item/food/grown/mushroom/glowshroom, -/obj/item/seeds/tower, +/obj/item/seeds/tree, /turf/open/misc/asteroid/basalt, /area/ruin/powered) "n" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/plating, /area/ruin/powered) "o" = ( @@ -168,7 +168,7 @@ }, /area/ruin/powered) "L" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/cultivator, /turf/open/floor/plating, /area/ruin/powered) diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm index 0c96ffbd6d5f..dbdebf0adf81 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_seed_vault.dmm @@ -385,7 +385,7 @@ /area/ruin/powered/seedvault) "bc" = ( /obj/machinery/light/directional/west, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/green/line{ dir = 8 @@ -423,7 +423,7 @@ /area/ruin/powered/seedvault) "bg" = ( /obj/machinery/light/directional/east, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/cobweb/cobweb2, /obj/effect/turf_decal/trimline/green/line{ @@ -435,7 +435,7 @@ /turf/open/floor/mineral/plastitanium, /area/ruin/powered/seedvault) "bh" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/green/line{ dir = 6 }, @@ -456,7 +456,7 @@ /turf/open/floor/mineral/plastitanium, /area/ruin/powered/seedvault) "bj" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/green/line{ dir = 10 }, @@ -474,7 +474,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/green/line{ dir = 8 @@ -522,7 +522,7 @@ /area/ruin/powered/seedvault) "bq" = ( /obj/machinery/light/directional/east, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/green/line{ dir = 8 @@ -559,7 +559,7 @@ /area/ruin/powered/seedvault) "bv" = ( /obj/machinery/light/directional/south, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/green/line{ dir = 1 @@ -569,7 +569,7 @@ /area/ruin/powered/seedvault) "bw" = ( /obj/machinery/light/directional/south, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/green/line{ @@ -601,14 +601,14 @@ /turf/closed/wall/r_wall, /area/ruin/powered/seedvault) "bB" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/green/line{ dir = 5 }, /turf/open/floor/mineral/plastitanium, /area/ruin/powered/seedvault) "bC" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/green/line{ dir = 9 }, diff --git a/_maps/RandomRuins/SpaceRuins/botanical_haven.dmm b/_maps/RandomRuins/SpaceRuins/botanical_haven.dmm index 010f710de6e8..b9a2a0cc79f4 100644 --- a/_maps/RandomRuins/SpaceRuins/botanical_haven.dmm +++ b/_maps/RandomRuins/SpaceRuins/botanical_haven.dmm @@ -17,11 +17,11 @@ "f" = ( /obj/structure/flora/bush/flowers_br/style_random, /obj/machinery/light/warm/directional/east, -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/misc/grass, /area/ruin/space/has_grav/powered/botanical_haven) "g" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/misc/grass, /area/ruin/space/has_grav/powered/botanical_haven) "h" = ( diff --git a/_maps/RandomRuins/SpaceRuins/clownplanet.dmm b/_maps/RandomRuins/SpaceRuins/clownplanet.dmm index fa0b50cc4435..90e5c179cf5b 100644 --- a/_maps/RandomRuins/SpaceRuins/clownplanet.dmm +++ b/_maps/RandomRuins/SpaceRuins/clownplanet.dmm @@ -63,7 +63,7 @@ }, /area/ruin/space/has_grav/powered/clownplanet) "ao" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/banana, /turf/open/floor/grass{ color = "#1eff00" diff --git a/_maps/RandomRuins/SpaceRuins/deepstorage.dmm b/_maps/RandomRuins/SpaceRuins/deepstorage.dmm index f2cf7a9f8a8c..3ba6eec1dca0 100644 --- a/_maps/RandomRuins/SpaceRuins/deepstorage.dmm +++ b/_maps/RandomRuins/SpaceRuins/deepstorage.dmm @@ -447,14 +447,14 @@ /turf/open/floor/iron, /area/ruin/space/has_grav/deepstorage/hydroponics) "bk" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4{ dir = 4 }, /turf/open/floor/light, /area/ruin/space/has_grav/deepstorage/hydroponics) "bl" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4{ dir = 10 }, @@ -664,7 +664,7 @@ /turf/open/floor/iron, /area/ruin/space/has_grav/deepstorage/hydroponics) "bS" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/light, /area/ruin/space/has_grav/deepstorage/hydroponics) "bT" = ( @@ -924,14 +924,14 @@ /turf/open/floor/iron, /area/ruin/space/has_grav/deepstorage/hydroponics) "cz" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2{ dir = 4 }, /turf/open/floor/light, /area/ruin/space/has_grav/deepstorage/hydroponics) "cA" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2{ dir = 9 }, @@ -2532,7 +2532,7 @@ /turf/open/floor/plating, /area/ruin/space/has_grav/deepstorage/crusher) "hf" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4{ dir = 4 }, diff --git a/_maps/RandomRuins/SpaceRuins/hilbertresearchfacility.dmm b/_maps/RandomRuins/SpaceRuins/hilbertresearchfacility.dmm index fb1cc9c069e9..50cdbe733db9 100644 --- a/_maps/RandomRuins/SpaceRuins/hilbertresearchfacility.dmm +++ b/_maps/RandomRuins/SpaceRuins/hilbertresearchfacility.dmm @@ -1893,7 +1893,7 @@ /turf/open/floor/plating, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "Rj" = ( -/obj/machinery/hydroponics, +/obj/machinery/growing, /turf/open/floor/mineral/titanium/white, /area/ruin/space/has_grav/powered/hilbertresearchfacility) "Rk" = ( diff --git a/_maps/RandomRuins/SpaceRuins/intactemptyship.dmm b/_maps/RandomRuins/SpaceRuins/intactemptyship.dmm index 6c0721fcbff8..ca31d4a0bf44 100644 --- a/_maps/RandomRuins/SpaceRuins/intactemptyship.dmm +++ b/_maps/RandomRuins/SpaceRuins/intactemptyship.dmm @@ -111,7 +111,7 @@ /turf/open/floor/mineral/titanium/purple, /area/ruin/space/has_grav/powered/authorship) "y" = ( -/mob/living/simple_animal/bot/medbot/derelict, +/mob/living/basic/bot/medbot/derelict, /turf/open/floor/mineral/titanium/purple, /area/ruin/space/has_grav/powered/authorship) "z" = ( diff --git a/_maps/RandomRuins/SpaceRuins/listeningstation.dmm b/_maps/RandomRuins/SpaceRuins/listeningstation.dmm index d0930b9f29dd..3e2ebf4927da 100644 --- a/_maps/RandomRuins/SpaceRuins/listeningstation.dmm +++ b/_maps/RandomRuins/SpaceRuins/listeningstation.dmm @@ -209,7 +209,7 @@ /area/ruin/space) "hK" = ( /obj/structure/extinguisher_cabinet/directional/south, -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/iron, /area/ruin/space/has_grav/listeningstation) "iU" = ( diff --git a/_maps/RandomRuins/SpaceRuins/oldstation.dmm b/_maps/RandomRuins/SpaceRuins/oldstation.dmm index d1b6e937645d..17cc96a0de54 100644 --- a/_maps/RandomRuins/SpaceRuins/oldstation.dmm +++ b/_maps/RandomRuins/SpaceRuins/oldstation.dmm @@ -888,7 +888,7 @@ /turf/open/floor/iron, /area/ruin/space/ancientstation/charlie/hydro) "dp" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/grass, /area/ruin/space/ancientstation/charlie/hydro) "dq" = ( @@ -5405,7 +5405,7 @@ /turf/open/floor/iron, /area/ruin/space/ancientstation/charlie/engie) "wO" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/machinery/light/small/directional/south, /turf/open/floor/grass, /area/ruin/space/ancientstation/charlie/hydro) @@ -7706,12 +7706,11 @@ "QS" = ( /obj/machinery/light/small/directional/south, /obj/effect/decal/cleanable/dirt, -/mob/living/simple_animal/bot/cleanbot/autopatrol{ +/mob/living/basic/bot/cleanbot/autopatrol{ bot_mode_flags = 12; name = "Ramboo"; pixel_x = -2; - pixel_y = 5; - bot_cover_flags = 0 + pixel_y = 5 }, /turf/open/floor/plating{ initial_gas_mix = "co2=6;o2=16;n2=82;TEMP=293.15" diff --git a/_maps/RandomRuins/SpaceRuins/spacehotel.dmm b/_maps/RandomRuins/SpaceRuins/spacehotel.dmm index e1bfb59abaa2..e176d4a791eb 100644 --- a/_maps/RandomRuins/SpaceRuins/spacehotel.dmm +++ b/_maps/RandomRuins/SpaceRuins/spacehotel.dmm @@ -1050,7 +1050,7 @@ /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/mob/living/simple_animal/bot/medbot/derelict, +/mob/living/basic/bot/medbot/derelict, /turf/open/floor/circuit/red, /area/ruin/space/has_grav/hotel/workroom) "iE" = ( diff --git a/_maps/RandomRuins/SpaceRuins/spinwardsmoothies.dmm b/_maps/RandomRuins/SpaceRuins/spinwardsmoothies.dmm index d3ca2e27e377..7a1bd14fef8f 100644 --- a/_maps/RandomRuins/SpaceRuins/spinwardsmoothies.dmm +++ b/_maps/RandomRuins/SpaceRuins/spinwardsmoothies.dmm @@ -18,7 +18,7 @@ /obj/effect/turf_decal/siding/thinplating/terracotta{ dir = 10 }, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/bamboo{ smoothing_flags = 0 }, @@ -96,7 +96,7 @@ /obj/effect/turf_decal/siding/thinplating/terracotta{ dir = 8 }, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/bamboo{ smoothing_flags = 0 }, @@ -194,7 +194,7 @@ /turf/open/floor/grass, /area/ruin/space/has_grav/spinwardsmoothies) "XG" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/siding/thinplating/terracotta{ dir = 9 }, diff --git a/_maps/RandomZLevels/snowdin.dmm b/_maps/RandomZLevels/snowdin.dmm index 05445ae1d834..577daa1036f5 100644 --- a/_maps/RandomZLevels/snowdin.dmm +++ b/_maps/RandomZLevels/snowdin.dmm @@ -2129,7 +2129,7 @@ /turf/open/floor/plating, /area/awaymission/snowdin/post/hydro) "gG" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/plating, /area/awaymission/snowdin/post/hydro) "gH" = ( @@ -2137,7 +2137,7 @@ /turf/open/floor/iron, /area/awaymission/snowdin/post/hydro) "gI" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/awaymission/snowdin/post/hydro) @@ -2146,7 +2146,7 @@ /turf/open/floor/plating, /area/awaymission/snowdin/post/hydro) "gK" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/awaymission/snowdin/post/hydro) @@ -3682,11 +3682,11 @@ /turf/open/floor/iron, /area/awaymission/snowdin/post/hydro) "kR" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/iron, /area/awaymission/snowdin/post/hydro) "kS" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, @@ -4400,7 +4400,7 @@ /turf/open/floor/plating, /area/awaymission/snowdin/post/hydro) "ms" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, diff --git a/_maps/RandomZLevels/undergroundoutpost45.dmm b/_maps/RandomZLevels/undergroundoutpost45.dmm index 8379a5185d3d..9ed55883ce32 100644 --- a/_maps/RandomZLevels/undergroundoutpost45.dmm +++ b/_maps/RandomZLevels/undergroundoutpost45.dmm @@ -1587,7 +1587,7 @@ }, /area/awaymission/undergroundoutpost45/central) "eb" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/iron/dark{ heat_capacity = 1e+006 }, diff --git a/_maps/map_files/Basketball/ash_gladiators.dmm b/_maps/map_files/Basketball/ash_gladiators.dmm index 75315e028a83..0a54af6cd227 100644 --- a/_maps/map_files/Basketball/ash_gladiators.dmm +++ b/_maps/map_files/Basketball/ash_gladiators.dmm @@ -266,7 +266,7 @@ /turf/open/floor/fakebasalt, /area/centcom/basketball) "nv" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/fakebasalt, /area/centcom/basketball) "oR" = ( diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm index 2f5e9148ffeb..79169c0351f8 100644 --- a/_maps/map_files/BoxStation/BoxStation.dmm +++ b/_maps/map_files/BoxStation/BoxStation.dmm @@ -393,6 +393,42 @@ /mob/living/basic/pet/dog/pug/mcgriff, /turf/open/floor/iron/showroomfloor, /area/station/security/warden) +"ahN" = ( +/obj/machinery/button/door/directional/west{ + id = "visitation"; + name = "Visitation Shutters"; + req_access = list("brig") + }, +/obj/machinery/button/door/directional/west{ + id = "visitation"; + name = "Visitation Shutters"; + req_access = list("brig") + }, +/obj/machinery/button/door/directional/west{ + id = "visitation"; + name = "Visitation Shutters"; + req_one_access = list("brig", "permabrig") + }, +/obj/machinery/button/flasher{ + id = "visitation_prisoner_flash"; + name = "Visitation Prisoner Flash"; + pixel_x = -24; + pixel_y = 9 + }, +/obj/machinery/button/flasher{ + id = "visitation_visitor_flash"; + name = "Visitation Visitor Flash"; + pixel_x = -24; + pixel_y = -9 + }, +/obj/structure/cable, +/obj/structure/table, +/obj/item/restraints/handcuffs{ + pixel_y = 3 + }, +/obj/item/assembly/flash/handheld, +/turf/open/floor/iron/dark, +/area/station/security/prison/visit) "ahW" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -1504,6 +1540,15 @@ dir = 8 }, /area/station/command/heads_quarters/cmo) +"aBO" = ( +/obj/machinery/camera/autoname/directional/south, +/obj/structure/closet/crate/hydroponics, +/obj/item/shovel/spade, +/obj/item/seeds/tree, +/obj/item/seeds/tree, +/obj/item/seeds/tree, +/turf/open/floor/wood, +/area/station/service/chapel/funeral) "aBX" = ( /obj/effect/turf_decal/stripes/line{ dir = 4 @@ -4760,6 +4805,13 @@ dir = 4 }, /area/station/engineering/break_room) +"bFP" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron/dark, +/area/station/security/brig/entrance) "bFX" = ( /obj/effect/turf_decal/box, /obj/machinery/holopad, @@ -5908,23 +5960,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/department/electrical) -"bYZ" = ( -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/cable, -/obj/machinery/door/airlock/security/glass{ - name = "Isolation Cell"; - id_tag = "iso_cell_hall_bolt" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/machinery/duct, -/obj/effect/mapping_helpers/airlock/cyclelink_helper, -/turf/open/floor/iron/dark/textured, -/area/station/security/prison/safe) "bZl" = ( /obj/machinery/light/small/directional/west, /obj/structure/frame/computer{ @@ -6380,6 +6415,37 @@ /obj/effect/spawner/random/structure/closet_maintenance, /turf/open/floor/plating, /area/station/maintenance/port/aft) +"cfY" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 8 + }, +/obj/structure/table/glass, +/obj/item/paper_bin{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/pen/red{ + pixel_x = -4 + }, +/obj/item/book/manual/wiki/medicine, +/obj/item/book/manual/wiki/infections{ + pixel_x = 10; + pixel_y = 2 + }, +/obj/item/reagent_containers/syringe/antiviral, +/obj/item/reagent_containers/dropper, +/obj/item/reagent_containers/spray/cleaner, +/obj/item/hand_labeler, +/obj/item/radio/headset/headset_med, +/obj/machinery/requests_console/auto_name/directional/west{ + department = "Virology"; + name = "Pathology Requests Console"; + receive_ore_updates = 1 + }, +/obj/item/clothing/glasses/science, +/obj/item/restraints/handcuffs, +/turf/open/floor/iron/white, +/area/station/medical/virology) "cgb" = ( /obj/structure/cable, /turf/open/floor/iron/dark, @@ -6774,6 +6840,17 @@ /obj/effect/spawner/random/engineering/tracking_beacon, /turf/open/floor/iron/dark, /area/station/security/courtroom) +"clE" = ( +/obj/effect/turf_decal/box, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/machinery/holopad, +/obj/machinery/duct, +/obj/effect/landmark/event_spawn, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron/dark/smooth_large, +/area/station/security/brig) "cmn" = ( /obj/machinery/atmospherics/components/unary/passive_vent, /turf/open/floor/plating/airless, @@ -7611,31 +7688,6 @@ }, /turf/open/floor/engine, /area/station/medical/cryo) -"cDC" = ( -/obj/effect/turf_decal/trimline/green/filled/line, -/obj/structure/table/glass, -/obj/item/paper_bin{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/pen/red{ - pixel_x = -4 - }, -/obj/item/book/manual/wiki/medicine, -/obj/item/book/manual/wiki/infections{ - pixel_x = 10; - pixel_y = 2 - }, -/obj/item/reagent_containers/syringe/antiviral, -/obj/item/reagent_containers/dropper, -/obj/item/reagent_containers/spray/cleaner, -/obj/item/hand_labeler, -/obj/item/radio/headset/headset_med, -/obj/machinery/newscaster/directional/south, -/obj/item/clothing/glasses/science, -/obj/item/extrapolator, -/turf/open/floor/iron/white, -/area/station/medical/virology) "cDF" = ( /obj/effect/turf_decal/siding/thinplating/corner{ dir = 4 @@ -8549,10 +8601,6 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/engineering/break_room) -"cSx" = ( -/obj/machinery/hydroponics/soil, -/turf/open/floor/grass, -/area/station/service/chapel/funeral) "cSz" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ @@ -8789,6 +8837,20 @@ /obj/effect/spawner/random/techstorage/engineering_all, /turf/open/floor/iron, /area/station/engineering/storage/tech) +"cVs" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/line, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/security/glass{ + name = "Visitation" + }, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/dark/textured, +/area/station/security/prison/visit) "cVu" = ( /obj/effect/turf_decal/bot_red, /obj/structure/cable, @@ -12523,6 +12585,31 @@ /obj/item/assembly/timer, /turf/open/floor/plating, /area/station/maintenance/port/aft) +"elh" = ( +/obj/effect/turf_decal/trimline/green/filled/line, +/obj/structure/table/glass, +/obj/item/paper_bin{ + pixel_x = -4; + pixel_y = 4 + }, +/obj/item/pen/red{ + pixel_x = -4 + }, +/obj/item/book/manual/wiki/medicine, +/obj/item/book/manual/wiki/infections{ + pixel_x = 10; + pixel_y = 2 + }, +/obj/item/reagent_containers/syringe/antiviral, +/obj/item/reagent_containers/dropper, +/obj/item/reagent_containers/spray/cleaner, +/obj/item/hand_labeler, +/obj/item/radio/headset/headset_med, +/obj/machinery/newscaster/directional/south, +/obj/item/clothing/glasses/science, +/obj/item/restraints/handcuffs, +/turf/open/floor/iron/white, +/area/station/medical/virology) "ell" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 8 @@ -15323,12 +15410,6 @@ }, /turf/open/floor/iron/white/textured, /area/station/medical/chemistry) -"fkD" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron/dark, -/area/station/security/brig/entrance) "fkT" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -21469,6 +21550,25 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) +"hnT" = ( +/obj/machinery/power/apc/auto_name/directional/south, +/obj/structure/cable, +/obj/structure/closet/crate/hydroponics, +/obj/machinery/light/small/directional/south, +/obj/item/paper/guides/jobs/hydroponics, +/obj/item/seeds/onion, +/obj/item/seeds/garlic, +/obj/item/seeds/potato, +/obj/item/seeds/tomato, +/obj/item/seeds/carrot, +/obj/item/seeds/grass, +/obj/item/seeds/ambrosia, +/obj/item/seeds/wheat, +/obj/item/seeds/pumpkin, +/obj/effect/spawner/random/contraband/prison, +/obj/item/seeds/tree, +/turf/open/floor/iron/dark, +/area/station/security/prison/garden) "hoa" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -23718,6 +23818,14 @@ }, /turf/open/floor/iron, /area/station/cargo/warehouse) +"idi" = ( +/obj/effect/turf_decal/tile/green/half/contrasted{ + dir = 1 + }, +/obj/machinery/light/directional/south, +/obj/machinery/growing/tray, +/turf/open/floor/iron/dark/side, +/area/station/service/hydroponics) "idF" = ( /obj/effect/turf_decal/stripes/line{ dir = 9 @@ -24311,6 +24419,10 @@ }, /turf/open/floor/iron/dark, /area/station/security/checkpoint/escape) +"inD" = ( +/obj/machinery/growing/soil, +/turf/open/floor/grass, +/area/station/hallway/primary/central) "inF" = ( /obj/structure/extinguisher_cabinet/directional/west, /obj/structure/disposalpipe/trunk{ @@ -24535,6 +24647,14 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/maintenance/starboard/aft) +"iqN" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/machinery/duct, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron/dark, +/area/station/security/brig) "iqP" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -25833,6 +25953,25 @@ /obj/structure/sign/poster/official/no_erp/directional/south, /turf/open/floor/iron/dark/side, /area/station/commons/dorms) +"iLi" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 1 + }, +/obj/machinery/newscaster/directional/north, +/obj/structure/closet/crate/hydroponics, +/obj/item/seeds/apple, +/obj/item/seeds/banana, +/obj/item/seeds/cocoapod, +/obj/item/seeds/grape, +/obj/item/seeds/orange, +/obj/item/seeds/sugarcane, +/obj/item/seeds/wheat, +/obj/item/seeds/watermelon, +/obj/item/seeds/tree, +/turf/open/floor/iron/dark/side{ + dir = 1 + }, +/area/station/service/hydroponics/garden) "iLj" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 5 @@ -29844,10 +29983,6 @@ "kfT" = ( /turf/closed/wall/r_wall, /area/station/security/evidence) -"kgn" = ( -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron, -/area/station/service/hydroponics) "kgv" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -29932,6 +30067,25 @@ /obj/machinery/duct, /turf/open/floor/iron/white, /area/station/medical/virology) +"khV" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/machinery/door/airlock/security/glass{ + name = "Isolation Cell"; + id_tag = "iso_cell_hall_bolt" + }, +/obj/machinery/duct, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 1 + }, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/dark/textured, +/area/station/security/prison) "khZ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -31843,6 +31997,10 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/port/aft) +"kPz" = ( +/obj/machinery/growing/soil, +/turf/open/floor/grass, +/area/station/security/prison/garden) "kPJ" = ( /obj/machinery/corral_corner{ mapping_id = "1" @@ -35708,15 +35866,6 @@ }, /turf/open/floor/engine/vacuum, /area/station/engineering/atmos) -"mfd" = ( -/obj/machinery/camera/autoname/directional/south, -/obj/structure/closet/crate/hydroponics, -/obj/item/shovel/spade, -/obj/item/seeds/tower, -/obj/item/seeds/tower, -/obj/item/seeds/tower, -/turf/open/floor/wood, -/area/station/service/chapel/funeral) "mfe" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -36567,6 +36716,10 @@ "mug" = ( /turf/open/floor/iron/dark, /area/station/security/checkpoint/supply) +"mun" = ( +/obj/machinery/growing/soil, +/turf/open/floor/grass, +/area/station/service/chapel/funeral) "mup" = ( /obj/machinery/chem_dispenser, /turf/open/floor/iron/white/textured, @@ -37145,25 +37298,6 @@ "mGb" = ( /turf/open/floor/iron/white, /area/station/medical/virology) -"mGg" = ( -/obj/machinery/power/apc/auto_name/directional/south, -/obj/structure/cable, -/obj/structure/closet/crate/hydroponics, -/obj/machinery/light/small/directional/south, -/obj/item/paper/guides/jobs/hydroponics, -/obj/item/seeds/onion, -/obj/item/seeds/garlic, -/obj/item/seeds/potato, -/obj/item/seeds/tomato, -/obj/item/seeds/carrot, -/obj/item/seeds/grass, -/obj/item/seeds/ambrosia, -/obj/item/seeds/wheat, -/obj/item/seeds/pumpkin, -/obj/effect/spawner/random/contraband/prison, -/obj/item/seeds/tower, -/turf/open/floor/iron/dark, -/area/station/security/prison/garden) "mGu" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -38993,10 +39127,6 @@ }, /turf/open/floor/iron/dark/textured, /area/station/command/teleporter) -"nmw" = ( -/obj/machinery/hydroponics/soil, -/turf/open/floor/grass, -/area/station/hallway/primary/central) "nmY" = ( /obj/machinery/airalarm/directional/south, /obj/structure/destructible/cult/item_dispenser/archives/library, @@ -41300,6 +41430,10 @@ /obj/structure/sign/warning/electric_shock/directional/south, /turf/open/space/basic, /area/station/solars/starboard/aft) +"nZC" = ( +/obj/machinery/growing/soil, +/turf/open/floor/grass, +/area/station/service/hydroponics/garden) "nZD" = ( /obj/machinery/airalarm/directional/south, /obj/structure/chair/stool/directional/west, @@ -42529,16 +42663,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/commons/vacant_room/commissary) -"ouL" = ( -/obj/effect/turf_decal/box, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/cable, -/obj/machinery/holopad, -/obj/machinery/duct, -/obj/effect/landmark/event_spawn, -/turf/open/floor/iron/dark/smooth_large, -/area/station/security/brig) "ouP" = ( /obj/effect/turf_decal/trimline/red/filled/corner{ dir = 4 @@ -47817,6 +47941,14 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /turf/open/floor/wood, /area/station/service/bar/backroom) +"qot" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/duct, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron/dark, +/area/station/security/brig) "qou" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 4 @@ -49236,6 +49368,10 @@ "qOl" = ( /turf/open/floor/iron/dark, /area/station/hallway/primary/fore) +"qOn" = ( +/obj/machinery/growing/tray, +/turf/open/floor/iron, +/area/station/service/hydroponics) "qOu" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/carpet, @@ -52253,42 +52389,6 @@ }, /turf/open/floor/plastic, /area/station/security/prison/safe) -"rOF" = ( -/obj/machinery/button/door/directional/west{ - id = "visitation"; - name = "Visitation Shutters"; - req_access = list("brig") - }, -/obj/machinery/button/door/directional/west{ - id = "visitation"; - name = "Visitation Shutters"; - req_access = list("brig") - }, -/obj/machinery/button/door/directional/west{ - id = "visitation"; - name = "Visitation Shutters"; - req_access = list("brig") - }, -/obj/machinery/button/flasher{ - id = "visitation_prisoner_flash"; - name = "Visitation Prisoner Flash"; - pixel_x = -24; - pixel_y = 9 - }, -/obj/machinery/button/flasher{ - id = "visitation_visitor_flash"; - name = "Visitation Visitor Flash"; - pixel_x = -24; - pixel_y = -9 - }, -/obj/structure/cable, -/obj/structure/table, -/obj/item/restraints/handcuffs{ - pixel_y = 3 - }, -/obj/item/assembly/flash/handheld, -/turf/open/floor/iron/dark, -/area/station/security/prison/visit) "rOQ" = ( /obj/machinery/duct, /obj/structure/table/reinforced, @@ -54356,6 +54456,13 @@ }, /turf/open/floor/iron, /area/station/science/ordnance) +"sCq" = ( +/obj/effect/turf_decal/tile/green/half/contrasted{ + dir = 1 + }, +/obj/machinery/growing/tray, +/turf/open/floor/iron/dark/side, +/area/station/service/hydroponics) "sDi" = ( /obj/item/radio/intercom/directional/east{ freerange = 1; @@ -55124,6 +55231,23 @@ /obj/machinery/disposal/bin, /turf/open/floor/iron/white, /area/station/science/genetics) +"sRE" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/machinery/door/airlock/security/glass{ + name = "Isolation Cell"; + id_tag = "iso_cell_hall_bolt" + }, +/obj/machinery/duct, +/obj/effect/mapping_helpers/airlock/cyclelink_helper, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/dark/textured, +/area/station/security/prison/safe) "sRJ" = ( /obj/machinery/cryopod{ dir = 4 @@ -56798,6 +56922,13 @@ }, /turf/open/floor/iron/dark, /area/station/ai_monitored/security/armory) +"tsS" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron/dark, +/area/station/security/brig) "ttl" = ( /obj/effect/turf_decal/tile/dark_blue/fourcorners, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, @@ -57622,10 +57753,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/ai_upload) -"tIO" = ( -/obj/machinery/hydroponics/soil, -/turf/open/floor/grass, -/area/station/service/hydroponics/garden) "tIU" = ( /turf/open/floor/iron/dark/smooth_large, /area/station/maintenance/department/security) @@ -57980,20 +58107,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/showroomfloor, /area/station/command/heads_quarters/rd) -"tPZ" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/effect/turf_decal/stripes/line, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/security/glass{ - name = "Visitation" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/turf/open/floor/iron/dark/textured, -/area/station/security/prison/visit) "tQa" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/hidden, @@ -58857,25 +58970,6 @@ /obj/machinery/door/firedoor, /turf/open/floor/iron/dark/textured, /area/station/hallway/secondary/entry) -"ugn" = ( -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/cable, -/obj/machinery/door/airlock/security/glass{ - name = "Isolation Cell"; - id_tag = "iso_cell_hall_bolt" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/machinery/duct, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 1 - }, -/turf/open/floor/iron/dark/textured, -/area/station/security/prison) "ugy" = ( /obj/effect/turf_decal/trimline/blue/filled/line, /obj/machinery/camera/autoname/directional/south, @@ -59033,37 +59127,6 @@ }, /turf/open/floor/iron/dark, /area/station/ai_monitored/security/armory) -"uit" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 8 - }, -/obj/structure/table/glass, -/obj/item/paper_bin{ - pixel_x = -4; - pixel_y = 4 - }, -/obj/item/pen/red{ - pixel_x = -4 - }, -/obj/item/book/manual/wiki/medicine, -/obj/item/book/manual/wiki/infections{ - pixel_x = 10; - pixel_y = 2 - }, -/obj/item/reagent_containers/syringe/antiviral, -/obj/item/reagent_containers/dropper, -/obj/item/reagent_containers/spray/cleaner, -/obj/item/hand_labeler, -/obj/item/radio/headset/headset_med, -/obj/machinery/requests_console/auto_name/directional/west{ - department = "Virology"; - name = "Pathology Requests Console"; - receive_ore_updates = 1 - }, -/obj/item/clothing/glasses/science, -/obj/item/extrapolator, -/turf/open/floor/iron/white, -/area/station/medical/virology) "uiu" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -63874,6 +63937,25 @@ /obj/machinery/computer/robotics, /turf/open/floor/carpet/purple, /area/station/command/heads_quarters/rd) +"vOC" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/line, +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/security/glass{ + name = "Prison Wing" + }, +/obj/machinery/door/poddoor/preopen{ + id = "perma_lockdown"; + name = "Lockdown Blast Door" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ + cycle_id = "perma-entrance" + }, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/dark/textured, +/area/station/security/execution/transfer) "vOP" = ( /obj/effect/turf_decal/tile/purple/full, /obj/effect/turf_decal/box/white, @@ -64153,10 +64235,6 @@ /obj/structure/closet/secure_closet/personal/cabinet, /turf/open/floor/wood, /area/station/commons/dorms) -"vSd" = ( -/obj/machinery/hydroponics/soil, -/turf/open/floor/grass, -/area/station/security/prison/garden) "vSj" = ( /obj/effect/turf_decal/stripes/line{ dir = 9 @@ -66323,14 +66401,6 @@ }, /turf/open/floor/plating/airless, /area/space/nearstation) -"wGE" = ( -/obj/effect/turf_decal/tile/green/half/contrasted{ - dir = 1 - }, -/obj/machinery/light/directional/south, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/dark/side, -/area/station/service/hydroponics) "wGN" = ( /obj/effect/turf_decal/tile/blue/half/contrasted, /obj/effect/decal/cleanable/dirt, @@ -66872,25 +66942,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark, /area/station/engineering/transit_tube) -"wPR" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/effect/turf_decal/stripes/line, -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/security/glass{ - name = "Prison Wing" - }, -/obj/machinery/door/poddoor/preopen{ - id = "perma_lockdown"; - name = "Lockdown Blast Door" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ - cycle_id = "perma-entrance" - }, -/turf/open/floor/iron/dark/textured, -/area/station/security/execution/transfer) "wPT" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -67932,25 +67983,6 @@ }, /turf/open/space/basic, /area/space/nearstation) -"xja" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 1 - }, -/obj/machinery/newscaster/directional/north, -/obj/structure/closet/crate/hydroponics, -/obj/item/seeds/apple, -/obj/item/seeds/banana, -/obj/item/seeds/cocoapod, -/obj/item/seeds/grape, -/obj/item/seeds/orange, -/obj/item/seeds/sugarcane, -/obj/item/seeds/wheat, -/obj/item/seeds/watermelon, -/obj/item/seeds/tower, -/turf/open/floor/iron/dark/side{ - dir = 1 - }, -/area/station/service/hydroponics/garden) "xjb" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -68244,13 +68276,6 @@ /obj/item/kirbyplants/random, /turf/open/floor/iron/dark, /area/station/security/brig/entrance) -"xqS" = ( -/obj/effect/turf_decal/tile/green/half/contrasted{ - dir = 1 - }, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/dark/side, -/area/station/service/hydroponics) "xqW" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/effect/turf_decal/bot_red, @@ -69237,6 +69262,10 @@ /obj/effect/mapping_helpers/airlock/access/any/science/maintenance, /turf/open/floor/catwalk_floor/iron_white, /area/station/maintenance/department/science) +"xHj" = ( +/mob/living/basic/pet/potty, +/turf/open/floor/iron, +/area/station/service/hydroponics) "xHp" = ( /obj/machinery/atmospherics/pipe/smart/simple/purple/visible, /turf/open/floor/iron/dark, @@ -83551,8 +83580,8 @@ vxd fPN kNO aKB -tIO -tIO +nZC +nZC bMv rud jmh @@ -84065,8 +84094,8 @@ vXb fPN etC aKB -tIO -tIO +nZC +nZC bMv iNK jmh @@ -84577,10 +84606,10 @@ fPN wMu fPN fPN -xja +iLi aKB -tIO -tIO +nZC +nZC bMv nDK wZk @@ -84596,7 +84625,7 @@ emg dSO juc juc -juc +xHj qxQ pig pff @@ -85109,8 +85138,8 @@ pEY xCd jRP juc -kgn -kgn +qOn +qOn nJa tJE pEY @@ -85366,10 +85395,10 @@ pEY pEY utE juc -kgn -kgn +qOn +qOn nJa -wGE +idi pEY kWu pfJ @@ -85623,10 +85652,10 @@ dyk mWH emg juc -kgn -kgn +qOn +qOn nJa -xqS +sCq rCa uGS wNw @@ -85883,7 +85912,7 @@ juc fzI juc nJa -xqS +sCq rCa jSO qPn @@ -86140,7 +86169,7 @@ juc juc juc nJa -xqS +sCq rCa knA wKr @@ -86394,10 +86423,10 @@ dyk ufA emg juc -kgn -kgn +qOn +qOn nJa -xqS +sCq rCa uGS wNw @@ -86651,10 +86680,10 @@ pEY pEY lsR juc -kgn -kgn +qOn +qOn nJa -wGE +idi pEY aSR guH @@ -86908,8 +86937,8 @@ pEY vaR uFv juc -kgn -kgn +qOn +qOn nJa vom pEY @@ -90679,11 +90708,11 @@ wZx cYs eLo dot -bYZ +sRE jou rdn jou -ugn +khV jou aco jou @@ -96102,13 +96131,13 @@ xUO rDO lMT qgq -wPR +vOC aKU aKU aya aKU aKU -wPR +vOC niu sey tMr @@ -96616,13 +96645,13 @@ xUO qgq lMT nqD -wPR +vOC aKU aKU hvc aKU aKU -wPR +vOC niu qQg tMr @@ -96672,7 +96701,7 @@ gbD hjB xLi wAZ -nmw +inD qkl ftk qkl @@ -96890,7 +96919,7 @@ sIr pCA xBB lPr -bYk +iqN oiV hDp hDp @@ -96929,7 +96958,7 @@ gbD wXC cjW wAZ -nmw +inD fuf dzH bXB @@ -97625,8 +97654,8 @@ xta xta xta tFK -vSd -vSd +kPz +kPz vQY dZe kxu @@ -97650,7 +97679,7 @@ iFw cbe iFw vFW -tPZ +cVs sUf qQg qow @@ -98139,8 +98168,8 @@ fEP fEP uoo rHA -vSd -vSd +kPz +kPz xWj iwL uNU @@ -98418,7 +98447,7 @@ ksa vpG ubp lLm -rOF +ahN qbg aUn vpG @@ -98653,8 +98682,8 @@ ksa fEP uoo rHA -vSd -vSd +kPz +kPz xWj ipb qNz @@ -98914,7 +98943,7 @@ yaX yaX gJK vTX -mGg +hnT oER oER mkI @@ -98942,11 +98971,11 @@ cWL nke fsm fsm +qot djF djF djF -djF -ouL +clE fFR lVI hDp @@ -99167,8 +99196,8 @@ ksa fEP uoo rHA -vSd -vSd +kPz +kPz xWj bMw sJm @@ -99681,8 +99710,8 @@ ksa fEP ksa tFK -vSd -vSd +kPz +kPz xWj bMw jJt @@ -100488,7 +100517,7 @@ cQe ghs cvK rWE -tWx +tsS xnG pNK vhd @@ -101519,9 +101548,9 @@ krU jDc etx htc -fkD +bFP nlr -fkD +bFP htc lig xpb @@ -102030,7 +102059,7 @@ mAr tTx rzM pwT -tWx +tsS vGY gPd lHi @@ -103572,7 +103601,7 @@ qlQ ciG pEr khr -tWx +tsS tWx wYE tWx @@ -109056,7 +109085,7 @@ gCT gSm nqG lFY -cDC +elh oqK lrg tgG @@ -110090,7 +110119,7 @@ rmM gDn iMj sxJ -uit +cfY rbW oqK iKJ @@ -117482,9 +117511,9 @@ aUh fMC eNN sfC -cSx +mun cSY -cSx +mun sms wSC qWj @@ -117739,9 +117768,9 @@ jxy sfC sfC sfC -cSx +mun oHe -cSx +mun sms sms pmV @@ -118256,7 +118285,7 @@ eUW eUW eUW eUW -mfd +aBO sms sOo bPd diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 6bfc274f6e11..e4c0d109551e 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -372,17 +372,6 @@ /obj/machinery/light/directional/north, /turf/open/floor/engine, /area/station/science/research) -"aeu" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/green{ - dir = 1 - }, -/obj/structure/sign/poster/random/directional/north, -/turf/open/floor/iron, -/area/station/service/hydroponics) "aev" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -1645,6 +1634,16 @@ /obj/effect/landmark/start/botanist, /turf/open/floor/iron, /area/station/service/hydroponics) +"arS" = ( +/obj/machinery/growing/soil, +/obj/effect/decal/cleanable/dirt, +/obj/structure/sign/poster/contraband/ambrosia_vulgaris{ + pixel_x = -30 + }, +/obj/machinery/light/directional/west, +/obj/effect/mapping_helpers/burnt_floor, +/turf/open/floor/plating, +/area/station/security/prison/garden) "arU" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/structure/cable, @@ -1890,6 +1889,12 @@ }, /turf/open/floor/iron/dark, /area/station/security/brig) +"auf" = ( +/obj/machinery/light/small/directional/south, +/obj/machinery/growing/soil, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/plating, +/area/station/service/hydroponics/garden/abandoned) "auh" = ( /obj/structure/table/reinforced, /obj/item/paper_bin, @@ -2272,6 +2277,19 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/security/execution/transfer) +"ayQ" = ( +/obj/effect/turf_decal/trimline/green/end, +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/mid_joiner{ + dir = 4 + }, +/obj/effect/turf_decal/trimline/green/mid_joiner{ + dir = 8 + }, +/turf/open/floor/iron/half{ + dir = 8 + }, +/area/station/service/hydroponics/garden) "aze" = ( /obj/machinery/chem_master/condimaster{ desc = "Used to separate out liquids - useful for purifying botanical extracts. Also dispenses condiments."; @@ -2954,15 +2972,6 @@ /obj/item/melee/chainofcommand, /turf/open/floor/iron/grimy, /area/station/command/heads_quarters/captain) -"aHd" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/green{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue, -/obj/structure/extinguisher_cabinet/directional/south, -/turf/open/floor/iron, -/area/station/service/hydroponics) "aHq" = ( /obj/structure/table/wood, /obj/item/storage/box/matches{ @@ -3785,6 +3794,14 @@ dir = 1 }, /area/station/service/bar) +"aRn" = ( +/obj/machinery/growing/soil, +/obj/item/cultivator, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/airalarm/directional/east, +/obj/effect/mapping_helpers/burnt_floor, +/turf/open/floor/plating, +/area/station/security/prison/garden) "aRr" = ( /turf/open/floor/circuit/red, /area/station/ai_monitored/turret_protected/ai_upload) @@ -4804,39 +4821,6 @@ /obj/machinery/computer/security/telescreen/entertainment/directional/north, /turf/open/floor/wood/large, /area/station/service/library) -"bgl" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/obj/effect/turf_decal/tile/green{ - dir = 1 - }, -/obj/item/radio/intercom/directional/north, -/obj/machinery/light/directional/west, -/turf/open/floor/iron, -/area/station/service/hydroponics) -"bgo" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/line{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/green/line{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/green/mid_joiner{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/green/mid_joiner{ - dir = 4 - }, -/turf/open/floor/iron/half{ - dir = 8 - }, -/area/station/service/hydroponics/garden) "bgz" = ( /obj/effect/decal/cleanable/blood/old, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -5056,6 +5040,16 @@ }, /turf/open/floor/iron/white, /area/station/medical/cryo) +"biH" = ( +/obj/effect/turf_decal/tile/green{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue, +/obj/machinery/light/directional/south, +/obj/machinery/growing/tray, +/obj/machinery/status_display/ai/directional/south, +/turf/open/floor/iron, +/area/station/service/hydroponics) "biO" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -6047,14 +6041,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos/storage/gas) -"buj" = ( -/obj/effect/landmark/blobstart, -/obj/effect/landmark/generic_maintenance_landmark, -/obj/effect/landmark/event_spawn, -/obj/machinery/hydroponics/soil, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/plating, -/area/station/service/hydroponics/garden/abandoned) "buK" = ( /obj/structure/table/wood/fancy, /turf/open/floor/iron/grimy, @@ -6629,6 +6615,20 @@ /obj/item/toy/figure/scientist, /turf/open/floor/iron, /area/station/science/lab) +"bCb" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/green{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/machinery/newscaster/directional/north, +/turf/open/floor/iron, +/area/station/service/hydroponics) "bCc" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/effect/turf_decal/stripes/line{ @@ -7235,16 +7235,6 @@ /obj/machinery/power/port_gen/pacman, /turf/open/floor/iron, /area/station/engineering/storage) -"bIG" = ( -/obj/effect/turf_decal/tile/green{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue, -/obj/machinery/light/directional/south, -/obj/machinery/hydroponics/constructable, -/obj/machinery/status_display/ai/directional/south, -/turf/open/floor/iron, -/area/station/service/hydroponics) "bIM" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -8331,6 +8321,27 @@ /obj/effect/mapping_helpers/airlock/access/all/engineering/engine_equipment, /turf/open/floor/iron, /area/station/engineering/storage) +"bUc" = ( +/obj/machinery/button/flasher{ + id = "Cell 5"; + name = "Prisoner Flash"; + pixel_x = 25; + pixel_y = 7 + }, +/obj/structure/cable, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/button/door/directional/east{ + id = "permashut5"; + name = "Cell Lockdown Button"; + pixel_y = -6; + req_one_access = list("brig", "permabrig") + }, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "bUd" = ( /obj/effect/turf_decal/siding/wood/corner{ dir = 4 @@ -9589,6 +9600,14 @@ /obj/effect/landmark/start/hangover, /turf/open/floor/iron/dark, /area/station/hallway/secondary/exit/departure_lounge) +"ckH" = ( +/obj/machinery/growing/soil, +/obj/item/cultivator, +/obj/effect/decal/cleanable/dirt, +/obj/structure/sign/warning/electric_shock/directional/west, +/obj/effect/mapping_helpers/burnt_floor, +/turf/open/floor/plating, +/area/station/security/prison/garden) "ckP" = ( /turf/open/floor/plating, /area/station/maintenance/starboard/aft) @@ -10717,17 +10736,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/wood/large, /area/station/command/corporate_showroom) -"czg" = ( -/obj/machinery/hydroponics/soil, -/obj/effect/decal/cleanable/dirt, -/obj/item/plant_analyzer, -/obj/machinery/camera/directional/east{ - c_tag = "Permabrig - Garden"; - network = list("ss13","prison") - }, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/plating, -/area/station/security/prison/garden) "czi" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/siding/purple{ @@ -10881,6 +10889,14 @@ }, /turf/open/floor/engine/vacuum, /area/station/science/ordnance/freezerchamber) +"cBn" = ( +/obj/effect/turf_decal/tile/green{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue, +/obj/machinery/growing/tray, +/turf/open/floor/iron, +/area/station/service/hydroponics) "cBr" = ( /obj/machinery/suit_storage_unit/atmos, /obj/effect/turf_decal/box/red/corners{ @@ -11993,6 +12009,17 @@ /obj/structure/sign/warning/secure_area/directional/north, /turf/open/floor/iron, /area/station/science/robotics/mechbay) +"cOY" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/green{ + dir = 8 + }, +/obj/effect/turf_decal/tile/green{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/turf/open/floor/iron, +/area/station/service/hydroponics) "cPb" = ( /obj/structure/table, /obj/effect/turf_decal/tile/neutral/fourcorners, @@ -13831,36 +13858,11 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/iron, /area/station/service/kitchen/abandoned) -"doM" = ( -/obj/machinery/door/airlock/security/glass{ - name = "Permabrig Cell 1" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/structure/cable, -/turf/open/floor/iron, -/area/station/security/prison/safe) "doR" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/engineering/atmos/project) -"dps" = ( -/obj/machinery/light/small/directional/south, -/obj/machinery/hydroponics/soil, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/plating, -/area/station/service/hydroponics/garden/abandoned) "dpI" = ( /obj/structure/table, /obj/item/clothing/gloves/latex, @@ -14539,25 +14541,6 @@ /obj/effect/mapping_helpers/airlock/cyclelink_helper, /turf/open/floor/iron/dark, /area/station/security/interrogation) -"dwX" = ( -/obj/machinery/door/airlock/security/glass{ - name = "Permabrig Cell 5" - }, -/obj/structure/cable, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/turf/open/floor/iron, -/area/station/security/prison/safe) "dwY" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/duct, @@ -14683,30 +14666,6 @@ "dyx" = ( /turf/open/floor/iron/white, /area/station/science/research) -"dyH" = ( -/obj/effect/landmark/event_spawn, -/obj/structure/cable, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/junction{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/security/brig) -"dzk" = ( -/obj/effect/turf_decal/trimline/green/end, -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/mid_joiner{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/green/mid_joiner{ - dir = 4 - }, -/turf/open/floor/iron/half{ - dir = 8 - }, -/area/station/service/hydroponics/garden) "dzl" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -15246,6 +15205,24 @@ }, /turf/open/floor/iron/white, /area/station/command/heads_quarters/rd) +"dGr" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/line{ + dir = 4 + }, +/obj/effect/turf_decal/trimline/green/line{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/green/mid_joiner{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/green/mid_joiner{ + dir = 4 + }, +/turf/open/floor/iron/half{ + dir = 8 + }, +/area/station/service/hydroponics/garden) "dGs" = ( /obj/effect/decal/cleanable/cobweb, /obj/structure/dresser, @@ -16027,6 +16004,18 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/science/breakroom) +"dQQ" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 1 + }, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron/dark/textured_edge{ + dir = 4 + }, +/area/station/security/brig) "dQS" = ( /obj/structure/cable, /obj/structure/lattice/catwalk, @@ -17487,6 +17476,13 @@ /obj/item/kirbyplants/random, /turf/open/floor/iron, /area/station/maintenance/port) +"ekR" = ( +/obj/machinery/growing/soil, +/obj/effect/decal/cleanable/dirt, +/obj/item/shovel/spade, +/obj/effect/mapping_helpers/burnt_floor, +/turf/open/floor/plating, +/area/station/security/prison/garden) "ekZ" = ( /obj/effect/turf_decal/box/white{ color = "#52B4E9" @@ -18759,6 +18755,12 @@ /obj/effect/turf_decal/bot_white, /turf/open/floor/iron/dark, /area/station/service/library/printer) +"eAI" = ( +/obj/machinery/growing/soil, +/obj/effect/decal/cleanable/dirt, +/obj/effect/spawner/random/food_or_drink/seed, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden/abandoned) "eAO" = ( /obj/structure/cable, /obj/effect/turf_decal/trimline/purple/filled/warning{ @@ -19285,6 +19287,24 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/iron/cafeteria, /area/station/service/cafeteria) +"eHJ" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/line{ + dir = 4 + }, +/obj/effect/turf_decal/trimline/green/line{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/green/mid_joiner{ + dir = 4 + }, +/obj/effect/turf_decal/trimline/green/mid_joiner{ + dir = 8 + }, +/turf/open/floor/iron/half{ + dir = 8 + }, +/area/station/service/hydroponics/garden) "eHL" = ( /obj/effect/landmark/event_spawn, /obj/effect/turf_decal/tile/neutral/fourcorners, @@ -20058,13 +20078,6 @@ /obj/effect/turf_decal/stripes/corner, /turf/open/floor/iron, /area/station/engineering/atmos) -"eQx" = ( -/obj/machinery/hydroponics/soil, -/obj/effect/decal/cleanable/dirt, -/obj/item/shovel/spade, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/plating, -/area/station/security/prison/garden) "eQB" = ( /obj/machinery/door/airlock/public/glass{ name = "Holodeck Access" @@ -22829,6 +22842,27 @@ /obj/effect/spawner/random/trash/cigbutt, /turf/open/floor/plating, /area/station/hallway/secondary/construction) +"fyB" = ( +/obj/structure/cable, +/obj/machinery/button/flasher{ + id = "Cell 4"; + name = "Prisoner Flash"; + pixel_x = 25; + pixel_y = 7 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/button/door/directional/east{ + id = "permashut4"; + name = "Cell Lockdown Button"; + pixel_y = -6; + req_one_access = list("brig", "permabrig") + }, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "fyH" = ( /obj/machinery/keycard_auth/directional/south{ pixel_x = 6 @@ -23557,6 +23591,17 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/department/chapel) +"fIO" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/green, +/obj/effect/turf_decal/tile/green{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/service/hydroponics) "fIQ" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -23613,17 +23658,6 @@ "fJq" = ( /turf/closed/wall, /area/station/security/brig) -"fJx" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/green{ - dir = 1 - }, -/obj/structure/extinguisher_cabinet/directional/north, -/turf/open/floor/iron, -/area/station/service/hydroponics) "fJF" = ( /obj/machinery/firealarm/directional/east, /obj/effect/turf_decal/tile/neutral/fourcorners, @@ -24255,6 +24289,14 @@ /obj/effect/turf_decal/tile/neutral/full, /turf/open/floor/iron/dark/smooth_large, /area/station/security/interrogation) +"fSd" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/green{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/security/prison/garden) "fSg" = ( /obj/structure/table/wood, /obj/item/folder/blue, @@ -24522,6 +24564,17 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/hallway/secondary/exit) +"fVW" = ( +/obj/machinery/growing/soil, +/obj/effect/decal/cleanable/dirt, +/obj/item/plant_analyzer, +/obj/machinery/camera/directional/east{ + c_tag = "Permabrig - Garden"; + network = list("ss13","prison") + }, +/obj/effect/mapping_helpers/burnt_floor, +/turf/open/floor/plating, +/area/station/security/prison/garden) "fWc" = ( /obj/structure/chair/pew/left, /turf/open/floor/iron/chapel{ @@ -24676,30 +24729,6 @@ "fXz" = ( /turf/open/space, /area/space/nearstation) -"fXC" = ( -/obj/structure/cable, -/obj/machinery/door/poddoor/preopen{ - id = "brigprison"; - name = "Prison Blast Door" - }, -/obj/machinery/button/flasher{ - id = "Cell 1"; - name = "Prisoner Flash"; - pixel_x = 25; - pixel_y = 7 - }, -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/delivery, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/button/door/directional/east{ - id = "permashut1"; - name = "Cell Lockdown Button"; - pixel_y = -6; - req_access = list("brig") - }, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "fXF" = ( /turf/closed/wall/r_wall, /area/station/engineering/atmos) @@ -25597,27 +25626,6 @@ /obj/structure/sign/poster/contraband/random/directional/north, /turf/open/floor/iron, /area/station/maintenance/port/fore) -"gjc" = ( -/obj/structure/cable, -/obj/machinery/button/flasher{ - id = "Cell 4"; - name = "Prisoner Flash"; - pixel_x = 25; - pixel_y = 7 - }, -/obj/effect/turf_decal/tile/red{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/button/door/directional/east{ - id = "permashut4"; - name = "Cell Lockdown Button"; - pixel_y = -6; - req_access = list("brig") - }, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "gjg" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -25670,6 +25678,15 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/white, /area/station/medical/paramedic) +"gjX" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/green{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/security/prison/garden) "gkp" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -26846,12 +26863,6 @@ /obj/structure/sign/directions/ptl/directional/west, /turf/open/floor/plating, /area/station/maintenance/port) -"gwL" = ( -/obj/machinery/hydroponics/soil, -/obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/random/food_or_drink/seed, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden/abandoned) "gwM" = ( /obj/effect/turf_decal/arrows/white, /turf/open/floor/iron/dark, @@ -27042,24 +27053,6 @@ /obj/structure/window/reinforced/spawner, /turf/open/floor/iron/dark, /area/station/ai_monitored/security/armory) -"gzQ" = ( -/obj/machinery/door/airlock/security/glass{ - name = "Permabrig Cell 3" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/turf/open/floor/iron, -/area/station/security/prison/safe) "gzV" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible, @@ -27980,14 +27973,6 @@ /obj/structure/sink/directional/west, /turf/open/floor/iron/white, /area/station/medical/pathology) -"gLT" = ( -/obj/machinery/hydroponics/soil, -/obj/item/cultivator, -/obj/effect/decal/cleanable/dirt, -/obj/structure/sign/warning/electric_shock/directional/west, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/plating, -/area/station/security/prison/garden) "gMh" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/effect/turf_decal/stripes/line, @@ -28242,13 +28227,6 @@ /obj/effect/mapping_helpers/airlock/access/all/engineering/maintenance, /turf/open/floor/iron, /area/station/maintenance/fore) -"gPj" = ( -/obj/machinery/hydroponics/soil, -/obj/effect/decal/cleanable/dirt, -/obj/effect/spawner/random/food_or_drink/seed, -/obj/machinery/airalarm/directional/north, -/turf/open/floor/plating, -/area/station/service/hydroponics/garden/abandoned) "gPm" = ( /obj/structure/disposalpipe/segment{ dir = 10 @@ -29066,6 +29044,24 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/engineering/supermatter/room) +"hay" = ( +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/machinery/door/poddoor/preopen{ + id = "brigprison"; + name = "Prison Blast Door" + }, +/obj/machinery/door/airlock/security/glass{ + name = "Permabrig Visitation" + }, +/obj/effect/turf_decal/delivery, +/obj/structure/cable, +/obj/effect/landmark/navigate_destination, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/prison/visit) "haF" = ( /obj/structure/sign/warning/secure_area/directional/west, /turf/open/space/basic, @@ -30475,6 +30471,18 @@ /obj/item/storage/fancy/candle_box, /turf/open/floor/carpet/royalblack, /area/station/service/chapel/office) +"hsK" = ( +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/effect/turf_decal/tile/green{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue, +/obj/machinery/growing/tray, +/obj/machinery/light/directional/west, +/turf/open/floor/iron, +/area/station/service/hydroponics) "hsQ" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -34154,13 +34162,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) -"inx" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/obj/effect/turf_decal/siding/green{ - dir = 1 - }, -/turf/open/floor/iron/dark/smooth_large, -/area/station/service/hydroponics) "inR" = ( /obj/machinery/door/airlock/research{ name = "Research and Development Lab" @@ -37301,16 +37302,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/hallway/secondary/service) -"jdl" = ( -/obj/machinery/hydroponics/soil, -/obj/effect/decal/cleanable/dirt, -/obj/structure/sign/poster/contraband/ambrosia_vulgaris{ - pixel_x = -30 - }, -/obj/machinery/light/directional/west, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/plating, -/area/station/security/prison/garden) "jds" = ( /obj/structure/table/reinforced, /obj/item/folder/red, @@ -37575,14 +37566,6 @@ }, /turf/open/floor/iron, /area/station/maintenance/port) -"jgb" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/green{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/security/prison/garden) "jgd" = ( /obj/item/radio/intercom/directional/south, /obj/effect/turf_decal/tile/red/anticorner/contrasted, @@ -38970,6 +38953,17 @@ /obj/effect/spawner/random/structure/tank_holder, /turf/open/floor/plating, /area/station/maintenance/department/science) +"jxU" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/green{ + dir = 1 + }, +/obj/structure/extinguisher_cabinet/directional/north, +/turf/open/floor/iron, +/area/station/service/hydroponics) "jye" = ( /obj/structure/lattice, /obj/structure/window/reinforced/spawner/directional/north, @@ -39240,14 +39234,6 @@ /obj/effect/mapping_helpers/airlock/access/all/engineering/atmos, /turf/open/floor/iron, /area/station/engineering/atmos/storage) -"jAp" = ( -/obj/machinery/hydroponics/soil, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/directional/east, -/obj/item/radio/intercom/directional/east, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/plating, -/area/station/security/prison/garden) "jAI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -39440,14 +39426,6 @@ /obj/machinery/light/directional/north, /turf/open/floor/iron, /area/station/cargo/miningoffice) -"jCv" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/green{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/security/prison/garden) "jCw" = ( /obj/machinery/power/terminal{ dir = 4 @@ -40133,6 +40111,25 @@ /obj/effect/turf_decal/bot, /turf/open/floor/iron, /area/station/cargo/storage) +"jLj" = ( +/obj/machinery/door/airlock/security/glass{ + name = "Permabrig Cell 1" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/prison/safe) "jLm" = ( /obj/machinery/door/airlock/security/glass{ name = "Security E.V.A. Storage" @@ -42048,6 +42045,22 @@ "khb" = ( /turf/closed/wall/r_wall, /area/station/science/robotics/lab) +"khc" = ( +/obj/structure/cable, +/obj/machinery/seed_extractor, +/obj/item/seeds/tree, +/obj/item/seeds/watermelon, +/obj/item/seeds/wheat, +/obj/item/seeds/sugarcane, +/obj/item/seeds/orange, +/obj/item/seeds/grape, +/obj/item/seeds/cocoapod, +/obj/item/seeds/banana, +/obj/item/seeds/apple, +/obj/machinery/power/apc/auto_name/directional/west, +/obj/machinery/light/small/directional/west, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) "khl" = ( /obj/structure/mirror/directional/north, /obj/structure/sink/directional/south, @@ -42486,6 +42499,21 @@ /obj/structure/window/reinforced/spawner/directional/west, /turf/open/space/basic, /area/space/nearstation) +"kmv" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 8 + }, +/obj/effect/turf_decal/tile/green{ + dir = 1 + }, +/obj/item/radio/intercom/directional/north, +/obj/machinery/light/directional/west, +/turf/open/floor/iron, +/area/station/service/hydroponics) "kmy" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/structure/railing{ @@ -42750,6 +42778,24 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/maintenance/department/chapel) +"krt" = ( +/obj/machinery/door/airlock/security/glass{ + name = "Permabrig Cell 2" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/prison/safe) "krx" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -43349,6 +43395,24 @@ /obj/effect/turf_decal/bot, /turf/open/floor/iron/large, /area/station/ai_monitored/command/storage/eva) +"kzi" = ( +/obj/machinery/door/airlock/security/glass{ + name = "Permabrig Cell 3" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/prison/safe) "kzm" = ( /obj/effect/turf_decal/tile/purple/half/contrasted{ dir = 4 @@ -43811,17 +43875,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/science/xenobiology) -"kFi" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/green, -/obj/effect/turf_decal/tile/green{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/service/hydroponics) "kFv" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -45272,6 +45325,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/green/visible/layer4, /turf/open/floor/engine, /area/station/engineering/supermatter) +"kXS" = ( +/obj/machinery/growing/soil, +/obj/effect/decal/cleanable/dirt, +/obj/effect/spawner/random/food_or_drink/seed, +/obj/machinery/airalarm/directional/north, +/turf/open/floor/plating, +/area/station/service/hydroponics/garden/abandoned) "kXV" = ( /obj/structure/lattice/catwalk, /obj/machinery/atmospherics/components/unary/passive_vent{ @@ -48312,6 +48372,17 @@ }, /turf/open/floor/plating, /area/station/hallway/secondary/entry) +"lJH" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/green{ + dir = 1 + }, +/obj/structure/sign/poster/random/directional/north, +/turf/open/floor/iron, +/area/station/service/hydroponics) "lJJ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -51089,6 +51160,24 @@ /obj/machinery/firealarm/directional/south, /turf/open/floor/iron, /area/station/commons/storage/primary) +"mur" = ( +/obj/effect/turf_decal/tile/green, +/obj/effect/turf_decal/tile/green{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/machinery/growing/tray, +/obj/item/radio/intercom/directional/north, +/obj/machinery/light/directional/east, +/obj/machinery/camera/directional/east{ + c_tag = "Service - Hydroponics Front Fore"; + dir = 6; + name = "service camera" + }, +/turf/open/floor/iron, +/area/station/service/hydroponics) "mus" = ( /obj/item/kirbyplants/random, /obj/machinery/airalarm/directional/north, @@ -51175,24 +51264,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/brown/visible, /turf/open/floor/iron, /area/station/engineering/supermatter/room) -"mvs" = ( -/obj/machinery/button/flasher{ - id = "visitorflash"; - pixel_x = -6; - pixel_y = 24 - }, -/obj/machinery/button/door/directional/north{ - id = "visitation"; - name = "Visitation Shutters"; - pixel_x = 6; - req_access = list("brig") - }, -/obj/structure/cable, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/security/prison/visit) "mvv" = ( /obj/machinery/conveyor{ dir = 4; @@ -51573,6 +51644,16 @@ /obj/structure/sink/kitchen/directional/west, /turf/open/floor/iron/cafeteria, /area/station/security/prison/mess) +"mAC" = ( +/obj/effect/turf_decal/tile/green{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/machinery/growing/tray, +/turf/open/floor/iron, +/area/station/service/hydroponics) "mAD" = ( /obj/machinery/door/poddoor/preopen{ id = "brigfront"; @@ -53531,24 +53612,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos/storage/gas) -"mYl" = ( -/obj/effect/turf_decal/tile/green, -/obj/effect/turf_decal/tile/green{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/machinery/hydroponics/constructable, -/obj/item/radio/intercom/directional/north, -/obj/machinery/light/directional/east, -/obj/machinery/camera/directional/east{ - c_tag = "Service - Hydroponics Front Fore"; - dir = 6; - name = "service camera" - }, -/turf/open/floor/iron, -/area/station/service/hydroponics) "mYo" = ( /obj/effect/turf_decal/trimline/yellow/line, /turf/open/floor/iron, @@ -55950,14 +56013,6 @@ /obj/structure/sign/departments/xenobio/directional/east, /turf/open/floor/iron, /area/station/maintenance/department/science) -"nFc" = ( -/obj/effect/turf_decal/tile/green{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron, -/area/station/service/hydroponics) "nFj" = ( /obj/structure/table/glass, /obj/effect/turf_decal/tile/blue/opposingcorners, @@ -56351,12 +56406,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"nJV" = ( -/obj/machinery/light/small/directional/north, -/obj/machinery/hydroponics/soil, -/obj/item/shovel/spade, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden/abandoned) "nJW" = ( /obj/effect/turf_decal/tile/purple{ dir = 8 @@ -56371,14 +56420,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/white, /area/station/science/robotics/lab) -"nJY" = ( -/obj/machinery/door/airlock/security{ - name = "Isolation Cell" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/turf/open/floor/plating, -/area/station/security/execution/transfer) "nKe" = ( /obj/structure/bed, /obj/item/bedsheet/dorms, @@ -57333,21 +57374,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) -"nXn" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/end{ - dir = 1 - }, -/obj/effect/turf_decal/trimline/green/mid_joiner{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/green/mid_joiner{ - dir = 8 - }, -/turf/open/floor/iron/half{ - dir = 8 - }, -/area/station/service/hydroponics/garden) "nXo" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -57704,27 +57730,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/engineering/atmos) -"obL" = ( -/obj/machinery/button/flasher{ - id = "Cell 5"; - name = "Prisoner Flash"; - pixel_x = 25; - pixel_y = 7 - }, -/obj/structure/cable, -/obj/effect/turf_decal/tile/red{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/button/door/directional/east{ - id = "permashut5"; - name = "Cell Lockdown Button"; - pixel_y = -6; - req_access = list("brig") - }, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "obO" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ @@ -62641,6 +62646,23 @@ /obj/machinery/portable_atmospherics/scrubber, /turf/open/floor/iron/textured_large, /area/station/engineering/atmos/project) +"pqI" = ( +/obj/effect/turf_decal/tile/green{ + dir = 8 + }, +/obj/effect/turf_decal/tile/green{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue, +/obj/machinery/growing/tray, +/obj/item/radio/intercom/directional/south, +/obj/machinery/camera/directional/east{ + c_tag = "Service - Hydroponics Front Aft"; + name = "service camera" + }, +/obj/machinery/light/directional/east, +/turf/open/floor/iron, +/area/station/service/hydroponics) "pqN" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -63603,24 +63625,6 @@ }, /turf/open/space/basic, /area/space/nearstation) -"pCV" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/line{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/green/line{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/green/mid_joiner{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/green/mid_joiner{ - dir = 8 - }, -/turf/open/floor/iron/half{ - dir = 8 - }, -/area/station/service/hydroponics/garden) "pCY" = ( /obj/effect/turf_decal/delivery, /obj/effect/turf_decal/tile/neutral/half/contrasted{ @@ -64276,24 +64280,6 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/science/breakroom) -"pKb" = ( -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/structure/closet/crate/hydroponics, -/obj/item/seeds/tower, -/obj/effect/spawner/random/contraband/prison, -/obj/item/seeds/pumpkin, -/obj/item/seeds/wheat, -/obj/item/seeds/ambrosia, -/obj/item/seeds/grass, -/obj/item/seeds/carrot, -/obj/item/seeds/tomato, -/obj/item/seeds/potato, -/obj/item/seeds/garlic, -/obj/item/seeds/onion, -/obj/item/paper/guides/jobs/hydroponics, -/turf/open/floor/iron, -/area/station/security/prison/garden) "pKc" = ( /obj/structure/cable, /obj/effect/decal/cleanable/glass, @@ -64676,22 +64662,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/hallway/secondary/entry) -"pOD" = ( -/obj/structure/cable, -/obj/machinery/seed_extractor, -/obj/item/seeds/tower, -/obj/item/seeds/watermelon, -/obj/item/seeds/wheat, -/obj/item/seeds/sugarcane, -/obj/item/seeds/orange, -/obj/item/seeds/grape, -/obj/item/seeds/cocoapod, -/obj/item/seeds/banana, -/obj/item/seeds/apple, -/obj/machinery/power/apc/auto_name/directional/west, -/obj/machinery/light/small/directional/west, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) "pOP" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/door/airlock/maintenance_hatch{ @@ -66579,6 +66549,12 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/hallway/secondary/entry) +"qle" = ( +/obj/machinery/light/small/directional/north, +/obj/machinery/growing/soil, +/obj/item/shovel/spade, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden/abandoned) "qlp" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -68076,6 +68052,18 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/maintenance/department/eva/abandoned) +"qFA" = ( +/obj/effect/landmark/event_spawn, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/junction{ + dir = 1 + }, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron, +/area/station/security/brig) "qFF" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/neutral{ @@ -69910,18 +69898,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/science/xenobiology) -"rdr" = ( -/obj/effect/turf_decal/tile/blue{ - dir = 1 - }, -/obj/effect/turf_decal/tile/green{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue, -/obj/machinery/hydroponics/constructable, -/obj/machinery/light/directional/west, -/turf/open/floor/iron, -/area/station/service/hydroponics) "rdu" = ( /obj/machinery/computer/security/mining, /obj/effect/turf_decal/tile/purple/half/contrasted{ @@ -70567,6 +70543,16 @@ }, /turf/open/floor/iron/grimy, /area/station/service/library) +"rkM" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/blue{ + dir = 4 + }, +/obj/effect/turf_decal/tile/green{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/service/hydroponics) "rkN" = ( /obj/item/kirbyplants/random, /obj/machinery/status_display/evac/directional/south, @@ -73136,6 +73122,14 @@ /obj/effect/turf_decal/siding/white/corner, /turf/open/floor/iron/freezer, /area/station/service/kitchen/coldroom) +"rQV" = ( +/obj/effect/landmark/blobstart, +/obj/effect/landmark/generic_maintenance_landmark, +/obj/effect/landmark/event_spawn, +/obj/machinery/growing/soil, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/plating, +/area/station/service/hydroponics/garden/abandoned) "rRa" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -73806,16 +73800,6 @@ }, /turf/open/floor/iron, /area/station/cargo/office) -"sao" = ( -/obj/effect/turf_decal/tile/green{ - dir = 4 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 1 - }, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron, -/area/station/service/hydroponics) "sau" = ( /obj/machinery/blackbox_recorder, /obj/machinery/atmospherics/pipe/heat_exchanging/simple, @@ -74323,6 +74307,30 @@ /obj/machinery/airalarm/directional/north, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"shF" = ( +/obj/structure/cable, +/obj/machinery/door/poddoor/preopen{ + id = "brigprison"; + name = "Prison Blast Door" + }, +/obj/machinery/button/flasher{ + id = "Cell 1"; + name = "Prisoner Flash"; + pixel_x = 25; + pixel_y = 7 + }, +/obj/machinery/door/firedoor, +/obj/effect/turf_decal/delivery, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/button/door/directional/east{ + id = "permashut1"; + name = "Cell Lockdown Button"; + pixel_y = -6; + req_one_access = list("brig", "permabrig") + }, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "shJ" = ( /obj/machinery/atmospherics/components/unary/vent_pump/siphon/monitored/mix_output{ dir = 4 @@ -74356,6 +74364,14 @@ dir = 10 }, /area/station/service/barber) +"sic" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/obj/effect/turf_decal/siding/green{ + dir = 1 + }, +/mob/living/basic/pet/potty, +/turf/open/floor/iron/dark/smooth_large, +/area/station/service/hydroponics) "sie" = ( /obj/machinery/status_display/evac/directional/south, /obj/effect/turf_decal/tile/neutral{ @@ -76064,23 +76080,6 @@ "sEs" = ( /turf/closed/wall, /area/station/command/heads_quarters/captain/private) -"sEt" = ( -/obj/effect/turf_decal/tile/green{ - dir = 8 - }, -/obj/effect/turf_decal/tile/green{ - dir = 4 - }, -/obj/effect/turf_decal/tile/blue, -/obj/machinery/hydroponics/constructable, -/obj/item/radio/intercom/directional/south, -/obj/machinery/camera/directional/east{ - c_tag = "Service - Hydroponics Front Aft"; - name = "service camera" - }, -/obj/machinery/light/directional/east, -/turf/open/floor/iron, -/area/station/service/hydroponics) "sEv" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/loading_area{ @@ -76292,20 +76291,6 @@ /obj/effect/turf_decal/trimline/green/filled/warning, /turf/open/floor/iron/white, /area/station/medical/medbay) -"sHL" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/green{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 8 - }, -/obj/machinery/newscaster/directional/north, -/turf/open/floor/iron, -/area/station/service/hydroponics) "sHQ" = ( /obj/effect/turf_decal/stripes/line, /turf/open/floor/iron, @@ -76918,6 +76903,18 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/maintenance/department/chapel) +"sOa" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/green{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue{ + dir = 1 + }, +/obj/structure/sign/poster/random/directional/south, +/turf/open/floor/iron, +/area/station/service/hydroponics) "sOi" = ( /obj/machinery/portable_atmospherics/pump, /obj/effect/turf_decal/bot, @@ -77099,24 +77096,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/hallway/secondary/exit) -"sQD" = ( -/obj/machinery/door/airlock/security/glass{ - name = "Permabrig Cell 4" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/turf/open/floor/iron, -/area/station/security/prison/safe) "sQH" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -77321,27 +77300,6 @@ }, /turf/open/floor/plating, /area/station/engineering/supermatter/room) -"sTq" = ( -/obj/structure/cable, -/obj/machinery/button/flasher{ - id = "Cell 2"; - name = "Prisoner Flash"; - pixel_x = 25; - pixel_y = 7 - }, -/obj/effect/turf_decal/tile/red{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/button/door/directional/east{ - id = "permashut2"; - name = "Cell Lockdown Button"; - pixel_y = -6; - req_access = list("brig") - }, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "sTv" = ( /obj/machinery/status_display/evac/directional/north, /obj/structure/cable, @@ -83109,17 +83067,6 @@ dir = 1 }, /area/station/hallway/primary/central/aft) -"unh" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/green{ - dir = 8 - }, -/obj/effect/turf_decal/tile/green{ - dir = 4 - }, -/obj/effect/turf_decal/tile/blue, -/turf/open/floor/iron, -/area/station/service/hydroponics) "unj" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, @@ -83660,6 +83607,14 @@ /obj/effect/turf_decal/siding/green, /turf/open/floor/iron/dark/smooth_large, /area/station/service/hydroponics) +"uvo" = ( +/obj/machinery/growing/soil, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/light/directional/east, +/obj/item/radio/intercom/directional/east, +/obj/effect/mapping_helpers/burnt_floor, +/turf/open/floor/plating, +/area/station/security/prison/garden) "uvr" = ( /obj/structure/window/reinforced/spawner/directional/east, /obj/structure/window/reinforced/spawner/directional/north, @@ -84574,6 +84529,15 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos/project) +"uGL" = ( +/obj/machinery/door/poddoor/preopen{ + id = "brigfront"; + name = "Brig Blast Door" + }, +/obj/effect/turf_decal/delivery, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron/dark/textured_large, +/area/station/security/brig) "uGP" = ( /obj/effect/landmark/generic_maintenance_landmark, /obj/effect/turf_decal/stripes/line, @@ -85457,24 +85421,6 @@ dir = 4 }, /area/station/hallway/secondary/entry) -"uSh" = ( -/obj/machinery/door/airlock/security/glass{ - name = "Permabrig Cell 2" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/turf/open/floor/iron, -/area/station/security/prison/safe) "uSp" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -86447,13 +86393,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos/project) -"vex" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/green, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron, -/area/station/security/prison/garden) "veD" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 @@ -86493,15 +86432,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/fore) -"vfN" = ( -/obj/effect/decal/cleanable/dirt, -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/green{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron, -/area/station/security/prison/garden) "vfS" = ( /obj/effect/turf_decal/tile/yellow/half/contrasted{ dir = 8 @@ -86681,6 +86611,24 @@ }, /turf/open/floor/iron, /area/station/security/execution/transfer) +"vhZ" = ( +/obj/machinery/door/airlock/security/glass{ + name = "Permabrig Cell 4" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/prison/safe) "vii" = ( /turf/open/floor/iron/white/side{ dir = 4 @@ -87178,24 +87126,6 @@ /obj/machinery/power/apc/auto_name/directional/north, /turf/open/floor/iron, /area/station/command/gateway) -"vos" = ( -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/machinery/door/poddoor/preopen{ - id = "brigprison"; - name = "Prison Blast Door" - }, -/obj/machinery/door/airlock/security/glass{ - name = "Permabrig Visitation" - }, -/obj/effect/turf_decal/delivery, -/obj/structure/cable, -/obj/effect/landmark/navigate_destination, -/turf/open/floor/iron, -/area/station/security/prison/visit) "voE" = ( /obj/effect/turf_decal/stripes/line{ dir = 10 @@ -88554,6 +88484,13 @@ /obj/machinery/duct, /turf/open/floor/iron/grimy, /area/station/service/bar/backroom) +"vDX" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/green, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/security/prison/garden) "vEg" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/effect/decal/cleanable/dirt, @@ -89077,17 +89014,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/maintenance/port/aft) -"vLM" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 1 - }, -/turf/open/floor/iron/dark/textured_edge{ - dir = 4 - }, -/area/station/security/brig) "vLP" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -89133,19 +89059,6 @@ }, /turf/open/floor/iron, /area/station/security/courtroom) -"vME" = ( -/obj/effect/turf_decal/trimline/green/end, -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/mid_joiner{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/green/mid_joiner{ - dir = 8 - }, -/turf/open/floor/iron/half{ - dir = 8 - }, -/area/station/service/hydroponics/garden) "vMU" = ( /obj/effect/turf_decal/tile/neutral/fourcorners, /obj/effect/turf_decal/stripes/line{ @@ -90483,6 +90396,27 @@ }, /turf/open/floor/iron/white, /area/station/science/research) +"weH" = ( +/obj/structure/cable, +/obj/machinery/button/flasher{ + id = "Cell 2"; + name = "Prisoner Flash"; + pixel_x = 25; + pixel_y = 7 + }, +/obj/effect/turf_decal/tile/red{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/button/door/directional/east{ + id = "permashut2"; + name = "Cell Lockdown Button"; + pixel_y = -6; + req_one_access = list("brig", "permabrig") + }, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "weM" = ( /obj/effect/turf_decal/tile/neutral/half{ dir = 1 @@ -91992,6 +91926,14 @@ /obj/effect/decal/cleanable/oil, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) +"wtJ" = ( +/obj/effect/decal/cleanable/dirt, +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/green{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/security/prison/garden) "wtS" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -92610,14 +92552,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/white, /area/station/medical/paramedic) -"wAt" = ( -/obj/machinery/hydroponics/soil, -/obj/item/cultivator, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/airalarm/directional/east, -/obj/effect/mapping_helpers/burnt_floor, -/turf/open/floor/plating, -/area/station/security/prison/garden) "wAu" = ( /obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 4 @@ -92787,6 +92721,24 @@ /obj/structure/closet/firecloset, /turf/open/floor/iron, /area/station/engineering/main) +"wCs" = ( +/obj/machinery/button/flasher{ + id = "visitorflash"; + pixel_x = -6; + pixel_y = 24 + }, +/obj/machinery/button/door/directional/north{ + id = "visitation"; + name = "Visitation Shutters"; + pixel_x = 6; + req_one_access = list("brig", "permabrig") + }, +/obj/structure/cable, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/security/prison/visit) "wCv" = ( /obj/effect/turf_decal/stripes/corner{ dir = 4 @@ -94024,6 +93976,13 @@ dir = 1 }, /area/station/medical/morgue) +"wUY" = ( +/obj/structure/disposalpipe/segment, +/obj/structure/cable, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron, +/area/station/security/brig) "wUZ" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/siding/yellow{ @@ -94315,6 +94274,22 @@ /obj/effect/landmark/start/hangover, /turf/open/floor/iron/dark, /area/station/service/chapel/funeral) +"wZo" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/end{ + dir = 1 + }, +/obj/item/reagent_containers/cup/watering_can, +/obj/effect/turf_decal/trimline/green/mid_joiner{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/green/mid_joiner{ + dir = 4 + }, +/turf/open/floor/iron/half{ + dir = 8 + }, +/area/station/service/hydroponics/garden) "wZu" = ( /obj/machinery/power/port_gen/pacman/pre_loaded, /obj/effect/turf_decal/delivery/red, @@ -95223,6 +95198,19 @@ /obj/item/flashlight, /turf/open/floor/iron, /area/station/commons/storage/primary) +"xlF" = ( +/obj/effect/turf_decal/trimline/green/end, +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/mid_joiner{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/green/mid_joiner{ + dir = 4 + }, +/turf/open/floor/iron/half{ + dir = 8 + }, +/area/station/service/hydroponics/garden) "xlG" = ( /obj/effect/spawner/structure/window/reinforced/plasma, /turf/open/floor/plating, @@ -95389,22 +95377,6 @@ /obj/effect/turf_decal/stripes/line, /turf/open/floor/iron/dark, /area/station/medical/medbay/lobby) -"xns" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/end{ - dir = 1 - }, -/obj/item/reagent_containers/cup/watering_can, -/obj/effect/turf_decal/trimline/green/mid_joiner{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/green/mid_joiner{ - dir = 4 - }, -/turf/open/floor/iron/half{ - dir = 8 - }, -/area/station/service/hydroponics/garden) "xnu" = ( /obj/item/kirbyplants/random, /obj/effect/turf_decal/tile/neutral/fourcorners, @@ -95916,6 +95888,24 @@ }, /turf/open/floor/iron, /area/station/cargo/storage) +"xvn" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/structure/closet/crate/hydroponics, +/obj/item/seeds/tree, +/obj/effect/spawner/random/contraband/prison, +/obj/item/seeds/pumpkin, +/obj/item/seeds/wheat, +/obj/item/seeds/ambrosia, +/obj/item/seeds/grass, +/obj/item/seeds/carrot, +/obj/item/seeds/tomato, +/obj/item/seeds/potato, +/obj/item/seeds/garlic, +/obj/item/seeds/onion, +/obj/item/paper/guides/jobs/hydroponics, +/turf/open/floor/iron, +/area/station/security/prison/garden) "xvo" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/red/half/contrasted{ @@ -96035,6 +96025,21 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/service/abandoned_gambling_den/gaming) +"xwV" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/end{ + dir = 1 + }, +/obj/effect/turf_decal/trimline/green/mid_joiner{ + dir = 4 + }, +/obj/effect/turf_decal/trimline/green/mid_joiner{ + dir = 8 + }, +/turf/open/floor/iron/half{ + dir = 8 + }, +/area/station/service/hydroponics/garden) "xxa" = ( /obj/structure/rack, /obj/effect/spawner/random/engineering/flashlight, @@ -96948,6 +96953,25 @@ /obj/structure/cable, /turf/open/floor/iron/white, /area/station/science/research) +"xGr" = ( +/obj/machinery/door/airlock/security/glass{ + name = "Permabrig Cell 5" + }, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/prison/safe) "xGu" = ( /obj/structure/frame/machine{ anchored = 1 @@ -97265,6 +97289,15 @@ /obj/machinery/duct, /turf/open/floor/iron, /area/station/science/research) +"xKs" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/green{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue, +/obj/structure/extinguisher_cabinet/directional/south, +/turf/open/floor/iron, +/area/station/service/hydroponics) "xKv" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/brown/visible, /obj/effect/spawner/structure/window/reinforced/plasma, @@ -97853,16 +97886,6 @@ /obj/effect/turf_decal/bot, /turf/open/floor/iron/dark, /area/station/security/lockers) -"xTm" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/blue{ - dir = 4 - }, -/obj/effect/turf_decal/tile/green{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/service/hydroponics) "xTr" = ( /turf/closed/wall, /area/station/science/robotics/lab) @@ -98170,18 +98193,6 @@ /obj/machinery/light/directional/south, /turf/open/floor/iron, /area/station/commons/storage/primary) -"xXd" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/blue, -/obj/effect/turf_decal/tile/green{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue{ - dir = 1 - }, -/obj/structure/sign/poster/random/directional/south, -/turf/open/floor/iron, -/area/station/service/hydroponics) "xXj" = ( /obj/structure/table/wood, /obj/item/clipboard, @@ -99152,6 +99163,14 @@ }, /turf/open/floor/iron/dark, /area/station/ai_monitored/command/nuke_storage) +"ykr" = ( +/obj/machinery/door/airlock/security{ + name = "Isolation Cell" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/plating, +/area/station/security/execution/transfer) "yks" = ( /obj/effect/turf_decal/bot_white, /obj/effect/turf_decal/tile/neutral/half, @@ -127936,11 +127955,11 @@ gKu csw qsn oYs -nJV +qle wNn -gwL +eAI kJr -dps +auf oYs tFp hLq @@ -128193,11 +128212,11 @@ vZQ auZ uTq oYs -gPj +kXS rTM -buj +rQV kJr -gwL +eAI oYs dqC fUQ @@ -129716,7 +129735,7 @@ oYs rcP oYs oYs -bgl +kmv mpH mrw mrw @@ -129724,7 +129743,7 @@ ogZ hwo dwN rID -rdr +hsK csw csw oUm @@ -129972,7 +129991,7 @@ oYs oYs nsf oYs -sHL +bCb glv hKE crg @@ -129982,7 +130001,7 @@ kQR whX tZN rID -xXd +sOa oYs vCn oYs @@ -130229,7 +130248,7 @@ oYs lMU llW oYs -xTm +rkM ueJ kyR jQd @@ -130486,15 +130505,15 @@ ltr qRN llW oYs -fJx +jxU ueJ -nFc +cBn vZo kDY ezw fdY bbQ -sao +mAC ueJ wsA kGt @@ -130745,15 +130764,15 @@ llW oYs hih ueJ -nFc +cBn bqC -inx +sic bqf uvl sXL -sao +mAC ueJ -bIG +biH csw frR cIa @@ -131000,17 +131019,17 @@ oYs qAB rVX oYs -xTm +rkM ueJ -nFc +cBn lPs iEr uGf hiT ygW -sao +mAC ueJ -aHd +xKs csw umN umN @@ -131257,7 +131276,7 @@ fhd thj pAy oYs -aeu +lJH ueJ rID xSz @@ -131514,7 +131533,7 @@ oYs oYs kAZ oYs -kFi +fIO csh rOc obH @@ -131524,7 +131543,7 @@ vGP xmg vYk crg -unh +cOY csw wpx ykM @@ -131772,7 +131791,7 @@ oYs cPL oYs oYs -mYl +mur csh dwN dwN @@ -131780,7 +131799,7 @@ eYR dwN dwN crg -sEt +pqI csw csw xhi @@ -137941,7 +137960,7 @@ prJ jYM qRw tPD -pOD +khc tWU sSH kVP @@ -138456,9 +138475,9 @@ rkS qnI gvq wkP -xns -bgo -dzk +wZo +dGr +xlF qAV daR dla @@ -138970,9 +138989,9 @@ rza qRw wTJ vxu -nXn -pCV -vME +xwV +eHJ +ayQ qAV jKA sHC @@ -149555,7 +149574,7 @@ pjj uzD fJq nlY -vLM +dQQ oSx qJZ aPx @@ -149812,7 +149831,7 @@ koq dmO fJq qdt -mAD +uGL mAD qJZ lFP @@ -150032,7 +150051,7 @@ vXr qIH qIH qIH -nJY +ykr qIH qIH vXr @@ -150540,19 +150559,19 @@ aaa vXr jbn xIl -obL +bUc amU xIl -gjc +fyB pJo xIl aSW pJo xIl -sTq +weH pJo ayM -fXC +shF iKL krO jrA @@ -150575,15 +150594,15 @@ dbw cOE plh tOP -jDB +wUY jPR -jDB +wUY gTb +wUY jDB -jDB -jDB +wUY kKa -dyH +qFA kKa lzJ pzP @@ -150796,19 +150815,19 @@ uHd qYo mSe mSe -dwX +xGr mSe mSe -sQD +vhZ mSe mSe -gzQ +kzi mSe mSe -uSh +krt mSe mSe -doM +jLj mSe hpN krO @@ -152590,9 +152609,9 @@ aaa uHd aaa arw -gLT -jdl -eQx +ckH +arS +ekR pgn gOo bfs @@ -152847,7 +152866,7 @@ qYo uHd aaa arw -pKb +xvn rRL ioT cVV @@ -153104,9 +153123,9 @@ aaa uHd aaa arw -jgb +fSd mze -vfN +gjX fJG vxr upM @@ -153361,9 +153380,9 @@ aaa qYo aaa arw -jCv +wtJ mze -vex +vDX pAx diC vTP @@ -153875,9 +153894,9 @@ qYo uHd aaa arw -wAt -jAp -czg +aRn +uvo +fVW rRm iiy tUB @@ -154661,10 +154680,10 @@ bgE gzj kia lAj -mvs +wCs iNf uin -vos +hay lCQ krO jrA diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 09037509e070..f1afbd4a31ca 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -209,6 +209,11 @@ /obj/effect/spawner/random/structure/crate, /turf/open/floor/plating, /area/station/maintenance/port/greater) +"afF" = ( +/obj/machinery/growing/soil, +/obj/item/shovel/spade, +/turf/open/floor/grass, +/area/station/security/prison/garden) "afK" = ( /obj/structure/cable, /turf/open/floor/iron/stairs/left{ @@ -328,16 +333,6 @@ /obj/machinery/newscaster/directional/west, /turf/open/floor/iron/smooth, /area/station/security/holding_cell) -"ahI" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "ahK" = ( /obj/effect/landmark/blobstart, /turf/open/floor/plating, @@ -1821,6 +1816,15 @@ }, /turf/open/floor/iron/white, /area/station/science/xenobiology) +"aGj" = ( +/obj/machinery/door/airlock/security/glass{ + name = "Permabrig Visitation" + }, +/obj/machinery/door/firedoor, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/prison/visit) "aGr" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/camera/directional/east{ @@ -2672,6 +2676,14 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/secondary/entry) +"aUy" = ( +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red/full, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron/dark/textured_large, +/area/station/security/brig/entrance) "aUA" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 4 @@ -2929,6 +2941,37 @@ }, /turf/open/floor/iron/white, /area/station/science/xenobiology) +"aZn" = ( +/obj/structure/table, +/obj/machinery/button/door{ + id = "Prison Gate"; + name = "Prison Wing Lockdown"; + pixel_x = 5; + pixel_y = 8; + req_access = list("brig", "permabrig") + }, +/obj/machinery/button/door{ + id = "Trial Transfer"; + name = "Trial Transfer Lockdown"; + pixel_x = -7; + pixel_y = 8; + req_access = list("brig", "permabrig") + }, +/obj/machinery/button/door{ + id = "Secure Gate"; + name = "Cell Shutters"; + pixel_x = -7; + pixel_y = -3; + req_access = list("brig", "permabrig") + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/cable, +/obj/effect/turf_decal/tile/red/anticorner/contrasted{ + dir = 1 + }, +/turf/open/floor/iron/textured, +/area/station/security/brig) "aZo" = ( /obj/structure/extinguisher_cabinet/directional/west, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -3433,6 +3476,29 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/mine/eva) +"bgu" = ( +/obj/machinery/button/door/directional/north{ + id = "permainner"; + name = "Inner Bolt Control"; + normaldoorcontrol = 1; + pixel_x = -6; + specialfunctions = 4; + req_one_access = list("brig", "permabrig") + }, +/obj/machinery/button/door/directional/north{ + id = "permaouter"; + name = "Outer Bolt Control"; + normaldoorcontrol = 1; + pixel_x = 6; + specialfunctions = 4; + req_one_access = list("brig", "permabrig") + }, +/obj/item/paper/crumpled{ + default_raw_text = "Remember! Corporate spent a lot of money to create this state of the art fashion show. If we EVER even so much as HEAR a rumor that a news crew or corporate rep is coming by, this place needs to be in TIP TOP condition. It's all of our asses (and our pensions) if it's not."; + name = "Crumpled Memo" + }, +/turf/open/floor/iron/smooth, +/area/station/security/execution/transfer) "bgx" = ( /turf/closed/wall/r_wall, /area/station/maintenance/starboard/aft) @@ -3984,16 +4050,6 @@ /obj/machinery/atmospherics/pipe/layer_manifold/supply/visible, /turf/closed/wall/r_wall, /area/station/maintenance/department/medical/central) -"boV" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 10 - }, -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 10 - }, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "bpd" = ( /obj/machinery/power/smes/engineering, /obj/effect/turf_decal/delivery, @@ -4265,6 +4321,15 @@ }, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) +"buC" = ( +/obj/machinery/growing/tray, +/obj/machinery/light/directional/east, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/green{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/service/hydroponics) "buW" = ( /obj/structure/lattice, /obj/structure/sign/warning/directional/south, @@ -5831,12 +5896,6 @@ /obj/effect/landmark/navigate_destination/dockescpod1, /turf/open/floor/plating, /area/station/hallway/secondary/entry) -"bRz" = ( -/obj/machinery/hydroponics/soil{ - pixel_y = 8 - }, -/turf/open/floor/grass, -/area/station/maintenance/starboard/aft) "bRA" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -6174,6 +6233,10 @@ /obj/effect/spawner/random/techstorage/ai_all, /turf/open/floor/iron, /area/station/engineering/storage/tech) +"bXI" = ( +/obj/machinery/growing/soil, +/turf/open/floor/grass, +/area/station/security/prison/garden) "bXL" = ( /obj/machinery/door/firedoor/heavy, /obj/machinery/airalarm/directional/west, @@ -6510,12 +6573,6 @@ /obj/item/pillow/random, /turf/open/floor/wood, /area/station/commons/dorms) -"ccr" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/red/half, -/turf/open/floor/iron/smooth_half, -/area/station/security/brig/upper) "ccs" = ( /obj/structure/disposalpipe/segment, /obj/effect/landmark/start/hangover, @@ -6734,16 +6791,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) -"cfR" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/decal/cleanable/dirt, -/obj/item/seeds/redbeet, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/tile/green/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/mine/laborcamp) "cfS" = ( /obj/item/clothing/suit/costume/snowman{ name = "Man of Snow" @@ -6899,6 +6946,16 @@ /obj/effect/turf_decal/stripes/line, /turf/open/floor/iron, /area/station/hallway/primary/starboard) +"cis" = ( +/obj/machinery/door/airlock/maintenance{ + name = "Permabrig Maintenance" + }, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/plating, +/area/station/security/prison/safe) "ciG" = ( /obj/machinery/door/airlock/external{ name = "Security Yard"; @@ -8254,16 +8311,6 @@ }, /turf/open/floor/iron/white, /area/station/medical/medbay/aft) -"cCF" = ( -/obj/machinery/hydroponics/constructable, -/obj/structure/window/reinforced/spawner/directional/west, -/obj/effect/decal/cleanable/dirt, -/obj/item/seeds/carrot, -/obj/effect/turf_decal/tile/green/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/mine/laborcamp) "cCV" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/components/unary/portables_connector/visible, @@ -9396,6 +9443,14 @@ /obj/machinery/holopad, /turf/open/floor/iron/white, /area/station/medical/medbay/central) +"cUf" = ( +/obj/structure/cable, +/obj/machinery/airalarm/directional/east, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/tile/red/half, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron/smooth_half, +/area/station/security/brig/upper) "cUt" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -10151,18 +10206,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/engineering/atmos/mix) -"dha" = ( -/obj/machinery/door/airlock/security/glass{ - id_tag = "permaouter"; - name = "Permabrig Transfer" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ - cycle_id = "perma-entrance" - }, -/obj/machinery/door/firedoor, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/turf/open/floor/iron/dark/textured, -/area/station/security/execution/transfer) "dhj" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/window/reinforced/spawner, @@ -10322,6 +10365,14 @@ }, /turf/open/floor/iron/white, /area/station/medical/surgery/aft) +"diT" = ( +/obj/machinery/growing/tray, +/obj/item/seeds/soya, +/obj/effect/turf_decal/tile/green/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/mine/laborcamp) "diV" = ( /obj/effect/turf_decal/stripes/asteroid/line{ dir = 6 @@ -11336,13 +11387,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron, /area/station/engineering/lobby) -"dAx" = ( -/obj/machinery/hydroponics/soil{ - pixel_y = 8 - }, -/obj/item/cultivator/rake, -/turf/open/floor/grass, -/area/station/maintenance/starboard/aft) "dAB" = ( /obj/machinery/door/window/brigdoor{ name = "Research Director Observation"; @@ -12547,11 +12591,6 @@ dir = 8 }, /area/station/science/research) -"dWK" = ( -/obj/machinery/hydroponics/soil, -/obj/item/shovel/spade, -/turf/open/floor/grass, -/area/station/security/prison/garden) "dWX" = ( /obj/machinery/modular_computer/preset/engineering, /obj/effect/turf_decal/tile/brown/anticorner/contrasted{ @@ -13460,6 +13499,16 @@ "elw" = ( /turf/closed/wall/r_wall, /area/station/maintenance/starboard/upper) +"elQ" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 9 + }, +/obj/effect/turf_decal/trimline/blue/filled/warning{ + dir = 9 + }, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "elU" = ( /obj/structure/window/reinforced/spawner/directional/west, /obj/effect/turf_decal/tile/blue/opposingcorners{ @@ -15473,19 +15522,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/port/fore) -"eUi" = ( -/obj/item/seeds/apple, -/obj/item/seeds/banana, -/obj/item/seeds/cocoapod, -/obj/item/seeds/grape, -/obj/item/seeds/orange, -/obj/item/seeds/sugarcane, -/obj/item/seeds/wheat, -/obj/item/seeds/watermelon, -/obj/structure/table/glass, -/obj/item/seeds/tower, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) "eUw" = ( /obj/structure/table, /obj/effect/turf_decal/tile/neutral/diagonal_edge, @@ -15726,6 +15762,17 @@ /obj/machinery/airalarm/directional/north, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"eXC" = ( +/obj/machinery/door/airlock/public/glass{ + name = "Prison Wing" + }, +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/smooth, +/area/station/security/execution/transfer) "eXH" = ( /turf/closed/wall/r_wall, /area/station/medical/chemistry) @@ -16147,6 +16194,11 @@ /obj/machinery/smartfridge/chemistry/virology/preloaded, /turf/open/floor/iron/white, /area/station/medical/pathology) +"ffY" = ( +/obj/machinery/growing/soil, +/obj/item/plant_analyzer, +/turf/open/floor/grass, +/area/station/security/prison/garden) "ffZ" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 @@ -16402,6 +16454,14 @@ /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /turf/open/floor/iron/white, /area/mine/living_quarters) +"fkf" = ( +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 8 + }, +/obj/effect/turf_decal/tile/red/full, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron/dark/textured_large, +/area/station/security/brig/entrance) "fkj" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -16448,16 +16508,6 @@ dir = 1 }, /area/station/engineering/atmos/storage/gas) -"fkV" = ( -/obj/machinery/door/airlock/maintenance{ - name = "Permabrig Maintenance" - }, -/obj/structure/cable, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/plating, -/area/station/security/prison/safe) "fkX" = ( /obj/structure/lattice/catwalk, /turf/open/openspace, @@ -16700,13 +16750,6 @@ dir = 9 }, /area/station/security/prison/safe) -"fpA" = ( -/obj/machinery/hydroponics/soil, -/obj/effect/turf_decal/siding/wideplating/dark{ - dir = 1 - }, -/turf/open/floor/grass, -/area/station/maintenance/starboard/fore) "fpD" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/orange/visible, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -17433,37 +17476,6 @@ /obj/structure/marker_beacon/burgundy, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/nospawn) -"fCd" = ( -/obj/structure/table, -/obj/machinery/button/door{ - id = "Prison Gate"; - name = "Prison Wing Lockdown"; - pixel_x = 5; - pixel_y = 8; - req_access = list("brig") - }, -/obj/machinery/button/door{ - id = "Trial Transfer"; - name = "Trial Transfer Lockdown"; - pixel_x = -7; - pixel_y = 8; - req_access = list("brig") - }, -/obj/machinery/button/door{ - id = "Secure Gate"; - name = "Cell Shutters"; - pixel_x = -7; - pixel_y = -3; - req_access = list("brig") - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/cable, -/obj/effect/turf_decal/tile/red/anticorner/contrasted{ - dir = 1 - }, -/turf/open/floor/iron/textured, -/area/station/security/brig) "fCw" = ( /obj/machinery/door/morgue{ name = "Relic Closet"; @@ -18496,11 +18508,6 @@ }, /turf/open/floor/wood, /area/station/security/prison/rec) -"fWe" = ( -/obj/machinery/hydroponics/soil, -/obj/item/plant_analyzer, -/turf/open/floor/grass, -/area/station/security/prison/garden) "fWl" = ( /obj/structure/table, /obj/item/stock_parts/micro_laser, @@ -19178,6 +19185,16 @@ }, /turf/open/floor/iron/cafeteria, /area/mine/laborcamp) +"ggm" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/blue/filled/warning{ + dir = 8 + }, +/obj/machinery/growing/tray, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "ggn" = ( /obj/machinery/door/airlock/atmos{ name = "Atmospherics Maintenance" @@ -19259,14 +19276,6 @@ "ghY" = ( /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat/atmos) -"giD" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/siding/wideplating/dark{ - dir = 1 - }, -/obj/item/seeds/watermelon, -/turf/open/floor/grass, -/area/station/maintenance/starboard/fore) "giH" = ( /obj/structure/lattice/catwalk, /obj/machinery/airalarm/directional/south, @@ -20028,14 +20037,6 @@ /obj/structure/window/reinforced/spawner/directional/south, /turf/open/floor/iron, /area/station/science/xenobiology) -"gvV" = ( -/obj/machinery/hydroponics/constructable, -/obj/item/seeds/soya, -/obj/effect/turf_decal/tile/green/half/contrasted{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/mine/laborcamp) "gwm" = ( /obj/machinery/door/firedoor/heavy, /turf/open/floor/iron/white/side{ @@ -21099,6 +21100,22 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/plating, /area/station/maintenance/aft/greater) +"gNX" = ( +/obj/machinery/button/door/directional/north{ + id = "visitation"; + name = "Visitation Shutters"; + pixel_x = 6; + pixel_y = -24; + req_one_access = list("brig", "permabrig") + }, +/obj/machinery/button/flasher{ + id = "visitorflash"; + pixel_x = -6; + pixel_y = -24 + }, +/obj/effect/turf_decal/tile/red/anticorner/contrasted, +/turf/open/floor/iron, +/area/station/security/prison/visit) "gOb" = ( /obj/structure/cable, /obj/machinery/door/poddoor/preopen{ @@ -22374,16 +22391,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/hallway/secondary/entry) -"hlP" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 9 - }, -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 9 - }, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "hlS" = ( /obj/structure/table, /obj/item/clothing/under/misc/burial, @@ -24029,35 +24036,12 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) -"hQP" = ( -/obj/structure/cable, -/obj/machinery/airalarm/directional/east, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/tile/red/half, -/turf/open/floor/iron/smooth_half, -/area/station/security/brig/upper) "hQQ" = ( /obj/docking_port/stationary/escape_pod{ dir = 4 }, /turf/open/floor/plating/snowed/icemoon, /area/icemoon/surface/outdoors/nospawn) -"hRp" = ( -/obj/machinery/button/door/directional/north{ - id = "visitation"; - name = "Visitation Shutters"; - pixel_x = 6; - pixel_y = -24; - req_access = list("brig") - }, -/obj/machinery/button/flasher{ - id = "visitorflash"; - pixel_x = -6; - pixel_y = -24 - }, -/obj/effect/turf_decal/tile/red/anticorner/contrasted, -/turf/open/floor/iron, -/area/station/security/prison/visit) "hRw" = ( /obj/structure/chair/wood{ dir = 8 @@ -26157,16 +26141,6 @@ /obj/structure/sign/poster/contraband/random/directional/east, /turf/open/floor/plating, /area/station/maintenance/port/aft) -"iAA" = ( -/obj/machinery/hydroponics/constructable, -/obj/structure/window/reinforced/spawner/directional/west, -/obj/item/cultivator, -/obj/item/seeds/potato, -/obj/effect/turf_decal/tile/green/half/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/mine/laborcamp) "iAJ" = ( /obj/effect/turf_decal/siding/white{ dir = 8 @@ -26317,6 +26291,12 @@ }, /turf/open/floor/plating, /area/station/maintenance/starboard/aft) +"iCM" = ( +/obj/structure/window/reinforced/spawner/directional/north, +/obj/machinery/growing/soil, +/obj/machinery/light/directional/east, +/turf/open/floor/grass, +/area/station/service/hydroponics) "iCO" = ( /obj/machinery/vending/cola/red, /turf/open/floor/iron/dark/textured, @@ -26949,6 +26929,12 @@ }, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) +"iOi" = ( +/obj/machinery/growing/soil{ + pixel_y = 8 + }, +/turf/open/floor/grass, +/area/station/maintenance/starboard/aft) "iOs" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/airalarm/directional/south, @@ -27702,13 +27688,6 @@ /obj/item/kitchen/spoon/plastic, /turf/open/floor/iron, /area/station/security/prison/mess) -"iZy" = ( -/obj/effect/turf_decal/siding/wideplating/dark{ - dir = 1 - }, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/grass, -/area/station/maintenance/starboard/fore) "iZz" = ( /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/labor_camp) @@ -28781,6 +28760,16 @@ dir = 1 }, /area/station/hallway/secondary/service) +"jtv" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 10 + }, +/obj/effect/turf_decal/trimline/blue/filled/warning{ + dir = 10 + }, +/obj/machinery/growing/tray, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "jtx" = ( /obj/item/kirbyplants/random, /obj/effect/turf_decal/tile/red, @@ -30761,6 +30750,14 @@ }, /turf/open/floor/iron, /area/mine/laborcamp) +"kby" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/green{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/service/hydroponics) "kbJ" = ( /obj/machinery/field/generator, /turf/open/floor/plating, @@ -30989,6 +30986,17 @@ dir = 4 }, /area/station/command/gateway) +"keH" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/siding/wideplating/dark{ + dir = 1 + }, +/obj/effect/turf_decal/tile/blue, +/obj/effect/turf_decal/tile/green{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/service/hydroponics) "keL" = ( /obj/machinery/light_switch/directional/south, /obj/effect/turf_decal/tile/neutral/half/contrasted, @@ -31222,6 +31230,14 @@ /obj/item/kirbyplants/random, /turf/open/floor/iron, /area/station/hallway/primary/starboard) +"khE" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/disposalpipe/segment, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron, +/area/station/security/brig/upper) "khF" = ( /obj/machinery/light/small/directional/north, /obj/structure/sign/warning/gas_mask/directional/north{ @@ -32137,14 +32153,6 @@ }, /turf/open/floor/iron/kitchen/diagonal, /area/station/service/kitchen) -"kvu" = ( -/obj/machinery/door/airlock/security{ - id_tag = "IsolationCell"; - name = "Isolation Cell" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/turf/open/floor/iron/smooth, -/area/station/security/prison/safe) "kvE" = ( /obj/structure/closet/firecloset, /turf/open/floor/iron/smooth, @@ -32155,6 +32163,16 @@ }, /turf/open/floor/iron/dark/textured, /area/station/security/office) +"kvN" = ( +/obj/machinery/growing/tray, +/obj/structure/window/reinforced/spawner/directional/west, +/obj/effect/decal/cleanable/dirt, +/obj/item/seeds/carrot, +/obj/effect/turf_decal/tile/green/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/mine/laborcamp) "kvR" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -33324,6 +33342,14 @@ /obj/effect/turf_decal/tile/dark_green, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/ai_upload) +"kPe" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/siding/wideplating/dark{ + dir = 1 + }, +/obj/item/seeds/watermelon, +/turf/open/floor/grass, +/area/station/maintenance/starboard/fore) "kPg" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -33629,14 +33655,6 @@ /obj/effect/turf_decal/tile/brown/half/contrasted, /turf/open/floor/iron/dark/side, /area/mine/eva) -"kTO" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/tile/blue, -/obj/effect/turf_decal/tile/green{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/service/hydroponics) "kTQ" = ( /obj/effect/turf_decal/siding/yellow{ dir = 6 @@ -33707,6 +33725,13 @@ /obj/effect/landmark/start/hangover, /turf/open/floor/iron, /area/station/engineering/lobby) +"kVV" = ( +/obj/machinery/growing/soil, +/obj/effect/turf_decal/siding/wideplating/dark{ + dir = 1 + }, +/turf/open/floor/grass, +/area/station/maintenance/starboard/fore) "kWa" = ( /obj/structure/fireplace, /turf/open/floor/plating, @@ -34384,13 +34409,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/port/greater) -"lgz" = ( -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 8 - }, -/obj/effect/turf_decal/tile/red/full, -/turf/open/floor/iron/dark/textured_large, -/area/station/security/brig/entrance) "lgA" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -35512,6 +35530,16 @@ /obj/structure/sign/poster/contraband/random/directional/north, /turf/open/floor/plating, /area/station/maintenance/department/medical/morgue) +"lzp" = ( +/obj/machinery/door/airlock/maintenance{ + name = "Permabrig Maintenance" + }, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/plating, +/area/station/security/prison/safe) "lzq" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -35649,6 +35677,13 @@ /obj/structure/flora/grass/green/style_random, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/nospawn) +"lBK" = ( +/obj/machinery/growing/tray, +/obj/effect/decal/cleanable/dirt, +/obj/item/seeds/onion, +/obj/effect/turf_decal/tile/green/anticorner/contrasted, +/turf/open/floor/iron/dark, +/area/mine/laborcamp) "lBR" = ( /turf/closed/wall, /area/station/security/prison/toilet) @@ -36901,10 +36936,23 @@ /obj/structure/closet/chefcloset, /turf/open/floor/plating, /area/station/service/kitchen/coldroom) +"lZc" = ( +/obj/machinery/growing/soil, +/obj/item/cultivator, +/turf/open/floor/grass, +/area/station/security/prison/garden) "lZi" = ( /obj/structure/railing, /turf/open/floor/iron, /area/station/hallway/primary/starboard) +"lZr" = ( +/obj/machinery/growing/soil, +/obj/machinery/light/directional/west, +/obj/structure/sign/poster/contraband/kudzu{ + pixel_x = -32 + }, +/turf/open/floor/grass, +/area/station/service/hydroponics/garden) "lZv" = ( /obj/structure/table/glass, /obj/item/seeds/bamboo, @@ -37483,6 +37531,13 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark, /area/station/maintenance/disposal/incinerator) +"mkj" = ( +/obj/effect/turf_decal/siding/wideplating/dark{ + dir = 1 + }, +/obj/machinery/growing/tray, +/turf/open/floor/grass, +/area/station/maintenance/starboard/fore) "mku" = ( /obj/effect/spawner/random/structure/grille, /obj/effect/decal/cleanable/glass, @@ -38356,13 +38411,6 @@ }, /turf/open/floor/iron, /area/station/maintenance/starboard/fore) -"mzP" = ( -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 8 - }, -/obj/effect/turf_decal/tile/red/full, -/turf/open/floor/iron/dark/textured_large, -/area/station/security/brig/entrance) "mAc" = ( /obj/machinery/door/airlock/maintenance{ name = "Mining Maintenance" @@ -38487,10 +38535,6 @@ /obj/structure/cable/layer3, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/ai) -"mCT" = ( -/obj/machinery/hydroponics/soil, -/turf/open/floor/grass, -/area/station/service/hydroponics/garden) "mCX" = ( /obj/machinery/duct, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -38923,6 +38967,14 @@ /obj/machinery/duct, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) +"mKi" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/siding/wideplating/dark{ + dir = 1 + }, +/obj/item/seeds/berry, +/turf/open/floor/grass, +/area/station/maintenance/starboard/fore) "mKq" = ( /obj/structure/closet/secure_closet/evidence, /obj/machinery/light/small/directional/north, @@ -39850,6 +39902,16 @@ dir = 8 }, /area/mine/eva) +"nas" = ( +/obj/machinery/growing/tray, +/obj/structure/window/reinforced/spawner/directional/west, +/obj/item/cultivator, +/obj/item/seeds/potato, +/obj/effect/turf_decal/tile/green/half/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/mine/laborcamp) "naO" = ( /obj/effect/turf_decal/tile/brown/anticorner/contrasted{ dir = 8 @@ -40177,10 +40239,6 @@ }, /turf/open/floor/wood, /area/station/security/courtroom) -"neM" = ( -/obj/machinery/hydroponics/soil, -/turf/open/misc/asteroid/snow/icemoon, -/area/icemoon/underground/explored) "neR" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/orange/visible, /obj/machinery/meter, @@ -40698,12 +40756,6 @@ }, /turf/open/floor/iron, /area/station/commons/dorms) -"nmr" = ( -/obj/machinery/hydroponics/soil, -/obj/item/cultivator, -/obj/effect/spawner/random/contraband/cannabis, -/turf/open/floor/grass, -/area/station/security/prison/garden) "nmz" = ( /obj/structure/lattice/catwalk, /obj/structure/railing{ @@ -42322,15 +42374,6 @@ }, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/underground/explored) -"nKn" = ( -/obj/machinery/hydroponics/constructable, -/obj/machinery/light/directional/east, -/obj/effect/turf_decal/tile/blue, -/obj/effect/turf_decal/tile/green{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/service/hydroponics) "nKr" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 @@ -42684,6 +42727,16 @@ /obj/item/pillow/random, /turf/open/floor/carpet, /area/station/commons/dorms) +"nOv" = ( +/obj/structure/disposalpipe/segment{ + dir = 9 + }, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron, +/area/station/security/brig/upper) "nOw" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment, @@ -43205,6 +43258,13 @@ /obj/item/stamp/qm, /turf/open/floor/carpet, /area/station/cargo/quartermaster) +"nWr" = ( +/obj/machinery/growing/soil{ + pixel_y = 8 + }, +/obj/item/cultivator/rake, +/turf/open/floor/grass, +/area/station/maintenance/starboard/aft) "nWy" = ( /obj/structure/sign/poster/official/work_for_a_future, /turf/closed/wall, @@ -44257,6 +44317,11 @@ /obj/structure/chair, /turf/open/floor/iron, /area/station/cargo/storage) +"ooB" = ( +/obj/machinery/growing/soil, +/obj/machinery/light/directional/east, +/turf/open/floor/grass, +/area/station/service/hydroponics/garden) "ooL" = ( /obj/effect/turf_decal/weather/snow/corner{ dir = 4 @@ -45005,10 +45070,6 @@ /obj/effect/turf_decal/tile/yellow/opposingcorners, /turf/open/floor/iron/white, /area/station/maintenance/port/fore) -"ozX" = ( -/obj/machinery/hydroponics/soil, -/turf/open/floor/grass, -/area/station/security/prison/garden) "ozZ" = ( /obj/effect/turf_decal/stripes/line{ dir = 10 @@ -46262,6 +46323,10 @@ /obj/machinery/light/small/directional/east, /turf/open/floor/iron/freezer, /area/station/commons/toilet) +"oVw" = ( +/obj/machinery/growing/soil, +/turf/open/floor/grass, +/area/station/service/hydroponics/garden) "oVy" = ( /obj/machinery/door/airlock/security{ name = "Permabrig Lab" @@ -50017,7 +50082,7 @@ /obj/effect/turf_decal/tile/blue{ dir = 8 }, -/mob/living/simple_animal/bot/cleanbot, +/mob/living/basic/bot/cleanbot, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat/service) "qhy" = ( @@ -50411,16 +50476,6 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, /area/station/maintenance/department/medical/morgue) -"qoU" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 8 - }, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "qoY" = ( /obj/effect/spawner/random/structure/crate_abandoned, /turf/open/floor/plating, @@ -51438,17 +51493,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat_interior) -"qFC" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/siding/wideplating/dark{ - dir = 1 - }, -/obj/effect/turf_decal/tile/blue, -/obj/effect/turf_decal/tile/green{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/service/hydroponics) "qFU" = ( /obj/structure/cable/multilayer/multiz, /obj/structure/window/reinforced/spawner, @@ -51693,16 +51737,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central) -"qJY" = ( -/obj/machinery/door/airlock/maintenance{ - name = "Permabrig Maintenance" - }, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/turf/open/floor/plating, -/area/station/security/prison/safe) "qKk" = ( /obj/machinery/door/airlock/mining/glass{ id_tag = "innercargo"; @@ -52247,14 +52281,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/hallway/primary/port) -"qSa" = ( -/obj/machinery/hydroponics/soil, -/obj/machinery/light/directional/west, -/obj/structure/sign/poster/contraband/kudzu{ - pixel_x = -32 - }, -/turf/open/floor/grass, -/area/station/service/hydroponics/garden) "qSb" = ( /obj/structure/window/reinforced/spawner/directional/north, /obj/structure/window/reinforced/spawner/directional/west, @@ -52963,15 +52989,6 @@ /obj/structure/sign/poster/official/random/directional/south, /turf/open/floor/iron/dark, /area/station/science/robotics/lab) -"rdd" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 1 - }, -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "rdl" = ( /obj/machinery/button/door/directional/east{ id = "misclab"; @@ -53949,12 +53966,6 @@ dir = 1 }, /area/station/security/prison) -"rwv" = ( -/obj/structure/window/reinforced/spawner/directional/north, -/obj/machinery/hydroponics/soil, -/obj/machinery/light/directional/east, -/turf/open/floor/grass, -/area/station/service/hydroponics) "rwB" = ( /obj/machinery/atmospherics/pipe/bridge_pipe/orange/visible, /obj/machinery/atmospherics/pipe/bridge_pipe/green/visible{ @@ -54279,15 +54290,6 @@ }, /turf/open/floor/plating, /area/station/ai_monitored/turret_protected/aisat/maint) -"rAN" = ( -/obj/structure/disposalpipe/segment{ - dir = 9 - }, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron, -/area/station/security/brig/upper) "rAO" = ( /obj/structure/closet/emcloset, /turf/open/floor/plating, @@ -55498,6 +55500,13 @@ }, /turf/open/floor/engine/plasma, /area/station/engineering/atmos) +"rWJ" = ( +/obj/machinery/growing/soil{ + pixel_y = 8 + }, +/obj/item/shovel/spade, +/turf/open/floor/grass, +/area/station/maintenance/starboard/aft) "rWO" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, @@ -55510,17 +55519,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos/hfr_room) -"rWU" = ( -/obj/machinery/door/airlock/public/glass{ - name = "Prison Wing" - }, -/obj/machinery/door/firedoor, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/turf/open/floor/iron/smooth, -/area/station/security/execution/transfer) "rWW" = ( /obj/machinery/vending/wardrobe/medi_wardrobe, /obj/effect/turf_decal/trimline/blue/filled/line{ @@ -55819,13 +55817,6 @@ }, /turf/open/floor/iron/smooth_half, /area/station/security/brig/upper) -"sbO" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/decal/cleanable/dirt, -/obj/item/seeds/onion, -/obj/effect/turf_decal/tile/green/anticorner/contrasted, -/turf/open/floor/iron/dark, -/area/mine/laborcamp) "sbU" = ( /obj/machinery/vending/cigarette, /turf/open/floor/iron/dark, @@ -57365,6 +57356,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/hallway/secondary/entry) +"sAY" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/filled/line, +/obj/effect/turf_decal/trimline/blue/filled/warning, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "sBi" = ( /obj/structure/cable, /obj/effect/decal/cleanable/dirt, @@ -57834,10 +57831,6 @@ /obj/structure/flora/bush/flowers_yw/style_random, /turf/open/floor/grass, /area/station/service/hydroponics) -"sIm" = ( -/obj/machinery/hydroponics/soil, -/turf/open/floor/grass, -/area/station/service/hydroponics) "sIt" = ( /turf/closed/wall, /area/station/maintenance/central/lesser) @@ -58015,6 +58008,10 @@ }, /turf/open/floor/iron, /area/mine/laborcamp/security) +"sLB" = ( +/obj/machinery/growing/soil, +/turf/open/misc/asteroid/snow/icemoon, +/area/icemoon/underground/explored) "sLD" = ( /obj/structure/table/reinforced, /obj/item/stack/sheet/iron/fifty, @@ -58286,17 +58283,6 @@ /obj/item/radio/intercom/directional/west, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"sRc" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 10 - }, -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 10 - }, -/obj/machinery/light/directional/south, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "sRp" = ( /obj/structure/fence, /obj/effect/turf_decal/weather/snow/corner{ @@ -58445,15 +58431,6 @@ /obj/effect/mapping_helpers/airlock/access/all/command/teleporter, /turf/open/floor/plating, /area/station/maintenance/central/lesser) -"sUb" = ( -/obj/machinery/door/airlock/security/glass{ - name = "Permabrig Visitation" - }, -/obj/machinery/door/firedoor, -/obj/structure/cable, -/obj/effect/mapping_helpers/airlock/access/any/security/brig, -/turf/open/floor/iron, -/area/station/security/prison/visit) "sUi" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -58481,6 +58458,18 @@ }, /turf/open/floor/iron/smooth, /area/mine/eva) +"sUL" = ( +/obj/machinery/door/airlock/security/glass{ + id_tag = "permaouter"; + name = "Permabrig Transfer" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ + cycle_id = "perma-entrance" + }, +/obj/machinery/door/firedoor, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/dark/textured, +/area/station/security/execution/transfer) "sUN" = ( /obj/machinery/power/solar{ id = "starboardsolar"; @@ -60066,6 +60055,12 @@ /obj/machinery/firealarm/directional/south, /turf/open/floor/plating, /area/station/medical/treatment_center) +"txr" = ( +/obj/machinery/growing/soil, +/obj/item/cultivator, +/obj/effect/spawner/random/contraband/cannabis, +/turf/open/floor/grass, +/area/station/security/prison/garden) "txE" = ( /obj/item/cigbutt, /obj/effect/decal/cleanable/dirt, @@ -61386,6 +61381,16 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/wood, /area/station/maintenance/space_hut/cabin) +"tUm" = ( +/obj/machinery/growing/tray, +/obj/structure/window/reinforced/spawner/directional/west, +/obj/effect/decal/cleanable/dirt, +/obj/item/plant_analyzer, +/obj/effect/turf_decal/tile/green/anticorner/contrasted{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/mine/laborcamp) "tUn" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -61732,13 +61737,6 @@ /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating/icemoon, /area/icemoon/surface/outdoors/nospawn) -"tZd" = ( -/obj/machinery/hydroponics/soil{ - pixel_y = 8 - }, -/obj/item/shovel/spade, -/turf/open/floor/grass, -/area/station/maintenance/starboard/aft) "tZe" = ( /obj/machinery/vending/wardrobe/gene_wardrobe, /obj/structure/sign/poster/official/random/directional/north, @@ -62328,6 +62326,13 @@ /obj/effect/spawner/random/maintenance/three, /turf/open/floor/plating, /area/station/maintenance/department/medical/central) +"uiJ" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/red/half, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron/smooth_half, +/area/station/security/brig/upper) "uiK" = ( /obj/machinery/light/directional/south, /obj/machinery/camera/directional/south, @@ -62839,15 +62844,20 @@ /obj/effect/spawner/random/structure/closet_private, /turf/open/floor/plating, /area/station/maintenance/port/fore) -"urt" = ( -/obj/machinery/hydroponics/soil, -/obj/machinery/light/directional/east, -/turf/open/floor/grass, -/area/station/service/hydroponics/garden) "urx" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) +"urC" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/blue/filled/warning{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "urG" = ( /obj/effect/turf_decal/weather/snow/corner, /turf/open/floor/plating/snowed/icemoon, @@ -65111,6 +65121,16 @@ }, /turf/open/floor/plating, /area/station/commons/storage/mining) +"vgw" = ( +/obj/machinery/growing/tray, +/obj/effect/decal/cleanable/dirt, +/obj/item/seeds/redbeet, +/obj/machinery/light/directional/east, +/obj/effect/turf_decal/tile/green/half/contrasted{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/mine/laborcamp) "vgx" = ( /obj/effect/turf_decal/trimline/blue/filled/line, /turf/open/floor/iron/white, @@ -65308,12 +65328,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/maintenance/aft/greater) -"vkg" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/filled/line, -/obj/effect/turf_decal/trimline/blue/filled/warning, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "vky" = ( /obj/machinery/computer/pod/old/mass_driver_controller/trash{ pixel_x = -24; @@ -66490,6 +66504,17 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/cyan/visible, /turf/open/floor/iron/white, /area/station/medical/cryo) +"vCA" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 10 + }, +/obj/effect/turf_decal/trimline/blue/filled/warning{ + dir = 10 + }, +/obj/machinery/light/directional/south, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "vCD" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -68237,11 +68262,6 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/plating, /area/station/medical/morgue) -"whr" = ( -/obj/machinery/hydroponics/soil, -/obj/item/cultivator, -/turf/open/floor/grass, -/area/station/security/prison/garden) "whu" = ( /obj/effect/turf_decal/stripes/line{ dir = 10 @@ -69194,14 +69214,6 @@ /obj/machinery/light/directional/east, /turf/open/floor/wood, /area/station/command/heads_quarters/captain) -"wwG" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/siding/wideplating/dark{ - dir = 1 - }, -/obj/item/seeds/berry, -/turf/open/floor/grass, -/area/station/maintenance/starboard/fore) "wwI" = ( /obj/structure/chair, /obj/effect/turf_decal/stripes/line{ @@ -69680,16 +69692,6 @@ /obj/machinery/space_heater, /turf/open/floor/plating, /area/station/maintenance/starboard/upper) -"wDs" = ( -/obj/machinery/hydroponics/constructable, -/obj/structure/window/reinforced/spawner/directional/west, -/obj/effect/decal/cleanable/dirt, -/obj/item/plant_analyzer, -/obj/effect/turf_decal/tile/green/anticorner/contrasted{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/mine/laborcamp) "wDu" = ( /obj/effect/landmark/generic_maintenance_landmark, /obj/structure/cable, @@ -69911,6 +69913,16 @@ }, /turf/open/floor/iron, /area/station/engineering/lobby) +"wHk" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 10 + }, +/obj/effect/turf_decal/trimline/blue/filled/warning{ + dir = 10 + }, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "wHH" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/red{ @@ -69960,6 +69972,16 @@ /obj/structure/table, /turf/open/floor/plating, /area/station/maintenance/aft/greater) +"wIY" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 1 + }, +/obj/effect/turf_decal/trimline/blue/filled/warning{ + dir = 1 + }, +/mob/living/basic/pet/potty, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "wJa" = ( /obj/machinery/light/warm/directional/north, /obj/effect/turf_decal/siding/wideplating/dark{ @@ -71421,29 +71443,6 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/glass/reinforced, /area/station/ai_monitored/security/armory/upper) -"xge" = ( -/obj/machinery/button/door/directional/north{ - id = "permainner"; - name = "Inner Bolt Control"; - normaldoorcontrol = 1; - pixel_x = -6; - req_access = list("brig"); - specialfunctions = 4 - }, -/obj/machinery/button/door/directional/north{ - id = "permaouter"; - name = "Outer Bolt Control"; - normaldoorcontrol = 1; - pixel_x = 6; - req_access = list("brig"); - specialfunctions = 4 - }, -/obj/item/paper/crumpled{ - default_raw_text = "Remember! Corporate spent a lot of money to create this state of the art fashion show. If we EVER even so much as HEAR a rumor that a news crew or corporate rep is coming by, this place needs to be in TIP TOP condition. It's all of our asses (and our pensions) if it's not."; - name = "Crumpled Memo" - }, -/turf/open/floor/iron/smooth, -/area/station/security/execution/transfer) "xgf" = ( /obj/structure/sign/directions/dorms/directional/north{ pixel_y = 1 @@ -72712,6 +72711,19 @@ }, /turf/open/floor/iron, /area/station/security/brig/upper) +"xAS" = ( +/obj/item/seeds/apple, +/obj/item/seeds/banana, +/obj/item/seeds/cocoapod, +/obj/item/seeds/grape, +/obj/item/seeds/orange, +/obj/item/seeds/sugarcane, +/obj/item/seeds/wheat, +/obj/item/seeds/watermelon, +/obj/structure/table/glass, +/obj/item/seeds/tree, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) "xAW" = ( /obj/machinery/door/airlock/security{ name = "Cafeteria" @@ -73199,6 +73211,10 @@ /obj/structure/stairs/east, /turf/open/floor/plating, /area/station/security/brig) +"xHU" = ( +/obj/machinery/growing/soil, +/turf/open/floor/grass, +/area/station/service/hydroponics) "xHY" = ( /obj/machinery/portable_atmospherics/scrubber, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ @@ -73805,6 +73821,14 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/starboard/upper) +"xTC" = ( +/obj/machinery/door/airlock/security{ + id_tag = "IsolationCell"; + name = "Isolation Cell" + }, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/smooth, +/area/station/security/prison/safe) "xTP" = ( /obj/machinery/computer/shuttle/mining{ dir = 8 @@ -73904,16 +73928,6 @@ }, /turf/open/floor/iron, /area/mine/laborcamp/security) -"xVD" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 10 - }, -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 10 - }, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "xVG" = ( /turf/open/floor/plating, /area/station/hallway/secondary/exit/departure_lounge) @@ -109369,9 +109383,9 @@ bEi rVX dqx pSn -cCF -iAA -wDs +kvN +nas +tUm vjh vjh vjh @@ -109883,9 +109897,9 @@ lmK rVX fez lxn -gvV -cfR -sbO +diT +vgw +lBK vjh drP czD @@ -161313,8 +161327,8 @@ jlF jNf dpC cGQ -whr -nmr +lZc +txr hVY gjq gjq @@ -161827,8 +161841,8 @@ exN jNf qIo tau -dWK -ozX +afF +bXI hVY gjq gjq @@ -162598,8 +162612,8 @@ tCL jNf wcz hKI -fWe -ozX +ffY +bXI hVY gjq gjq @@ -163112,8 +163126,8 @@ eCD jNf pbB tzf -ozX -ozX +bXI +bXI hVY gjq pfw @@ -164661,7 +164675,7 @@ hpF pXZ bZk gAu -sUb +aGj eCE eCE eCE @@ -165688,7 +165702,7 @@ xMy eDq iXh lWb -hRp +gNX dAZ wqI xSn @@ -166201,7 +166215,7 @@ uBs uBs uBs xaH -fCd +aZn nDp ubo lgD @@ -166719,7 +166733,7 @@ pRB cGl hgM pJu -rWU +eXC hgM frS wPD @@ -168245,7 +168259,7 @@ gjq gjq xhK vVH -fkV +lzp vVH kqn oVY @@ -168260,10 +168274,10 @@ uME doq trA uME -xge +bgu hBg ihD -kvu +xTC aaK oCT xby @@ -168516,7 +168530,7 @@ bBn uZd kZt ryu -dha +sUL svF hBg ufF @@ -169030,7 +169044,7 @@ fTz pdz eic kBh -dha +sUL rRA vpi uME @@ -169285,7 +169299,7 @@ gTw gTw mMM xhK -qJY +cis xhK pNm tpH @@ -172408,10 +172422,10 @@ scw scw hmb hmb -hlP -ahI -qoU -boV +elQ +urC +ggm +wHk hmb hmb gjq @@ -172669,7 +172683,7 @@ eYX sCZ sCZ lmm -sRc +vCA hmb gjq gjq @@ -172926,7 +172940,7 @@ syE jwm pqx hml -vkg +sAY hmb hmb gjq @@ -173182,16 +173196,16 @@ fKy bdr jHK rhh -rdd +wIY lmm -boV +wHk hmb hmb iDt qau iDt jZN -neM +sLB qau xMq xMq @@ -173442,13 +173456,13 @@ lEH lgA gAy bqH -xVD +jtv exw hmb exw tJb gUF -neM +sLB qau xMq xMq @@ -174215,11 +174229,11 @@ jqJ oBl hbT hmb -neM +sLB iDt scw iDt -neM +sLB qau iZm fdP @@ -174472,11 +174486,11 @@ bSU nVz iNt hmb -neM +sLB iDt iDt scw -neM +sLB qau iDt kRF @@ -220969,9 +220983,9 @@ lJO lJO lJO lJO -mCT -qSa -mCT +oVw +lZr +oVw gbS mZu oCO @@ -221741,7 +221755,7 @@ lJO anl lJO dZJ -eUi +xAS eBB nxV nor @@ -222511,9 +222525,9 @@ lJO lJO anl lJO -mCT -urt -mCT +oVw +ooB +oVw fzF mAV jzr @@ -234568,10 +234582,10 @@ cAC cEL hwZ vdf -xFm +khE riv xHq -ccr +uiJ txU jzC qml @@ -234837,9 +234851,9 @@ oAh oAh oAh vDb -lgz +aUy hBr -mzP +fkf vDb vvh vPC @@ -235085,7 +235099,7 @@ wAq jaw xwf aPd -hQP +cUf lWg ncB bgN @@ -236881,7 +236895,7 @@ fXi vhL law eqI -rAN +nOv wAT lbk bln @@ -240514,12 +240528,12 @@ qYD oUK gEL mpU -qFC -nKn -kTO -kTO -rwv -sIm +keH +buC +kby +kby +iCM +xHU hmb lso dEV @@ -246173,7 +246187,7 @@ kKL kKl beT nwI -giD +kPe kKL veJ vwO @@ -246430,7 +246444,7 @@ kKL eie lli lli -wwG +mKi kKL rjP qEM @@ -246687,7 +246701,7 @@ cDw lli lli lli -iZy +mkj kKL cwO vwO @@ -246944,7 +246958,7 @@ kKL vMR lZv hrt -fpA +kVV kKL iko qEM @@ -252660,7 +252674,7 @@ loQ fwD too mHw -bRz +iOi vzD bln bln @@ -252917,7 +252931,7 @@ iTP uCo lEz kPw -dAx +nWr gQw bln bln @@ -253174,7 +253188,7 @@ ftS too lbf esF -bRz +iOi gQw bln bln @@ -253428,9 +253442,9 @@ jOj vzD vzD kqm -tZd -bRz -bRz +rWJ +iOi +iOi bVL vzD bln diff --git a/_maps/map_files/KiloStation/KiloStation.dmm b/_maps/map_files/KiloStation/KiloStation.dmm index 63d1b8f7a7eb..75f89259a70b 100644 --- a/_maps/map_files/KiloStation/KiloStation.dmm +++ b/_maps/map_files/KiloStation/KiloStation.dmm @@ -225,7 +225,7 @@ /area/station/security/detectives_office) "acj" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/blue/line, /obj/effect/turf_decal/trimline/blue/line{ @@ -2221,7 +2221,7 @@ /turf/open/floor/iron/showroomfloor, /area/station/security/lockers) "aJM" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/shovel/spade, /obj/item/cultivator{ pixel_x = 9 @@ -3583,7 +3583,7 @@ /turf/open/floor/iron/showroomfloor, /area/station/science/ordnance/office) "bmn" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/potato, /obj/structure/cable, /turf/open/floor/grass, @@ -4446,7 +4446,7 @@ /turf/closed/wall, /area/station/cargo/storage) "bBY" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/watermelon, /turf/open/floor/grass, /area/station/security/prison/garden) @@ -7223,7 +7223,7 @@ /turf/open/floor/plating, /area/station/security/execution/transfer) "cyZ" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/ambrosia, /turf/open/floor/grass, /area/station/security/prison/garden) @@ -8770,7 +8770,7 @@ /turf/open/floor/iron/dark, /area/station/service/library) "dcF" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/structure/railing{ dir = 8 }, @@ -14294,7 +14294,7 @@ /obj/structure/disposalpipe/segment{ dir = 10 }, -/mob/living/simple_animal/bot/cleanbot/medbay, +/mob/living/basic/bot/cleanbot/medbay, /obj/effect/turf_decal/tile/neutral/half/contrasted{ dir = 4 }, @@ -17619,7 +17619,7 @@ /turf/open/floor/iron/dark, /area/station/maintenance/port/fore) "fSK" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/grass, /area/station/service/hydroponics/garden) "fSS" = ( @@ -23314,7 +23314,7 @@ /obj/effect/decal/cleanable/dirt/dust, /obj/machinery/power/apc/auto_name/directional/west, /obj/structure/cable, -/mob/living/simple_animal/bot/cleanbot/medbay{ +/mob/living/basic/bot/cleanbot/medbay{ maints_access_required = list(22); name = "Deacon Scrubsy" }, @@ -31327,7 +31327,7 @@ }, /obj/structure/rack, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/item/seeds/tower, +/obj/item/seeds/tree, /obj/item/seeds/watermelon, /obj/item/seeds/wheat, /obj/item/seeds/sugarcane, @@ -31874,7 +31874,7 @@ "kxf" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/blue/end{ dir = 4 }, @@ -34789,7 +34789,7 @@ /area/station/commons/locker) "lsZ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/green/end{ dir = 8 @@ -35736,7 +35736,7 @@ /area/station/cargo/storage) "lJJ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/green/line, /obj/effect/turf_decal/trimline/green/line{ dir = 1 @@ -44607,7 +44607,7 @@ /turf/open/floor/iron/dark, /area/station/science/ordnance) "oQe" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/grass, /area/station/service/hydroponics/garden) "oQg" = ( @@ -46280,7 +46280,7 @@ /area/station/security/detectives_office) "ppH" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/blue/line, /obj/effect/turf_decal/trimline/blue/line{ dir = 1 @@ -46705,7 +46705,7 @@ /turf/closed/wall/r_wall, /area/station/command/heads_quarters/hop) "pxH" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/structure/railing/corner, /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, @@ -53529,7 +53529,7 @@ /area/station/science/xenobiology) "rJS" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/green/line, /obj/effect/turf_decal/trimline/green/line{ @@ -53872,8 +53872,8 @@ /turf/open/floor/iron, /area/station/commons/storage/primary) "rOX" = ( -/obj/machinery/hydroponics/constructable, -/obj/item/seeds/tower{ +/obj/machinery/growing/tray, +/obj/item/seeds/tree{ pixel_y = -6 }, /turf/open/floor/plating, @@ -54154,7 +54154,7 @@ /area/station/maintenance/department/electrical) "rSN" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/green/end{ dir = 8 }, @@ -54395,7 +54395,7 @@ /turf/open/floor/wood, /area/station/service/library) "rYx" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/tile/green{ dir = 1 }, @@ -58968,7 +58968,7 @@ /obj/structure/disposalpipe/segment{ dir = 5 }, -/mob/living/simple_animal/bot/cleanbot/autopatrol, +/mob/living/basic/bot/cleanbot/autopatrol, /obj/effect/turf_decal/tile/purple/half/contrasted, /obj/effect/turf_decal/tile/blue, /turf/open/floor/iron, @@ -59294,7 +59294,7 @@ /turf/open/floor/iron/dark, /area/station/construction/mining/aux_base) "tAM" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/carrot, /turf/open/floor/grass, /area/station/security/prison/garden) @@ -59389,7 +59389,7 @@ /area/station/hallway/primary/aft) "tCd" = ( /obj/effect/landmark/event_spawn, -/mob/living/simple_animal/bot/medbot/autopatrol, +/mob/living/basic/bot/medbot/autopatrol, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/turf_decal/tile/blue/opposingcorners, @@ -66029,7 +66029,7 @@ /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) "vJN" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/structure/railing{ dir = 4 }, @@ -66677,7 +66677,7 @@ /turf/open/floor/plating, /area/station/service/chapel/monastery) "vTs" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/item/seeds/apple, /obj/item/reagent_containers/cup/bottle/nutrient/l4z, /obj/effect/decal/cleanable/dirt, @@ -68685,7 +68685,7 @@ /turf/open/floor/carpet/blue, /area/station/command/heads_quarters/hop) "wzp" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/structure/railing/corner{ dir = 8 }, @@ -70208,7 +70208,7 @@ "wYk" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/blue/end{ dir = 4 }, @@ -70677,7 +70677,7 @@ /turf/open/floor/plating, /area/station/maintenance/aft) "xhI" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/shovel/spade, /obj/item/cultivator{ pixel_x = 8 diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm index 848b92cedae6..51278ded1861 100644 --- a/_maps/map_files/MetaStation/MetaStation.dmm +++ b/_maps/map_files/MetaStation/MetaStation.dmm @@ -1847,12 +1847,6 @@ }, /turf/open/floor/iron, /area/station/construction/storage_wing) -"aII" = ( -/obj/machinery/hydroponics/soil, -/obj/item/cultivator, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/grass, -/area/station/security/prison/garden) "aIX" = ( /obj/machinery/portable_atmospherics/pump, /obj/machinery/light/small/directional/north, @@ -3936,6 +3930,20 @@ /obj/effect/turf_decal/tile/purple, /turf/open/floor/iron, /area/station/hallway/primary/central) +"bpH" = ( +/obj/machinery/light/small/directional/east, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/button/door/directional/east{ + id = "Prison Gate"; + name = "Prison Wing Lockdown"; + req_one_access = list("brig", "permabrig") + }, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/security/brig) "bpK" = ( /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/plating, @@ -4405,11 +4413,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/dark, /area/station/engineering/transit_tube) -"byW" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/bot, -/turf/open/floor/iron, -/area/station/service/hydroponics) "bzv" = ( /obj/effect/spawner/random/entertainment/arcade, /obj/structure/cable, @@ -7639,6 +7642,16 @@ /obj/machinery/light_switch/directional/south, /turf/open/floor/wood, /area/station/service/theater) +"cRM" = ( +/obj/machinery/growing/soil, +/obj/machinery/camera/directional/west{ + c_tag = "Prison Forestry"; + network = list("ss13","prison") + }, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/light/directional/west, +/turf/open/floor/grass, +/area/station/security/prison/garden) "cRW" = ( /obj/machinery/light/directional/south, /obj/machinery/button/door/directional/south{ @@ -9623,12 +9636,6 @@ /obj/effect/turf_decal/trimline/green/filled/corner, /turf/open/floor/iron, /area/station/hallway/primary/central) -"dGW" = ( -/obj/machinery/holopad, -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/trimline/red/filled/line, -/turf/open/floor/iron, -/area/station/security/brig) "dHa" = ( /obj/machinery/navbeacon{ codes_txt = "delivery;dir=1"; @@ -9737,13 +9744,6 @@ /obj/machinery/door/window/right/directional/west, /turf/open/floor/wood, /area/station/command/heads_quarters/captain/private) -"dJo" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/bot, -/obj/machinery/airalarm/directional/east, -/obj/machinery/light/directional/east, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "dJK" = ( /turf/open/floor/iron/stairs/right{ dir = 1 @@ -10525,11 +10525,6 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/entry) -"dXP" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/bot, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "dXQ" = ( /obj/structure/extinguisher_cabinet/directional/east, /obj/effect/turf_decal/tile/neutral, @@ -10870,6 +10865,12 @@ "ecO" = ( /turf/open/floor/carpet, /area/station/service/library) +"ecQ" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/bot, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron, +/area/station/service/hydroponics/garden) "edl" = ( /obj/docking_port/stationary/escape_pod, /turf/open/space/basic, @@ -11036,6 +11037,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/hallway/primary/central) +"egc" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/bot, +/obj/machinery/airalarm/directional/east, +/obj/machinery/light/directional/east, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "ege" = ( /obj/effect/turf_decal/trimline/yellow/filled/line{ dir = 4 @@ -11156,7 +11164,7 @@ /turf/open/floor/iron, /area/station/hallway/primary/fore) "eih" = ( -/mob/living/simple_animal/bot/cleanbot/medbay, +/mob/living/basic/bot/cleanbot/medbay, /turf/open/floor/iron/white, /area/station/medical/storage) "eio" = ( @@ -12436,6 +12444,12 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) +"eGE" = ( +/obj/machinery/growing/soil, +/obj/item/cultivator, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/grass, +/area/station/security/prison/garden) "eGJ" = ( /obj/machinery/door/firedoor, /obj/effect/turf_decal/stripes/line, @@ -13563,6 +13577,13 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) +"ffr" = ( +/obj/machinery/holopad, +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/trimline/red/filled/line, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron, +/area/station/security/brig) "ffH" = ( /turf/closed/wall, /area/station/hallway/primary/fore) @@ -14857,17 +14878,6 @@ /obj/machinery/light/small/directional/east, /turf/open/floor/iron/dark/textured, /area/station/engineering/atmos) -"fGb" = ( -/obj/machinery/door/airlock/security/glass{ - name = "Prison Sanitarium" - }, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/firedoor, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/turf/open/floor/iron/white, -/area/station/security/execution/transfer) "fGp" = ( /obj/effect/turf_decal/stripes/line, /obj/structure/cable, @@ -15537,6 +15547,16 @@ }, /turf/open/floor/iron, /area/station/service/hydroponics) +"fTm" = ( +/obj/machinery/door/airlock/security/glass{ + name = "Permabrig Visitation" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/door/firedoor, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/brig) "fTn" = ( /obj/structure/table/wood, /obj/item/folder/blue, @@ -16749,6 +16769,13 @@ }, /turf/open/floor/iron, /area/station/engineering/main) +"goq" = ( +/obj/machinery/growing/soil, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/item/plant_analyzer, +/turf/open/floor/grass, +/area/station/security/prison/garden) "goG" = ( /obj/item/radio/intercom/directional/north, /obj/machinery/camera/directional/north{ @@ -17118,21 +17145,6 @@ }, /turf/open/floor/iron, /area/station/security/brig) -"guU" = ( -/obj/machinery/button/flasher{ - id = "visitorflash"; - pixel_x = -6; - pixel_y = 24 - }, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/obj/machinery/button/door/directional/north{ - id = "visitation"; - name = "Visitation Shutters"; - pixel_x = 6; - req_access = list("brig") - }, -/turf/open/floor/iron, -/area/station/security/brig) "guX" = ( /turf/closed/wall, /area/station/commons/storage/primary) @@ -17759,6 +17771,16 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron, /area/station/hallway/primary/central) +"gIG" = ( +/obj/effect/turf_decal/tile/green{ + dir = 8 + }, +/obj/effect/turf_decal/tile/blue, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/delivery, +/mob/living/basic/pet/potty, +/turf/open/floor/iron, +/area/station/service/hydroponics) "gIK" = ( /obj/structure/closet/bombcloset, /turf/open/floor/plating, @@ -17787,24 +17809,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/starboard/greater) -"gJi" = ( -/obj/machinery/door/poddoor/shutters/preopen{ - dir = 4; - id = "PermaLockdown"; - name = "Lockdown Shutters" - }, -/obj/effect/turf_decal/delivery, -/obj/structure/cable, -/obj/machinery/door/airlock/security/glass{ - id_tag = "permaouter"; - name = "Permabrig Transfer" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/firedoor, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/turf/open/floor/iron, -/area/station/security/prison) "gJm" = ( /obj/machinery/atmospherics/pipe/smart/simple/cyan/visible{ dir = 9 @@ -19964,6 +19968,11 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/aft) +"hvA" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/bot, +/turf/open/floor/iron, +/area/station/service/hydroponics) "hvB" = ( /obj/effect/turf_decal/trimline/green/filled/line, /obj/effect/turf_decal/trimline/brown/filled/warning, @@ -21014,6 +21023,21 @@ dir = 1 }, /area/station/service/chapel) +"hQH" = ( +/obj/machinery/button/flasher{ + id = "visitorflash"; + pixel_x = -6; + pixel_y = 24 + }, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/obj/machinery/button/door/directional/north{ + id = "visitation"; + name = "Visitation Shutters"; + pixel_x = 6; + req_one_access = list("brig", "permabrig") + }, +/turf/open/floor/iron, +/area/station/security/brig) "hQT" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -21104,18 +21128,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/hallway/primary/central) -"hSd" = ( -/obj/structure/cable, -/obj/machinery/light/directional/south, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/button/door/directional/south{ - id = "PermaLockdown"; - name = "Panic Button"; - req_access = list("brig") - }, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "hSe" = ( /obj/machinery/light/small/directional/east, /obj/item/radio/intercom/directional/north, @@ -22118,13 +22130,6 @@ "ikb" = ( /turf/open/floor/iron, /area/station/commons/dorms) -"ikw" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/bot, -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/turf/open/floor/iron, -/area/station/service/hydroponics/garden) "ikC" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/maintenance_hatch{ @@ -22206,12 +22211,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) -"ilg" = ( -/obj/machinery/hydroponics/soil, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/turf/open/floor/grass, -/area/station/security/prison/garden) "ilh" = ( /turf/closed/wall, /area/station/maintenance/fore) @@ -22621,16 +22620,6 @@ /obj/effect/turf_decal/siding/yellow, /turf/open/floor/iron, /area/station/hallway/primary/starboard) -"irh" = ( -/obj/machinery/hydroponics/soil, -/obj/machinery/camera/directional/west{ - c_tag = "Prison Forestry"; - network = list("ss13","prison") - }, -/obj/effect/decal/cleanable/dirt, -/obj/machinery/light/directional/west, -/turf/open/floor/grass, -/area/station/security/prison/garden) "irL" = ( /obj/effect/turf_decal/trimline/blue/filled/line, /turf/open/floor/iron/white, @@ -22841,6 +22830,16 @@ /obj/structure/sign/delamination_counter/directional/north, /turf/open/floor/iron/dark, /area/station/engineering/break_room) +"iuc" = ( +/obj/machinery/door/airlock/security{ + id_tag = "IsolationCell"; + name = "Isolation Cell" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/prison/safe) "iug" = ( /obj/machinery/mechpad, /turf/open/floor/circuit/green, @@ -25540,6 +25539,11 @@ }, /turf/open/floor/iron, /area/station/security/office) +"jkv" = ( +/obj/structure/cable, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron, +/area/station/security/brig) "jkT" = ( /obj/effect/turf_decal/stripes/line{ dir = 6 @@ -26554,11 +26558,6 @@ /obj/structure/cable, /turf/open/floor/grass, /area/station/medical/pathology) -"jBY" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/bot, -/turf/open/floor/iron, -/area/station/service/hydroponics/garden) "jCj" = ( /obj/item/toy/basketball, /turf/open/floor/plating, @@ -28132,11 +28131,6 @@ }, /turf/open/floor/iron/white, /area/station/medical/pharmacy) -"keL" = ( -/obj/structure/cable, -/obj/structure/disposalpipe/junction, -/turf/open/floor/iron, -/area/station/security/brig) "keR" = ( /obj/effect/turf_decal/tile/neutral{ dir = 8 @@ -28573,6 +28567,16 @@ }, /turf/open/floor/iron, /area/station/service/hydroponics) +"klO" = ( +/obj/structure/disposalpipe/segment{ + dir = 9 + }, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron, +/area/station/security/brig) "klS" = ( /obj/machinery/griddle, /turf/open/floor/iron/cafeteria, @@ -29433,16 +29437,6 @@ "kCZ" = ( /turf/closed/wall, /area/station/service/hydroponics) -"kDk" = ( -/obj/machinery/door/airlock/security/glass{ - name = "Permabrig Visitation" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/firedoor, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/turf/open/floor/iron, -/area/station/security/brig) "kDG" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -29459,6 +29453,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/port) +"kDV" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/bot, +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/turf/open/floor/iron, +/area/station/service/hydroponics/garden) "kDY" = ( /obj/machinery/door/airlock/highsecurity{ name = "Secure Tech Storage" @@ -31227,6 +31228,11 @@ /obj/machinery/light/small/directional/south, /turf/open/floor/engine, /area/station/science/xenobiology) +"ljG" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/bot, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "ljL" = ( /obj/structure/chair/comfy/black, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -31816,6 +31822,16 @@ }, /turf/open/floor/wood, /area/station/smithing) +"lsI" = ( +/obj/machinery/growing/soil{ + pixel_y = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/spawner/random/food_or_drink/seed, +/turf/open/floor/plating, +/area/station/maintenance/starboard/aft) "lsJ" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -33397,12 +33413,6 @@ /obj/structure/sign/warning/vacuum/directional/east, /turf/open/floor/plating, /area/station/maintenance/aft/lesser) -"lXl" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/bot, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron, -/area/station/service/hydroponics/garden) "lXm" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/sign/warning/vacuum/external, @@ -33864,6 +33874,16 @@ }, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"mgA" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/machinery/growing/soil{ + pixel_y = 8 + }, +/obj/effect/spawner/random/food_or_drink/seed, +/turf/open/floor/plating, +/area/station/maintenance/starboard/aft) "mgE" = ( /obj/structure/mirror/directional/west, /obj/machinery/shower/directional/east, @@ -35669,6 +35689,16 @@ /obj/machinery/duct, /turf/open/floor/stone, /area/station/science/xenobiology) +"mMi" = ( +/obj/machinery/growing/soil{ + pixel_y = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/spawner/random/food_or_drink/seed, +/turf/open/floor/plating, +/area/station/maintenance/starboard/aft) "mMl" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/neutral/fourcorners, @@ -36356,6 +36386,14 @@ /obj/structure/cable, /turf/open/space/basic, /area/station/solars/starboard/fore) +"mYH" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 1 + }, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron, +/area/station/security/brig) "mYJ" = ( /obj/structure/table/wood, /obj/item/poster/random_official, @@ -36447,7 +36485,7 @@ /obj/structure/disposalpipe/segment{ dir = 4 }, -/mob/living/simple_animal/bot/cleanbot/autopatrol, +/mob/living/basic/bot/cleanbot/autopatrol, /turf/open/floor/iron, /area/station/hallway/primary/central) "mZL" = ( @@ -38596,7 +38634,7 @@ /obj/machinery/light/small/directional/east, /obj/machinery/firealarm/directional/east, /obj/effect/turf_decal/tile/blue, -/mob/living/simple_animal/bot/cleanbot, +/mob/living/basic/bot/cleanbot, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat_interior) "nMK" = ( @@ -40176,13 +40214,6 @@ /obj/structure/window/reinforced/spawner/directional/west, /turf/open/floor/plating/airless, /area/space/nearstation) -"opF" = ( -/obj/machinery/hydroponics/soil, -/obj/item/cultivator, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/turf/open/floor/grass, -/area/station/security/prison/garden) "opG" = ( /obj/structure/cable, /obj/machinery/firealarm/directional/north, @@ -40720,6 +40751,11 @@ /obj/machinery/airalarm/directional/north, /turf/open/floor/iron/dark, /area/station/science/ordnance) +"ozn" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/bot, +/turf/open/floor/iron, +/area/station/service/hydroponics/garden) "ozs" = ( /obj/machinery/door/airlock/engineering{ name = "Starboard Bow Solar Access" @@ -40791,6 +40827,13 @@ }, /turf/open/floor/iron/dark, /area/station/security/brig) +"oAP" = ( +/obj/machinery/growing/soil, +/obj/item/cultivator, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/turf/open/floor/grass, +/area/station/security/prison/garden) "oAQ" = ( /obj/structure/lattice/catwalk, /obj/structure/transit_tube/horizontal, @@ -44320,6 +44363,17 @@ /obj/machinery/light_switch/directional/east, /turf/open/floor/iron/dark, /area/station/command/bridge) +"pOW" = ( +/obj/machinery/growing/soil{ + pixel_y = 8 + }, +/obj/item/food/grown/harebell, +/obj/item/food/grown/harebell, +/obj/item/food/grown/harebell, +/obj/item/food/grown/harebell, +/obj/machinery/light/small/directional/north, +/turf/open/floor/cult, +/area/station/service/chapel/funeral) "pPh" = ( /obj/structure/closet/secure_closet/miner, /obj/item/clothing/suit/hooded/wintercoat/miner, @@ -44360,16 +44414,6 @@ /obj/structure/flora/bush/flowers_yw/style_random, /turf/open/floor/grass, /area/station/maintenance/starboard/aft) -"pPR" = ( -/obj/machinery/door/airlock/security{ - id_tag = "IsolationCell"; - name = "Isolation Cell" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/turf/open/floor/iron, -/area/station/security/prison/safe) "pPV" = ( /turf/closed/wall, /area/station/bitrunning/den) @@ -45379,6 +45423,12 @@ /obj/structure/reagent_dispensers/watertank, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"qiY" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/bot, +/obj/machinery/firealarm/directional/east, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "qjf" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -47418,15 +47468,6 @@ }, /turf/open/floor/iron, /area/station/engineering/atmos) -"qTH" = ( -/obj/structure/disposalpipe/segment{ - dir = 9 - }, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron, -/area/station/security/brig) "qTJ" = ( /obj/machinery/camera/directional/west{ c_tag = "Departure Lounge - Port Aft" @@ -50876,18 +50917,11 @@ /obj/effect/turf_decal/tile/blue/anticorner/contrasted{ dir = 4 }, -/mob/living/simple_animal/bot/medbot/autopatrol, +/mob/living/basic/bot/medbot/autopatrol, /turf/open/floor/iron/white/corner{ dir = 8 }, /area/station/medical/medbay/lobby) -"sbX" = ( -/obj/machinery/hydroponics/soil, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/item/plant_analyzer, -/turf/open/floor/grass, -/area/station/security/prison/garden) "scb" = ( /obj/effect/decal/cleanable/dirt/dust, /obj/effect/spawner/random/trash/box, @@ -53249,6 +53283,18 @@ /obj/effect/turf_decal/siding/purple, /turf/open/floor/iron/white, /area/station/science/ordnance/storage) +"sTP" = ( +/obj/structure/cable, +/obj/machinery/light/directional/south, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/button/door/directional/south{ + id = "PermaLockdown"; + name = "Panic Button"; + req_one_access = list("brig", "permabrig") + }, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "sTQ" = ( /obj/structure/bed/roller, /obj/effect/turf_decal/trimline/blue/filled/warning{ @@ -53300,20 +53346,6 @@ }, /turf/open/floor/wood, /area/station/command/corporate_showroom) -"sUC" = ( -/obj/machinery/light/small/directional/east, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/button/door/directional/east{ - id = "Prison Gate"; - name = "Prison Wing Lockdown"; - req_access = list("brig") - }, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/security/brig) "sUJ" = ( /obj/effect/spawner/random/structure/closet_maintenance, /obj/effect/spawner/random/maintenance/two, @@ -54083,6 +54115,24 @@ dir = 1 }, /area/station/engineering/atmos/pumproom) +"thj" = ( +/obj/machinery/door/poddoor/shutters/preopen{ + dir = 4; + id = "PermaLockdown"; + name = "Lockdown Shutters" + }, +/obj/effect/turf_decal/delivery, +/obj/structure/cable, +/obj/machinery/door/airlock/security/glass{ + id_tag = "permaouter"; + name = "Permabrig Transfer" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/door/firedoor, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/prison) "thQ" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment{ @@ -54131,16 +54181,6 @@ }, /turf/open/floor/wood, /area/station/commons/dorms) -"tjf" = ( -/obj/machinery/hydroponics/soil{ - pixel_y = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/spawner/random/food_or_drink/seed, -/turf/open/floor/plating, -/area/station/maintenance/starboard/aft) "tjh" = ( /turf/closed/wall, /area/station/security/brig) @@ -54168,13 +54208,6 @@ }, /turf/open/floor/iron, /area/station/service/bar) -"tjv" = ( -/obj/structure/cable, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/turf/open/floor/iron, -/area/station/security/brig) "tjG" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/simple/orange/visible, @@ -54671,16 +54704,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/hallway/secondary/command) -"tsv" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/machinery/hydroponics/soil{ - pixel_y = 8 - }, -/obj/effect/spawner/random/food_or_drink/seed, -/turf/open/floor/plating, -/area/station/maintenance/starboard/aft) "tsy" = ( /obj/effect/turf_decal/stripes/line{ dir = 1 @@ -54821,16 +54844,6 @@ }, /turf/open/floor/iron/white, /area/station/medical/treatment_center) -"tuG" = ( -/obj/machinery/hydroponics/soil{ - pixel_y = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/spawner/random/food_or_drink/seed, -/turf/open/floor/plating, -/area/station/maintenance/starboard/aft) "tvE" = ( /turf/closed/wall/r_wall, /area/station/command/gateway) @@ -58897,6 +58910,12 @@ /obj/machinery/light/directional/east, /turf/open/floor/iron/white, /area/station/medical/medbay/central) +"uST" = ( +/obj/machinery/growing/soil, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/turf/open/floor/grass, +/area/station/security/prison/garden) "uTj" = ( /obj/effect/landmark/start/medical_doctor, /turf/open/floor/iron/dark, @@ -59602,6 +59621,14 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark, /area/station/service/chapel/office) +"vhV" = ( +/obj/structure/cable, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron, +/area/station/security/brig) "vhZ" = ( /obj/structure/window/reinforced/spawner/directional/south, /turf/open/floor/holofloor/dark, @@ -61273,6 +61300,17 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/commons/locker) +"vLD" = ( +/obj/machinery/door/airlock/security/glass{ + name = "Prison Sanitarium" + }, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/door/firedoor, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/white, +/area/station/security/execution/transfer) "vLM" = ( /obj/structure/table/wood/poker, /obj/item/storage/dice, @@ -62657,13 +62695,6 @@ }, /turf/open/floor/wood, /area/station/command/corporate_showroom) -"wkh" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/security/brig) "wki" = ( /obj/effect/spawner/random/engineering/tracking_beacon, /turf/open/floor/wood, @@ -62733,17 +62764,6 @@ /obj/item/kirbyplants/random, /turf/open/floor/iron, /area/station/service/hydroponics) -"wlj" = ( -/obj/machinery/hydroponics/soil{ - pixel_y = 8 - }, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/item/food/grown/harebell, -/obj/machinery/light/small/directional/north, -/turf/open/floor/cult, -/area/station/service/chapel/funeral) "wlt" = ( /obj/structure/cable, /obj/effect/turf_decal/trimline/blue/filled/warning, @@ -63348,12 +63368,6 @@ /obj/effect/spawner/random/structure/grille, /turf/open/floor/plating, /area/station/maintenance/aft/greater) -"wyn" = ( -/obj/machinery/hydroponics/soil, -/obj/item/shovel/spade, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/grass, -/area/station/security/prison/garden) "wyo" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -64661,15 +64675,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/maintenance/starboard/lesser) -"wVQ" = ( -/obj/effect/turf_decal/tile/green{ - dir = 8 - }, -/obj/effect/turf_decal/tile/blue, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/delivery, -/turf/open/floor/iron, -/area/station/service/hydroponics) "wVW" = ( /obj/effect/turf_decal/tile/neutral, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -64923,12 +64928,6 @@ /obj/machinery/firealarm/directional/south, /turf/open/floor/iron, /area/station/hallway/secondary/entry) -"xaG" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/bot, -/obj/machinery/firealarm/directional/east, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "xaL" = ( /obj/effect/turf_decal/trimline/red/filled/corner, /obj/item/kirbyplants/random, @@ -67315,6 +67314,12 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/white, /area/station/medical/medbay/central) +"xTn" = ( +/obj/machinery/growing/soil, +/obj/item/shovel/spade, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/grass, +/area/station/security/prison/garden) "xTs" = ( /obj/machinery/light/small/directional/south, /obj/effect/landmark/blobstart, @@ -67702,6 +67707,12 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/catwalk_floor/iron_dark, /area/station/maintenance/solars/port/fore) +"xZz" = ( +/obj/structure/cable, +/obj/structure/disposalpipe/junction, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron, +/area/station/security/brig) "xZB" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/yellow/half/contrasted{ @@ -86961,10 +86972,10 @@ aaa aaa cmB cmB -opF -sbX -irh -wyn +oAP +goq +cRM +xTn iOr aaa raz @@ -87736,7 +87747,7 @@ rVb viQ dTQ rOP -aII +eGE iOr aUn aUn @@ -87993,7 +88004,7 @@ aXa aXa dHQ jjM -ilg +uST cmB aaa mji @@ -88250,7 +88261,7 @@ dhW aXa fbX cnk -wyn +xTn cmB aaa aUn @@ -88376,7 +88387,7 @@ pjb gGh rxz gYU -wlj +pOW rIG aaa aaa @@ -92110,7 +92121,7 @@ iUv iUv igS dXl -gJi +thj lAM txg rGd @@ -92621,7 +92632,7 @@ gEg ikO pNY qjr -fGb +vLD yey cWI gYi @@ -92629,7 +92640,7 @@ tjh oCR rGd tjh -guU +hQH eXj vSo sch @@ -92871,7 +92882,7 @@ sjP dcN vae slI -pPR +iuc xnS gYi jTZ @@ -92881,14 +92892,14 @@ ouj aBL yey cWI -hSd +sTP tjh tjh mAm tjh wsX wsX -kDk +fTm sch wLH lvu @@ -93401,16 +93412,16 @@ xyt vQO wsX rKZ -rKZ +jkv lsP rKZ rKZ prv -rKZ +jkv hSH uGD rlg -keL +xZz eay tDz tjh @@ -93655,7 +93666,7 @@ yey yey swe fNh -sUC +bpH mtG uyd ltt @@ -96752,11 +96763,11 @@ pxN drw nZk sVi -tjv +vhV pHb syc -wkh -dGW +mYH +ffr hhp bJq gFi @@ -98037,7 +98048,7 @@ nkX ocl lvZ iMo -qTH +klO lsP neO quA @@ -98294,7 +98305,7 @@ nOv wxj aGm xrq -rKZ +jkv pHb qwh mrL @@ -102201,15 +102212,15 @@ xbT wYB epF gFL -byW -byW -byW +hvA +hvA +hvA pdl xlF -byW -byW -byW -byW +hvA +hvA +hvA +hvA enS bwb kCZ @@ -102468,7 +102479,7 @@ emv svk xdF sWG -wVQ +gIG yeI gIM tAg @@ -102715,15 +102726,15 @@ eLI ltW jyF gFL -byW -byW -byW +hvA +hvA +hvA pdl xlF -byW -byW -byW -byW +hvA +hvA +hvA +hvA xlF nfT kCZ @@ -103234,9 +103245,9 @@ beO fWk rhR gMO -dXP -dJo -xaG +ljG +egc +qiY kCZ kCZ kCZ @@ -104552,9 +104563,9 @@ bLd ojt aTP bLd -tjf -tjf -tjf +mMi +mMi +mMi bLd lMJ aaa @@ -104732,9 +104743,9 @@ wfN lqQ nMK mos -jBY +ozn aKk -lXl +ecQ nzS gpk qXB @@ -104989,9 +105000,9 @@ xUh lqQ aMb mos -jBY +ozn knZ -jBY +ozn weS qdr qXB @@ -105246,9 +105257,9 @@ lnc lqQ uKv mos -jBY +ozn dCm -ikw +kDV qVy hzJ uiB @@ -105578,11 +105589,11 @@ bLd bLd bLd bLd -tsv -tuG -tuG -tuG -tuG +mgA +lsI +lsI +lsI +lsI bLd lMJ aaa diff --git a/_maps/map_files/Mining/Lavaland.dmm b/_maps/map_files/Mining/Lavaland.dmm index 69cf4ecfe203..34d550810cdb 100644 --- a/_maps/map_files/Mining/Lavaland.dmm +++ b/_maps/map_files/Mining/Lavaland.dmm @@ -2306,7 +2306,7 @@ }, /area/mine/medical) "mP" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/structure/window/spawner/directional/west, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/green/filled/line{ @@ -3892,7 +3892,7 @@ /obj/effect/turf_decal/trimline/green/filled/corner{ dir = 4 }, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/item/seeds/tomato, /turf/open/floor/iron/dark, /area/mine/laborcamp) @@ -3987,7 +3987,7 @@ /area/mine/lounge) "wr" = ( /obj/item/seeds/plump, -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) "wt" = ( @@ -4000,7 +4000,7 @@ /turf/open/floor/iron/dark/smooth_edge, /area/mine/production) "wu" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/green/filled/line{ dir = 5 @@ -7417,7 +7417,7 @@ /turf/open/floor/iron/large, /area/mine/lounge) "Sn" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/airalarm/directional/west, /obj/effect/turf_decal/trimline/green/filled/line{ dir = 9 @@ -7458,7 +7458,7 @@ }, /area/mine/laborcamp/security) "Sz" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/light/small/directional/west, /obj/effect/turf_decal/trimline/green/filled/line{ dir = 8 @@ -7919,7 +7919,7 @@ }, /area/mine/laborcamp) "Vm" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/structure/window/spawner/directional/west, /obj/machinery/light/small/directional/north, /obj/effect/turf_decal/trimline/green/filled/line{ @@ -8027,7 +8027,7 @@ /turf/open/floor/plating, /area/mine/maintenance/labor) "VS" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/green/filled/line{ dir = 4 @@ -8331,7 +8331,7 @@ /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) "XJ" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/structure/window/spawner/directional/west, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/green/filled/line{ diff --git a/_maps/map_files/Mining/Oshan.dmm b/_maps/map_files/Mining/Oshan.dmm index 6bc35bec685c..d61005b96f37 100644 --- a/_maps/map_files/Mining/Oshan.dmm +++ b/_maps/map_files/Mining/Oshan.dmm @@ -323,7 +323,7 @@ /turf/open/floor/iron/dark, /area/station/security/prison) "nl" = ( -/obj/machinery/hydroponics, +/obj/machinery/growing, /obj/machinery/light/directional/east, /turf/open/floor/iron/white, /area/station/security/prison/garden) @@ -422,7 +422,7 @@ /turf/open/floor/plating, /area/station/security/prison/visit) "qL" = ( -/obj/machinery/hydroponics, +/obj/machinery/growing, /obj/machinery/power/apc/auto_name/directional/west, /obj/structure/cable, /turf/open/floor/iron, @@ -437,7 +437,7 @@ /turf/open/floor/iron, /area/station/security/prison/mess) "rA" = ( -/obj/machinery/hydroponics, +/obj/machinery/growing, /turf/open/floor/iron/white, /area/station/security/prison/garden) "rN" = ( @@ -837,7 +837,7 @@ /turf/closed/wall, /area/station/security/prison/rec) "Jk" = ( -/obj/machinery/hydroponics, +/obj/machinery/growing, /obj/machinery/light/directional/west, /turf/open/floor/iron, /area/station/security/prison/garden) @@ -845,7 +845,7 @@ /turf/closed/wall, /area/mine/production) "Kc" = ( -/obj/machinery/hydroponics, +/obj/machinery/growing, /obj/machinery/airalarm/directional/south, /turf/open/floor/iron, /area/station/security/prison/garden) @@ -890,7 +890,7 @@ /turf/open/floor/iron/dark, /area/station/security/prison/rec) "Np" = ( -/obj/machinery/hydroponics, +/obj/machinery/growing, /obj/machinery/camera/autoname/directional/north, /turf/open/floor/iron/white, /area/station/security/prison/garden) @@ -994,7 +994,7 @@ /turf/open/floor/iron/white, /area/station/security/prison) "Rx" = ( -/obj/machinery/hydroponics, +/obj/machinery/growing, /turf/open/floor/iron, /area/station/security/prison/garden) "Sq" = ( diff --git a/_maps/map_files/NorthStar/north_star.dmm b/_maps/map_files/NorthStar/north_star.dmm index 056d85d90db0..be045da91462 100644 --- a/_maps/map_files/NorthStar/north_star.dmm +++ b/_maps/map_files/NorthStar/north_star.dmm @@ -820,11 +820,6 @@ /obj/structure/disposalpipe/trunk, /turf/open/space/basic, /area/space/nearstation) -"akl" = ( -/obj/machinery/status_display/evac/directional/north, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/white, -/area/station/science/research/abandoned) "akr" = ( /obj/machinery/button/door/directional/south{ id = "captain_privacy"; @@ -4239,6 +4234,11 @@ /obj/structure/cable, /turf/open/floor/circuit/green, /area/station/science/robotics/mechbay) +"bbG" = ( +/obj/item/seeds/coffee, +/obj/machinery/growing/soil, +/turf/open/misc/sandy_dirt, +/area/station/maintenance/floor3/starboard) "bbI" = ( /obj/structure/stairs/north, /obj/structure/sign/departments/science/alt/directional/west, @@ -5298,11 +5298,6 @@ /obj/machinery/airalarm/directional/east, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat/hallway) -"boB" = ( -/obj/machinery/hydroponics/soil, -/obj/item/shovel/spade, -/turf/open/misc/dirt/jungle, -/area/station/security/prison/garden) "boH" = ( /obj/structure/railing{ dir = 8 @@ -7921,6 +7916,11 @@ /obj/structure/extinguisher_cabinet/directional/west, /turf/open/floor/iron, /area/station/hallway/floor3/aft) +"bVa" = ( +/obj/structure/flora/bush/sparsegrass/style_random, +/obj/machinery/growing/tray, +/turf/open/floor/grass, +/area/station/service/hydroponics/garden/abandoned) "bVd" = ( /obj/effect/turf_decal/trimline/blue/mid_joiner{ dir = 4 @@ -8441,6 +8441,12 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/hos) +"cdg" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/green/full, +/obj/machinery/growing/tray, +/turf/open/floor/iron, +/area/station/service/hydroponics) "cdj" = ( /obj/effect/mapping_helpers/airlock/access/any/engineering/maintenance, /obj/machinery/door/airlock/hatch{ @@ -11631,16 +11637,6 @@ /obj/structure/window/reinforced/spawner/directional/east, /turf/open/floor/grass, /area/station/science/xenobiology) -"cVz" = ( -/obj/machinery/door/airlock/security{ - name = "Gulag" - }, -/obj/effect/mapping_helpers/airlock/access/any/security/brig, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/dark, -/area/station/security/execution/transfer) "cVD" = ( /obj/structure/cable, /turf/open/floor/iron, @@ -13409,6 +13405,11 @@ }, /turf/open/floor/catwalk_floor, /area/station/maintenance/floor2/port/fore) +"dwz" = ( +/obj/machinery/status_display/evac/directional/north, +/obj/machinery/growing/tray, +/turf/open/floor/iron/white, +/area/station/science/research/abandoned) "dwD" = ( /obj/machinery/light/cold/no_nightlight/directional/north, /obj/structure/sign/poster/official/random/directional/north, @@ -13683,15 +13684,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/catwalk_floor, /area/station/maintenance/floor2/starboard/fore) -"dAr" = ( -/obj/machinery/hydroponics/soil, -/obj/machinery/camera/directional/west{ - c_tag = "Prison Forestry"; - network = list("ss13","prison") - }, -/obj/machinery/light/directional/west, -/turf/open/misc/dirt/jungle, -/area/station/security/prison/garden) "dAB" = ( /obj/structure/closet/firecloset, /turf/open/floor/iron/smooth_large, @@ -14065,6 +14057,13 @@ }, /turf/open/floor/iron/textured_large, /area/station/engineering/lobby) +"dFS" = ( +/obj/effect/turf_decal/stripes/line, +/obj/machinery/firealarm/directional/west, +/obj/structure/window/reinforced/spawner/directional/south, +/obj/machinery/growing/tray, +/turf/open/floor/iron/white, +/area/station/science/research/abandoned) "dFT" = ( /obj/machinery/airalarm/directional/west, /obj/effect/decal/cleanable/dirt/dust, @@ -15903,6 +15902,13 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/wood, /area/station/service/bar/atrium) +"efo" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 6 + }, +/obj/machinery/growing/tray, +/turf/open/floor/iron, +/area/station/service/hydroponics) "efp" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/landmark/start/depsec/engineering, @@ -17451,11 +17457,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/textured_large, /area/station/hallway/secondary/entry) -"eAv" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) "eAE" = ( /turf/open/floor/pod/dark, /area/station/maintenance/floor3/starboard/fore) @@ -17769,6 +17770,14 @@ /obj/machinery/light/small/directional/south, /turf/open/floor/pod/light, /area/station/maintenance/floor3/starboard) +"eFL" = ( +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 4 + }, +/obj/machinery/newscaster/directional/east, +/obj/machinery/growing/tray, +/turf/open/floor/iron, +/area/station/service/hydroponics) "eFU" = ( /obj/structure/disposalpipe/segment, /turf/open/floor/iron/white, @@ -18052,6 +18061,14 @@ }, /turf/open/floor/iron/dark/side, /area/station/command/teleporter) +"eJY" = ( +/obj/structure/flora/bush/sparsegrass/style_random, +/obj/structure/flora/bush/lavendergrass/style_random, +/obj/machinery/growing/tray{ + anchored = 0 + }, +/turf/open/floor/grass, +/area/station/service/hydroponics/garden/abandoned) "eKd" = ( /obj/machinery/door/airlock/science/glass{ name = "Applied Mechanics" @@ -18295,14 +18312,6 @@ icon_state = "snow4" }, /area/station/hallway/floor2/fore) -"eNN" = ( -/obj/effect/turf_decal/tile/purple{ - dir = 8 - }, -/obj/machinery/airalarm/directional/south, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/white, -/area/station/cargo/miningdock) "eNS" = ( /obj/effect/turf_decal/delivery, /obj/structure/sign/departments/restroom/directional/south, @@ -19298,6 +19307,22 @@ }, /turf/open/floor/iron/white, /area/station/science/xenobiology) +"feP" = ( +/obj/machinery/button/door/directional/north{ + id = "visitation"; + name = "Visitation Shutters"; + pixel_x = 6; + req_one_access = list("security","permabrig") + }, +/obj/machinery/button/flasher{ + id = "visitorflash"; + pixel_x = -6; + pixel_y = 24 + }, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, +/obj/machinery/airalarm/directional/west, +/turf/open/floor/iron/dark, +/area/station/security/prison/visit) "feR" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/dark/side{ @@ -19929,12 +19954,6 @@ /obj/effect/spawner/random/maintenance, /turf/open/openspace, /area/station/maintenance/floor3/starboard) -"fnB" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/light/small/directional/west, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) "fnJ" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/griddle, @@ -20552,7 +20571,7 @@ codes_txt = "patrol;next_patrol=2-0"; location = "2-19" }, -/mob/living/simple_animal/bot/medbot/autopatrol, +/mob/living/basic/bot/medbot/autopatrol, /turf/open/floor/iron/dark, /area/station/hallway/floor2/aft) "fwV" = ( @@ -21044,6 +21063,17 @@ }, /turf/open/floor/iron, /area/station/science/genetics) +"fDp" = ( +/obj/machinery/door/airlock/security{ + name = "Isolation" + }, +/obj/machinery/door/firedoor, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/turf_decal/tile/dark_red/fourcorners, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/brig) "fDt" = ( /obj/structure/table/reinforced, /obj/item/book/manual/wiki/ordnance{ @@ -21743,11 +21773,6 @@ /obj/structure/sign/departments/medbay/alt/directional/south, /turf/open/floor/iron/dark, /area/station/hallway/floor2/fore) -"fNg" = ( -/obj/effect/turf_decal/tile/green/full, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron, -/area/station/service/hydroponics) "fNh" = ( /obj/effect/landmark/start/hangover, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ @@ -22863,12 +22888,6 @@ /obj/machinery/holopad, /turf/open/floor/iron, /area/station/science/robotics/lab) -"gbW" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/firealarm/directional/west, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) "gbZ" = ( /obj/structure/bodycontainer/crematorium{ dir = 8; @@ -23118,12 +23137,6 @@ /obj/structure/window/reinforced/tinted/frosted, /turf/open/openspace, /area/station/maintenance/floor2/port) -"gfI" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/light_switch/directional/east, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) "gfP" = ( /obj/effect/turf_decal/siding/wood/end{ dir = 4 @@ -23241,13 +23254,6 @@ }, /turf/open/floor/catwalk_floor/iron_smooth, /area/station/maintenance/disposal) -"ghz" = ( -/obj/effect/turf_decal/stripes/line, -/obj/machinery/firealarm/directional/west, -/obj/structure/window/reinforced/spawner/directional/south, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/white, -/area/station/science/research/abandoned) "ghF" = ( /obj/effect/turf_decal/tile/purple/half/contrasted{ dir = 4 @@ -24170,6 +24176,11 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/catwalk_floor/iron_white, /area/station/medical/abandoned) +"gvy" = ( +/obj/machinery/growing/tray, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/iron, +/area/station/maintenance/floor1/port/aft) "gvO" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 @@ -25456,12 +25467,6 @@ /obj/structure/sign/departments/psychology/directional/west, /turf/open/floor/iron/white, /area/station/medical/medbay/central) -"gOd" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/camera/autoname/directional/west, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) "gOf" = ( /obj/effect/turf_decal/trimline/yellow/corner, /obj/structure/cable, @@ -25916,6 +25921,12 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/pod/light, /area/station/maintenance/floor3/port/fore) +"gVI" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/firealarm/directional/west, +/obj/machinery/growing/tray, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) "gVR" = ( /obj/effect/spawner/structure/window/reinforced, /obj/machinery/door/poddoor/shutters/preopen{ @@ -27121,6 +27132,11 @@ }, /turf/open/floor/iron/white, /area/station/command/heads_quarters/cmo) +"hml" = ( +/obj/machinery/newscaster/directional/east, +/obj/machinery/growing/tray, +/turf/open/floor/iron/white, +/area/station/cargo/miningdock) "hmn" = ( /obj/effect/turf_decal/siding/thinplating_new{ dir = 4 @@ -28098,6 +28114,12 @@ /obj/structure/flora/bush/style_random, /turf/open/floor/grass, /area/station/security/courtroom) +"hAv" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/light/small/directional/east, +/obj/machinery/growing/tray, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) "hAI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, @@ -29873,13 +29895,6 @@ /obj/effect/decal/cleanable/cobweb/cobweb2, /turf/open/floor/catwalk_floor/iron_dark, /area/station/bitrunning/den) -"iaM" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 6 - }, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron, -/area/station/service/hydroponics) "iaS" = ( /obj/effect/turf_decal/siding/wood{ dir = 5 @@ -31959,6 +31974,11 @@ dir = 8 }, /area/station/security/office) +"iGF" = ( +/obj/effect/turf_decal/tile/green/full, +/obj/machinery/growing/tray, +/turf/open/floor/iron, +/area/station/service/hydroponics) "iGG" = ( /obj/structure/chair/office/light{ dir = 8 @@ -32702,11 +32722,6 @@ }, /turf/open/floor/iron, /area/station/hallway/floor2/aft) -"iQF" = ( -/obj/machinery/hydroponics/soil, -/obj/item/plant_analyzer, -/turf/open/misc/dirt/jungle, -/area/station/security/prison/garden) "iQH" = ( /obj/structure/disposalpipe/segment, /turf/open/floor/iron/dark/side, @@ -36697,10 +36712,6 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/floor2/starboard/aft) -"jSa" = ( -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron, -/area/station/maintenance/floor1/port/aft) "jSt" = ( /obj/machinery/computer/prisoner/gulag_teleporter_computer, /obj/effect/turf_decal/tile/red/anticorner/contrasted{ @@ -38763,6 +38774,10 @@ "kvE" = ( /turf/open/floor/light/colour_cycle/dancefloor_b, /area/station/maintenance/floor2/port/fore) +"kvI" = ( +/obj/machinery/growing/tray, +/turf/open/floor/iron, +/area/station/maintenance/floor1/port/aft) "kvR" = ( /obj/structure/disposalpipe/segment{ dir = 10 @@ -38937,19 +38952,6 @@ }, /turf/open/floor/iron/white, /area/station/medical/medbay/central) -"kye" = ( -/obj/machinery/door/airlock/security{ - name = "Perma" - }, -/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ - cycle_id = "perma-entrance" - }, -/obj/effect/mapping_helpers/airlock/access/any/security/brig, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/turf/open/floor/iron/dark, -/area/station/security/prison/visit) "kyj" = ( /obj/effect/turf_decal/loading_area/white{ color = "#52B4E9"; @@ -39153,18 +39155,6 @@ /obj/item/kitchen/rollingpin, /turf/open/floor/iron/kitchen, /area/station/service/kitchen) -"kAR" = ( -/obj/machinery/door/airlock/security{ - name = "Perma" - }, -/obj/effect/mapping_helpers/airlock/access/any/security/brig, -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/tile/dark_red/fourcorners, -/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ - cycle_id = "perma-entrance" - }, -/turf/open/floor/iron/dark, -/area/station/security/prison/visit) "kAW" = ( /obj/structure/window/reinforced/plasma/spawner/directional/north, /obj/structure/window/reinforced/plasma/spawner/directional/east, @@ -39652,12 +39642,6 @@ name = "lab floor" }, /area/station/science/robotics/lab) -"kHN" = ( -/obj/effect/turf_decal/tile/purple, -/obj/machinery/camera/autoname/directional/east, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/white, -/area/station/cargo/miningdock) "kHO" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, @@ -40249,6 +40233,16 @@ dir = 4 }, /area/station/commons/storage/primary) +"kQc" = ( +/obj/machinery/door/airlock/security{ + name = "Gulag" + }, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/mapping_helpers/airlock/access/all/security/brig, +/turf/open/floor/iron/dark, +/area/station/security/execution/transfer) "kQk" = ( /turf/open/floor/iron/dark/side{ dir = 4 @@ -40556,6 +40550,11 @@ /obj/structure/sign/poster/random/directional/west, /turf/open/floor/iron, /area/station/hallway/floor3/aft) +"kTN" = ( +/obj/machinery/growing/soil, +/obj/item/plant_analyzer, +/turf/open/misc/dirt/jungle, +/area/station/security/prison/garden) "kTZ" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -44974,6 +44973,12 @@ }, /turf/open/floor/iron/dark/side, /area/station/command/teleporter) +"lYZ" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/camera/autoname/directional/west, +/obj/machinery/growing/tray, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) "lZa" = ( /obj/structure/rack, /obj/item/clothing/suit/armor/vest/old, @@ -50302,11 +50307,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/science/breakroom) -"nof" = ( -/obj/item/seeds/coffee, -/obj/machinery/hydroponics/soil, -/turf/open/misc/sandy_dirt, -/area/station/maintenance/floor3/starboard) "noh" = ( /obj/effect/turf_decal/tile/yellow/opposingcorners, /obj/structure/cable, @@ -53428,6 +53428,12 @@ }, /turf/open/floor/iron/dark, /area/station/science/ordnance/testlab) +"obJ" = ( +/obj/effect/turf_decal/tile/purple, +/obj/machinery/camera/autoname/directional/east, +/obj/machinery/growing/tray, +/turf/open/floor/iron/white, +/area/station/cargo/miningdock) "obK" = ( /obj/machinery/power/apc/auto_name/directional/east, /obj/structure/cable, @@ -55123,12 +55129,6 @@ /obj/effect/turf_decal/tile/red/fourcorners, /turf/open/floor/iron/dark, /area/station/ai_monitored/security/armory) -"ozF" = ( -/obj/effect/turf_decal/bot, -/obj/machinery/light/small/directional/east, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics/garden) "ozJ" = ( /obj/structure/disposalpipe/junction/yjunction{ dir = 8 @@ -56474,6 +56474,12 @@ }, /turf/open/floor/pod/dark, /area/station/maintenance/floor2/starboard) +"oSM" = ( +/obj/machinery/growing/soil, +/obj/item/cultivator, +/obj/structure/sign/poster/official/random/directional/north, +/turf/open/misc/dirt/jungle, +/area/station/security/prison/garden) "oSQ" = ( /obj/machinery/portable_atmospherics/canister/carbon_dioxide, /obj/effect/turf_decal/bot, @@ -56571,6 +56577,11 @@ /obj/structure/cable, /turf/open/floor/wood, /area/station/command/meeting_room) +"oTB" = ( +/obj/machinery/growing/soil, +/obj/item/radio/intercom/prison/directional/west, +/turf/open/misc/dirt/jungle, +/area/station/security/prison/garden) "oTF" = ( /obj/item/storage/secure/safe/directional/east, /obj/structure/cable, @@ -58142,6 +58153,18 @@ dir = 1 }, /area/station/hallway/secondary/exit/departure_lounge) +"psV" = ( +/obj/machinery/door/airlock/security{ + name = "Perma" + }, +/obj/machinery/door/firedoor, +/obj/effect/turf_decal/tile/dark_red/fourcorners, +/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ + cycle_id = "perma-entrance" + }, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/dark, +/area/station/security/prison/visit) "ptc" = ( /obj/effect/decal/cleanable/dirt, /obj/item/shard, @@ -58342,14 +58365,6 @@ /obj/effect/decal/cleanable/dirt/dust, /turf/open/floor/carpet/red, /area/station/maintenance/floor3/port/aft) -"pwE" = ( -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 4 - }, -/obj/machinery/newscaster/directional/east, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron, -/area/station/service/hydroponics) "pwL" = ( /obj/effect/turf_decal/siding/blue{ dir = 4 @@ -59077,11 +59092,6 @@ dir = 4 }, /area/station/command/teleporter) -"pHe" = ( -/obj/structure/flora/bush/sparsegrass/style_random, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/grass, -/area/station/service/hydroponics/garden/abandoned) "pHf" = ( /obj/machinery/holopad, /obj/effect/turf_decal/trimline/green, @@ -60542,16 +60552,6 @@ /obj/machinery/holopad, /turf/open/floor/iron, /area/station/service/hydroponics/garden) -"qcP" = ( -/obj/effect/decal/cleanable/dirt, -/obj/effect/turf_decal/weather/dirt{ - dir = 4 - }, -/obj/machinery/hydroponics/constructable{ - anchored = 0 - }, -/turf/open/floor/iron, -/area/station/service/hydroponics/garden/abandoned) "qcQ" = ( /obj/effect/turf_decal/trimline/red/filled/line{ dir = 6 @@ -61054,11 +61054,6 @@ }, /turf/open/floor/iron/dark, /area/station/service/chapel/funeral) -"qkx" = ( -/obj/machinery/newscaster/directional/east, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/white, -/area/station/cargo/miningdock) "qkz" = ( /obj/effect/turf_decal/delivery, /obj/machinery/door/airlock/command/glass{ @@ -61791,14 +61786,6 @@ }, /turf/open/floor/iron/dark, /area/station/ai_monitored/security/armory) -"qtR" = ( -/obj/structure/flora/bush/sparsegrass/style_random, -/obj/machinery/light/broken/directional/south, -/obj/machinery/hydroponics/constructable{ - anchored = 0 - }, -/turf/open/floor/grass, -/area/station/service/hydroponics/garden/abandoned) "qtS" = ( /obj/effect/turf_decal/tile/red/half, /turf/open/floor/iron/dark, @@ -62650,7 +62637,7 @@ /area/station/science/xenobiology) "qCZ" = ( /obj/structure/table/reinforced, -/mob/living/simple_animal/bot/medbot/stationary{ +/mob/living/basic/bot/medbot/stationary{ name = "Doctor Oktoberfest"; desc = "A little medical robot. He helps keep the individual you're performing surgery on alive" }, @@ -65189,7 +65176,7 @@ codes_txt = "patrol;next_patrol=4-1"; location = "4-0" }, -/mob/living/simple_animal/bot/medbot/autopatrol, +/mob/living/basic/bot/medbot/autopatrol, /turf/open/floor/iron/dark/side{ dir = 9 }, @@ -65803,6 +65790,15 @@ }, /turf/open/floor/plating, /area/station/cargo/office) +"rvf" = ( +/obj/machinery/growing/soil, +/obj/machinery/camera/directional/west{ + c_tag = "Prison Forestry"; + network = list("ss13","prison") + }, +/obj/machinery/light/directional/west, +/turf/open/misc/dirt/jungle, +/area/station/security/prison/garden) "rvh" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, @@ -66026,11 +66022,6 @@ /obj/machinery/flasher/directional/west, /turf/open/floor/iron/dark, /area/station/hallway/floor4/fore) -"ryQ" = ( -/obj/effect/decal/cleanable/dirt/dust, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/plating, -/area/station/maintenance/floor4/starboard) "rza" = ( /obj/effect/turf_decal/trimline/brown/warning{ dir = 9 @@ -67866,6 +67857,14 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/floor2/starboard/aft) +"sct" = ( +/obj/structure/flora/bush/sparsegrass/style_random, +/obj/machinery/light/broken/directional/south, +/obj/machinery/growing/tray{ + anchored = 0 + }, +/turf/open/floor/grass, +/area/station/service/hydroponics/garden/abandoned) "scv" = ( /turf/closed/wall/r_wall, /area/station/maintenance/disposal) @@ -68393,6 +68392,15 @@ /obj/structure/bookcase/random/fiction, /turf/open/floor/wood/tile, /area/station/service/library) +"slE" = ( +/obj/machinery/power/apc/auto_name/directional/south, +/obj/structure/cable, +/obj/machinery/airalarm/directional/east, +/obj/effect/decal/cleanable/dirt, +/obj/item/seeds/apple, +/obj/machinery/growing/tray, +/turf/open/floor/grass, +/area/station/service/library/garden) "slP" = ( /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, @@ -68664,6 +68672,12 @@ /obj/machinery/door/firedoor, /turf/open/floor/iron/dark/smooth_large, /area/station/hallway/secondary/entry) +"sqD" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/light_switch/directional/east, +/obj/machinery/growing/tray, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) "sqK" = ( /obj/structure/railing{ dir = 4 @@ -68674,6 +68688,14 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /turf/open/floor/iron, /area/station/cargo/storage) +"srk" = ( +/obj/effect/turf_decal/tile/purple{ + dir = 8 + }, +/obj/machinery/airalarm/directional/south, +/obj/machinery/growing/tray, +/turf/open/floor/iron/white, +/area/station/cargo/miningdock) "srq" = ( /obj/effect/turf_decal/trimline/blue/corner{ dir = 8 @@ -70164,6 +70186,11 @@ /obj/machinery/light/small/directional/west, /turf/open/floor/iron/dark, /area/station/security/brig) +"sLH" = ( +/obj/machinery/growing/soil, +/obj/item/shovel/spade, +/turf/open/misc/dirt/jungle, +/area/station/security/prison/garden) "sLI" = ( /obj/item/radio/intercom/directional/north, /turf/open/floor/iron/dark/side{ @@ -72985,14 +73012,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark, /area/station/maintenance/floor2/starboard/aft) -"tzD" = ( -/obj/structure/flora/bush/sparsegrass/style_random, -/obj/structure/flora/bush/lavendergrass/style_random, -/obj/machinery/hydroponics/constructable{ - anchored = 0 - }, -/turf/open/floor/grass, -/area/station/service/hydroponics/garden/abandoned) "tzQ" = ( /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 9 @@ -73819,6 +73838,12 @@ /obj/structure/filingcabinet/chestdrawer, /turf/open/floor/iron/dark/smooth_large, /area/station/service/library/printer) +"tLV" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/light/small/directional/west, +/obj/machinery/growing/tray, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) "tMd" = ( /obj/effect/turf_decal/siding/wood/corner{ dir = 1 @@ -74545,22 +74570,6 @@ }, /turf/open/floor/iron, /area/station/cargo/miningdock) -"tXR" = ( -/obj/machinery/button/door/directional/north{ - id = "visitation"; - name = "Visitation Shutters"; - pixel_x = 6; - req_access = list("brig") - }, -/obj/machinery/button/flasher{ - id = "visitorflash"; - pixel_x = -6; - pixel_y = 24 - }, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, -/obj/machinery/airalarm/directional/west, -/turf/open/floor/iron/dark, -/area/station/security/prison/visit) "tXV" = ( /obj/structure/sign/poster/official/random/directional/south, /turf/open/floor/wood/tile, @@ -75283,12 +75292,6 @@ }, /turf/open/openspace, /area/station/maintenance/floor3/port) -"uiE" = ( -/obj/structure/cable, -/obj/effect/turf_decal/tile/green/full, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron, -/area/station/service/hydroponics) "uiH" = ( /obj/structure/window/reinforced/spawner/directional/south, /obj/structure/chair/sofa/bench/right{ @@ -77615,11 +77618,6 @@ /obj/effect/turf_decal/tile/blue/fourcorners, /turf/open/floor/iron/white/small, /area/station/medical/chemistry) -"uRy" = ( -/obj/machinery/hydroponics/soil, -/obj/item/radio/intercom/prison/directional/west, -/turf/open/misc/dirt/jungle, -/area/station/security/prison/garden) "uRE" = ( /obj/machinery/door/airlock/engineering{ name = "Auxiliary Base Supplies" @@ -79498,6 +79496,11 @@ /obj/structure/marker_beacon/burgundy, /turf/open/floor/pod/light, /area/station/maintenance/floor4/starboard/aft) +"vqT" = ( +/obj/effect/decal/cleanable/dirt/dust, +/obj/machinery/growing/tray, +/turf/open/floor/plating, +/area/station/maintenance/floor4/starboard) "vrh" = ( /obj/effect/turf_decal/stripes/white/line{ dir = 4 @@ -80731,11 +80734,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/disposal/incinerator) -"vHE" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/iron, -/area/station/maintenance/floor1/port/aft) "vHI" = ( /obj/effect/spawner/random/trash/mess, /obj/effect/decal/cleanable/dirt, @@ -82205,6 +82203,17 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/iron/dark, /area/station/security/execution/education) +"wcP" = ( +/obj/machinery/door/airlock/security/glass{ + name = "Permabrig Visitation" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/door/firedoor, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/dark, +/area/station/security/prison/visit) "wcQ" = ( /obj/machinery/disposal/bin, /obj/structure/disposalpipe/trunk{ @@ -82975,17 +82984,6 @@ }, /turf/open/floor/pod/dark, /area/station/maintenance/floor1/port) -"wmI" = ( -/obj/machinery/door/airlock/security{ - name = "Isolation" - }, -/obj/effect/mapping_helpers/airlock/access/any/security/brig, -/obj/machinery/door/firedoor, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/turf_decal/tile/dark_red/fourcorners, -/turf/open/floor/iron, -/area/station/security/brig) "wmQ" = ( /obj/structure/window/reinforced/spawner/directional/east, /obj/effect/turf_decal/trimline/blue/filled/line{ @@ -85893,7 +85891,7 @@ /area/station/maintenance/floor2/starboard) "wXy" = ( /obj/machinery/airalarm/directional/north, -/mob/living/simple_animal/bot/cleanbot, +/mob/living/basic/bot/cleanbot, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat_interior) "wXE" = ( @@ -86120,12 +86118,6 @@ /obj/effect/landmark/start/hangover, /turf/open/floor/iron/white, /area/station/hallway/floor2/fore) -"xaW" = ( -/obj/machinery/hydroponics/soil, -/obj/item/cultivator, -/obj/structure/sign/poster/official/random/directional/north, -/turf/open/misc/dirt/jungle, -/area/station/security/prison/garden) "xaY" = ( /obj/structure/closet/crate/hydroponics, /obj/item/wirecutters, @@ -86756,17 +86748,6 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos/pumproom) -"xiS" = ( -/obj/machinery/door/airlock/security/glass{ - name = "Permabrig Visitation" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/firedoor, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/structure/cable, -/turf/open/floor/iron/dark, -/area/station/security/prison/visit) "xjc" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /obj/effect/turf_decal/tile/red/anticorner{ @@ -87167,15 +87148,6 @@ /obj/effect/mapping_helpers/airlock/access/all/science/ordnance, /turf/open/floor/engine/vacuum, /area/station/science/ordnance/burnchamber) -"xpO" = ( -/obj/machinery/power/apc/auto_name/directional/south, -/obj/structure/cable, -/obj/machinery/airalarm/directional/east, -/obj/effect/decal/cleanable/dirt, -/obj/item/seeds/apple, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/grass, -/area/station/service/library/garden) "xpQ" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -89140,6 +89112,16 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/catwalk_floor, /area/station/maintenance/floor1/port) +"xSm" = ( +/obj/effect/decal/cleanable/dirt, +/obj/effect/turf_decal/weather/dirt{ + dir = 4 + }, +/obj/machinery/growing/tray{ + anchored = 0 + }, +/turf/open/floor/iron, +/area/station/service/hydroponics/garden/abandoned) "xSn" = ( /obj/structure/table/reinforced, /obj/item/restraints/handcuffs/cable/zipties/used, @@ -89308,6 +89290,19 @@ /obj/item/radio/intercom/directional/south, /turf/open/floor/iron/dark, /area/station/security/holding_cell) +"xVM" = ( +/obj/machinery/door/airlock/security{ + name = "Perma" + }, +/obj/effect/mapping_helpers/airlock/cyclelink_helper_multi{ + cycle_id = "perma-entrance" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/dark, +/area/station/security/prison/visit) "xVV" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -90057,6 +90052,11 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/security/prison) +"yfE" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/growing/tray, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics/garden) "yfH" = ( /obj/machinery/door/airlock/engineering/glass{ name = "Engineering" @@ -121185,7 +121185,7 @@ yiw lIU czK bBb -eNN +srk dpH vOK vOK @@ -121954,9 +121954,9 @@ pGW jfd yiw xwi -qkx +hml vcM -kHN +obJ dpH pCG cax @@ -130458,7 +130458,7 @@ mRq lwW lwW rsa -pHe +bVa xgH xgH xgH @@ -130975,7 +130975,7 @@ lwW lwW yey lwW -qtR +sct xgH hdA duP @@ -131224,7 +131224,7 @@ rjD xgH maL xgH -qcP +xSm fVf mRq lAH @@ -131484,7 +131484,7 @@ xgH xgH xgH xgH -tzD +eJY xkN nzK yey @@ -137649,7 +137649,7 @@ dEc ggX uov vcr -vHE +gvy khc arn vcr @@ -137906,7 +137906,7 @@ dEc dEc rJO vcr -jSa +kvI khc gNs vcr @@ -138163,7 +138163,7 @@ uyC emq sJp vcr -jSa +kvI lDI bjk vcr @@ -178508,9 +178508,9 @@ bkj ccP cvP dxG -gOd -fnB -gbW +lYZ +tLV +gVI pWf hLz uXA @@ -179279,9 +179279,9 @@ cIl dWl cEb dFy -eAv -gfI -ozF +yfE +sqD +hAv ksr hLz jRy @@ -186709,9 +186709,9 @@ rkM iOA crl vnK -akl +dwz nHd -ghz +dFS cnA ukm nqo @@ -198018,7 +198018,7 @@ deG oYb pKg xRp -xpO +slE amt vUc evJ @@ -248668,9 +248668,9 @@ nCL qBx oNs akD -fNg +iGF sWw -fNg +iGF nBC mOh qBx @@ -248925,9 +248925,9 @@ dFd nIv pjh ixH -fNg +iGF sWw -uiE +cdg uYg fDF jZP @@ -249439,15 +249439,15 @@ toO ouZ uOl gAe -fNg +iGF mZy -fNg +iGF rRX cSf qBx dFN -pwE -iaM +eFL +efo anb tGn tGn @@ -249696,9 +249696,9 @@ dFd nIv dPv grb -fNg +iGF mZy -fNg +iGF nBC vyH qBx @@ -250210,13 +250210,13 @@ dFd qBx qUs grb -fNg +iGF tff -fNg +iGF nBC -fNg +iGF grb -fNg +iGF tuH smi dLV @@ -250467,13 +250467,13 @@ tQF qBx qBx grb -fNg +iGF tff -fNg +iGF nBC -fNg +iGF grb -fNg +iGF mFB smi xpR @@ -250728,9 +250728,9 @@ brO ruS umG yhz -fNg +iGF xmM -fNg +iGF nGG rhu aPH @@ -258421,7 +258421,7 @@ isU qrd uFc pOZ -nof +bbG qrd rfU rfU @@ -314955,7 +314955,7 @@ gDy qFM bWn neJ -ryQ +vqT gDy uDE nPE @@ -321670,7 +321670,7 @@ qFI qFI qFI qFI -wmI +fDp xZL fbo kTI @@ -322444,7 +322444,7 @@ gDx xTw tBk edj -kAR +psV tBk ycM nCA @@ -322960,7 +322960,7 @@ tBk qTA lwY lwY -cVz +kQc fCA foF rbr @@ -323982,9 +323982,9 @@ fCz obT wNR tBk -tXR +feP aDD -xiS +wcP gqV iyR rKX @@ -324756,7 +324756,7 @@ tBk lmk eIq hMd -kye +xVM waJ rbr rbr @@ -327314,11 +327314,11 @@ kgW eYj wMF wMF -xaW -iQF -dAr -uRy -boB +oSM +kTN +rvf +oTB +sLH wQo xOF fZg diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index e5b29c800e85..3878822fd60d 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -37,6 +37,10 @@ }, /turf/open/floor/wood/large, /area/centcom/central_command_areas/kitchen) +"ag" = ( +/obj/structure/hive_exit, +/turf/closed/indestructible/hive, +/area/station/hive/one) "ah" = ( /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 10 @@ -216,7 +220,7 @@ /turf/open/floor/iron/dark/herringbone, /area/centcom/central_command_areas/supply) "aG" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/blue, /turf/open/floor/iron/dark, /area/centcom/central_command_areas/botany) @@ -693,13 +697,6 @@ /obj/structure/railing, /turf/open/lava/plasma/ice_moon, /area/centcom/syndicate_mothership/control) -"bS" = ( -/obj/structure/hedge, -/obj/effect/turf_decal/siding/wood/corner{ - dir = 4 - }, -/turf/open/floor/wood/large, -/area/centcom/central_command_areas/ghost_spawn) "bT" = ( /obj/effect/turf_decal/stripes/corner{ dir = 8 @@ -772,12 +769,7 @@ /obj/effect/turf_decal/siding/wood{ dir = 1 }, -/obj/machinery/hydroponics/constructable{ - self_growing = 1; - self_sustaining = 1; - sustaining_precent = 100; - multi = 15 - }, +/obj/machinery/growing/tray, /turf/open/floor/wood/large, /area/centcom/central_command_areas/botany) "cd" = ( @@ -1363,6 +1355,11 @@ }, /turf/open/floor/carpet, /area/centcom/syndicate_mothership/control) +"dG" = ( +/obj/effect/turf_decal/tile/dark/opposingcorners, +/obj/machinery/light/directional/south, +/turf/open/floor/iron/dark, +/area/centcom/central_command_areas/ghost_spawn) "dH" = ( /obj/effect/turf_decal/stripes/line{ dir = 5 @@ -1443,7 +1440,7 @@ dir = 4 }, /turf/open/floor/glass/reinforced, -/area/centcom/central_command_areas/ghost_spawn) +/area/centcom/central_command_areas/hall) "dP" = ( /obj/machinery/dna_scannernew, /turf/open/floor/circuit, @@ -1574,7 +1571,7 @@ /obj/effect/turf_decal/siding/wood{ dir = 1 }, -/obj/machinery/hydroponics, +/obj/machinery/growing, /obj/item/seeds/pumpkin, /turf/open/floor/carpet/red, /area/centcom/central_command_areas/admin) @@ -1672,7 +1669,7 @@ /turf/open/floor/iron/dark, /area/centcom/tdome/observation) "ex" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/mineral/titanium/tiled, /area/centcom/syndicate_mothership/expansion_bioterrorism) "ey" = ( @@ -2138,10 +2135,6 @@ /obj/machinery/light/directional/east, /turf/open/floor/iron/dark/herringbone, /area/centcom/central_command_areas/hall) -"fZ" = ( -/obj/effect/landmark/ctf, -/turf/open/space/basic, -/area/space) "ga" = ( /obj/machinery/atmospherics/pipe/heat_exchanging/simple, /turf/closed/indestructible/syndicate, @@ -2177,11 +2170,6 @@ /obj/machinery/computer/records/security/laptop, /turf/open/floor/iron/dark, /area/centcom/central_command_areas/admin) -"gg" = ( -/obj/structure/hedge, -/obj/effect/turf_decal/siding/wood/corner, -/turf/open/floor/wood/large, -/area/centcom/central_command_areas/ghost_spawn) "gh" = ( /obj/structure/sign/poster/contraband/revolver{ pixel_y = -32 @@ -2768,11 +2756,6 @@ /obj/item/clipboard, /turf/open/indestructible/hotelwood, /area/centcom/central_command_areas/admin) -"hL" = ( -/obj/structure/flora/tree/jungle/style_3, -/obj/machinery/light/floor/has_bulb, -/turf/open/floor/grass, -/area/centcom/central_command_areas/ghost_spawn) "hM" = ( /obj/effect/turf_decal/siding/wood/corner{ dir = 4 @@ -2820,7 +2803,7 @@ /area/centcom/central_command_areas/admin) "hS" = ( /obj/structure/window/reinforced/survival_pod/spawner/directional/east, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/mineral/titanium/tiled, /area/centcom/syndicate_mothership/expansion_bioterrorism) "hT" = ( @@ -3241,7 +3224,7 @@ /area/centcom/syndicate_mothership/control) "jf" = ( /obj/machinery/light/floor/has_bulb, -/obj/machinery/hydroponics/constructable/helper, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/green/line{ dir = 8 }, @@ -3599,7 +3582,7 @@ /turf/open/floor/iron/dark, /area/centcom/central_command_areas/botany) "kh" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/green/line{ dir = 5 }, @@ -4091,7 +4074,7 @@ dir = 4 }, /turf/open/floor/glass/reinforced, -/area/centcom/central_command_areas/ghost_spawn) +/area/centcom/central_command_areas/hall) "lB" = ( /obj/effect/turf_decal/siding/wideplating{ dir = 1 @@ -4236,6 +4219,9 @@ /obj/machinery/light/floor/has_bulb, /turf/open/floor/mineral/plastitanium, /area/centcom/syndicate_mothership/control) +"lV" = ( +/turf/open/indestructible/hive, +/area/station/hive/four) "lW" = ( /obj/effect/turf_decal/siding/thinplating_new/dark, /obj/machinery/camera/autoname/directional/south{ @@ -4740,13 +4726,6 @@ /obj/structure/closet/crate/cardboard/mothic, /turf/open/floor/plating, /area/centcom/syndicate_mothership/control) -"np" = ( -/obj/structure/hedge, -/obj/effect/turf_decal/siding/wood/corner{ - dir = 8 - }, -/turf/open/floor/wood/large, -/area/centcom/central_command_areas/ghost_spawn) "nq" = ( /obj/structure/stone_tile/slab, /turf/open/misc/snow/actually_safe, @@ -4877,7 +4856,7 @@ dir = 1 }, /turf/open/floor/glass/reinforced, -/area/centcom/central_command_areas/ghost_spawn) +/area/centcom/central_command_areas/hall) "nO" = ( /obj/structure/cannon{ dir = 4; @@ -5261,6 +5240,9 @@ }, /turf/open/floor/mineral/plastitanium, /area/centcom/syndicate_mothership/control) +"oR" = ( +/turf/closed/indestructible/hive, +/area/station/hive/three) "oS" = ( /obj/effect/turf_decal/siding/thinplating_new/dark, /turf/open/floor/mineral/plastitanium, @@ -6279,6 +6261,9 @@ /obj/effect/turf_decal/trimline/red, /turf/open/floor/mineral/plastitanium, /area/centcom/syndicate_mothership/expansion_chemicalwarfare) +"rD" = ( +/turf/closed/indestructible/hive, +/area/station/hive/two) "rE" = ( /obj/structure/table/reinforced, /obj/item/radio, @@ -6482,7 +6467,7 @@ /turf/open/floor/mineral/titanium/white, /area/centcom/central_command_areas/admin) "si" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/light/cold/directional/west, /obj/item/seeds/cannabis{ pixel_y = 8 @@ -6713,6 +6698,10 @@ /obj/structure/closet/crate/cardboard, /turf/open/floor/plating, /area/centcom/syndicate_mothership/control) +"sO" = ( +/obj/effect/turf_decal/tile/dark/opposingcorners, +/turf/open/floor/iron/dark, +/area/centcom/central_command_areas/ghost_spawn) "sP" = ( /obj/structure/hedge, /obj/effect/turf_decal/siding/dark{ @@ -7386,7 +7375,7 @@ "uC" = ( /obj/structure/railing/wood, /turf/open/floor/glass/reinforced, -/area/centcom/central_command_areas/ghost_spawn) +/area/centcom/central_command_areas/hall) "uD" = ( /obj/structure/table/wood, /obj/machinery/computer/records/medical/laptop, @@ -8864,7 +8853,7 @@ /turf/open/floor/wood, /area/centcom/wizard_station) "yG" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/green/line{ dir = 10 }, @@ -9366,7 +9355,7 @@ dir = 8 }, /turf/open/floor/glass/reinforced, -/area/centcom/central_command_areas/ghost_spawn) +/area/centcom/central_command_areas/hall) "Ac" = ( /obj/effect/turf_decal/siding/dark{ dir = 4 @@ -9964,6 +9953,10 @@ /obj/structure/fake_stairs/wood/directional/south, /turf/open/floor/wood/large, /area/centcom/central_command_areas/kitchen) +"BF" = ( +/obj/structure/hive_exit, +/turf/closed/indestructible/hive, +/area/station/hive/two) "BG" = ( /obj/structure/fence{ dir = 4 @@ -10055,7 +10048,7 @@ /turf/open/floor/grass, /area/centcom/central_command_areas/evacuation/ship) "BS" = ( -/mob/living/simple_animal/bot/medbot/mysterious{ +/mob/living/basic/bot/medbot/mysterious{ desc = "If you don't accidentally blow yourself up from time to time you're not really a wizard anyway."; faction = list("neutral","silicon","creature"); name = "Nobody's Perfect" @@ -10110,6 +10103,9 @@ }, /turf/open/floor/wood/parquet, /area/centcom/central_command_areas/borbop) +"BZ" = ( +/turf/open/indestructible/hive, +/area/station/hive/one) "Cb" = ( /obj/effect/turf_decal/siding/wood, /obj/structure/sign{ @@ -10154,7 +10150,7 @@ dir = 1 }, /turf/open/floor/glass/reinforced, -/area/centcom/central_command_areas/ghost_spawn) +/area/centcom/central_command_areas/hall) "Cj" = ( /obj/effect/turf_decal/siding/dark/corner{ dir = 8 @@ -10727,7 +10723,7 @@ dir = 1 }, /turf/open/floor/glass/reinforced, -/area/centcom/central_command_areas/ghost_spawn) +/area/centcom/central_command_areas/hall) "DS" = ( /obj/effect/turf_decal/siding/thinplating_new/dark{ dir = 1 @@ -10749,6 +10745,9 @@ }, /turf/open/floor/wood/parquet, /area/centcom/central_command_areas/borbop) +"DW" = ( +/turf/open/indestructible/hive, +/area/station/hive/three) "DX" = ( /obj/machinery/light/directional/north, /obj/effect/turf_decal/siding/dark{ @@ -10889,6 +10888,10 @@ }, /turf/open/floor/iron/dark/textured_large, /area/centcom/syndicate_mothership/control) +"Et" = ( +/obj/structure/hive_exit, +/turf/closed/indestructible/hive, +/area/station/hive/three) "Eu" = ( /obj/effect/decal/cleanable/blood/splatter, /obj/effect/decal/cleanable/blood/gibs/body, @@ -11164,7 +11167,7 @@ /area/centcom/central_command_areas/evacuation/ship) "Fg" = ( /obj/machinery/light/floor/has_bulb, -/obj/machinery/hydroponics/constructable/helper, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/green/line{ dir = 4 }, @@ -11273,7 +11276,7 @@ /obj/effect/turf_decal/siding/wood, /obj/structure/centcom_teleporter/spawn_area, /turf/open/floor/wood/large, -/area/centcom/central_command_areas/ghost_spawn) +/area/centcom/central_command_areas/hall) "Fz" = ( /turf/open/floor/mineral/plastitanium/red, /area/centcom/syndicate_mothership) @@ -11777,12 +11780,7 @@ /obj/effect/turf_decal/siding/wood{ dir = 5 }, -/obj/machinery/hydroponics/constructable{ - self_growing = 1; - self_sustaining = 1; - sustaining_precent = 100; - multi = 15 - }, +/obj/machinery/growing/tray, /turf/open/floor/wood/large, /area/centcom/central_command_areas/botany) "GS" = ( @@ -12043,6 +12041,9 @@ }, /turf/open/floor/iron/dark/herringbone, /area/centcom/central_command_areas/borbop) +"HE" = ( +/turf/open/indestructible/hive, +/area/station/hive/two) "HF" = ( /obj/structure/flora/rock/icy/style_random, /turf/open/misc/asteroid/snow/airless, @@ -12722,7 +12723,7 @@ /turf/open/floor/iron/dark/textured_large, /area/centcom/central_command_areas/evacuation/ship) "JF" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/iron/dark, /area/centcom/central_command_areas/botany) "JG" = ( @@ -12912,13 +12913,6 @@ }, /turf/open/floor/iron/dark/textured_large, /area/centcom/syndicate_mothership/control) -"Kg" = ( -/obj/structure/hedge, -/obj/effect/turf_decal/siding/wood/corner{ - dir = 1 - }, -/turf/open/floor/wood/large, -/area/centcom/central_command_areas/ghost_spawn) "Kh" = ( /obj/effect/turf_decal/siding/dark{ dir = 10 @@ -13805,7 +13799,7 @@ /turf/closed/indestructible/riveted/uranium, /area/centcom/wizard_station) "Mt" = ( -/mob/living/simple_animal/bot/cleanbot/medbay, +/mob/living/basic/bot/cleanbot/medbay, /obj/machinery/light/floor/has_bulb, /turf/open/floor/iron/dark, /area/centcom/central_command_areas/medical) @@ -14263,7 +14257,7 @@ /turf/open/floor/stone, /area/centcom/central_command_areas/evacuation/ship) "NJ" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/green/line{ dir = 6 }, @@ -14302,6 +14296,9 @@ /obj/machinery/door/airlock/centcom, /turf/open/floor/wood/large, /area/centcom/central_command_areas/evacuation) +"NP" = ( +/turf/closed/indestructible/hive, +/area/station/hive/one) "NQ" = ( /obj/structure/bookcase/random, /obj/machinery/light/directional/north, @@ -14343,6 +14340,10 @@ }, /turf/open/floor/wood/large, /area/centcom/central_command_areas/hall) +"NX" = ( +/obj/structure/hive_exit, +/turf/closed/indestructible/hive, +/area/station/hive/four) "NY" = ( /obj/effect/turf_decal/siding/wideplating/dark, /obj/effect/turf_decal/siding/wideplating/dark{ @@ -14547,7 +14548,7 @@ dir = 8 }, /turf/open/floor/glass/reinforced, -/area/centcom/central_command_areas/ghost_spawn) +/area/centcom/central_command_areas/hall) "OC" = ( /obj/structure/table/reinforced, /obj/item/toy/plush/space_lizard_plushie{ @@ -15622,7 +15623,7 @@ /obj/machinery/light/floor/has_bulb, /obj/structure/flora/bush/large/style_3, /turf/open/floor/grass, -/area/centcom/central_command_areas/ghost_spawn) +/area/centcom/central_command_areas/hall) "RF" = ( /obj/structure/table/reinforced, /obj/item/crowbar/red, @@ -16387,7 +16388,7 @@ /turf/open/floor/iron, /area/centcom/central_command_areas/ferry) "TL" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/misc/snow/actually_safe, /area/centcom/central_command_areas/admin) "TM" = ( @@ -16684,7 +16685,7 @@ /turf/closed/indestructible/syndicate, /area/centcom/syndicate_mothership/control) "UF" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/turf_decal/trimline/green/line{ dir = 9 }, @@ -17063,6 +17064,9 @@ }, /turf/open/floor/wood/large, /area/centcom/central_command_areas/borbop) +"VG" = ( +/turf/closed/indestructible/hive, +/area/station/hive/four) "VH" = ( /obj/effect/turf_decal/siding/wood{ dir = 5 @@ -17410,12 +17414,7 @@ /obj/effect/turf_decal/siding/wood{ dir = 9 }, -/obj/machinery/hydroponics/constructable{ - self_growing = 1; - self_sustaining = 1; - sustaining_precent = 100; - multi = 15 - }, +/obj/machinery/growing/tray, /turf/open/floor/wood/large, /area/centcom/central_command_areas/botany) "WK" = ( @@ -61184,13 +61183,13 @@ An xT bw dE -rj -rj +sO +sO nk iu Xc -rj -rj +sO +sO Of xu VF @@ -61441,13 +61440,13 @@ An An in mQ -rj +sO uA -Kg +TW RE -np +FK nF -io +dG wc hE lI @@ -61958,7 +61957,7 @@ BE nk wH DR -hL +YF uC wH Fx @@ -62469,13 +62468,13 @@ An An DO mQ -rj +sO zJ -bS +Tr RE -gg +kO nR -io +dG wc hE lI @@ -62726,13 +62725,13 @@ An xo nd dE -rj -rj +sO +sO nk iu Xc -rj -rj +sO +sO eP Ut fe @@ -66727,7 +66726,7 @@ aa aa aa aa -fZ +aa aa aa aa @@ -80278,21 +80277,21 @@ aa aa "} (241,1,1) = {" +NP +NP +NP +ag +NP +NP +NP aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +rD +rD +rD +BF +rD +rD +rD aa aa aa @@ -80535,21 +80534,21 @@ aa aa "} (242,1,1) = {" +NP +BZ +BZ +BZ +BZ +BZ +NP aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +rD +HE +HE +HE +HE +HE +rD aa aa aa @@ -80792,21 +80791,21 @@ aa aa "} (243,1,1) = {" +NP +BZ +BZ +BZ +BZ +BZ +NP aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +rD +HE +HE +HE +HE +HE +rD aa aa aa @@ -81049,21 +81048,21 @@ aa aa "} (244,1,1) = {" +NP +BZ +BZ +BZ +BZ +BZ +NP aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +rD +HE +HE +HE +HE +HE +rD aa aa aa @@ -81306,21 +81305,21 @@ aa aa "} (245,1,1) = {" +NP +BZ +BZ +BZ +BZ +BZ +NP aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +rD +HE +HE +HE +HE +HE +rD aa aa aa @@ -81563,21 +81562,21 @@ aa aa "} (246,1,1) = {" +NP +BZ +BZ +BZ +BZ +BZ +NP aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +rD +HE +HE +HE +HE +HE +rD aa aa aa @@ -81820,21 +81819,21 @@ aa aa "} (247,1,1) = {" +NP +NP +NP +NP +NP +NP +NP aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +rD +rD +rD +rD +rD +rD +rD aa aa aa @@ -82334,21 +82333,21 @@ aa aa "} (249,1,1) = {" +oR +oR +oR +Et +oR +oR +oR aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +VG +VG +VG +NX +VG +VG +VG aa aa aa @@ -82591,21 +82590,21 @@ aa aa "} (250,1,1) = {" +oR +DW +DW +DW +DW +DW +oR aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +VG +lV +lV +lV +lV +lV +VG aa aa aa @@ -82848,21 +82847,21 @@ aa aa "} (251,1,1) = {" +oR +DW +DW +DW +DW +DW +oR aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +VG +lV +lV +lV +lV +lV +VG aa aa aa @@ -83105,21 +83104,21 @@ aa aa "} (252,1,1) = {" +oR +DW +DW +DW +DW +DW +oR aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +VG +lV +lV +lV +lV +lV +VG aa aa aa @@ -83362,21 +83361,21 @@ aa aa "} (253,1,1) = {" +oR +DW +DW +DW +DW +DW +oR aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +VG +lV +lV +lV +lV +lV +VG aa aa aa @@ -83619,21 +83618,21 @@ aa aa "} (254,1,1) = {" +oR +DW +DW +DW +DW +DW +oR aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +VG +lV +lV +lV +lV +lV +VG aa aa aa @@ -83876,21 +83875,21 @@ aa aa "} (255,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +oR +oR +oR +oR +oR +oR +oR +aa +VG +VG +VG +VG +VG +VG +VG aa aa aa diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index a69184242f6e..b6941ca13a96 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -2,16 +2,6 @@ "aaa" = ( /turf/closed/mineral/random/stationside/asteroid/porus, /area/station/asteroid) -"aab" = ( -/obj/structure/cable, -/obj/machinery/door/airlock/external{ - name = "External Access" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/effect/turf_decal/sand/plating, -/obj/effect/mapping_helpers/airlock/cyclelink_helper, -/turf/open/floor/plating, -/area/station/maintenance/central/greater) "aac" = ( /turf/open/misc/asteroid/airless, /area/station/asteroid) @@ -21,18 +11,6 @@ /obj/effect/turf_decal/sand/plating, /turf/open/floor/plating, /area/station/maintenance/central/greater) -"aae" = ( -/obj/structure/cable, -/obj/machinery/door/airlock/external{ - name = "External Access" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/effect/turf_decal/sand/plating, -/obj/effect/mapping_helpers/airlock/cyclelink_helper{ - dir = 1 - }, -/turf/open/floor/plating, -/area/station/maintenance/central/greater) "aaf" = ( /obj/structure/table, /obj/effect/decal/cleanable/dirt, @@ -824,7 +802,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/turf_decal/trimline/white/warning, -/mob/living/simple_animal/bot/medbot/autopatrol{ +/mob/living/basic/bot/medbot/autopatrol{ desc = "A Trauma Response Activation Medibot. It seems overwhelmed."; name = "T.R.A.M Unit" }, @@ -1169,18 +1147,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron, /area/station/escapepodbay) -"adw" = ( -/obj/machinery/hydroponics/soil, -/obj/machinery/power/apc/auto_name/directional/east, -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 6 - }, -/obj/structure/cable, -/obj/effect/turf_decal/trimline/neutral/filled/corner{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/security/prison/garden) "adx" = ( /obj/machinery/power/apc/auto_name/directional/west, /obj/effect/turf_decal/trimline/blue/filled/corner{ @@ -4100,10 +4066,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/disposal) -"azy" = ( -/obj/effect/turf_decal/siding/thinplating, -/turf/open/floor/glass/reinforced, -/area/station/security/brig) "azM" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/trimline/neutral/filled/line, @@ -4405,6 +4367,10 @@ /obj/structure/window/reinforced/spawner/directional/west, /turf/open/floor/circuit, /area/station/ai_monitored/turret_protected/ai) +"aCo" = ( +/obj/machinery/growing/tray, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "aCv" = ( /obj/machinery/mineral/ore_redemption, /obj/machinery/door/firedoor, @@ -7774,6 +7740,17 @@ /obj/machinery/portable_atmospherics/canister/carbon_dioxide, /turf/open/floor/iron/dark, /area/station/science/ordnance/storage) +"bra" = ( +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 1 + }, +/obj/structure/cable, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 4 + }, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron, +/area/station/security/brig) "brm" = ( /turf/open/openspace, /area/station/hallway/primary/tram/right) @@ -9591,6 +9568,19 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/dark, /area/station/service/chapel) +"bUx" = ( +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 4 + }, +/obj/machinery/door/airlock/security/glass{ + name = "Permanent Convict Item Storage" + }, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/dark, +/area/station/security/execution/transfer) "bUT" = ( /obj/structure/chair/comfy/black{ dir = 8 @@ -12806,22 +12796,6 @@ /obj/effect/decal/cleanable/cobweb, /turf/open/floor/iron/smooth, /area/station/maintenance/starboard/greater) -"cXt" = ( -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 4 - }, -/obj/machinery/door/airlock/security/glass{ - name = "Isolation Cell C" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "cXy" = ( /obj/machinery/igniter/incinerator_ordmix, /turf/open/floor/engine/vacuum, @@ -13286,6 +13260,18 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/wood, /area/station/commons/dorms) +"dfk" = ( +/obj/machinery/growing/soil, +/obj/machinery/power/apc/auto_name/directional/east, +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 6 + }, +/obj/structure/cable, +/obj/effect/turf_decal/trimline/neutral/filled/corner{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/security/prison/garden) "dfs" = ( /obj/machinery/door/poddoor/preopen{ id = "atmos"; @@ -14332,6 +14318,11 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/engineering/atmos) +"dxq" = ( +/obj/effect/turf_decal/siding/thinplating, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/glass/reinforced, +/area/station/security/brig) "dxw" = ( /obj/machinery/newscaster/directional/north, /obj/structure/cable, @@ -15954,10 +15945,6 @@ /obj/effect/turf_decal/tile/blue/fourcorners, /turf/open/floor/iron/white, /area/station/medical/treatment_center) -"dXA" = ( -/obj/structure/cable, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "dXC" = ( /obj/structure/window/reinforced/spawner/directional/west, /obj/structure/table/wood/fancy/blue, @@ -16542,18 +16529,6 @@ /obj/effect/turf_decal/stripes/white/line, /turf/open/floor/catwalk_floor/iron_smooth, /area/station/maintenance/tram/left) -"egD" = ( -/obj/machinery/door/airlock/highsecurity{ - name = "Prison Maintenance Access"; - security_level = 4 - }, -/obj/effect/mapping_helpers/airlock/locked, -/obj/effect/decal/cleanable/dirt, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/smooth, -/area/station/maintenance/central/greater) "egJ" = ( /obj/effect/turf_decal/trimline/yellow/warning{ dir = 8 @@ -17309,7 +17284,7 @@ /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 1 }, -/mob/living/simple_animal/bot/cleanbot, +/mob/living/basic/bot/cleanbot, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat_interior) "esl" = ( @@ -18711,14 +18686,6 @@ /obj/structure/cable, /turf/open/floor/catwalk_floor, /area/station/maintenance/port/aft) -"eRw" = ( -/obj/machinery/hydroponics/soil, -/obj/machinery/firealarm/directional/west, -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 9 - }, -/turf/open/floor/iron/dark, -/area/station/security/prison/garden) "eRQ" = ( /obj/effect/turf_decal/trimline/neutral/filled/corner, /obj/machinery/camera{ @@ -18811,6 +18778,14 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/engineering/gravity_generator) +"eTe" = ( +/obj/machinery/growing/soil, +/obj/effect/turf_decal/trimline/green/filled/line, +/obj/effect/turf_decal/trimline/neutral/filled/corner{ + dir = 4 + }, +/turf/open/floor/iron/dark, +/area/station/security/prison/garden) "eTl" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -18960,6 +18935,15 @@ /obj/machinery/light/small/directional/north, /turf/open/floor/iron/smooth, /area/station/maintenance/port/central) +"eVH" = ( +/obj/effect/turf_decal/trimline/red/filled/line, +/obj/structure/cable, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 4 + }, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/iron, +/area/station/security/brig) "eVK" = ( /obj/effect/turf_decal/trimline/yellow/filled/line{ dir = 8 @@ -19118,14 +19102,6 @@ /obj/effect/turf_decal/stripes/white/line, /turf/open/floor/plating, /area/station/maintenance/tram/mid) -"eYu" = ( -/obj/effect/turf_decal/trimline/red/filled/line, -/obj/structure/cable, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/security/brig) "eYM" = ( /obj/effect/turf_decal/siding/thinplating/dark{ dir = 4 @@ -19401,14 +19377,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/dark, /area/station/security/courtroom) -"feC" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/line{ - dir = 1 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "feK" = ( /obj/effect/turf_decal/delivery, /obj/effect/decal/cleanable/dirt, @@ -19779,6 +19747,10 @@ }, /turf/open/floor/iron, /area/station/maintenance/tram/left) +"fjI" = ( +/obj/machinery/growing/tray, +/turf/open/floor/grass, +/area/station/service/hydroponics) "fjN" = ( /obj/effect/turf_decal/trimline/neutral/filled/line{ dir = 4 @@ -20147,6 +20119,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/service/janitor) +"foZ" = ( +/obj/effect/turf_decal/trimline/green/line{ + dir = 1 + }, +/obj/machinery/growing/tray, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "fpf" = ( /obj/structure/flora/bush/leavy/style_random, /obj/structure/flora/tree/palm/style_random, @@ -24203,22 +24182,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/smooth, /area/station/maintenance/department/eva) -"gLa" = ( -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 8 - }, -/obj/machinery/door/airlock/security/glass{ - name = "Isolation Cell B" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "gLd" = ( /obj/machinery/field/generator, /obj/effect/turf_decal/stripes/line, @@ -28869,6 +28832,13 @@ /obj/effect/spawner/random/trash/moisture, /turf/open/floor/iron/smooth, /area/station/maintenance/starboard/greater) +"ipS" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/line{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "ipU" = ( /obj/effect/turf_decal/trimline/purple/filled/line{ dir = 6 @@ -29502,6 +29472,22 @@ }, /turf/open/floor/iron, /area/station/cargo/miningdock) +"iyr" = ( +/obj/machinery/door/firedoor, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 4 + }, +/obj/machinery/door/airlock/security/glass{ + name = "Isolation Cell C" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "iyC" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -29778,22 +29764,6 @@ }, /turf/open/space/basic, /area/space/nearstation) -"iEO" = ( -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 8 - }, -/obj/machinery/door/airlock/security/glass{ - name = "Isolation Cell D" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "iFz" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ @@ -29923,32 +29893,6 @@ /obj/machinery/light/floor/has_bulb, /turf/open/floor/grass, /area/station/service/hydroponics) -"iHG" = ( -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 8 - }, -/obj/structure/sink{ - dir = 4; - pixel_x = -12 - }, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/closet/crate/hydroponics, -/obj/item/seeds/onion, -/obj/item/paper/guides/jobs/hydroponics, -/obj/item/seeds/garlic, -/obj/item/seeds/potato, -/obj/item/seeds/tomato, -/obj/item/seeds/carrot, -/obj/item/seeds/grass, -/obj/item/seeds/ambrosia, -/obj/item/seeds/wheat, -/obj/item/seeds/pumpkin, -/obj/effect/spawner/random/contraband/prison, -/obj/item/seeds/tower, -/turf/open/floor/iron, -/area/station/security/prison/garden) "iHH" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 1 @@ -33188,6 +33132,18 @@ }, /turf/open/floor/iron, /area/station/security/processing) +"jGY" = ( +/obj/machinery/door/firedoor, +/obj/effect/turf_decal/trimline/red/filled/line, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 1 + }, +/obj/machinery/door/airlock/security/glass{ + name = "Prison Wing" + }, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "jHc" = ( /obj/structure/rack, /turf/open/floor/plating, @@ -35631,6 +35587,11 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/tram/left) +"kyb" = ( +/obj/structure/window/reinforced/spawner/directional/north, +/obj/machinery/growing/tray, +/turf/open/floor/grass, +/area/station/service/hydroponics) "kye" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/cafeteria, @@ -35709,6 +35670,18 @@ /obj/effect/turf_decal/sand/plating, /turf/open/floor/plating, /area/station/maintenance/department/cargo) +"kzv" = ( +/obj/machinery/door/airlock/highsecurity{ + name = "Prison Maintenance Access"; + security_level = 4 + }, +/obj/effect/mapping_helpers/airlock/locked, +/obj/effect/decal/cleanable/dirt, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron/smooth, +/area/station/maintenance/central/greater) "kzw" = ( /obj/effect/spawner/random/vending/snackvend, /obj/effect/turf_decal/stripes/line{ @@ -38789,6 +38762,14 @@ }, /turf/open/floor/carpet/royalblack, /area/station/command/meeting_room) +"ltR" = ( +/obj/machinery/growing/tray, +/obj/effect/turf_decal/trimline/green/line{ + dir = 1 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "ltZ" = ( /obj/effect/turf_decal/siding/thinplating/dark{ dir = 5 @@ -39804,16 +39785,6 @@ /obj/structure/fluff/tram_rail, /turf/open/openspace, /area/station/hallway/primary/tram/left) -"lNZ" = ( -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 1 - }, -/obj/structure/cable, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/security/brig) "lOd" = ( /obj/structure/table, /obj/item/storage/portable_chem_mixer, @@ -40014,16 +39985,6 @@ "lQw" = ( /turf/open/misc/asteroid/dug, /area/station/maintenance/starboard/lesser) -"lQx" = ( -/obj/machinery/hydroponics/soil, -/obj/machinery/status_display/ai/directional/south, -/obj/effect/turf_decal/trimline/green/filled/line, -/obj/item/cultivator, -/obj/effect/turf_decal/trimline/neutral/filled/line{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/security/prison/garden) "lQC" = ( /obj/effect/turf_decal/trimline/yellow/filled/line{ dir = 5 @@ -40065,6 +40026,11 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron/smooth, /area/station/hallway/primary/tram/right) +"lRw" = ( +/obj/structure/window/reinforced/spawner/directional/south, +/obj/machinery/growing/tray, +/turf/open/floor/grass, +/area/station/service/hydroponics) "lRC" = ( /obj/effect/turf_decal/trimline/blue/filled/corner, /obj/effect/turf_decal/trimline/neutral/corner, @@ -41546,10 +41512,6 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/tram/center) -"moV" = ( -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "moX" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -41582,10 +41544,6 @@ /obj/effect/landmark/start/hangover, /turf/open/floor/iron, /area/station/hallway/secondary/construction/engineering) -"mpw" = ( -/obj/machinery/hydroponics/soil, -/turf/open/floor/grass, -/area/station/service/hydroponics/garden) "mpz" = ( /obj/machinery/atmospherics/components/binary/pump{ dir = 8; @@ -43747,6 +43705,11 @@ }, /turf/open/floor/iron/white, /area/station/science/ordnance/burnchamber) +"nbL" = ( +/obj/structure/cable, +/mob/living/basic/pet/potty, +/turf/open/floor/iron/dark, +/area/station/service/hydroponics) "nbP" = ( /obj/effect/turf_decal/trimline/yellow/arrow_ccw{ dir = 8 @@ -46121,6 +46084,22 @@ /obj/effect/turf_decal/tile/bar/opposingcorners, /turf/open/floor/iron, /area/station/cargo/miningdock/cafeteria) +"nOP" = ( +/obj/machinery/door/firedoor, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 4 + }, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 8 + }, +/obj/machinery/door/airlock/security/glass{ + name = "Isolation Cell D" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "nPb" = ( /turf/closed/mineral/random/stationside/asteroid/porus, /area/ruin/powered/clownplanet) @@ -46131,6 +46110,16 @@ "nPe" = ( /turf/open/floor/carpet, /area/station/medical/psychology) +"nPx" = ( +/obj/structure/cable, +/obj/machinery/door/airlock/external{ + name = "External Access" + }, +/obj/effect/turf_decal/sand/plating, +/obj/effect/mapping_helpers/airlock/cyclelink_helper, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/plating, +/area/station/maintenance/central/greater) "nPA" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/crate, @@ -46957,6 +46946,14 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/hallway/primary/tram/center) +"odb" = ( +/obj/machinery/growing/soil, +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 10 + }, +/obj/item/plant_analyzer, +/turf/open/floor/iron/dark, +/area/station/security/prison/garden) "odf" = ( /obj/structure/bed{ dir = 4 @@ -47700,15 +47697,6 @@ /obj/machinery/atmospherics/pipe/smart/simple/dark/visible, /turf/open/floor/plating, /area/station/engineering/atmos) -"ork" = ( -/obj/machinery/door/firedoor/border_only{ - dir = 4 - }, -/obj/machinery/door/firedoor/border_only{ - dir = 8 - }, -/turf/open/floor/glass/reinforced, -/area/station/security/brig) "orr" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/table, @@ -47726,6 +47714,18 @@ /obj/item/wallframe/apc, /turf/open/misc/asteroid, /area/station/maintenance/department/security) +"orC" = ( +/obj/machinery/door/airlock/maintenance{ + name = "Permabrig Maintenance" + }, +/obj/machinery/duct, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/catwalk_floor, +/area/station/maintenance/central/greater) "orH" = ( /obj/item/weldingtool/empty, /obj/effect/decal/cleanable/dirt, @@ -47815,16 +47815,6 @@ }, /turf/open/floor/glass/reinforced, /area/station/security/brig) -"otn" = ( -/obj/machinery/hydroponics/soil, -/obj/machinery/status_display/evac/directional/north, -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 1 - }, -/obj/effect/turf_decal/trimline/neutral/filled/corner, -/obj/effect/turf_decal/trimline/neutral/filled/line, -/turf/open/floor/iron/dark, -/area/station/security/prison/garden) "ots" = ( /obj/effect/turf_decal/trimline/yellow/filled/line, /obj/effect/turf_decal/trimline/yellow/filled/corner{ @@ -50684,13 +50674,6 @@ "ptQ" = ( /turf/open/openspace, /area/station/science/genetics) -"ptR" = ( -/obj/effect/turf_decal/trimline/green/line{ - dir = 1 - }, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "ptT" = ( /obj/structure/railing{ dir = 1 @@ -50866,6 +50849,22 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/service) +"pvY" = ( +/obj/machinery/door/firedoor, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 4 + }, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 8 + }, +/obj/machinery/door/airlock/security/glass{ + name = "Isolation Cell B" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "pwd" = ( /obj/structure/cable, /obj/structure/disposalpipe/sorting/mail{ @@ -50956,22 +50955,6 @@ dir = 4 }, /area/station/command/bridge) -"pxj" = ( -/obj/effect/decal/cleanable/dirt, -/obj/structure/table, -/obj/item/stack/cable_coil{ - pixel_x = 3; - pixel_y = -7 - }, -/obj/effect/turf_decal/bot, -/obj/item/stack/sheet/plasteel/fifty, -/obj/effect/decal/cleanable/dirt, -/obj/item/hfr_box/body/fuel_input, -/obj/item/hfr_box/body/interface, -/obj/item/hfr_box/body/moderator_input, -/obj/item/hfr_box/body/waste_output, -/turf/open/floor/iron, -/area/station/engineering/atmospherics_engine) "pxo" = ( /obj/structure/chair/pew, /turf/open/floor/iron/chapel{ @@ -52166,6 +52149,16 @@ /obj/effect/landmark/navigate_destination/chapel, /turf/open/floor/carpet, /area/station/service/chapel) +"pNA" = ( +/obj/machinery/door/firedoor/border_only{ + dir = 4 + }, +/obj/machinery/door/firedoor/border_only{ + dir = 8 + }, +/obj/effect/landmark/start/security_assistant, +/turf/open/floor/glass/reinforced, +/area/station/security/brig) "pNB" = ( /obj/structure/chair/comfy/beige, /obj/effect/landmark/start/hangover, @@ -53483,14 +53476,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/white, /area/station/medical/pathology) -"qjG" = ( -/obj/machinery/hydroponics/soil, -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 10 - }, -/obj/item/plant_analyzer, -/turf/open/floor/iron/dark, -/area/station/security/prison/garden) "qjJ" = ( /obj/machinery/light/small/directional/south, /obj/effect/decal/cleanable/dirt, @@ -53646,6 +53631,10 @@ /obj/effect/turf_decal/trimline/yellow/filled/corner, /turf/open/floor/iron/dark/corner, /area/station/ai_monitored/command/storage/eva) +"qnb" = ( +/obj/machinery/growing/soil, +/turf/open/floor/grass, +/area/station/service/hydroponics/garden) "qnd" = ( /obj/structure/rack, /obj/item/reagent_containers/cup/bottle/iron{ @@ -53696,21 +53685,6 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/entry) -"qnU" = ( -/obj/effect/turf_decal/trimline/red/filled/corner, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 1 - }, -/obj/machinery/button/flasher{ - id = "permafrontdoor"; - pixel_x = 9; - pixel_y = 24; - req_access = list("brig") - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "qnW" = ( /obj/effect/turf_decal/siding/wood{ dir = 1 @@ -54875,6 +54849,19 @@ }, /turf/open/floor/iron, /area/station/security/execution/transfer) +"qGw" = ( +/obj/machinery/growing/soil, +/obj/machinery/airalarm/directional/east, +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 5 + }, +/obj/effect/turf_decal/trimline/neutral/filled/corner, +/obj/item/cultivator, +/obj/effect/turf_decal/trimline/neutral/filled/corner{ + dir = 8 + }, +/turf/open/floor/iron/dark, +/area/station/security/prison/garden) "qGy" = ( /obj/structure/transit_tube/crossing, /turf/open/floor/plating/airless, @@ -56843,14 +56830,6 @@ }, /turf/open/floor/plating, /area/station/science/xenobiology) -"rkP" = ( -/obj/machinery/hydroponics/soil, -/obj/effect/turf_decal/trimline/green/filled/line, -/obj/effect/turf_decal/trimline/neutral/filled/corner{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/security/prison/garden) "rle" = ( /obj/effect/turf_decal/trimline/dark_green/filled/corner{ dir = 1 @@ -57853,19 +57832,6 @@ "rBz" = ( /turf/open/floor/iron, /area/station/security/prison) -"rBB" = ( -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 4 - }, -/obj/machinery/door/airlock/security/glass{ - name = "Permanent Convict Item Storage" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/general, -/turf/open/floor/iron/dark, -/area/station/security/execution/transfer) "rBE" = ( /obj/effect/turf_decal/trimline/purple/filled/corner{ dir = 4 @@ -58030,6 +57996,22 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/commons/fitness/recreation) +"rEz" = ( +/obj/machinery/door/firedoor, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 8 + }, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 4 + }, +/obj/machinery/door/airlock/security/glass{ + name = "Isolation Cell A" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "rEA" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -59148,15 +59130,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/security/prison) -"rZI" = ( -/obj/machinery/hydroponics/soil, -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 1 - }, -/obj/item/shovel/spade, -/obj/effect/turf_decal/trimline/neutral/filled/corner, -/turf/open/floor/iron/dark, -/area/station/security/prison/garden) "rZU" = ( /obj/effect/turf_decal/trimline/green/filled/corner{ dir = 4 @@ -60751,6 +60724,21 @@ /obj/machinery/light/directional/south, /turf/open/floor/iron, /area/station/engineering/atmos) +"szG" = ( +/obj/effect/turf_decal/trimline/red/filled/corner, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 1 + }, +/obj/machinery/button/flasher{ + id = "permafrontdoor"; + pixel_x = 9; + pixel_y = 24; + req_one_access = list("brig", "permabrig") + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "szM" = ( /obj/effect/turf_decal/trimline/red/filled/corner{ dir = 1 @@ -62516,9 +62504,29 @@ icon_state = "clown_carpet" }, /area/station/security/execution/education) +"tbu" = ( +/obj/structure/cable, +/obj/machinery/door/airlock/external{ + name = "External Access" + }, +/obj/effect/turf_decal/sand/plating, +/obj/effect/mapping_helpers/airlock/cyclelink_helper{ + dir = 1 + }, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/plating, +/area/station/maintenance/central/greater) "tby" = ( /turf/open/floor/iron/white, /area/station/science/research) +"tbA" = ( +/obj/machinery/growing/soil, +/obj/machinery/firealarm/directional/west, +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 9 + }, +/turf/open/floor/iron/dark, +/area/station/security/prison/garden) "tbF" = ( /obj/effect/turf_decal/trimline/purple/filled/line{ dir = 1 @@ -62871,6 +62879,16 @@ /obj/structure/sink/directional/east, /turf/open/floor/iron/white, /area/station/medical/medbay/central) +"tgZ" = ( +/obj/machinery/growing/soil, +/obj/machinery/status_display/ai/directional/south, +/obj/effect/turf_decal/trimline/green/filled/line, +/obj/item/cultivator, +/obj/effect/turf_decal/trimline/neutral/filled/line{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/security/prison/garden) "thi" = ( /obj/structure/curtain, /obj/machinery/shower/directional/south, @@ -63669,19 +63687,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/engineering/atmos) -"tsw" = ( -/obj/machinery/hydroponics/soil, -/obj/machinery/airalarm/directional/east, -/obj/effect/turf_decal/trimline/green/filled/line{ - dir = 5 - }, -/obj/effect/turf_decal/trimline/neutral/filled/corner, -/obj/item/cultivator, -/obj/effect/turf_decal/trimline/neutral/filled/corner{ - dir = 8 - }, -/turf/open/floor/iron/dark, -/area/station/security/prison/garden) "tsE" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/landmark/blobstart, @@ -63740,13 +63745,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/dark/visible, /turf/open/floor/engine, /area/station/science/xenobiology) -"tto" = ( -/obj/machinery/hydroponics/constructable, -/obj/effect/turf_decal/trimline/green/line{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/service/hydroponics) "ttr" = ( /obj/effect/turf_decal/trimline/purple/filled/line{ dir = 8 @@ -64571,22 +64569,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/command/bridge) -"tJJ" = ( -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 8 - }, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 4 - }, -/obj/machinery/door/airlock/security/glass{ - name = "Isolation Cell A" - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "tJR" = ( /obj/structure/table/reinforced, /obj/effect/turf_decal/trimline/red/filled/line{ @@ -68365,6 +68347,16 @@ /obj/structure/cable, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat/maint) +"uTq" = ( +/obj/machinery/growing/soil, +/obj/machinery/status_display/evac/directional/north, +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 1 + }, +/obj/effect/turf_decal/trimline/neutral/filled/corner, +/obj/effect/turf_decal/trimline/neutral/filled/line, +/turf/open/floor/iron/dark, +/area/station/security/prison/garden) "uTz" = ( /obj/structure/railing{ dir = 8 @@ -70641,6 +70633,15 @@ }, /turf/open/floor/iron/dark, /area/station/cargo/miningdock/oresilo) +"vAp" = ( +/obj/machinery/growing/soil, +/obj/effect/turf_decal/trimline/green/filled/line{ + dir = 1 + }, +/obj/item/shovel/spade, +/obj/effect/turf_decal/trimline/neutral/filled/corner, +/turf/open/floor/iron/dark, +/area/station/security/prison/garden) "vAs" = ( /obj/structure/railing, /turf/open/openspace, @@ -70926,18 +70927,6 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/commons/vacant_room/commissary) -"vEc" = ( -/obj/machinery/door/airlock/maintenance{ - name = "Permabrig Maintenance" - }, -/obj/machinery/duct, -/obj/effect/decal/cleanable/dirt, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/catwalk_floor, -/area/station/maintenance/central/greater) "vEj" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/structure/cable, @@ -71352,6 +71341,32 @@ /obj/machinery/status_display/evac/directional/east, /turf/open/floor/iron/dark, /area/station/command/teleporter) +"vMH" = ( +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 8 + }, +/obj/structure/sink{ + dir = 4; + pixel_x = -12 + }, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/closet/crate/hydroponics, +/obj/item/seeds/onion, +/obj/item/paper/guides/jobs/hydroponics, +/obj/item/seeds/garlic, +/obj/item/seeds/potato, +/obj/item/seeds/tomato, +/obj/item/seeds/carrot, +/obj/item/seeds/grass, +/obj/item/seeds/ambrosia, +/obj/item/seeds/wheat, +/obj/item/seeds/pumpkin, +/obj/effect/spawner/random/contraband/prison, +/obj/item/seeds/tree, +/turf/open/floor/iron, +/area/station/security/prison/garden) "vMI" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/disposalpipe/segment{ @@ -72447,11 +72462,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/commons/fitness/recreation) -"wed" = ( -/obj/structure/window/reinforced/spawner/directional/north, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/grass, -/area/station/service/hydroponics) "wei" = ( /obj/effect/turf_decal/trimline/red/filled/corner, /obj/effect/turf_decal/trimline/red/filled/corner{ @@ -75389,18 +75399,6 @@ }, /turf/open/floor/iron, /area/station/security/prison/workout) -"xcj" = ( -/obj/machinery/door/airlock/security/glass{ - name = "Prison Wing" - }, -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/trimline/red/filled/line, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 1 - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "xck" = ( /turf/open/floor/iron/white, /area/station/medical/medbay/lobby) @@ -75473,10 +75471,6 @@ }, /turf/open/floor/engine, /area/station/science/ordnance/burnchamber) -"xet" = ( -/obj/machinery/hydroponics/constructable, -/turf/open/floor/grass, -/area/station/service/hydroponics) "xez" = ( /obj/effect/turf_decal/trimline/dark_red/warning{ dir = 10 @@ -76022,21 +76016,6 @@ /obj/effect/turf_decal/tile/brown/fourcorners, /turf/open/floor/iron/smooth, /area/station/cargo/drone_bay) -"xog" = ( -/obj/machinery/door/airlock/security/glass{ - name = "Isolation Wing" - }, -/obj/machinery/door/firedoor, -/obj/effect/turf_decal/trimline/red/filled/line, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 1 - }, -/obj/effect/mapping_helpers/airlock/access/all/security/brig, -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "xom" = ( /obj/effect/turf_decal/trimline/blue/filled/line{ dir = 6 @@ -77094,11 +77073,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/wood, /area/station/command/meeting_room) -"xIu" = ( -/obj/structure/window/reinforced/spawner/directional/south, -/obj/machinery/hydroponics/constructable, -/turf/open/floor/grass, -/area/station/service/hydroponics) "xIL" = ( /obj/structure/cable, /turf/open/floor/iron, @@ -77683,6 +77657,23 @@ /obj/effect/spawner/structure/window, /turf/open/floor/plating, /area/station/medical/treatment_center) +"xTo" = ( +/obj/effect/decal/cleanable/dirt, +/obj/structure/table, +/obj/item/stack/cable_coil{ + pixel_x = 3; + pixel_y = -7 + }, +/obj/effect/turf_decal/bot, +/obj/item/stack/sheet/plasteel/fifty, +/obj/effect/decal/cleanable/dirt, +/obj/item/hfr_box/body/fuel_input, +/obj/item/hfr_box/body/interface, +/obj/item/hfr_box/body/moderator_input, +/obj/item/hfr_box/body/waste_output, +/obj/item/circuitboard/machine/crystallizer, +/turf/open/floor/iron, +/area/station/engineering/atmospherics_engine) "xTr" = ( /obj/effect/turf_decal/trimline/dark_green/filled/corner{ dir = 8 @@ -78023,7 +78014,7 @@ }, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/mob/living/simple_animal/bot/cleanbot, +/mob/living/basic/bot/cleanbot, /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/aisat/maint) "xZi" = ( @@ -78383,6 +78374,21 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/commons/dorms) +"ydS" = ( +/obj/machinery/door/airlock/security/glass{ + name = "Isolation Wing" + }, +/obj/machinery/door/firedoor, +/obj/effect/turf_decal/trimline/red/filled/line, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 1 + }, +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "ydU" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/closet/emcloset, @@ -92158,11 +92164,11 @@ aaa jWs ems pJb -cXt +iyr tnB eTl wei -iEO +nOP eOc ong jWs @@ -92929,11 +92935,11 @@ aaa jWs ems pJb -tJJ +rEz tnB eTl wei -gLa +pvY eOc ong jWs @@ -93446,7 +93452,7 @@ aaa jWs jWs cWF -xog +ydS cWF msg kaT @@ -93967,7 +93973,7 @@ eTl pbY fHl adz -rBB +bUx nZP iGG bSU @@ -94992,7 +94998,7 @@ mXD jiF fea jWs -qnU +szG amU ggX jWs @@ -95251,7 +95257,7 @@ oEN oEN uLW neR -xcj +jGY oEN oVM adN @@ -100374,7 +100380,7 @@ aaf dqp dqp anU -vEc +orC ddN dLN xXt @@ -100624,9 +100630,9 @@ aaa aaa abM sYd -aab +nPx aad -aae +tbu mST bMV nMu @@ -101142,7 +101148,7 @@ aaa aaa xwf sul -egD +kzv xwf xwf xwf @@ -101929,11 +101935,11 @@ hFr qDc mSK duB -eRw +tbA sTq -iHG +vMH eay -qjG +odb duB aaa aaa @@ -102186,11 +102192,11 @@ wxM hsH pIt duB -rZI +vAp gJR cOv xNm -rkP +eTe duB aaa aaa @@ -102443,11 +102449,11 @@ hFr sYb cAU duB -otn +uTq ecs cOv biS -lQx +tgZ duB aaa aaa @@ -102700,11 +102706,11 @@ hFr cNy hFr duB -tsw +qGw kwk acV adq -adw +dfk duB aaa aaa @@ -110411,10 +110417,10 @@ aqu lQo pHX mpL -moV -tto -tto -feC +aCo +ipS +ipS +ltR voF pVJ nUP @@ -110925,10 +110931,10 @@ xvN nUP wDw ale -moV -ptR -tto -feC +aCo +foZ +ipS +ltR sbv yka nUP @@ -111439,10 +111445,10 @@ mek dvi smF jsz -moV -tto -tto -feC +aCo +ipS +ipS +ltR voF tac nUP @@ -111685,17 +111691,17 @@ aac nUP rKb tka -wed +kyb nSi -xet +fjI nSi -xIu +lRw nUP gcE mDy nUP oIk -dXA +nbL uLD sqF adh @@ -115122,7 +115128,7 @@ fjQ fjQ fjQ bNz -pxj +xTo nDh oGy eqY @@ -156202,7 +156208,7 @@ yfz nEF jBy iHr -mpw +qnb tdx mQa aju @@ -156459,7 +156465,7 @@ ckM pVW aow mdY -mpw +qnb tdx mQa tdx @@ -156716,7 +156722,7 @@ ckM ftt igT mdY -mpw +qnb tdx mQa tdx @@ -156973,7 +156979,7 @@ ilh ufK igT mdY -mpw +qnb tdx mQa tdx @@ -157487,7 +157493,7 @@ vpS mNC gkR mdY -mpw +qnb tdx tdx tdx @@ -157744,7 +157750,7 @@ ckM ijf aUh mdY -mpw +qnb ckM rrM puN @@ -158001,7 +158007,7 @@ ckM qgx sMX mdY -mpw +qnb ckM aHD iOi @@ -158258,7 +158264,7 @@ ckM pMz pzw cGm -mpw +qnb ckM kwo iOi @@ -161047,8 +161053,8 @@ dWK qdY uCZ dSo -lNZ -eYu +bra +eVH dSo rMS psv @@ -162840,13 +162846,13 @@ mGN gNK rOh anD -ork +pNA oFY -ork +pNA lSo lSo lSo -azy +dxq vqE qyZ aPP @@ -164645,8 +164651,8 @@ xLO hMA wEx dSo -lNZ -eYu +bra +eVH dSo rMS bPV diff --git a/_maps/shuttles/emergency_casino.dmm b/_maps/shuttles/emergency_casino.dmm index f07d740850a8..cc37cd9fafb0 100644 --- a/_maps/shuttles/emergency_casino.dmm +++ b/_maps/shuttles/emergency_casino.dmm @@ -1177,7 +1177,7 @@ "MS" = ( /obj/structure/mop_bucket, /obj/item/mop, -/mob/living/simple_animal/bot/cleanbot, +/mob/living/basic/bot/cleanbot, /turf/open/floor/mineral/titanium/yellow, /area/shuttle/escape) "MT" = ( @@ -1306,7 +1306,7 @@ /turf/open/floor/carpet/green, /area/shuttle/escape) "PP" = ( -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "\improper emergency medibot"; pixel_x = -3; pixel_y = 2 diff --git a/_maps/shuttles/emergency_cere.dmm b/_maps/shuttles/emergency_cere.dmm index 447700659028..ff5f44d8c9f4 100644 --- a/_maps/shuttles/emergency_cere.dmm +++ b/_maps/shuttles/emergency_cere.dmm @@ -521,7 +521,7 @@ /turf/open/floor/iron, /area/shuttle/escape) "ch" = ( -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "Speedy* Recovery" }, /obj/effect/turf_decal/tile/blue/anticorner/contrasted{ diff --git a/_maps/shuttles/emergency_cruise.dmm b/_maps/shuttles/emergency_cruise.dmm index 5d34fc5a521b..3c61e456b8ae 100644 --- a/_maps/shuttles/emergency_cruise.dmm +++ b/_maps/shuttles/emergency_cruise.dmm @@ -380,7 +380,7 @@ /area/shuttle/escape/brig) "gr" = ( /obj/item/radio/intercom/directional/south, -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "Doktor Oktoberfest" }, /turf/open/floor/iron/white/textured_edge, @@ -1672,7 +1672,7 @@ }, /area/shuttle/escape/brig) "EK" = ( -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "Doctor Patches" }, /obj/structure/window/reinforced/survival_pod/spawner/directional/south, diff --git a/_maps/shuttles/emergency_delta.dmm b/_maps/shuttles/emergency_delta.dmm index a4c9050a73a2..1bd6e1f520c4 100644 --- a/_maps/shuttles/emergency_delta.dmm +++ b/_maps/shuttles/emergency_delta.dmm @@ -1012,7 +1012,7 @@ }, /obj/item/lazarus_injector, /obj/effect/turf_decal/bot, -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "\improper emergency medibot"; pixel_x = -3; pixel_y = 2 diff --git a/_maps/shuttles/emergency_donut.dmm b/_maps/shuttles/emergency_donut.dmm index 795d6bf9efba..2d78d9f07043 100644 --- a/_maps/shuttles/emergency_donut.dmm +++ b/_maps/shuttles/emergency_donut.dmm @@ -362,7 +362,7 @@ /obj/item/lazarus_injector, /obj/effect/turf_decal/bot, /obj/machinery/light/directional/south, -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "\improper emergency medibot"; pixel_x = -3; pixel_y = 2 diff --git a/_maps/shuttles/emergency_kilo.dmm b/_maps/shuttles/emergency_kilo.dmm index 76d3c0b95f71..a3b2ee924ff2 100644 --- a/_maps/shuttles/emergency_kilo.dmm +++ b/_maps/shuttles/emergency_kilo.dmm @@ -443,7 +443,7 @@ }, /obj/item/lazarus_injector, /obj/effect/turf_decal/bot, -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "\improper emergency medibot"; pixel_x = -3; pixel_y = 2 diff --git a/_maps/shuttles/emergency_lance.dmm b/_maps/shuttles/emergency_lance.dmm index 191a65e6b934..8d035b9750bd 100644 --- a/_maps/shuttles/emergency_lance.dmm +++ b/_maps/shuttles/emergency_lance.dmm @@ -1629,7 +1629,7 @@ /turf/open/floor/iron, /area/shuttle/escape) "ZZ" = ( -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "\improper emergency medibot"; pixel_x = -3; pixel_y = 2 diff --git a/_maps/shuttles/emergency_luxury.dmm b/_maps/shuttles/emergency_luxury.dmm index c66927d73704..86d1b628bb8c 100644 --- a/_maps/shuttles/emergency_luxury.dmm +++ b/_maps/shuttles/emergency_luxury.dmm @@ -1092,7 +1092,7 @@ /turf/open/floor/mineral/titanium/blue, /area/shuttle/escape/luxury) "SE" = ( -/mob/living/simple_animal/bot/medbot, +/mob/living/basic/bot/medbot, /obj/structure/extinguisher_cabinet/directional/south, /turf/open/floor/mineral/titanium/blue, /area/shuttle/escape/luxury) diff --git a/_maps/shuttles/emergency_medisim.dmm b/_maps/shuttles/emergency_medisim.dmm index d571dc6649d9..6bdf6acdbd5c 100644 --- a/_maps/shuttles/emergency_medisim.dmm +++ b/_maps/shuttles/emergency_medisim.dmm @@ -1366,7 +1366,7 @@ pixel_y = 3 }, /obj/item/lazarus_injector, -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "\improper emergency medibot"; pixel_x = -3; pixel_y = 2 diff --git a/_maps/shuttles/emergency_meta.dmm b/_maps/shuttles/emergency_meta.dmm index 8191ac47a581..16d338e80ce5 100644 --- a/_maps/shuttles/emergency_meta.dmm +++ b/_maps/shuttles/emergency_meta.dmm @@ -617,7 +617,7 @@ }, /obj/item/lazarus_injector, /obj/effect/turf_decal/bot, -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "\improper emergency medibot"; pixel_x = -3; pixel_y = 2 diff --git a/_maps/shuttles/emergency_monastery.dmm b/_maps/shuttles/emergency_monastery.dmm index c497f1150c3c..7380fe8fe3ff 100644 --- a/_maps/shuttles/emergency_monastery.dmm +++ b/_maps/shuttles/emergency_monastery.dmm @@ -181,7 +181,7 @@ /turf/open/floor/iron/dark, /area/shuttle/escape) "bK" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/watermelon/holy, /turf/open/floor/grass, /area/shuttle/escape) @@ -865,7 +865,7 @@ /turf/open/floor/iron/dark, /area/shuttle/escape) "mQ" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/machinery/light/small/directional/north, /obj/item/seeds/watermelon/holy, /turf/open/floor/grass, @@ -2144,7 +2144,7 @@ /turf/open/floor/iron/dark, /area/shuttle/escape) "EL" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/carrot, /turf/open/floor/grass, /area/shuttle/escape) @@ -2155,7 +2155,7 @@ /turf/open/floor/grass, /area/shuttle/escape) "EU" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/wheat, /obj/machinery/light/small/directional/west, /turf/open/floor/grass, @@ -2267,7 +2267,7 @@ /turf/open/floor/iron/dark, /area/shuttle/escape) "Gk" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/harebell, /turf/open/floor/grass, /area/shuttle/escape) @@ -2349,7 +2349,7 @@ /turf/open/floor/iron/chapel, /area/shuttle/escape) "HL" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/poppy, /turf/open/floor/grass, /area/shuttle/escape) @@ -2951,7 +2951,7 @@ /turf/open/floor/iron/dark, /area/shuttle/escape) "Py" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/sugarcane, /turf/open/floor/grass, /area/shuttle/escape) @@ -3084,7 +3084,7 @@ /turf/open/floor/carpet, /area/shuttle/escape) "Rd" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/machinery/light/small/directional/south, /obj/item/seeds/poppy, /turf/open/floor/grass, @@ -3174,7 +3174,7 @@ /turf/open/floor/iron, /area/shuttle/escape) "RZ" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/sugarcane, /obj/machinery/light/small/directional/east, /turf/open/floor/grass, @@ -3460,7 +3460,7 @@ /turf/open/floor/iron/dark, /area/shuttle/escape) "UC" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/wheat, /turf/open/floor/grass, /area/shuttle/escape) diff --git a/_maps/shuttles/emergency_nature.dmm b/_maps/shuttles/emergency_nature.dmm index 10cc53aebff3..6b14e8103b68 100644 --- a/_maps/shuttles/emergency_nature.dmm +++ b/_maps/shuttles/emergency_nature.dmm @@ -205,7 +205,7 @@ /area/shuttle/escape) "gR" = ( /obj/effect/turf_decal/weather/dirt, -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/grass, /area/shuttle/escape) "ht" = ( @@ -784,7 +784,7 @@ dir = 9 }, /obj/structure/window/reinforced/spawner/directional/west, -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "\improper emergency medibot"; pixel_x = -3; pixel_y = 2 @@ -1119,7 +1119,7 @@ /obj/effect/turf_decal/weather/dirt{ dir = 1 }, -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/grass, /area/shuttle/escape) "TH" = ( @@ -1320,7 +1320,7 @@ /turf/open/floor/iron/white, /area/shuttle/escape) "ZL" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/seeds/banana, /turf/open/floor/grass, /area/shuttle/escape) diff --git a/_maps/shuttles/emergency_northstar.dmm b/_maps/shuttles/emergency_northstar.dmm index 0c6f05f8c54f..1d28f01a6b19 100644 --- a/_maps/shuttles/emergency_northstar.dmm +++ b/_maps/shuttles/emergency_northstar.dmm @@ -178,7 +178,7 @@ /turf/open/floor/iron/dark, /area/shuttle/escape) "oA" = ( -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "\improper emergency medibot"; pixel_x = -3; pixel_y = 2 diff --git a/_maps/shuttles/emergency_omega.dmm b/_maps/shuttles/emergency_omega.dmm index 5207f884f85b..1e73eea267d4 100644 --- a/_maps/shuttles/emergency_omega.dmm +++ b/_maps/shuttles/emergency_omega.dmm @@ -602,7 +602,7 @@ /obj/item/lazarus_injector, /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/bot, -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "\improper emergency medibot"; pixel_x = -3; pixel_y = 2 diff --git a/_maps/shuttles/emergency_raven.dmm b/_maps/shuttles/emergency_raven.dmm index 56d4df5299e3..e047eea34987 100644 --- a/_maps/shuttles/emergency_raven.dmm +++ b/_maps/shuttles/emergency_raven.dmm @@ -1200,7 +1200,7 @@ pixel_y = 3 }, /obj/item/lazarus_injector, -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "\improper emergency medibot"; pixel_x = -3; pixel_y = 2 diff --git a/_maps/shuttles/emergency_shadow.dmm b/_maps/shuttles/emergency_shadow.dmm index aeeef079c53a..6650a0c31f04 100644 --- a/_maps/shuttles/emergency_shadow.dmm +++ b/_maps/shuttles/emergency_shadow.dmm @@ -366,7 +366,7 @@ /turf/open/floor/catwalk_floor, /area/shuttle/escape/engine) "oK" = ( -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ name = "Doctor Patches" }, /turf/open/floor/iron/dark/smooth_large, @@ -1027,7 +1027,7 @@ /turf/open/floor/eighties, /area/shuttle/escape) "RV" = ( -/mob/living/simple_animal/bot/cleanbot{ +/mob/living/basic/bot/cleanbot{ name = "Mopsy" }, /obj/machinery/holopad, diff --git a/_maps/shuttles/emergency_tranquility.dmm b/_maps/shuttles/emergency_tranquility.dmm index 52f97d015c2f..88c061cd0276 100644 --- a/_maps/shuttles/emergency_tranquility.dmm +++ b/_maps/shuttles/emergency_tranquility.dmm @@ -1480,7 +1480,7 @@ /obj/effect/turf_decal/siding/wood{ dir = 9 }, -/mob/living/simple_animal/bot/cleanbot, +/mob/living/basic/bot/cleanbot, /obj/machinery/vending/wallmed/directional/south, /turf/open/floor/wood, /area/shuttle/escape) @@ -2202,7 +2202,7 @@ /obj/effect/turf_decal/siding/thinplating_new{ dir = 4 }, -/mob/living/simple_animal/bot/medbot, +/mob/living/basic/bot/medbot, /turf/open/floor/iron/herringbone, /area/shuttle/escape) "NC" = ( @@ -2375,7 +2375,7 @@ /turf/open/floor/iron/dark/herringbone, /area/shuttle/escape) "Rx" = ( -/mob/living/simple_animal/bot/hygienebot, +/mob/living/basic/bot/hygienebot, /obj/machinery/light/directional/north, /turf/open/floor/wood, /area/shuttle/escape) diff --git a/_maps/shuttles/ruin_cyborg_mothership.dmm b/_maps/shuttles/ruin_cyborg_mothership.dmm index 70d39e221b8b..e01b65a69d71 100644 --- a/_maps/shuttles/ruin_cyborg_mothership.dmm +++ b/_maps/shuttles/ruin_cyborg_mothership.dmm @@ -23,7 +23,7 @@ /turf/open/floor/plating/airless, /area/shuttle/ruin/cyborg_mothership) "bE" = ( -/mob/living/simple_animal/bot/cleanbot, +/mob/living/basic/bot/cleanbot, /turf/open/floor/iron/showroomfloor, /area/shuttle/ruin/cyborg_mothership) "cU" = ( @@ -602,7 +602,7 @@ /turf/open/floor/iron/showroomfloor, /area/shuttle/ruin/cyborg_mothership) "Fe" = ( -/mob/living/simple_animal/bot/hygienebot, +/mob/living/basic/bot/hygienebot, /obj/machinery/camera/directional/south, /turf/open/floor/iron/showroomfloor, /area/shuttle/ruin/cyborg_mothership) diff --git a/_maps/shuttles/whiteship_meta.dmm b/_maps/shuttles/whiteship_meta.dmm index 0e3c921935f6..ae5b59b08897 100644 --- a/_maps/shuttles/whiteship_meta.dmm +++ b/_maps/shuttles/whiteship_meta.dmm @@ -1201,7 +1201,7 @@ /area/shuttle/abandoned/bar) "dm" = ( /obj/effect/decal/cleanable/dirt/dust, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt/dust, /obj/machinery/light/small/directional/west, /obj/effect/turf_decal/tile/green/half/contrasted, @@ -1507,7 +1507,7 @@ "ef" = ( /obj/effect/decal/cleanable/dirt/dust, /obj/machinery/light/small/built/directional/west, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/effect/decal/cleanable/dirt/dust, /obj/effect/turf_decal/tile/green/half/contrasted{ dir = 1 @@ -1760,7 +1760,7 @@ /area/shuttle/abandoned/cargo) "tq" = ( /obj/effect/decal/cleanable/dirt/dust, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/firealarm/directional/east, /obj/effect/spawner/random/food_or_drink/seed_rare, /obj/effect/turf_decal/tile/green/half/contrasted{ @@ -2138,7 +2138,7 @@ /area/shuttle/abandoned/cargo) "SW" = ( /obj/effect/decal/cleanable/dirt/dust, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/airalarm/all_access{ dir = 4; pixel_x = 24 diff --git a/_maps/templates/battlecruiser_starfury.dmm b/_maps/templates/battlecruiser_starfury.dmm index 0dfdd46b5ddf..b460a85c31d4 100644 --- a/_maps/templates/battlecruiser_starfury.dmm +++ b/_maps/templates/battlecruiser_starfury.dmm @@ -4794,7 +4794,7 @@ /area/shuttle/sbc_starfury) "Gz" = ( /obj/structure/cable, -/mob/living/simple_animal/bot/medbot{ +/mob/living/basic/bot/medbot{ desc = "A medical bot of syndicate origins. Probably plots about how to stab you full of toxins in its free time."; faction = list("neutral","silicon","turret","Syndicate"); name = "Syndicate Medibot"; diff --git a/_maps/templates/holodeck_petpark.dmm b/_maps/templates/holodeck_petpark.dmm index 0217e5098511..b0c5a34722f6 100644 --- a/_maps/templates/holodeck_petpark.dmm +++ b/_maps/templates/holodeck_petpark.dmm @@ -8,7 +8,7 @@ /turf/open/floor/holofloor/grass, /area/template_noop) "c" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/holofloor/grass, /area/template_noop) "f" = ( @@ -33,7 +33,7 @@ /turf/open/floor/holofloor/grass, /area/template_noop) "q" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/effect/holodeck_effect/mobspawner/pet, /turf/open/floor/holofloor/grass, /area/template_noop) @@ -94,7 +94,7 @@ /turf/open/floor/holofloor, /area/template_noop) "U" = ( -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /obj/item/cultivator, /turf/open/floor/holofloor/grass, /area/template_noop) diff --git a/_maps/templates/lazy_templates/abductor_ships.dmm b/_maps/templates/lazy_templates/abductor_ships.dmm index 4c7d2e172ff2..fe8bccabc803 100644 --- a/_maps/templates/lazy_templates/abductor_ships.dmm +++ b/_maps/templates/lazy_templates/abductor_ships.dmm @@ -184,7 +184,7 @@ }, /area/centcom/abductor_ship) "Bh" = ( -/obj/machinery/abductor/gland_dispenser, +/obj/machinery/smartfridge/abductor, /turf/open/floor/plating/abductor, /area/centcom/abductor_ship) "BX" = ( diff --git a/_maps/templates/lazy_templates/ninja_den.dmm b/_maps/templates/lazy_templates/ninja_den.dmm index 981fd15854fa..69310e58d091 100644 --- a/_maps/templates/lazy_templates/ninja_den.dmm +++ b/_maps/templates/lazy_templates/ninja_den.dmm @@ -643,7 +643,7 @@ }, /area/centcom/central_command_areas/holding) "qi" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/grass, /area/centcom/central_command_areas/holding) "ql" = ( @@ -814,7 +814,7 @@ /turf/open/floor/iron/showroomfloor, /area/centcom/central_command_areas/holding) "uw" = ( -/mob/living/simple_animal/bot/medbot/stationary{ +/mob/living/basic/bot/medbot/stationary{ desc = "A little medical robot. You can make out the word \"sincerity\" on its chassis."; name = "Hijikata"; radio_key = null @@ -1014,7 +1014,7 @@ /turf/open/floor/iron/sepia, /area/centcom/central_command_areas/holding) "Al" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/light/directional/east, /turf/open/floor/grass, /area/centcom/central_command_areas/holding) @@ -1432,7 +1432,7 @@ /turf/open/floor/iron/cafeteria, /area/centcom/central_command_areas/holding) "II" = ( -/mob/living/simple_animal/bot/medbot/stationary{ +/mob/living/basic/bot/medbot/stationary{ desc = "When engaged in combat, the vanquishing of thine enemy can be the warrior's only concern."; name = "Momo"; radio_key = null @@ -2045,7 +2045,7 @@ /turf/open/floor/carpet/black, /area/centcom/central_command_areas/holding) "Wm" = ( -/mob/living/simple_animal/bot/medbot/stationary{ +/mob/living/basic/bot/medbot/stationary{ desc = "When engaged in combat, the vanquishing of thine enemy can be the warrior's only concern."; name = "Hattori"; radio_key = null diff --git a/_maps/templates/lazy_templates/nukie_base.dmm b/_maps/templates/lazy_templates/nukie_base.dmm index 751488dbff04..506f5b101314 100644 --- a/_maps/templates/lazy_templates/nukie_base.dmm +++ b/_maps/templates/lazy_templates/nukie_base.dmm @@ -419,7 +419,7 @@ /turf/open/floor/catwalk_floor/iron_smooth, /area/centcom/syndicate_mothership/control) "fK" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/mineral/titanium/tiled, /area/centcom/syndicate_mothership/expansion_bioterrorism) "fR" = ( @@ -1326,7 +1326,7 @@ /area/centcom/syndicate_mothership/expansion_chemicalwarfare) "pY" = ( /obj/structure/window/reinforced/survival_pod/spawner/directional/east, -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/mineral/titanium/tiled, /area/centcom/syndicate_mothership/expansion_bioterrorism) "qh" = ( @@ -3271,7 +3271,7 @@ /turf/open/floor/catwalk_floor/iron, /area/centcom/syndicate_mothership/control) "MH" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/light/cold/directional/west, /obj/item/seeds/cannabis{ pixel_y = 8 diff --git a/_maps/templates/lazy_templates/wizard_den.dmm b/_maps/templates/lazy_templates/wizard_den.dmm index 75ee8f52454b..53672531b9fd 100644 --- a/_maps/templates/lazy_templates/wizard_den.dmm +++ b/_maps/templates/lazy_templates/wizard_den.dmm @@ -497,7 +497,7 @@ /turf/open/floor/grass, /area/centcom/wizard_station) "XJ" = ( -/mob/living/simple_animal/bot/medbot/mysterious{ +/mob/living/basic/bot/medbot/mysterious{ desc = "If you don't accidentally blow yourself up from time to time you're not really a wizard anyway."; faction = list("neutral","silicon","creature"); name = "Nobody's Perfect" diff --git a/_maps/virtual_domains/beach_bar.dmm b/_maps/virtual_domains/beach_bar.dmm index 6d0420d9b3d9..ed3c56409c3c 100644 --- a/_maps/virtual_domains/beach_bar.dmm +++ b/_maps/virtual_domains/beach_bar.dmm @@ -642,7 +642,7 @@ /turf/open/misc/beach/sand, /area/virtual_domain/powered) "Cv" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /turf/open/floor/iron/grimy, /area/virtual_domain/powered) "CA" = ( @@ -788,7 +788,7 @@ /turf/open/misc/beach/sand, /area/virtual_domain/powered) "IM" = ( -/obj/machinery/hydroponics/constructable, +/obj/machinery/growing/tray, /obj/machinery/light/directional/east, /turf/open/floor/iron/grimy, /area/virtual_domain/powered) diff --git a/_maps/~monkestation/RandomBars/Tram/tram_bar_biodome.dmm b/_maps/~monkestation/RandomBars/Tram/tram_bar_biodome.dmm index 53e84271edb1..15c9c93944e6 100644 --- a/_maps/~monkestation/RandomBars/Tram/tram_bar_biodome.dmm +++ b/_maps/~monkestation/RandomBars/Tram/tram_bar_biodome.dmm @@ -392,7 +392,7 @@ /area/station/commons/lounge) "kz" = ( /obj/structure/flora/tree/pine/style_random, -/obj/machinery/hydroponics/soil, +/obj/machinery/growing/soil, /turf/open/floor/pod/light, /area/station/service/theater) "kA" = ( diff --git a/_maps/~monkestation/RandomBars/Tram/tram_bar_cult.dmm b/_maps/~monkestation/RandomBars/Tram/tram_bar_cult.dmm index 9128c24b9be1..94d74489e55c 100644 --- a/_maps/~monkestation/RandomBars/Tram/tram_bar_cult.dmm +++ b/_maps/~monkestation/RandomBars/Tram/tram_bar_cult.dmm @@ -7,6 +7,9 @@ name = "Church Door" }, /obj/machinery/duct, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, /turf/open/floor/cult, /area/station/service/theater) "ap" = ( @@ -30,6 +33,16 @@ "aL" = ( /turf/open/openspace, /area/station/service/kitchen) +"bm" = ( +/obj/structure/disposalpipe/trunk{ + dir = 1 + }, +/obj/machinery/disposal/bin, +/obj/effect/turf_decal/bot{ + dir = 1 + }, +/turf/open/floor/cult, +/area/station/service/bar) "br" = ( /obj/machinery/door/airlock/cult/glass/friendly{ name = "Church Door" @@ -66,8 +79,12 @@ /turf/open/floor/cult, /area/station/commons/lounge) "cm" = ( -/obj/machinery/disposal/bin{ - pixel_x = 14 +/obj/machinery/disposal/bin, +/obj/structure/disposalpipe/trunk{ + dir = 1 + }, +/obj/effect/turf_decal/bot{ + dir = 1 }, /turf/open/floor/cult, /area/station/service/theater) @@ -89,6 +106,11 @@ /obj/machinery/light/warm/directional/south, /turf/open/floor/cult, /area/station/commons/lounge) +"cD" = ( +/obj/effect/spawner/structure/window/reinforced, +/obj/effect/decal/cleanable/blood/splatter/over_window, +/turf/open/floor/cult, +/area/station/service/theater) "cL" = ( /obj/effect/turf_decal/tile/red/opposingcorners, /obj/machinery/vending/autodrobe, @@ -141,12 +163,12 @@ "dD" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/door/airlock/cult/friendly{ - name = "Church Backstage" - }, /obj/effect/mapping_helpers/airlock/access/all/service/theatre, /obj/machinery/door/firedoor, /obj/structure/disposalpipe/segment, +/obj/machinery/door/airlock/service{ + name = "Theater Backstage" + }, /turf/open/floor/cult, /area/station/commons/lounge) "dF" = ( @@ -418,6 +440,9 @@ pixel_x = 22; pixel_y = 7 }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, /turf/open/floor/cult, /area/station/service/kitchen) "jv" = ( @@ -490,6 +515,12 @@ }, /turf/open/floor/cult, /area/station/service/theater) +"lh" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/open/floor/cult, +/area/station/service/kitchen) "li" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -547,10 +578,10 @@ /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/disposalpipe/segment{ - dir = 6 - }, /obj/machinery/duct, +/obj/structure/disposalpipe/junction/flip{ + dir = 8 + }, /turf/open/floor/cult, /area/station/commons/lounge) "mm" = ( @@ -754,6 +785,9 @@ /obj/structure/disposalpipe/trunk{ dir = 8 }, +/obj/effect/turf_decal/bot{ + dir = 1 + }, /turf/open/floor/cult, /area/station/commons/lounge) "rj" = ( @@ -780,9 +814,11 @@ /turf/open/floor/cult, /area/station/service/bar) "sf" = ( -/obj/structure/disposalpipe/segment, /obj/structure/table/wood/fancy/red, /obj/machinery/light/warm/directional/south, +/obj/structure/disposalpipe/segment{ + dir = 9 + }, /turf/open/floor/cult, /area/station/service/bar) "sk" = ( @@ -813,6 +849,9 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/duct, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, /turf/open/floor/cult, /area/station/commons/lounge) "tK" = ( @@ -924,6 +963,11 @@ /obj/effect/spawner/structure/window, /turf/open/floor/plating, /area/template_noop) +"xn" = ( +/obj/effect/spawner/structure/window/reinforced, +/obj/effect/decal/cleanable/blood/splatter/over_window, +/turf/open/floor/cult, +/area/station/service/kitchen) "xq" = ( /obj/effect/turf_decal/tile/red/opposingcorners, /obj/structure/chair/wood{ @@ -1070,6 +1114,13 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/cult, /area/station/commons/lounge) +"AU" = ( +/obj/structure/table/wood/fancy/red, +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/turf/open/floor/cult, +/area/station/service/bar) "Bi" = ( /obj/structure/table/wood/fancy/red, /obj/structure/displaycase/forsale/kitchen{ @@ -1165,6 +1216,9 @@ /obj/effect/turf_decal/trimline/yellow/corner{ dir = 1 }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, /turf/open/floor/cult, /area/station/service/kitchen) "Cp" = ( @@ -1195,6 +1249,9 @@ /obj/effect/turf_decal/trimline/yellow/corner{ dir = 4 }, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, /turf/open/floor/cult, /area/station/service/kitchen) "CL" = ( @@ -1230,14 +1287,16 @@ /turf/open/floor/cult, /area/station/service/kitchen) "Dj" = ( -/obj/machinery/door/firedoor, -/obj/machinery/door/airlock/cult/glass/friendly, /obj/effect/mapping_helpers/airlock/access/all/service/kitchen, /obj/structure/cable, /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/duct, +/obj/machinery/door/firedoor, +/obj/machinery/door/airlock/service{ + name = "Kitchen" + }, /turf/open/floor/cult, /area/station/service/kitchen) "Dq" = ( @@ -1318,6 +1377,9 @@ /obj/structure/window{ dir = 4 }, +/obj/structure/disposalpipe/segment{ + dir = 10 + }, /turf/open/floor/cult, /area/station/service/kitchen) "Fc" = ( @@ -1532,6 +1594,14 @@ }, /turf/open/floor/cult, /area/station/service/bar) +"Kj" = ( +/obj/machinery/disposal/bin, +/obj/effect/turf_decal/bot{ + dir = 1 + }, +/obj/structure/disposalpipe/trunk, +/turf/open/floor/cult, +/area/station/service/kitchen) "Kl" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4, /obj/effect/decal/cleanable/dirt, @@ -1611,14 +1681,6 @@ }, /turf/closed/wall/mineral/cult/artificer, /area/station/maintenance/department/cargo) -"ME" = ( -/obj/structure/disposalpipe/trunk, -/obj/machinery/disposal/bin, -/obj/effect/turf_decal/bot{ - dir = 1 - }, -/turf/open/floor/cult, -/area/station/service/kitchen) "MH" = ( /obj/effect/turf_decal/trimline/neutral/filled/corner{ dir = 8 @@ -1646,10 +1708,10 @@ /turf/open/floor/cult, /area/station/commons/lounge) "Na" = ( -/obj/machinery/door/airlock/cult/glass/friendly{ - name = "Bartender Access" - }, /obj/effect/mapping_helpers/airlock/access/all/service/bar, +/obj/machinery/door/airlock/service{ + name = "Bar" + }, /turf/open/floor/cult, /area/station/service/bar) "NH" = ( @@ -1928,11 +1990,6 @@ /obj/machinery/light/floor/has_bulb, /turf/open/floor/cult, /area/station/commons/lounge) -"VA" = ( -/obj/effect/decal/cleanable/blood/splatter/over_window, -/obj/effect/spawner/structure/window/reinforced, -/turf/open/floor/cult, -/area/station/service/theater) "VB" = ( /obj/machinery/stove, /obj/item/reagent_containers/cup/soup_pot{ @@ -1951,16 +2008,6 @@ /obj/structure/chair/stool/directional/west, /turf/open/floor/cult, /area/station/commons/lounge) -"VU" = ( -/obj/machinery/disposal/bin{ - pixel_y = -8; - name = "wall-mounted disposal unit" - }, -/obj/structure/disposalpipe/trunk{ - dir = 1 - }, -/turf/closed/wall/mineral/cult, -/area/station/service/bar) "VY" = ( /obj/effect/decal/cleanable/blood, /obj/structure/table/wood/fancy/red, @@ -2005,6 +2052,9 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, /obj/machinery/duct, +/obj/structure/disposalpipe/segment{ + dir = 6 + }, /turf/open/floor/cult, /area/station/service/theater) "Xa" = ( @@ -2094,7 +2144,6 @@ /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/door/firedoor, /obj/machinery/duct, /turf/open/floor/cult, /area/station/commons/lounge) @@ -2224,7 +2273,7 @@ QC QC "} (2,1,1) = {" -jF +cu PH Bm ey @@ -2245,13 +2294,13 @@ rj dF RJ wO -jH -jH -jH -jH +QC +QC +QC +QC "} (3,1,1) = {" -VA +cu QW dA mm @@ -2272,13 +2321,13 @@ KN RJ Uf Bx -jH +QC QC QC QC "} (4,1,1) = {" -jF +cD dA dA dA @@ -2299,7 +2348,7 @@ EG VL YR Ol -jH +QC QC QC QC @@ -2326,13 +2375,13 @@ Xq uO uO FA -jH +QC QC QC QC "} (6,1,1) = {" -jF +cD dA dA dA @@ -2353,13 +2402,13 @@ eG Vd Vd cB -jH +QC xf Ij xf "} (7,1,1) = {" -VA +cu xc fV Et @@ -2380,13 +2429,13 @@ if YX lL qr -jH +QC Lq uL MH "} (8,1,1) = {" -jF +cu oq qN xc @@ -2467,7 +2516,7 @@ EV jH "} (11,1,1) = {" -OY +xn hl up Bk @@ -2494,9 +2543,9 @@ dC EV "} (12,1,1) = {" -SG -up -up +xn +Kj +lz VY RO ox @@ -2521,9 +2570,9 @@ Sa NH "} (13,1,1) = {" -OY +yu xe -up +lh up up up @@ -2548,7 +2597,7 @@ pa Pp "} (14,1,1) = {" -OY +yu lb Fb CJ @@ -2575,7 +2624,7 @@ Vd Wz "} (15,1,1) = {" -OY +yu vP aL js @@ -2602,7 +2651,7 @@ oA EV "} (16,1,1) = {" -OY +yu oN JN Co @@ -2615,8 +2664,8 @@ CR fZ pa Xu -ed -LQ +AU +bm Wq kb yL @@ -2629,10 +2678,10 @@ Vd Wz "} (17,1,1) = {" -SG +yu Ch -ME -lz +up +lh up up tc @@ -2643,7 +2692,7 @@ Bs Vd AE sf -VU +TV Wq NP Wq diff --git a/_maps/~monkestation/templates/hives.dmm b/_maps/~monkestation/templates/hives.dmm new file mode 100644 index 000000000000..0211acf88ab0 --- /dev/null +++ b/_maps/~monkestation/templates/hives.dmm @@ -0,0 +1,300 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/closed/indestructible/hive, +/area/station/hive/four) +"c" = ( +/obj/structure/hive_exit, +/turf/closed/indestructible/hive, +/area/station/hive/one) +"k" = ( +/turf/open/space/basic, +/area/space) +"n" = ( +/turf/open/indestructible/hive, +/area/station/hive/two) +"r" = ( +/turf/closed/indestructible/hive, +/area/station/hive/one) +"A" = ( +/turf/open/indestructible/hive, +/area/station/hive/four) +"B" = ( +/obj/structure/hive_exit, +/turf/closed/indestructible/hive, +/area/station/hive/four) +"C" = ( +/turf/closed/indestructible/hive, +/area/station/hive/three) +"F" = ( +/turf/closed/indestructible/hive, +/area/station/hive/two) +"K" = ( +/turf/open/indestructible/hive, +/area/station/hive/one) +"L" = ( +/turf/open/indestructible/hive, +/area/station/hive/three) +"N" = ( +/obj/structure/hive_exit, +/turf/closed/indestructible/hive, +/area/station/hive/two) +"O" = ( +/obj/structure/hive_exit, +/turf/closed/indestructible/hive, +/area/station/hive/three) + +(1,1,1) = {" +r +r +r +c +r +r +r +k +F +F +F +N +F +F +F +"} +(2,1,1) = {" +r +K +K +K +K +K +r +k +F +n +n +n +n +n +F +"} +(3,1,1) = {" +r +K +K +K +K +K +r +k +F +n +n +n +n +n +F +"} +(4,1,1) = {" +r +K +K +K +K +K +r +k +F +n +n +n +n +n +F +"} +(5,1,1) = {" +r +K +K +K +K +K +r +k +F +n +n +n +n +n +F +"} +(6,1,1) = {" +r +K +K +K +K +K +r +k +F +n +n +n +n +n +F +"} +(7,1,1) = {" +r +r +r +r +r +r +r +k +F +F +F +F +F +F +F +"} +(8,1,1) = {" +k +k +k +k +k +k +k +k +k +k +k +k +k +k +k +"} +(9,1,1) = {" +C +C +C +O +C +C +C +k +a +a +a +B +a +a +a +"} +(10,1,1) = {" +C +L +L +L +L +L +C +k +a +A +A +A +A +A +a +"} +(11,1,1) = {" +C +L +L +L +L +L +C +k +a +A +A +A +A +A +a +"} +(12,1,1) = {" +C +L +L +L +L +L +C +k +a +A +A +A +A +A +a +"} +(13,1,1) = {" +C +L +L +L +L +L +C +k +a +A +A +A +A +A +a +"} +(14,1,1) = {" +C +L +L +L +L +L +C +k +a +A +A +A +A +A +a +"} +(15,1,1) = {" +C +C +C +C +C +C +C +k +a +a +a +a +a +a +a +"} diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm index e7e8451b4b17..f9105932154a 100644 --- a/code/__DEFINES/access.dm +++ b/code/__DEFINES/access.dm @@ -290,8 +290,8 @@ ACCESS_COURT, \ ACCESS_CREMATORIUM, \ ACCESS_DETECTIVE, \ - ACCESS_ENGINE_EQUIP, \ ACCESS_ENGINEERING, \ + ACCESS_ENGINE_EQUIP, \ ACCESS_EXTERNAL_AIRLOCKS, \ ACCESS_GENETICS, \ ACCESS_HYDROPONICS, \ @@ -300,11 +300,11 @@ ACCESS_LAWYER, \ ACCESS_LIBRARY, \ ACCESS_MAINT_TUNNELS, \ - ACCESS_MECH_MINING, \ + ACCESS_MECH_ENGINE, \ ACCESS_MECH_MEDICAL, \ - ACCESS_MECH_SECURITY, \ + ACCESS_MECH_MINING, \ ACCESS_MECH_SCIENCE, \ - ACCESS_MECH_ENGINE, \ + ACCESS_MECH_SECURITY, \ ACCESS_MEDICAL, \ ACCESS_MINERAL_STOREROOM, \ ACCESS_MINING, \ @@ -313,6 +313,7 @@ ACCESS_NETWORK, \ ACCESS_ORDNANCE, \ ACCESS_ORDNANCE_STORAGE, \ + ACCESS_PERMABRIG, \ ACCESS_PHARMACY, \ ACCESS_PLUMBING, \ ACCESS_PSYCHOLOGY, \ @@ -328,7 +329,7 @@ ACCESS_VIROLOGY, \ ACCESS_WEAPONS, \ ACCESS_XENOBIOLOGY, \ -) +) /* monkestation edit: add permabrig-only access */ /// Command staff/secure accesses, think bridge/armoury, ai_upload, notably access to modify ID cards themselves. Do not use direct, access via SSid_access.get_flag_access_list(ACCESS_FLAG_COMMAND) #define COMMAND_ACCESS list( \ @@ -434,9 +435,11 @@ ACCESS_DETECTIVE, \ ACCESS_HOS, \ ACCESS_MECH_SECURITY, \ + ACCESS_PERMABRIG, \ ACCESS_SECURITY, \ ACCESS_WEAPONS, \ -) +) /* monkestation edit: add permabrig-only access */ + /// Name for the Medbay region. #define REGION_MEDBAY "Medbay" /// Used to seed the accesses_by_region list in SSid_access. A list of all medbay regional accesses that are overseen by the CMO. diff --git a/code/__DEFINES/ai/ai.dm b/code/__DEFINES/ai/ai.dm index eee874a041c9..8bcb27351573 100644 --- a/code/__DEFINES/ai/ai.dm +++ b/code/__DEFINES/ai/ai.dm @@ -2,11 +2,21 @@ #define GET_TARGETING_STRATEGY(targeting_type) SSai_behaviors.targeting_strategies[targeting_type] #define HAS_AI_CONTROLLER_TYPE(thing, type) istype(thing?.ai_controller, type) -#define AI_STATUS_ON 1 -#define AI_STATUS_OFF 2 +//AI controller flags +//If you add a new status, be sure to add it to the ai_controllers subsystem's ai_controllers_by_status list. +///The AI is currently active. +#define AI_STATUS_ON "ai_on" +///The AI is currently offline for any reason. +#define AI_STATUS_OFF "ai_off" +///The AI is currently in idle mode. +#define AI_STATUS_IDLE "ai_idle" ///For JPS pathing, the maximum length of a path we'll try to generate. Should be modularized depending on what we're doing later on #define AI_MAX_PATH_LENGTH 30 // 30 is possibly overkill since by default we lose interest after 14 tiles of distance, but this gives wiggle room for weaving around obstacles +#define AI_BOT_PATH_LENGTH 150 + +// How far should we, by default, be looking for interesting things to de-idle? +#define AI_DEFAULT_INTERESTING_DIST 10 ///Cooldown on planning if planning failed last time diff --git a/code/__DEFINES/ai/ai_blackboard.dm b/code/__DEFINES/ai/ai_blackboard.dm index 1e7c0e892e8c..6807990c1b6d 100644 --- a/code/__DEFINES/ai/ai_blackboard.dm +++ b/code/__DEFINES/ai/ai_blackboard.dm @@ -142,3 +142,11 @@ #define BB_BASIC_MOB_REINFORCEMENT_TARGET "BB_basic_mob_reinforcement_target" /// The next time at which this mob can call for reinforcements #define BB_BASIC_MOB_REINFORCEMENTS_COOLDOWN "BB_basic_mob_reinforcements_cooldown" + +///Text we display when we befriend someone +#define BB_FRIENDLY_MESSAGE "friendly_message" + +// Keys used by one and only one behavior +// Used to hold state without making bigass lists +/// For /datum/ai_behavior/find_potential_targets, what if any field are we using currently +#define BB_FIND_TARGETS_FIELD(type) "bb_find_targets_field_[type]" diff --git a/code/__DEFINES/ai/bot_keys.dm b/code/__DEFINES/ai/bot_keys.dm new file mode 100644 index 000000000000..5cf2e4263d42 --- /dev/null +++ b/code/__DEFINES/ai/bot_keys.dm @@ -0,0 +1,73 @@ +// bot keys +///The first beacon we find +#define BB_BEACON_TARGET "beacon_target" +///The last beacon we found, we will use its codes to find the next beacon +#define BB_PREVIOUS_BEACON_TARGET "previous_beacon_target" +///Location of whoever summoned us +#define BB_BOT_SUMMON_TARGET "bot_summon_target" +///salute messages to beepsky +#define BB_SALUTE_MESSAGES "salute_messages" +///the beepsky we will salute +#define BB_SALUTE_TARGET "salute_target" +///our announcement ability +#define BB_ANNOUNCE_ABILITY "announce_ability" +///list of our radio channels +#define BB_RADIO_CHANNEL "radio_channel" +///list of unreachable things we will temporarily ignore +#define BB_TEMPORARY_IGNORE_LIST "temporary_ignore_list" +///Last thing we attempted to reach +#define BB_LAST_ATTEMPTED_PATHING "last_attempted_pathing" + +// medbot keys +///the patient we must heal +#define BB_PATIENT_TARGET "patient_target" +///list holding our wait dialogue +#define BB_WAIT_SPEECH "wait_speech" +///what we will say to our patient after we heal them +#define BB_AFTERHEAL_SPEECH "afterheal_speech" +///things we will say when we are bored +#define BB_IDLE_SPEECH "idle_speech" +///speech unlocked after being emagged +#define BB_EMAGGED_SPEECH "emagged_speech" +///speech when we are tipped +#define BB_WORRIED_ANNOUNCEMENTS "worried_announcements" +///speech when our patient is near death +#define BB_NEAR_DEATH_SPEECH "near_death_speech" +///in crit patient we must alert medbay about +#define BB_PATIENT_IN_CRIT "patient_in_crit" + +// cleanbots +///key that holds the foaming ability +#define BB_CLEANBOT_FOAM "cleanbot_foam" +///key that holds decals we hunt +#define BB_CLEANABLE_DECALS "cleanable_decals" +///key that holds blood we hunt +#define BB_CLEANABLE_BLOOD "cleanable_blood" +///key that holds pests we hunt +#define BB_HUNTABLE_PESTS "huntable_pests" +///key that holds emagged speech +#define BB_CLEANBOT_EMAGGED_PHRASES "emagged_phrases" +///key that holds drawings we hunt +#define BB_CLEANABLE_DRAWINGS "cleanable_drawings" +///Key that holds our clean target +#define BB_CLEAN_TARGET "clean_target" +///key that holds the janitor we will befriend +#define BB_FRIENDLY_JANITOR "friendly_janitor" +///key that holds the victim we will spray +#define BB_ACID_SPRAY_TARGET "acid_spray_target" +///key that holds trash we will burn +#define BB_HUNTABLE_TRASH "huntable_trash" + +//hygienebots +///key that holds our threats +#define BB_WASH_THREATS "wash_threats" +///key that holds speech when we find our target +#define BB_WASH_FOUND "wash_found" +///key that holds speech when we cleaned our target +#define BB_WASH_DONE "wash_done" +///key that holds target we will wash +#define BB_WASH_TARGET "wash_target" +///key that holds how frustrated we are when target is running away +#define BB_WASH_FRUSTRATION "wash_frustration" +///key that holds cooldown after we finish cleaning something, so we dont immediately run off to patrol +#define BB_POST_CLEAN_COOLDOWN "post_clean_cooldown" diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 0eba623df047..4255724f00fb 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -340,3 +340,23 @@ GLOBAL_LIST_INIT(human_invader_antagonists, list( /// For changelings, this is how many recent say lines are retained when absorbing a mob #define LING_ABSORB_RECENT_SPEECH 8 + +// Various abductor equipment modes. + +#define VEST_STEALTH 1 +#define VEST_COMBAT 2 + +#define GIZMO_SCAN 1 +#define GIZMO_MARK 2 + +#define MIND_DEVICE_MESSAGE 1 +#define MIND_DEVICE_CONTROL 2 + +#define TOOLSET_MEDICAL 1 +#define TOOLSET_HACKING 2 + +#define BATON_STUN 0 +#define BATON_SLEEP 1 +#define BATON_CUFF 2 +#define BATON_PROBE 3 +#define BATON_MODES 4 diff --git a/code/__DEFINES/bloodsuckers.dm b/code/__DEFINES/bloodsuckers.dm index 464fb6a900ac..6860403ba89f 100644 --- a/code/__DEFINES/bloodsuckers.dm +++ b/code/__DEFINES/bloodsuckers.dm @@ -2,7 +2,7 @@ //#define BLOODSUCKER_TESTING /// You have special interactions with Bloodsuckers -#define TRAIT_BLOODSUCKER_HUNTER "bloodsucker_hunter" +#define TRAIT_OCCULTIST "occultist" /** * Blood-level defines diff --git a/code/__DEFINES/dcs/signals/signals_ai_controller.dm b/code/__DEFINES/dcs/signals/signals_ai_controller.dm index fa442c3d186a..a405ad65e873 100644 --- a/code/__DEFINES/dcs/signals/signals_ai_controller.dm +++ b/code/__DEFINES/dcs/signals/signals_ai_controller.dm @@ -1,3 +1,7 @@ ///sent from ai controllers when they possess a pawn: (datum/ai_controller/source_controller) #define COMSIG_AI_CONTROLLER_POSSESSED_PAWN "ai_controller_possessed_pawn" +///sent from ai controllers when they pick behaviors: (list/datum/ai_behavior/old_behaviors, list/datum/ai_behavior/new_behaviors) +#define COMSIG_AI_CONTROLLER_PICKED_BEHAVIORS "ai_controller_picked_behaviors" +///sent from ai controllers when a behavior is inserted into the queue: (list/new_arguments) +#define AI_CONTROLLER_BEHAVIOR_QUEUED(type) "ai_controller_behavior_queued_[type]" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm index 40aca24b600d..d7b6e903930f 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm @@ -3,8 +3,6 @@ // All signals send the source datum of the signal as the first argument // /atom signals -///from base of atom/proc/Initialize(): sent any time a new atom is created in this atom -#define COMSIG_ATOM_INITIALIZED_ON "atom_initialized_on" //from SSatoms InitAtom - Only if the atom was not deleted or failed initialization #define COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZE "atom_init_success" //from SSatoms InitAtom - Only if the atom was not deleted or failed initialization and has a loc diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm index 4c7270a38d92..ea2673379bd3 100644 --- a/code/__DEFINES/dcs/signals/signals_global.dm +++ b/code/__DEFINES/dcs/signals/signals_global.dm @@ -73,6 +73,8 @@ #define COMSIG_GLOB_NUKE_DEVICE_ARMED "!nuclear_device_armed" /// global signal sent when a nuclear device is disarmed (/obj/machinery/nuclearbomb/nuke/disarmed_nuke) #define COMSIG_GLOB_NUKE_DEVICE_DISARMED "!nuclear_device_disarmed" +/// global signal sent when a nuclear device is detonating (/obj/machinery/nuclearbomb/nuke/exploding_nuke) +#define COMSIG_GLOB_NUKE_DEVICE_DETONATING "!nuclear_device_detonating" /// Global signal sent when a light mechanism is completed (try_id) #define COMSIG_GLOB_LIGHT_MECHANISM_COMPLETED "!light_mechanism_completed" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm index a04b8e751a0c..6cca71839a92 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm @@ -4,3 +4,5 @@ /// Signal sent when a blackboard key is cleared #define COMSIG_AI_BLACKBOARD_KEY_CLEARED(blackboard_key) "ai_blackboard_key_clear_[blackboard_key]" +///Signal sent when a bot is reset +#define COMSIG_BOT_RESET "bot_reset" diff --git a/code/__DEFINES/dcs/signals/signals_moveloop.dm b/code/__DEFINES/dcs/signals/signals_moveloop.dm index 38ab63a59698..8a354f8bfbb1 100644 --- a/code/__DEFINES/dcs/signals/signals_moveloop.dm +++ b/code/__DEFINES/dcs/signals/signals_moveloop.dm @@ -9,3 +9,5 @@ #define COMSIG_MOVELOOP_POSTPROCESS "moveloop_postprocess" //from [/datum/move_loop/has_target/jps/recalculate_path] (): #define COMSIG_MOVELOOP_JPS_REPATH "moveloop_jps_repath" +///from [/datum/move_loop/has_target/jps/on_finish_pathing] +#define COMSIG_MOVELOOP_JPS_FINISHED_PATHING "moveloop_jps_finished_pathing" diff --git a/code/__DEFINES/icon_smoothing.dm b/code/__DEFINES/icon_smoothing.dm index 22a4a91cab40..1bb7712f6f2b 100644 --- a/code/__DEFINES/icon_smoothing.dm +++ b/code/__DEFINES/icon_smoothing.dm @@ -174,6 +174,7 @@ DEFINE_BITFIELD(smoothing_flags, list( #define SMOOTH_GROUP_GRILLE S_OBJ(75) +#define SMOOTH_GROUP_WAXWALL S_OBJ(76) /// Performs the work to set smoothing_groups and canSmoothWith. /// An inlined function used in both turf/Initialize and atom/Initialize. #define SETUP_SMOOTHING(...) \ diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index af7d22f4a911..991d1d03d7e4 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -155,7 +155,9 @@ GLOBAL_LIST_INIT(turfs_openspace, typecacheof(list( #define isrevenant(A) (istype(A, /mob/living/basic/revenant)) -#define isbot(A) (istype(A, /mob/living/simple_animal/bot)) +#define isbot(A) (istype(A, /mob/living/simple_animal/bot) || istype(A, /mob/living/basic/bot)) + +#define isbasicbot(A) (istype(A, /mob/living/basic/bot)) #define ismouse(A) (istype(A, /mob/living/basic/mouse)) diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm index 658d363ded37..8ab7647d47e8 100644 --- a/code/__DEFINES/jobs.dm +++ b/code/__DEFINES/jobs.dm @@ -152,7 +152,8 @@ #define JOB_DISPLAY_ORDER_WARDEN 33 #define JOB_DISPLAY_ORDER_DETECTIVE 34 #define JOB_DISPLAY_ORDER_SECURITY_OFFICER 35 -#define JOB_DISPLAY_ORDER_PRISONER 36 +#define JOB_DISPLAY_ORDER_SECURITY_ASSISTANT 36 // monkestation edit: security assistants +#define JOB_DISPLAY_ORDER_PRISONER 37 #define DEPARTMENT_UNASSIGNED "No Department" diff --git a/code/__DEFINES/language.dm b/code/__DEFINES/language.dm index 8f9c76299bca..e3f775b8fa39 100644 --- a/code/__DEFINES/language.dm +++ b/code/__DEFINES/language.dm @@ -19,6 +19,7 @@ #define LANGUAGE_MALF "malf" #define LANGUAGE_PIRATE "pirate" #define LANGUAGE_MASTER "master" +#define LANGUAGE_PAI "pai" #define LANGUAGE_SOFTWARE "software" #define LANGUAGE_STONER "stoner" #define LANGUAGE_VOICECHANGE "voicechange" diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 27a48520a404..172f2cdf4682 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -79,6 +79,7 @@ // Log entry keys #define LOG_ENTRY_KEY_TIMESTAMP "ts" +#define LOG_ENTRY_KEY_ROUNDID "round_id" #define LOG_ENTRY_KEY_CATEGORY "cat" #define LOG_ENTRY_KEY_MESSAGE "msg" #define LOG_ENTRY_KEY_DATA "data" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 2618161affc6..2f9d7f60588f 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -348,7 +348,6 @@ #define AI_ON 1 #define AI_IDLE 2 #define AI_OFF 3 -#define AI_Z_OFF 4 //The range at which a mob should wake up if you spawn into the z level near it #define MAX_SIMPLEMOB_WAKEUP_RANGE 5 @@ -371,6 +370,8 @@ #define GALOSHES_DONT_HELP (1<<3) /// Slip works even if you're already on the ground #define SLIP_WHEN_CRAWLING (1<<4) +/// the mob won't slip if the turf has the TRAIT_TURF_IGNORE_SLIPPERY trait. +#define SLIPPERY_TURF (1<<5) #define MAX_CHICKENS 50 @@ -742,6 +743,7 @@ GLOBAL_LIST_INIT(layers_to_offset, list( "[BELT_LAYER]" = LOWER_BODY, // Everything below looks fine with or without a filter, so we can skip it and just offset // (In practice they'd be fine if they got a filter but we can optimize a bit by not.) + /* monkestation edit: fix some weirdness with heights, most notably gloves "[GLASSES_LAYER]" = UPPER_BODY, "[ABOVE_BODY_FRONT_GLASSES_LAYER]" = UPPER_BODY, // currently unused "[ABOVE_BODY_FRONT_HEAD_LAYER]" = UPPER_BODY, // only used for head stuff @@ -751,6 +753,7 @@ GLOBAL_LIST_INIT(layers_to_offset, list( "[ID_CARD_LAYER]" = UPPER_BODY, // unused "[ID_LAYER]" = UPPER_BODY, "[FACEMASK_LAYER]" = UPPER_BODY, + monkestation end */ "[FACE_LAYER]" = UPPER_BODY, // These two are cached, and have their appearance shared(?), so it's safer to just not touch it "[MUTATIONS_LAYER]" = NO_MODIFY, diff --git a/code/__DEFINES/research/anomalies.dm b/code/__DEFINES/research/anomalies.dm index 3a6b02f91888..c5b626cf9249 100644 --- a/code/__DEFINES/research/anomalies.dm +++ b/code/__DEFINES/research/anomalies.dm @@ -24,12 +24,20 @@ GLOBAL_LIST_INIT(bioscrambler_parts_blacklist, typecacheof(list( /obj/item/bodypart/leg/left/monkey, /obj/item/bodypart/leg/right/monkey, /obj/item/bodypart/leg/left/tallboy, - /obj/item/bodypart/leg/right/tallboy, + /obj/item/bodypart/leg/right/tallboy ))) +/// Blacklist of limb IDs which should not appear when bioscrambled, mostly because they looks awful and buggy. +GLOBAL_LIST_INIT(bioscrambler_limb_id_blacklist, list( + BODYPART_ID_PSYKER, + SPECIES_SIMIAN, + SPECIES_MONKEY, + SPECIES_GOBLIN +)) + /// Blacklist of organs which should not appear when bioscrambled. /// Either will look terrible outside of intended host, give you magical powers, are irreversible, or kill you -GLOBAL_LIST_INIT(bioscrambler_organs_blacklist, typecacheof(list ( +GLOBAL_LIST_INIT(bioscrambler_organs_blacklist, typecacheof(list( /obj/item/organ/external/pod_hair, /obj/item/organ/external/spines, /obj/item/organ/external/wings, @@ -38,14 +46,35 @@ GLOBAL_LIST_INIT(bioscrambler_organs_blacklist, typecacheof(list ( /obj/item/organ/internal/brain, /obj/item/organ/internal/body_egg, /obj/item/organ/internal/cyberimp, + /obj/item/organ/internal/ears/dullahan, + /obj/item/organ/internal/eyes/dullahan, /obj/item/organ/internal/heart/cursed, /obj/item/organ/internal/heart/demon, /obj/item/organ/internal/lungs, /obj/item/organ/internal/monster_core, + /obj/item/organ/internal/tongue/dullahan, /obj/item/organ/internal/vocal_cords/colossus, /obj/item/organ/internal/zombie_infection, - /obj/item/organ/internal/empowered_borer_egg, // MONKESTATION ADDITION -- CORTICAL_BORERS -))) + // monkestation additions + /obj/item/organ/internal/heart/gland/egg, + /obj/item/organ/internal/heart/gland/electric, + /obj/item/organ/internal/heart/gland/mindshock, + /obj/item/organ/internal/heart/gland/plasma, + /obj/item/organ/internal/heart/gland/quantum, + /obj/item/organ/internal/heart/gland/slime, + /obj/item/organ/internal/heart/gland/trauma, + /obj/item/organ/internal/heart/gland/viral, + /obj/item/organ/external/anime_head, + /obj/item/organ/external/anime_middle, + /obj/item/organ/external/anime_bottom, + /obj/item/organ/internal/tongue/fly, + /obj/item/organ/internal/stomach/fly, + /obj/item/organ/internal/legion_tumour, + /obj/item/organ/internal/liver/gondola, + /obj/item/organ/internal/heart/gondola, + /obj/item/organ/internal/tongue/gondola, + /obj/item/organ/internal/empowered_borer_egg +)) - subtypesof(/obj/item/organ/external/wings/functional) - typesof(/obj/item/organ/external/wings/moth)) /// List of body parts we can apply to people GLOBAL_LIST_EMPTY(bioscrambler_valid_parts) diff --git a/code/__DEFINES/robots.dm b/code/__DEFINES/robots.dm index 67c79553f358..4bef079f1f20 100644 --- a/code/__DEFINES/robots.dm +++ b/code/__DEFINES/robots.dm @@ -101,6 +101,22 @@ ///The Bot has been hacked by a Silicon, emagging them, but revertable. #define BOT_COVER_HACKED (1<<3) + +//basic bots defines + +///is our maintenancle panel currently open +#define BOT_MAINTS_PANEL_OPEN (1<<0) +///is our control panel currently open +#define BOT_CONTROL_PANEL_OPEN (1<<1) + +///bitfield for our access flags +DEFINE_BITFIELD(bot_access_flags, list( + "MAINTS_OPEN" = BOT_MAINTS_PANEL_OPEN, + "CONTROL_OPEN" = BOT_CONTROL_PANEL_OPEN, + "COVER_EMAGGED" = BOT_COVER_EMAGGED, + "COVER_HACKED" = BOT_COVER_HACKED, +)) + //Bot types /// Secutritrons (Beepsky) #define SEC_BOT "Securitron" @@ -130,8 +146,6 @@ #define BOT_IDLE "Idle" /// Found target, hunting #define BOT_HUNT "In Pursuit" -/// Currently tipped over. -#define BOT_TIPPED "Tipped" /// Start patrol #define BOT_START_PATROL "Beginning Patrol" /// Patrolling @@ -202,11 +216,17 @@ DEFINE_BITFIELD(security_mode_flags, list( #define MEDBOT_STATIONARY_MODE (1<<1) ///Whether the bot will randomly speak from time to time. This will not actually prevent all speech. #define MEDBOT_SPEAK_MODE (1<<2) +/// is the bot currently tipped over? +#define MEDBOT_TIPPED_MODE (1<<3) + +///can we heal all damage? +#define HEAL_ALL_DAMAGE "all_damage" DEFINE_BITFIELD(medical_mode_flags, list( "MEDBOT_DECLARE_CRIT" = MEDBOT_DECLARE_CRIT, "MEDBOT_STATIONARY_MODE" = MEDBOT_STATIONARY_MODE, "MEDBOT_SPEAK_MODE" = MEDBOT_SPEAK_MODE, + "MEDBOT_TIPPED_MODE" = MEDBOT_TIPPED_MODE, )) //cleanBOT defines on what to clean @@ -227,3 +247,76 @@ DEFINE_BITFIELD(janitor_mode_flags, list( #define NAVBEACON_PATROL_NEXT "next_patrol" #define NAVBEACON_DELIVERY_MODE "delivery" #define NAVBEACON_DELIVERY_DIRECTION "dir" + +// Defines for lines that bots can speak which also have corresponding voice lines + +#define ED209_VOICED_DOWN_WEAPONS "Please put down your weapon. You have 20 seconds to comply." + +#define HONKBOT_VOICED_HONK_HAPPY "Honk!" +#define HONKBOT_VOICED_HONK_SAD "Honk..." + +#define BEEPSKY_VOICED_CRIMINAL_DETECTED "Criminal detected!" +#define BEEPSKY_VOICED_FREEZE "Freeze, scumbag!" +#define BEEPSKY_VOICED_JUSTICE "Prepare for justice!" +#define BEEPSKY_VOICED_YOUR_MOVE "Your move, creep." +#define BEEPSKY_VOICED_I_AM_THE_LAW "I am the law!" +#define BEEPSKY_VOICED_SECURE_DAY "Have a secure day." + +#define FIREBOT_VOICED_FIRE_DETECTED "Fire detected!" +#define FIREBOT_VOICED_STOP_DROP "Stop, drop and roll!" +#define FIREBOT_VOICED_EXTINGUISHING "Extinguishing!" +#define FIREBOT_VOICED_NO_FIRES "No fires detected." +#define FIREBOT_VOICED_ONLY_YOU "Only you can prevent station fires." +#define FIREBOT_VOICED_TEMPERATURE_NOMINAL "Temperature nominal." +#define FIREBOT_VOICED_KEEP_COOL "Keep it cool." + +#define HYGIENEBOT_VOICED_UNHYGIENIC "Unhygienic client found. Please stand still so I can clean you." +#define HYGIENEBOT_VOICED_ENJOY_DAY "Enjoy your clean and tidy day!" +#define HYGIENEBOT_VOICED_THREAT_AIRLOCK "Either you stop running or I will fucking drag you out of an airlock." +#define HYGIENEBOT_VOICED_FOUL_SMELL "Get back here you foul smelling fucker." +#define HYGIENEBOT_VOICED_TROGLODYTE "I just want to fucking clean you you troglodyte." +#define HYGIENEBOT_VOICED_GREEN_CLOUD "If you don't come back here I'll put a green cloud around you cunt." +#define HYGIENEBOT_VOICED_ARSEHOLE "Just fucking let me clean you you arsehole!" +#define HYGIENEBOT_VOICED_THREAT_ARTERIES "STOP RUNNING OR I WILL CUT YOUR ARTERIES!" +#define HYGIENEBOT_VOICED_STOP_RUNNING "STOP. RUNNING." +#define HYGIENEBOT_VOICED_FUCKING_FINALLY "Fucking finally." +#define HYGIENEBOT_VOICED_THANK_GOD "Thank god, you finally stopped." +#define HYGIENEBOT_VOICED_DEGENERATE "Well about fucking time you degenerate." + +#define MEDIBOT_VOICED_HOLD_ON "Hey! Hold on, I'm coming." +#define MEDIBOT_VOICED_WANT_TO_HELP "Wait! I want to help!" +#define MEDIBOT_VOICED_YOU_ARE_INJURED "You appear to be injured!" +#define MEDIBOT_VOICED_ALL_PATCHED_UP "All patched up!" +#define MEDIBOT_VOICED_APPLE_A_DAY "An apple a day keeps me away." +#define MEDIBOT_VOICED_FEEL_BETTER "Feel better soon!" +#define MEDIBOT_VOICED_STAY_WITH_ME "No! Stay with me!" +#define MEDIBOT_VOICED_LIVE "Live, damnit! LIVE!" +#define MEDIBOT_VOICED_NEVER_LOST "I...I've never lost a patient before. Not today, I mean." +#define MEDIBOT_VOICED_DELICIOUS "Delicious!" +#define MEDIBOT_VOICED_PLASTIC_SURGEON "I knew it, I should've been a plastic surgeon." +#define MEDIBOT_VOICED_MASK_ON "Radar, put a mask on!" +#define MEDIBOT_VOICED_ALWAYS_A_CATCH "There's always a catch, and I'm the best there is." +#define MEDIBOT_VOICED_LIKE_FLIES "What kind of medbay is this? Everyone's dropping like flies." +#define MEDIBOT_VOICED_SUFFER "Why are we still here? Just to suffer?" +#define MEDIBOT_VOICED_FUCK_YOU "Fuck you." +#define MEDIBOT_VOICED_NOT_A_GAME "Turn off your computer. This is not a game." +#define MEDIBOT_VOICED_IM_DIFFERENT "I'm different!" +#define MEDIBOT_VOICED_FOURTH_WALL "Close Dreamseeker.exe now. Or else." +#define MEDIBOT_VOICED_SHINDEMASHOU "Shindemashou." +#define MEDIBOT_VOICED_WAIT "Hey, wait..." +#define MEDIBOT_VOICED_DONT "Please don't..." +#define MEDIBOT_VOICED_TRUSTED_YOU "I trusted you..." +#define MEDIBOT_VOICED_NO_SAD "Nooo..." +#define MEDIBOT_VOICED_OH_FUCK "Oh fuck-" +#define MEDIBOT_VOICED_FORGIVE "I forgive you." +#define MEDIBOT_VOICED_THANKS "Thank you!" +#define MEDIBOT_VOICED_GOOD_PERSON "You are a good person." +#define MEDIBOT_VOICED_BEHAVIOUR_REPORTED "Your behavior has been reported, have a nice day." +#define MEDIBOT_VOICED_ASSISTANCE "I require assistance." +#define MEDIBOT_VOICED_PUT_BACK "Please put me back." +#define MEDIBOT_VOICED_IM_SCARED "Please, I am scared!" +#define MEDIBOT_VOICED_NEED_HELP "I don't like this, I need help!" +#define MEDIBOT_VOICED_THIS_HURTS "This hurts, my pain is real!" +#define MEDIBOT_VOICED_THE_END "Is this the end?" +#define MEDIBOT_VOICED_NOOO "Nooo!" +#define MEDIBOT_VOICED_CHICKEN "LOOK AT ME?! i am a chicken." diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 82858223e1db..a6a2d61c495b 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -197,7 +197,6 @@ // If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child) #define FIRE_PRIORITY_PING 10 #define FIRE_PRIORITY_TWITCH 10 -#define FIRE_PRIORITY_IDLE_NPC 10 #define FIRE_PRIORITY_SERVER_MAINT 10 #define FIRE_PRIORITY_RESEARCH 10 #define FIRE_PRIORITY_VIS 10 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index a15e21fd8110..02eb071670ab 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -184,6 +184,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_FENCE_CLIMBER "can_climb_fences" /// means that you can't use weapons with normal trigger guards. #define TRAIT_CHUNKYFINGERS "chunkyfingers" +#define TRAIT_CHUNKYFINGERS_IGNORE_BATON "chunkyfingers_ignore_baton" #define TRAIT_DUMB "dumb" /// Whether a mob is dexterous enough to use machines and certain items or not. #define TRAIT_ADVANCEDTOOLUSER "advancedtooluser" @@ -635,6 +636,15 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// This movable atom has the explosive block element #define TRAIT_BLOCKING_EXPLOSIVES "blocking_explosives" +///Lava will be safe to cross while it has this trait. +#define TRAIT_LAVA_STOPPED "lava_stopped" +///Chasms will be safe to cross while they've this trait. +#define TRAIT_CHASM_STOPPED "chasm_stopped" +///Turf slowdown will be ignored when this trait is added to a turf. +#define TRAIT_TURF_IGNORE_SLOWDOWN "turf_ignore_slowdown" +///Mobs won't slip on a wet turf while it has this trait +#define TRAIT_TURF_IGNORE_SLIPPERY "turf_ignore_slippery" + /// Mobs with this trait can't send the mining shuttle console when used outside the station itself #define TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION "forbid_mining_shuttle_console_outside_station" @@ -817,6 +827,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///Trait applied to turfs when an atmos holosign is placed on them. It will stop firedoors from closing. #define TRAIT_FIREDOOR_STOP "firedoor_stop" +///Trait applied to turf blocked by a containment field +#define TRAIT_CONTAINMENT_FIELD "containment_field" + /// Trait applied when the MMI component is added to an [/obj/item/integrated_circuit] #define TRAIT_COMPONENT_MMI "component_mmi" @@ -1300,3 +1313,19 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai ///trait determines if this mob can breed given by /datum/component/breeding #define TRAIT_MOB_BREEDER "mob_breeder" +///trait given to food that can be baked by /datum/component/bakeable +#define TRAIT_BAKEABLE "bakeable" + +/// Trait given to foam darts that have an insert in them +#define TRAIT_DART_HAS_INSERT "dart_has_insert" + +///Trait granted by janitor skillchip, allows communication with cleanbots +#define TRAIT_CLEANBOT_WHISPERER "cleanbot_whisperer" + +/// Trait given when a mob is currently in invisimin mode +#define TRAIT_INVISIMIN "invisimin" + +///Trait given when a mob has been tipped +#define TRAIT_MOB_TIPPED "mob_tipped" + +// END TRAIT DEFINES diff --git a/code/__DEFINES/~monkestation/access.dm b/code/__DEFINES/~monkestation/access.dm index 432c65e208e8..49f1c04f9401 100644 --- a/code/__DEFINES/~monkestation/access.dm +++ b/code/__DEFINES/~monkestation/access.dm @@ -1 +1,6 @@ +/// Access to clockwork cult stuff. + //Special, for anything that's basically internal #define ACCESS_CLOCKCULT "clockcult" + +/// Access to permabrig. [ACCESS_BRIG] also grants permabrig access, this is just for ONLY permabrig accesss, for security assistants. +#define ACCESS_PERMABRIG "permabrig" diff --git a/code/__DEFINES/~monkestation/antagonists.dm b/code/__DEFINES/~monkestation/antagonists.dm index 656868ba893e..f14f07cac1c8 100644 --- a/code/__DEFINES/~monkestation/antagonists.dm +++ b/code/__DEFINES/~monkestation/antagonists.dm @@ -1,3 +1,8 @@ +// Monster Hunter stuff +#define upgraded_val(x,y) ( CEILING((x * (1.07 ** y)), 1) ) +#define CALIBER_BLOODSILVER "bloodsilver" +#define WEAPON_UPGRADE "weapon_upgrade" + /// List of areas blacklisted from area based traitor objectives #define TRAITOR_OBJECTIVE_BLACKLISTED_AREAS list(/area/station/engineering/hallway, \ /area/station/engineering/lobby, \ diff --git a/code/__DEFINES/~monkestation/atom_hud.dm b/code/__DEFINES/~monkestation/atom_hud.dm new file mode 100644 index 000000000000..6b14bf0eee9e --- /dev/null +++ b/code/__DEFINES/~monkestation/atom_hud.dm @@ -0,0 +1 @@ +#define SECHUD_SECURITY_ASSISTANT "hudsecass" diff --git a/code/__DEFINES/~monkestation/botany.dm b/code/__DEFINES/~monkestation/botany.dm new file mode 100644 index 000000000000..6cd5b1f0ea3b --- /dev/null +++ b/code/__DEFINES/~monkestation/botany.dm @@ -0,0 +1,44 @@ +#define COMSIG_GROWING_WATER_UPDATE "growing_water_update" +#define COMSIG_PLANT_TRY_POLLINATE "try_pollinate" +#define COMSIG_PLANT_TRY_HARVEST "plant_try_harvest" +#define COMSIG_PLANT_BUILD_IMAGE "plant_build_image" +#define COMSIG_PLANT_ADJUST_WEED "plant_adjust_weeds" +#define COMSIG_PLANT_GROWTH_PROCESS "process_plant_growth" +#define COMSIG_TRY_HARVEST_SEEDS "try_harvest_seeds" +#define COMSIG_TRY_PLANT_SEED "try_plant_seeds" +#define COMSIG_PLANT_CHANGE_PLANTER "plant_change_planter" +#define COMSIG_PLANT_SENDING_IMAGE "plant_sending_image" +#define COMSIG_TRY_POLLINATE "try_pollinate_grower" +#define COMSIG_ADJUST_PLANT_HEALTH "adjust_plant_health" +#define COMSIG_GROWING_ADJUST_TOXIN "adjust_growing_toxicity" +#define COMSIG_GROWING_ADJUST_PEST "adjust_growing_pests" +#define COMSIG_PLANT_UPDATE_HEALTH_COLOR "update_health_color" +#define COMSIG_GROWING_ADJUST_WEED "adjust_growing_weed" +#define COMSIG_GROWER_ADJUST_SELFGROW "adjust_grower_selfgrow" +#define COMSIG_GROWER_INCREASE_WORK_PROCESSES "increase_work_process_grower" +#define COMSIG_NUTRIENT_UPDATE "nutrient_update" +#define COMSIG_TOXICITY_UPDATE "toxicity_update" +#define COMSIG_PEST_UPDATE "pest_update" +#define COMSIG_WEEDS_UPDATE "weeds_update" +#define COMSIG_GROWER_SET_HARVESTABLE "set_harvestable_grower" +#define COMSIG_REMOVE_PLANT "remove_plant_grower" +#define REMOVE_PLANT_VISUALS "remove_plant_visuals" +#define COMSIG_GROWER_CHECK_POLLINATED "check_grower_pollinated" +#define COMSIG_ATTEMPT_BIOBOOST "attempt_bioboost" +#define COMSIG_PLANTER_REMOVE_PLANTS "remove_all_plants" +#define COMSIG_TOGGLE_BIOBOOST "toggle_bioboost" +#define COMSIG_REAGENT_CACHE_ADD_ATTEMPT "reagent_cache_attempt" +#define COMSIG_REAGENT_PRE_TRANS_TO "reagent_pre_trans" +#define COMSIG_GROWING_TRY_SECATEUR "try_secateur" +#define COMSIG_PLANT_TRY_SECATEUR "plant_try_secateur" +#define COMSIG_GROWER_TRY_GRAFT "plant_grower_try_graft" + +#define SHOW_WATER (1<<0) +#define SHOW_HEALTH (1<<1) +#define SHOW_WEED (1<<2) +#define SHOW_PEST (1<<3) +#define SHOW_TOXIC (1<<4) +#define SHOW_NUTRIENT (1<<5) +#define SHOW_HARVEST (1<<6) + +#define SPECIES_APID "apid" diff --git a/code/__DEFINES/~monkestation/jobs.dm b/code/__DEFINES/~monkestation/jobs.dm new file mode 100644 index 000000000000..2a2c5ae5e3a4 --- /dev/null +++ b/code/__DEFINES/~monkestation/jobs.dm @@ -0,0 +1 @@ +#define JOB_SECURITY_ASSISTANT "Security Assistant" diff --git a/code/__DEFINES/~monkestation/slimes.dm b/code/__DEFINES/~monkestation/slimes.dm index 0ef955442c3e..ac49e1b0ef4c 100644 --- a/code/__DEFINES/~monkestation/slimes.dm +++ b/code/__DEFINES/~monkestation/slimes.dm @@ -59,20 +59,6 @@ #define EMOTION_SUPRISED "suprised" #define EMOTION_HUNGRY "hungry" -///key that holds decals we hunt -#define BB_CLEANABLE_DECALS "cleanable_decals" -///key that holds blood we hunt -#define BB_CLEANABLE_BLOOD "cleanable_blood" -///key that holds pests we hunt -#define BB_HUNTABLE_PESTS "huntable_pests" -///key that holds drawings we hunt -#define BB_CLEANABLE_DRAWINGS "cleanable_drawings" -///Key that holds our clean target -#define BB_CLEAN_TARGET "clean_target" -///key that holds trash we will burn -#define BB_HUNTABLE_TRASH "huntable_trash" - - #define FOOD_CHANGE "food_change" #define ENVIRONMENT_CHANGE "enviro_change" #define BEHAVIOUR_CHANGE "behaviour_change" diff --git a/code/__DEFINES/~monkestation/traits.dm b/code/__DEFINES/~monkestation/traits.dm index 91ef22414513..2eb3c1442a24 100644 --- a/code/__DEFINES/~monkestation/traits.dm +++ b/code/__DEFINES/~monkestation/traits.dm @@ -22,3 +22,5 @@ // /obj/item /// Whether a storage item can be compressed by the bluespace compression kit, without the usual storage limitation. #define TRAIT_BYPASS_COMPRESS_CHECK "can_compress_anyways" + +#define ABDUCTOR_GLAND_VENTCRAWLING_TRAIT "abductor_gland_ventcrawling" diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index c6cf55d002a0..86e0f81367c7 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -33,6 +33,7 @@ init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_wings, GLOB.moth_wings_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_antennae, GLOB.moth_antennae_list) init_sprite_accessory_subtypes(/datum/sprite_accessory/moth_markings, GLOB.moth_markings_list) + init_sprite_accessory_subtypes(/datum/sprite_accessory/apid_antenna, GLOB.apid_antenna_list) //Monkestation Addition init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_screens, GLOB.ipc_screens_list) //Monkestation Addition init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_antennas, GLOB.ipc_antennas_list) //Monkestation Addition init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_chassis, GLOB.ipc_chassis_list) //Monkestation Addition @@ -45,6 +46,7 @@ init_sprite_accessory_subtypes(/datum/sprite_accessory/arachnid_chelicerae, GLOB.arachnid_chelicerae_list) //Monkestation Addition init_sprite_accessory_subtypes(/datum/sprite_accessory/goblin_ears, GLOB.goblin_ears_list) //Monkestation Addition init_sprite_accessory_subtypes(/datum/sprite_accessory/floran_leaves, GLOB.floran_leaves_list) //Monkestation Addition + init_sprite_accessory_subtypes(/datum/sprite_accessory/apid_wings, GLOB.apid_wings_list) //Monkestation Addition /// Inits GLOB.species_list. Not using GLOBAL_LIST_INIT b/c it depends on GLOB.string_lists /proc/init_species_list() diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 07ffc03e49a6..c6ecf3f4881d 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -90,6 +90,10 @@ if(!GLOB.pod_hair_list.len) init_sprite_accessory_subtypes(/datum/sprite_accessory/pod_hair, GLOB.pod_hair_list) //Monkestation Addition Start + if(!GLOB.apid_antenna_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/apid_antenna, GLOB.apid_antenna_list) + if(!GLOB.apid_wings_list.len) + init_sprite_accessory_subtypes(/datum/sprite_accessory/apid_wings, GLOB.apid_wings_list) if(!GLOB.ipc_screens_list.len) init_sprite_accessory_subtypes(/datum/sprite_accessory/ipc_screens, GLOB.ipc_screens_list) if(!GLOB.ipc_antennas_list.len) @@ -133,6 +137,8 @@ "moth_wings" = pick(GLOB.moth_wings_list), "moth_antennae" = pick(GLOB.moth_antennae_list), "moth_markings" = pick(GLOB.moth_markings_list), + "apid_antenna" = pick(GLOB.apid_antenna_list), //Monkestation Addition + "apid_wings" = pick(GLOB.apid_wings_list), //Monkestation Addition "ipc_screen" = pick(GLOB.ipc_screens_list), //Monkestation Addition "ipc_antenna" = pick(GLOB.ipc_antennas_list), //Monkestation Addition "ipc_chassis" = pick(GLOB.ipc_chassis_list), //Monkestation Addition diff --git a/code/__HELPERS/~monkestation-helpers/antags.dm b/code/__HELPERS/~monkestation-helpers/antags.dm new file mode 100644 index 000000000000..02dde8b9646c --- /dev/null +++ b/code/__HELPERS/~monkestation-helpers/antags.dm @@ -0,0 +1,25 @@ +/// List of antagonists that can be considered prey by monster hunters. +GLOBAL_LIST_INIT(monster_hunter_prey_antags, typecacheof(list( + /datum/antagonist/bloodsucker, + /datum/antagonist/changeling, + /datum/antagonist/heretic +))) + +/proc/is_monster_hunter_prey(datum/mind/victim) + . = FALSE + if(isliving(victim)) + var/mob/living/living_victim = victim + victim = living_victim.mind + if(!istype(victim) || QDELING(victim)) + return FALSE + for(var/datum/antagonist/antag as anything in victim.antag_datums) + if(is_type_in_typecache(antag, GLOB.monster_hunter_prey_antags)) + return TRUE + +/proc/get_all_monster_hunter_prey(include_dead = FALSE) + . = list() + for(var/datum/antagonist/monster as anything in GLOB.antagonists) + if(QDELETED(monster?.owner?.current) || (!include_dead && monster.owner.current.stat == DEAD)) + continue + if(is_type_in_typecache(monster, GLOB.monster_hunter_prey_antags)) + . += monster.owner diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm index 36ad9188f834..0448c8aa73ca 100644 --- a/code/_globalvars/lists/flavor_misc.dm +++ b/code/_globalvars/lists/flavor_misc.dm @@ -38,9 +38,11 @@ GLOBAL_LIST_EMPTY(wings_open_list) GLOBAL_LIST_EMPTY(moth_wings_list) GLOBAL_LIST_EMPTY(moth_antennae_list) GLOBAL_LIST_EMPTY(moth_markings_list) +GLOBAL_LIST_EMPTY(apid_antenna_list) //Monkestation Addition GLOBAL_LIST_EMPTY(ipc_screens_list) //Monkestation Addition GLOBAL_LIST_EMPTY(ipc_antennas_list) //Monkestation Addition GLOBAL_LIST_EMPTY(ipc_chassis_list) //Monkestation Addition +GLOBAL_LIST_EMPTY(apid_wings_list) //Monkestation Addition GLOBAL_LIST_EMPTY(caps_list) GLOBAL_LIST_EMPTY(pod_hair_list) GLOBAL_LIST_EMPTY(tails_list_monkey) //Monkestation Addition diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 12927d237b59..dc86dbef96f0 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -15,6 +15,7 @@ GLOBAL_LIST_INIT(dangerous_turfs, typecacheof(list( /// List of types of abstract mob which shouldn't usually exist in the world on its own if we're spawning random mobs GLOBAL_LIST_INIT(abstract_mob_types, list( /mob/living/basic/blob_minion, + /mob/living/basic/bot, /mob/living/basic/construct, /mob/living/basic/guardian, /mob/living/basic/heretic_summon, @@ -64,7 +65,7 @@ GLOBAL_LIST_EMPTY(human_list) //all instances of /mob/living/carbon/human and su GLOBAL_LIST_EMPTY(ai_list) GLOBAL_LIST_EMPTY(pai_list) GLOBAL_LIST_EMPTY(available_ai_shells) -GLOBAL_LIST_INIT(simple_animals, list(list(),list(),list(),list())) // One for each AI_* status define +GLOBAL_LIST_INIT(simple_animals, list(list(),list(),list())) // One for each AI_* status define GLOBAL_LIST_EMPTY(spidermobs) //all sentient spider mobs GLOBAL_LIST_EMPTY(bots_list) GLOBAL_LIST_EMPTY(aiEyes) diff --git a/code/_globalvars/phobias.dm b/code/_globalvars/phobias.dm index 160e3e0a4ca3..fc83ffae2e5a 100644 --- a/code/_globalvars/phobias.dm +++ b/code/_globalvars/phobias.dm @@ -69,7 +69,7 @@ GLOBAL_LIST_INIT(phobia_mobs, list( /mob/living/basic/pet/penguin, /mob/living/simple_animal/bot/secbot, )), - "doctors" = typecacheof(list(/mob/living/simple_animal/bot/medbot)), + "doctors" = typecacheof(list(/mob/living/basic/bot/medbot)), "heresy" = typecacheof(list( /mob/living/basic/heretic_summon, )), diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index 2b2ee17563d0..bc6edbad51dc 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -27,6 +27,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_BADDNA" = TRAIT_BADDNA, "TRAIT_CLUMSY" = TRAIT_CLUMSY, "TRAIT_CHUNKYFINGERS" = TRAIT_CHUNKYFINGERS, + "TRAIT_CHUNKYFINGERS_IGNORE_BATON" = TRAIT_CHUNKYFINGERS_IGNORE_BATON, "TRAIT_DUMB" = TRAIT_DUMB, "TRAIT_ADVANCEDTOOLUSER" = TRAIT_ADVANCEDTOOLUSER, "TRAIT_DISCOORDINATED_TOOL_USER" = TRAIT_DISCOORDINATED_TOOL_USER, diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 83582c4b0f6f..4bf89bac58cd 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -309,7 +309,8 @@ if(item_flags & NOBLUDGEON) return user.changeNext_move(attack_speed) - user.do_attack_animation(attacked_atom) + if(!is_reagent_container(src) || force) + user.do_attack_animation(attacked_atom) attacked_atom.attacked_by(src, user) /// Called from [/obj/item/proc/attack_atom] and [/obj/item/proc/attack] if the attack succeeds diff --git a/code/controllers/subsystem/ai_controllers.dm b/code/controllers/subsystem/ai_controllers.dm index 3d8d26531497..a6badb44a3f0 100644 --- a/code/controllers/subsystem/ai_controllers.dm +++ b/code/controllers/subsystem/ai_controllers.dm @@ -3,27 +3,43 @@ SUBSYSTEM_DEF(ai_controllers) name = "AI Controller Ticker" flags = SS_POST_FIRE_TIMING|SS_BACKGROUND priority = FIRE_PRIORITY_NPC - runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME init_order = INIT_ORDER_AI_CONTROLLERS wait = 0.5 SECONDS //Plan every half second if required, not great not terrible. + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME ///List of all ai_subtree singletons, key is the typepath while assigned value is a newly created instance of the typepath. See setup_subtrees() - var/list/ai_subtrees = list() - ///List of all ai controllers currently running - var/list/active_ai_controllers = list() + var/list/datum/ai_planning_subtree/ai_subtrees = list() + ///Assoc List of all AI statuses and all AI controllers with that status. + var/list/ai_controllers_by_status = list( + AI_STATUS_ON = list(), + AI_STATUS_OFF = list(), + AI_STATUS_IDLE = list(), + ) + ///Assoc List of all AI controllers and the Z level they are on, which we check when someone enters/leaves a Z level to turn them on/off. + var/list/ai_controllers_by_zlevel = list() + /// The tick cost of all active AI, calculated on fire. + var/cost_on + /// The tick cost of all idle AI, calculated on fire. + var/cost_idle + /datum/controller/subsystem/ai_controllers/Initialize() setup_subtrees() return SS_INIT_SUCCESS -/datum/controller/subsystem/ai_controllers/proc/setup_subtrees() - ai_subtrees = list() - for(var/subtree_type in subtypesof(/datum/ai_planning_subtree)) - var/datum/ai_planning_subtree/subtree = new subtree_type - ai_subtrees[subtree_type] = subtree +/datum/controller/subsystem/ai_controllers/stat_entry(msg) + var/list/active_list = ai_controllers_by_status[AI_STATUS_ON] + var/list/inactive_list = ai_controllers_by_status[AI_STATUS_OFF] + var/list/idle_list = ai_controllers_by_status[AI_STATUS_IDLE] + msg = "Active AIs:[length(active_list)]/[round(cost_on,1)]%|Inactive:[length(inactive_list)]|Idle:[length(idle_list)]/[round(cost_idle,1)]%" + return ..() /datum/controller/subsystem/ai_controllers/fire(resumed) - for(var/datum/ai_controller/ai_controller as anything in active_ai_controllers) + var/timer = TICK_USAGE_REAL + cost_idle = MC_AVERAGE(cost_idle, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + + timer = TICK_USAGE_REAL + for(var/datum/ai_controller/ai_controller as anything in ai_controllers_by_status[AI_STATUS_ON]) if(!COOLDOWN_FINISHED(ai_controller, failed_planning_cooldown)) continue @@ -32,3 +48,19 @@ SUBSYSTEM_DEF(ai_controllers) ai_controller.SelectBehaviors(wait * 0.1) if(!LAZYLEN(ai_controller.current_behaviors)) //Still no plan COOLDOWN_START(ai_controller, failed_planning_cooldown, AI_FAILED_PLANNING_COOLDOWN) + + cost_on = MC_AVERAGE(cost_on, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer)) + +///Creates all instances of ai_subtrees and assigns them to the ai_subtrees list. +/datum/controller/subsystem/ai_controllers/proc/setup_subtrees() + for(var/subtree_type in subtypesof(/datum/ai_planning_subtree)) + var/datum/ai_planning_subtree/subtree = new subtree_type + ai_subtrees[subtree_type] = subtree + +///Called when the max Z level was changed, updating our coverage. +/datum/controller/subsystem/ai_controllers/proc/on_max_z_changed() + if (!islist(ai_controllers_by_zlevel)) + ai_controllers_by_zlevel = new /list(world.maxz,0) + while (SSai_controllers.ai_controllers_by_zlevel.len < world.maxz) + SSai_controllers.ai_controllers_by_zlevel.len++ + SSai_controllers.ai_controllers_by_zlevel[ai_controllers_by_zlevel.len] = list() diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm index 396d5caeab2e..4b4c1a4c0417 100644 --- a/code/controllers/subsystem/garbage.dm +++ b/code/controllers/subsystem/garbage.dm @@ -56,6 +56,8 @@ SUBSYSTEM_DEF(garbage) #endif #endif + /// Toggle for enabling/disabling hard deletes. Objects that don't explicitly request hard deletion with this disabled will leak. + var/enable_hard_deletes = FALSE /datum/controller/subsystem/garbage/PreInit() InitQueues() @@ -263,10 +265,29 @@ SUBSYSTEM_DEF(garbage) queue[++queue.len] = list(queue_time, D, D.gc_destroyed) // not += for byond reasons //this is mainly to separate things profile wise. -/datum/controller/subsystem/garbage/proc/HardDelete(datum/D) +/datum/controller/subsystem/garbage/proc/HardDelete(datum/D, non_datum = FALSE, override = FALSE) + if(!D) + return + + if (!override && !enable_hard_deletes) + return + ++delslasttick ++totaldels - var/type = D.type + var/type + if (!non_datum) + type = D.type + else if (islist(D)) + type = "/list" + else if (istext(D)) + type = "string" + else if (isnum(D)) + type = "number" + else if (isfile(D)) + type = "file" + else + type = "unknown" + var/refID = text_ref(D) var/tick_usage = TICK_USAGE @@ -274,28 +295,29 @@ SUBSYSTEM_DEF(garbage) tick_usage = TICK_USAGE_TO_MS(tick_usage) var/datum/qdel_item/I = items[type] - I.hard_deletes++ - I.hard_delete_time += tick_usage - if (tick_usage > I.hard_delete_max) - I.hard_delete_max = tick_usage - if (tick_usage > highest_del_ms) - highest_del_ms = tick_usage - highest_del_type_string = "[type]" - - var/time = MS2DS(tick_usage) - - if (time > 0.1 SECONDS) - postpone(time) - var/threshold = CONFIG_GET(number/hard_deletes_overrun_threshold) - if (threshold && (time > threshold SECONDS)) - if (!(I.qdel_flags & QDEL_ITEM_ADMINS_WARNED)) - log_game("Error: [type]([refID]) took longer than [threshold] seconds to delete (took [round(time/10, 0.1)] seconds to delete)") - message_admins("Error: [type]([refID]) took longer than [threshold] seconds to delete (took [round(time/10, 0.1)] seconds to delete).") - I.qdel_flags |= QDEL_ITEM_ADMINS_WARNED - I.hard_deletes_over_threshold++ - var/overrun_limit = CONFIG_GET(number/hard_deletes_overrun_limit) - if (overrun_limit && I.hard_deletes_over_threshold >= overrun_limit) - I.qdel_flags |= QDEL_ITEM_SUSPENDED_FOR_LAG + if(I) + I.hard_deletes++ + I.hard_delete_time += tick_usage + if (tick_usage > I.hard_delete_max) + I.hard_delete_max = tick_usage + if (tick_usage > highest_del_ms) + highest_del_ms = tick_usage + highest_del_type_string = "[type]" + + var/time = MS2DS(tick_usage) + + if (time > 0.1 SECONDS) + postpone(time) + var/threshold = CONFIG_GET(number/hard_deletes_overrun_threshold) + if (threshold && (time > threshold SECONDS)) + if (!(I.qdel_flags & QDEL_ITEM_ADMINS_WARNED)) + log_game("Error: [type]([refID]) took longer than [threshold] seconds to delete (took [round(time/10, 0.1)] seconds to delete)") + message_admins("Error: [type]([refID]) took longer than [threshold] seconds to delete (took [round(time/10, 0.1)] seconds to delete).") + I.qdel_flags |= QDEL_ITEM_ADMINS_WARNED + I.hard_deletes_over_threshold++ + var/overrun_limit = CONFIG_GET(number/hard_deletes_overrun_limit) + if (overrun_limit && I.hard_deletes_over_threshold >= overrun_limit) + I.qdel_flags |= QDEL_ITEM_SUSPENDED_FOR_LAG /datum/controller/subsystem/garbage/Recover() InitQueues() //We first need to create the queues before recovering data @@ -327,7 +349,7 @@ SUBSYSTEM_DEF(garbage) /// Datums passed to this will be given a chance to clean up references to allow the GC to collect them. /proc/qdel(datum/D, force=FALSE, ...) if(!istype(D)) - del(D) + SSgarbage.HardDelete(D, TRUE, TRUE) return var/datum/qdel_item/I = SSgarbage.items[D.type] @@ -378,10 +400,10 @@ SUBSYSTEM_DEF(garbage) SSgarbage.Queue(D, GC_QUEUE_HARDDELETE) if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste. SSdemo.mark_destroyed(D) //Monkestation Edit: REPLAYS - SSgarbage.HardDelete(D) + SSgarbage.HardDelete(D, override = TRUE) #ifdef REFERENCE_TRACKING if (QDEL_HINT_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled, display all references to this object, then queue the object for deletion. - SSgarbage.Queue(D) + SSgarbage.HardDelete(D, override = TRUE) // Need to override enable_hard_deletes, stuff like /client uses this D.find_references() //This breaks ci. Consider it insurance against somehow pring reftracking on accident if (QDEL_HINT_IFFAIL_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled and the object fails to collect, display all references to this object. SSgarbage.Queue(D) diff --git a/code/controllers/subsystem/id_access.dm b/code/controllers/subsystem/id_access.dm index c7c630032c22..1b9ec128136c 100644 --- a/code/controllers/subsystem/id_access.dm +++ b/code/controllers/subsystem/id_access.dm @@ -323,6 +323,7 @@ SUBSYSTEM_DEF(id_access) desc_by_access["[ACCESS_CENT_CAPTAIN]"] = "Code Gold" desc_by_access["[ACCESS_CENT_BAR]"] = "Code Scotch" desc_by_access["[ACCESS_BIT_DEN]"] = "Bitrunner Den" + desc_by_access["[ACCESS_PERMABRIG]"] = "Permabrig" // monkestation edit: add permabrig-only access /** * Returns the access bitflags associated with any given access level. diff --git a/code/controllers/subsystem/idlenpcpool.dm b/code/controllers/subsystem/idlenpcpool.dm deleted file mode 100644 index ee98e8c4e67c..000000000000 --- a/code/controllers/subsystem/idlenpcpool.dm +++ /dev/null @@ -1,47 +0,0 @@ -SUBSYSTEM_DEF(idlenpcpool) - name = "Idling NPC Pool" - flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT - priority = FIRE_PRIORITY_IDLE_NPC - wait = 60 - runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME - - var/list/currentrun = list() - var/static/list/idle_mobs_by_zlevel[][] - -/datum/controller/subsystem/idlenpcpool/stat_entry(msg) - var/list/idlelist = GLOB.simple_animals[AI_IDLE] - var/list/zlist = GLOB.simple_animals[AI_Z_OFF] - msg = "IdleNPCS:[length(idlelist)]|Z:[length(zlist)]" - return ..() - -/datum/controller/subsystem/idlenpcpool/proc/MaxZChanged() - if (!islist(idle_mobs_by_zlevel)) - idle_mobs_by_zlevel = new /list(world.maxz,0) - while (SSidlenpcpool.idle_mobs_by_zlevel.len < world.maxz) - SSidlenpcpool.idle_mobs_by_zlevel.len++ - SSidlenpcpool.idle_mobs_by_zlevel[idle_mobs_by_zlevel.len] = list() - -/datum/controller/subsystem/idlenpcpool/fire(resumed = FALSE) - - if (!resumed) - var/list/idlelist = GLOB.simple_animals[AI_IDLE] - src.currentrun = idlelist.Copy() - - //cache for sanic speed (lists are references anyways) - var/list/currentrun = src.currentrun - - while(currentrun.len) - var/mob/living/simple_animal/SA = currentrun[currentrun.len] - --currentrun.len - if (QDELETED(SA)) - GLOB.simple_animals[AI_IDLE] -= SA - stack_trace("Found a null in simple_animals deactive list [SA.type]!") - continue - - if(!SA.ckey) - if(SA.stat != DEAD) - SA.handle_automated_movement() - if(SA.stat != DEAD) - SA.consider_wakeup() - if (MC_TICK_CHECK) - return diff --git a/code/controllers/subsystem/movement/movement_types.dm b/code/controllers/subsystem/movement/movement_types.dm index ddd7e88e68ed..89a1cbb44333 100644 --- a/code/controllers/subsystem/movement/movement_types.dm +++ b/code/controllers/subsystem/movement/movement_types.dm @@ -352,6 +352,7 @@ turf/avoid, skip_first, subsystem, + diagonal_handling, priority, flags, datum/extra_info, @@ -372,6 +373,7 @@ simulated_only, avoid, skip_first, + diagonal_handling, initial_path) /datum/move_loop/has_target/jps @@ -389,6 +391,8 @@ var/turf/avoid ///Should we skip the first step? This is the tile we're currently on, which breaks some things var/skip_first + ///Whether we replace diagonal movements with cardinal movements or follow through with them + var/diagonal_handling ///A list for the path we're currently following var/list/movement_path ///Cooldown for repathing, prevents spam @@ -402,7 +406,7 @@ . = ..() on_finish_callbacks += CALLBACK(src, PROC_REF(on_finish_pathing)) -/datum/move_loop/has_target/jps/setup(delay, timeout, atom/chasing, repath_delay, max_path_length, minimum_distance, list/access, simulated_only, turf/avoid, skip_first, list/initial_path) +/datum/move_loop/has_target/jps/setup(delay, timeout, atom/chasing, repath_delay, max_path_length, minimum_distance, list/access, simulated_only, turf/avoid, skip_first, diagonal_handling, list/initial_path) . = ..() if(!.) return @@ -413,6 +417,7 @@ src.simulated_only = simulated_only src.avoid = avoid src.skip_first = skip_first + src.diagonal_handling = diagonal_handling movement_path = initial_path?.Copy() /datum/move_loop/has_target/jps/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, atom/chasing, repath_delay, max_path_length, minimum_distance, list/access, simulated_only, turf/avoid, skip_first, initial_path) @@ -439,7 +444,7 @@ if(!COOLDOWN_FINISHED(src, repath_cooldown)) return COOLDOWN_START(src, repath_cooldown, repath_delay) - if(SSpathfinder.pathfind(moving, target, max_path_length, minimum_distance, access, simulated_only, avoid, skip_first, on_finish = on_finish_callbacks)) + if(SSpathfinder.pathfind(moving, target, max_path_length, minimum_distance, access, simulated_only, avoid, skip_first, diagonal_handling, on_finish = on_finish_callbacks)) is_pathing = TRUE SEND_SIGNAL(src, COMSIG_MOVELOOP_JPS_REPATH) @@ -447,6 +452,7 @@ /datum/move_loop/has_target/jps/proc/on_finish_pathing(list/path) movement_path = path is_pathing = FALSE + SEND_SIGNAL(src, COMSIG_MOVELOOP_JPS_FINISHED_PATHING, path) /datum/move_loop/has_target/jps/move() if(!length(movement_path)) diff --git a/code/datums/ai/_ai_behavior.dm b/code/datums/ai/_ai_behavior.dm index 98ab8f2fc7e2..233b57b15772 100644 --- a/code/datums/ai/_ai_behavior.dm +++ b/code/datums/ai/_ai_behavior.dm @@ -5,8 +5,14 @@ ///Flags for extra behavior var/behavior_flags = NONE ///Cooldown between actions performances, defaults to the value of CLICK_CD_MELEE because that seemed like a nice standard for the speed of AI behavior + ///Do not read directly or mutate, instead use get_cooldown() var/action_cooldown = CLICK_CD_MELEE +/// Returns the delay to use for this behavior in the moment +/// Override to return a conditional delay +/datum/ai_behavior/proc/get_cooldown(datum/ai_controller/cooldown_for) + return action_cooldown + /// Called by the ai controller when first being added. Additional arguments depend on the behavior type. /// Return FALSE to cancel /datum/ai_behavior/proc/setup(datum/ai_controller/controller, ...) @@ -14,7 +20,7 @@ ///Called by the AI controller when this action is performed /datum/ai_behavior/proc/perform(seconds_per_tick, datum/ai_controller/controller, ...) - controller.behavior_cooldowns[src] = world.time + action_cooldown + controller.behavior_cooldowns[src] = world.time + get_cooldown(controller) return ///Called when the action is finished. This needs the same args as perform besides the default ones diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm index b1b44d50ec78..05e129d55fe9 100644 --- a/code/datums/ai/_ai_controller.dm +++ b/code/datums/ai/_ai_controller.dm @@ -46,6 +46,8 @@ multiple modular subtrees with behaviors ///The idle behavior this AI performs when it has no actions. var/datum/idle_behavior/idle_behavior = null + ///our current cell grid + var/datum/cell_tracker/our_cells // Movement related things here ///Reference to the movement datum we use. Is a type on initialize but becomes a ref afterwards. @@ -56,6 +58,10 @@ multiple modular subtrees with behaviors // The variables below are fucking stupid and should be put into the blackboard at some point. ///AI paused time var/paused_until = 0 + ///Can this AI idle? + var/can_idle = TRUE + ///What distance should we be checking for interesting things when considering idling/deidling? Defaults to AI_DEFAULT_INTERESTING_DIST + var/interesting_dist = AI_DEFAULT_INTERESTING_DIST /datum/ai_controller/New(atom/new_pawn) change_ai_movement_type(ai_movement) @@ -67,9 +73,9 @@ multiple modular subtrees with behaviors if(!isnull(new_pawn)) // unit tests need the ai_controller to exist in isolation due to list schenanigans i hate it here PossessPawn(new_pawn) -/datum/ai_controller/Destroy(force, ...) - set_ai_status(AI_STATUS_OFF) +/datum/ai_controller/Destroy(force) UnpossessPawn(FALSE) + our_cells = null set_movement_target(type, null) if(ai_movement.moving_controllers[src]) ai_movement.stop_moving_towards(src) @@ -116,36 +122,128 @@ multiple modular subtrees with behaviors pawn = new_pawn pawn.ai_controller = src + var/turf/pawn_turf = get_turf(pawn) + if(pawn_turf) + SSai_controllers.ai_controllers_by_zlevel[pawn_turf.z] += src + SEND_SIGNAL(src, COMSIG_AI_CONTROLLER_POSSESSED_PAWN) reset_ai_status() + RegisterSignal(pawn, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_changed_z_level)) RegisterSignal(pawn, COMSIG_MOB_STATCHANGE, PROC_REF(on_stat_changed)) RegisterSignal(pawn, COMSIG_MOB_LOGIN, PROC_REF(on_sentience_gained)) RegisterSignal(pawn, COMSIG_QDELETING, PROC_REF(on_pawn_qdeleted)) + our_cells = new(interesting_dist, interesting_dist, 1) + set_new_cells() + + RegisterSignal(pawn, COMSIG_MOVABLE_MOVED, PROC_REF(update_grid)) + +/datum/ai_controller/proc/update_grid(datum/source, datum/spatial_grid_cell/new_cell) + SIGNAL_HANDLER + + set_new_cells() + +/datum/ai_controller/proc/set_new_cells() + if(isnull(our_cells)) + return + + var/turf/our_turf = get_turf(pawn) + + if(isnull(our_turf)) + return + + var/list/cell_collections = our_cells.recalculate_cells(our_turf) + + for(var/datum/old_grid as anything in cell_collections[2]) + UnregisterSignal(old_grid, list(SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS), SPATIAL_GRID_CELL_EXITED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS))) + + for(var/datum/spatial_grid_cell/new_grid as anything in cell_collections[1]) + RegisterSignal(new_grid, SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS), PROC_REF(on_client_enter)) + RegisterSignal(new_grid, SPATIAL_GRID_CELL_EXITED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS), PROC_REF(on_client_exit)) + + recalculate_idle() + +/datum/ai_controller/proc/should_idle() + if(!can_idle || isnull(our_cells)) + return FALSE + + for(var/datum/spatial_grid_cell/grid as anything in our_cells.member_cells) + if(length(grid.client_contents)) + return FALSE + return TRUE + +/datum/ai_controller/proc/recalculate_idle() + if(ai_status == AI_STATUS_OFF) + return + if(should_idle()) + set_ai_status(AI_STATUS_IDLE) + +/datum/ai_controller/proc/on_client_enter(datum/source, atom/target) + SIGNAL_HANDLER + + if(ai_status == AI_STATUS_IDLE) + set_ai_status(AI_STATUS_ON) + +/datum/ai_controller/proc/on_client_exit(datum/source, datum/exited) + SIGNAL_HANDLER + + recalculate_idle() + /// Sets the AI on or off based on current conditions, call to reset after you've manually disabled it somewhere /datum/ai_controller/proc/reset_ai_status() set_ai_status(get_expected_ai_status()) -/// Returns what the AI status should be based on current conditions. +/** + * Gets the AI status we expect the AI controller to be on at this current moment. + * Returns AI_STATUS_OFF if it's inhabited by a Client and shouldn't be, if it's dead and cannot act while dead, or is on a z level without clients. + * Returns AI_STATUS_ON otherwise. + */ /datum/ai_controller/proc/get_expected_ai_status() - var/final_status = AI_STATUS_ON if (!ismob(pawn)) - return final_status + return AI_STATUS_ON var/mob/living/mob_pawn = pawn - if(!continue_processing_when_client && mob_pawn.client) - final_status = AI_STATUS_OFF - - if(ai_traits & CAN_ACT_WHILE_DEAD) - return final_status + return AI_STATUS_OFF if(mob_pawn.stat == DEAD) - final_status = AI_STATUS_OFF + if(ai_traits & CAN_ACT_WHILE_DEAD) + return AI_STATUS_ON + return AI_STATUS_OFF + + var/turf/pawn_turf = get_turf(mob_pawn) +#ifdef TESTING + if(!pawn_turf) + CRASH("AI controller [src] controlling pawn ([pawn]) is not on a turf.") +#endif + if(!length(SSmobs.clients_by_zlevel[pawn_turf?.z])) + return AI_STATUS_OFF + if(should_idle()) + return AI_STATUS_IDLE + return AI_STATUS_ON + +/datum/ai_controller/proc/get_current_turf() + var/mob/living/mob_pawn = pawn + var/turf/pawn_turf = get_turf(mob_pawn) + to_chat(world, "[pawn_turf]") - return final_status +///Called when the AI controller pawn changes z levels, we check if there's any clients on the new one and wake up the AI if there is. +/datum/ai_controller/proc/on_changed_z_level(atom/source, turf/old_turf, turf/new_turf, same_z_layer, notify_contents) + SIGNAL_HANDLER + var/mob/mob_pawn = pawn + if((mob_pawn?.client && !continue_processing_when_client)) + return + if(old_turf) + SSai_controllers.ai_controllers_by_zlevel[old_turf.z] -= src + if(new_turf) + SSai_controllers.ai_controllers_by_zlevel[new_turf.z] += src + var/new_level_clients = SSmobs.clients_by_zlevel[new_turf.z].len + if(new_level_clients) + set_ai_status(AI_STATUS_IDLE) + else + set_ai_status(AI_STATUS_OFF) ///Abstract proc for initializing the pawn to the new controller /datum/ai_controller/proc/TryPossessPawn(atom/new_pawn) @@ -156,9 +254,15 @@ multiple modular subtrees with behaviors if(isnull(pawn)) return // instantiated without an applicable pawn, fine - UnregisterSignal(pawn, list(COMSIG_MOB_LOGIN, COMSIG_MOB_LOGOUT, COMSIG_MOB_STATCHANGE, COMSIG_QDELETING)) + set_ai_status(AI_STATUS_OFF) + UnregisterSignal(pawn, list(COMSIG_MOVABLE_Z_CHANGED, COMSIG_MOB_LOGIN, COMSIG_MOB_LOGOUT, COMSIG_MOB_STATCHANGE, COMSIG_QDELETING)) if(ai_movement.moving_controllers[src]) ai_movement.stop_moving_towards(src) + var/turf/pawn_turf = get_turf(pawn) + if(pawn_turf) + SSai_controllers.ai_controllers_by_zlevel[pawn_turf.z] -= src + if(ai_status) + SSai_controllers.ai_controllers_by_status[ai_status] -= src pawn.ai_controller = null pawn = null if(destroy) @@ -172,7 +276,6 @@ multiple modular subtrees with behaviors return FALSE return TRUE - ///Runs any actions that are currently running /datum/ai_controller/process(seconds_per_tick) if(!able_to_run()) @@ -199,7 +302,7 @@ multiple modular subtrees with behaviors // Convert the current behaviour action cooldown to realtime seconds from deciseconds.current_behavior // Then pick the max of this and the seconds_per_tick passed to ai_controller.process() // Action cooldowns cannot happen faster than seconds_per_tick, so seconds_per_tick should be the value used in this scenario. - var/action_seconds_per_tick = max(current_behavior.action_cooldown * 0.1, seconds_per_tick) + var/action_seconds_per_tick = max(current_behavior.get_cooldown(src) * 0.1, seconds_per_tick) if(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_MOVEMENT) //Might need to move closer if(!current_movement_target) @@ -252,6 +355,7 @@ multiple modular subtrees with behaviors if(subtree.SelectBehaviors(src, seconds_per_tick) == SUBTREE_RETURN_FINISH_PLANNING) break + SEND_SIGNAL(src, COMSIG_AI_CONTROLLER_PICKED_BEHAVIORS, current_behaviors, planned_behaviors) for(var/datum/ai_behavior/current_behavior as anything in current_behaviors) if(LAZYACCESS(planned_behaviors, current_behavior)) continue @@ -266,19 +370,27 @@ multiple modular subtrees with behaviors if(ai_status == new_ai_status) return FALSE //no change + //remove old status, if we've got one + if(ai_status) + SSai_controllers.ai_controllers_by_status[ai_status] -= src ai_status = new_ai_status + SSai_controllers.ai_controllers_by_status[new_ai_status] += src switch(ai_status) if(AI_STATUS_ON) - SSai_controllers.active_ai_controllers += src START_PROCESSING(SSai_behaviors, src) if(AI_STATUS_OFF) STOP_PROCESSING(SSai_behaviors, src) - SSai_controllers.active_ai_controllers -= src + CancelActions() + if(AI_STATUS_IDLE) + STOP_PROCESSING(SSai_behaviors, src) CancelActions() /datum/ai_controller/proc/PauseAi(time) paused_until = world.time + time +/datum/ai_controller/proc/modify_cooldown(datum/ai_behavior/behavior, new_cooldown) + behavior_cooldowns[behavior.type] = new_cooldown + ///Call this to add a behavior to the stack. /datum/ai_controller/proc/queue_behavior(behavior_type, ...) var/datum/ai_behavior/behavior = GET_AI_BEHAVIOR(behavior_type) @@ -300,6 +412,7 @@ multiple modular subtrees with behaviors behavior_args[behavior_type] = arguments else behavior_args -= behavior_type + SEND_SIGNAL(src, AI_CONTROLLER_BEHAVIOR_QUEUED(behavior_type), arguments) /datum/ai_controller/proc/ProcessBehavior(seconds_per_tick, datum/ai_behavior/behavior) var/list/arguments = list(seconds_per_tick, src) @@ -334,7 +447,7 @@ multiple modular subtrees with behaviors /datum/ai_controller/proc/on_sentience_lost() SIGNAL_HANDLER UnregisterSignal(pawn, COMSIG_MOB_LOGOUT) - set_ai_status(AI_STATUS_ON) //Can't do anything while player is connected + set_ai_status(AI_STATUS_IDLE) //Can't do anything while player is connected RegisterSignal(pawn, COMSIG_MOB_LOGIN, PROC_REF(on_sentience_gained)) // Turn the controller off the controller if the pawn has been qdeleted diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/befriend_target.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/befriend_target.dm new file mode 100644 index 000000000000..e1fd8bed640d --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/befriend_target.dm @@ -0,0 +1,21 @@ +///behavior to befriend any targets +/datum/ai_behavior/befriend_target + +/datum/ai_behavior/befriend_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key, befriend_message) + . = ..() + var/mob/living/living_pawn = controller.pawn + var/mob/living/living_target = controller.blackboard[target_key] + if(QDELETED(living_target)) + finish_action(controller, FALSE, target_key) + return + + living_pawn.befriend(living_target) + var/befriend_text = controller.blackboard[befriend_message] + if(befriend_text) + to_chat(living_target, span_nicegreen("[living_pawn] [befriend_text]")) + + finish_action(controller, TRUE, target_key) + +/datum/ai_behavior/befriend_target/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + controller.clear_blackboard_key(target_key) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/stop_and_stare.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/stop_and_stare.dm index 989837cf88d7..377420021a5c 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/stop_and_stare.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/stop_and_stare.dm @@ -8,9 +8,10 @@ var/atom/movable/target = weak_target?.resolve() return ismovable(target) && isturf(target.loc) && ismob(controller.pawn) +/datum/ai_behavior/stop_and_stare/get_cooldown(datum/ai_controller/cooldown_for) + return cooldown_for.blackboard[BB_STATIONARY_COOLDOWN] + /datum/ai_behavior/stop_and_stare/perform(seconds_per_tick, datum/ai_controller/controller, target_key) - // i don't really like doing this but we wanna make sure that the cooldown is pertinent to what we need for this specific controller before we invoke parent - action_cooldown = controller.blackboard[BB_STATIONARY_COOLDOWN] . = ..() var/datum/weakref/weak_target = controller.blackboard[target_key] var/atom/movable/target = weak_target?.resolve() diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm index 28f89bcf2974..485c6eb8c1ec 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm @@ -1,3 +1,10 @@ +/// List of objects that AIs will treat as targets +GLOBAL_LIST_EMPTY_TYPED(hostile_machines, /atom) +/// Static typecache list of things we are interested in +/// Consider this a union of the for loop and the hearers call from below +/// Must be kept up to date with the contents of hostile_machines +GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/porta_turret, /obj/vehicle/sealed/mecha))) + /datum/ai_behavior/find_potential_targets action_cooldown = 2 SECONDS /// How far can we see stuff? @@ -7,8 +14,12 @@ /// Static typecache list of potentially dangerous objs var/static/list/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/vehicle/sealed/mecha)) +/datum/ai_behavior/find_potential_targets/get_cooldown(datum/ai_controller/cooldown_for) + if(cooldown_for.blackboard[BB_FIND_TARGETS_FIELD(type)]) + return 60 SECONDS + return ..() + /datum/ai_behavior/find_potential_targets/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - . = ..() var/mob/living/living_mob = controller.pawn var/datum/targeting_strategy/targeting_strategy = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) @@ -25,11 +36,16 @@ controller.clear_blackboard_key(target_key) var/list/potential_targets = hearers(aggro_range, controller.pawn) - living_mob //Remove self, so we don't suicide + // If we're using a field rn, just don't do anything yeah? + if(controller.blackboard[BB_FIND_TARGETS_FIELD(type)]) + return + for(var/HM in typecache_filter_list(range(aggro_range, living_mob), hostile_machines)) //Can we see any hostile machines? if(can_see(living_mob, HM, aggro_range)) potential_targets += HM if(!potential_targets.len) + failed_to_find_anyone(controller, target_key, targeting_strategy_key, hiding_location_key) finish_action(controller, succeeded = FALSE) return @@ -41,6 +57,7 @@ continue if(!filtered_targets.len) + failed_to_find_anyone(controller, target_key, targeting_strategy_key, hiding_location_key) finish_action(controller, succeeded = FALSE) return @@ -54,10 +71,86 @@ finish_action(controller, succeeded = TRUE) -/datum/ai_behavior/find_potential_targets/finish_action(datum/ai_controller/controller, succeeded, ...) +/datum/ai_behavior/find_potential_targets/proc/failed_to_find_anyone(datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) + var/aggro_range = controller.blackboard[aggro_range_key] || vision_range + // takes the larger between our range() input and our implicit hearers() input (world.view) + aggro_range = max(aggro_range, ROUND_UP(max(getviewsize(world.view)) / 2)) + // Alright, here's the interesting bit + // We're gonna use this max range to hook into a proximity field so we can just await someone interesting to come along + // Rather then trying to check every few seconds + var/datum/proximity_monitor/advanced/ai_target_tracking/detection_field = new( + controller.pawn, + aggro_range, + TRUE, + src, + controller, + target_key, + targeting_strategy_key, + hiding_location_key, + ) + // We're gonna store this field in our blackboard, so we can clear it away if we end up finishing successsfully + controller.set_blackboard_key(BB_FIND_TARGETS_FIELD(type), detection_field) + +/datum/ai_behavior/find_potential_targets/proc/new_turf_found(turf/found, datum/ai_controller/controller, datum/targeting_strategy/strategy) + var/valid_found = FALSE + var/mob/pawn = controller.pawn + for(var/maybe_target as anything in found) + if(maybe_target == pawn) + continue + if(!is_type_in_typecache(maybe_target, GLOB.target_interested_atoms)) + continue + if(!strategy.can_attack(pawn, maybe_target)) + continue + valid_found = TRUE + break + if(!valid_found) + return + // If we found any one thing we "could" attack, then run the full search again so we can select from the best possible canidate + var/datum/proximity_monitor/field = controller.blackboard[BB_FIND_TARGETS_FIELD(type)] + qdel(field) // autoclears so it's fine + // Fire instantly, you should find something I hope + controller.modify_cooldown(src, world.time) + +/datum/ai_behavior/find_potential_targets/proc/atom_allowed(atom/movable/checking, datum/targeting_strategy/strategy, mob/pawn) + if(checking == pawn) + return FALSE + if(!ismob(checking) && !is_type_in_typecache(checking, GLOB.target_interested_atoms)) + return FALSE + if(!strategy.can_attack(pawn, checking)) + return FALSE + return TRUE + +/datum/ai_behavior/find_potential_targets/proc/new_atoms_found(list/atom/movable/found, datum/ai_controller/controller, target_key, datum/targeting_strategy/strategy, hiding_location_key) + var/mob/pawn = controller.pawn + var/list/accepted_targets = list() + for(var/maybe_target as anything in found) + if(maybe_target == pawn) + continue + // Need to better handle viewers here + if(!ismob(maybe_target) && !is_type_in_typecache(maybe_target, GLOB.target_interested_atoms)) + continue + if(!strategy.can_attack(pawn, maybe_target)) + continue + accepted_targets += maybe_target + + // Alright, we found something acceptable, let's use it yeah? + var/atom/target = pick_final_target(controller, accepted_targets) + controller.set_blackboard_key(target_key, target) + + var/atom/potential_hiding_location = strategy.find_hidden_mobs(pawn, target) + + if(potential_hiding_location) //If they're hiding inside of something, we need to know so we can go for that instead initially. + controller.set_blackboard_key(hiding_location_key, potential_hiding_location) + + finish_action(controller, succeeded = TRUE) + +/datum/ai_behavior/find_potential_targets/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) . = ..() if (succeeded) + var/datum/proximity_monitor/field = controller.blackboard[BB_FIND_TARGETS_FIELD(type)] + qdel(field) // autoclears so it's fine controller.CancelActions() // On retarget cancel any further queued actions so that they will setup again with new target + controller.modify_cooldown(controller, get_cooldown(controller)) /// Returns the desired final target from the filtered list of targets /datum/ai_behavior/find_potential_targets/proc/pick_final_target(datum/ai_controller/controller, list/filtered_targets) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/ventcrawling.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/ventcrawling.dm index e162cc612990..889b474ad038 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/ventcrawling.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/ventcrawling.dm @@ -8,8 +8,10 @@ /datum/ai_behavior/crawl_through_vents action_cooldown = 10 SECONDS +/datum/ai_behavior/crawl_through_vents/get_cooldown(datum/ai_controller/cooldown_for) + return cooldown_for.blackboard[BB_VENTCRAWL_COOLDOWN] || initial(action_cooldown) + /datum/ai_behavior/crawl_through_vents/setup(datum/ai_controller/controller, target_key) - action_cooldown = controller.blackboard[BB_VENTCRAWL_COOLDOWN] || initial(action_cooldown) . = ..() var/obj/machinery/atmospherics/components/unary/vent_pump/target = controller.blackboard[target_key] || controller.blackboard[BB_ENTRY_VENT_TARGET] return istype(target) && isliving(controller.pawn) // only mobs can vent crawl in the current framework diff --git a/code/datums/ai/dog/dog_behaviors.dm b/code/datums/ai/dog/dog_behaviors.dm index c6bdc46afe72..d0c55a347cff 100644 --- a/code/datums/ai/dog/dog_behaviors.dm +++ b/code/datums/ai/dog/dog_behaviors.dm @@ -8,7 +8,7 @@ required_distance = 3 /datum/ai_behavior/basic_melee_attack/dog/perform(seconds_per_tick, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - controller.behavior_cooldowns[src] = world.time + action_cooldown + controller.behavior_cooldowns[src] = world.time + get_cooldown(controller) var/mob/living/living_pawn = controller.pawn if(!(isturf(living_pawn.loc) || HAS_TRAIT(living_pawn, TRAIT_AI_BAGATTACK))) // Void puppies can attack from inside bags finish_action(controller, FALSE, target_key, targeting_strategy_key, hiding_location_key) diff --git a/code/datums/ai/movement/_ai_movement.dm b/code/datums/ai/movement/_ai_movement.dm index 0be133279f7e..b979db7644ee 100644 --- a/code/datums/ai/movement/_ai_movement.dm +++ b/code/datums/ai/movement/_ai_movement.dm @@ -62,7 +62,8 @@ /datum/ai_movement/proc/post_move(datum/move_loop/source, succeeded) SIGNAL_HANDLER var/datum/ai_controller/controller = source.extra_info - if(succeeded != MOVELOOP_FAILURE) - reset_pathing_failures(controller) - return - increment_pathing_failures(controller) + switch(succeeded) + if(MOVELOOP_SUCCESS) + reset_pathing_failures(controller) + if(MOVELOOP_FAILURE) + increment_pathing_failures(controller) diff --git a/code/datums/ai/movement/ai_movement_jps.dm b/code/datums/ai/movement/ai_movement_jps.dm index 150833991330..3644869140d7 100644 --- a/code/datums/ai/movement/ai_movement_jps.dm +++ b/code/datums/ai/movement/ai_movement_jps.dm @@ -3,20 +3,24 @@ */ /datum/ai_movement/jps max_pathing_attempts = 20 + var/maximum_length = AI_MAX_PATH_LENGTH + ///how we deal with diagonal movement, whether we try to avoid them or follow through with them + var/diagonal_flags = DIAGONAL_REMOVE_CLUNKY /datum/ai_movement/jps/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance) . = ..() var/atom/movable/moving = controller.pawn var/delay = controller.movement_delay - var/datum/move_loop/loop = SSmove_manager.jps_move(moving, + var/datum/move_loop/has_target/jps/loop = SSmove_manager.jps_move(moving, current_movement_target, delay, repath_delay = 0.5 SECONDS, - max_path_length = AI_MAX_PATH_LENGTH, + max_path_length = maximum_length, minimum_distance = controller.get_minimum_distance(), access = controller.get_access(), subsystem = SSai_movement, + diagonal_handling = diagonal_flags, extra_info = controller, ) @@ -24,9 +28,23 @@ RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_move)) RegisterSignal(loop, COMSIG_MOVELOOP_JPS_REPATH, PROC_REF(repath_incoming)) + return loop + /datum/ai_movement/jps/proc/repath_incoming(datum/move_loop/has_target/jps/source) SIGNAL_HANDLER var/datum/ai_controller/controller = source.extra_info source.access = controller.get_access() source.minimum_distance = controller.get_minimum_distance() + +/datum/ai_movement/jps/bot + max_pathing_attempts = 25 + maximum_length = AI_BOT_PATH_LENGTH + diagonal_flags = DIAGONAL_REMOVE_ALL + +/datum/ai_movement/jps/bot/start_moving_towards(datum/ai_controller/controller, atom/current_movement_target, min_distance) + var/datum/move_loop/loop = ..() + var/atom/our_pawn = controller.pawn + if(isnull(our_pawn)) + return + our_pawn.RegisterSignal(loop, COMSIG_MOVELOOP_JPS_FINISHED_PATHING, TYPE_PROC_REF(/mob/living/basic/bot, generate_bot_path)) diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm index 7c5c8a6df5b1..804562afe277 100644 --- a/code/datums/chatmessage.dm +++ b/code/datums/chatmessage.dm @@ -44,10 +44,6 @@ var/eol_complete /// Contains the approximate amount of lines for height decay var/approx_lines - /// Contains the reference to the next chatmessage in the bucket, used by runechat subsystem - var/datum/chatmessage/next - /// Contains the reference to the previous chatmessage in the bucket, used by runechat subsystem - var/datum/chatmessage/prev /// The current index used for adjusting the layer of each sequential chat message such that recent messages will overlay older ones var/static/current_z_idx = 0 /// When we started animating the message @@ -125,12 +121,6 @@ if (length_char(text) > maxlen) text = copytext_char(text, 1, maxlen + 1) + "..." // BYOND index moment - // Calculate target color if not already present - if (!target.chat_color || target.chat_color_name != target.name) - target.chat_color = colorize_string(target.name) - target.chat_color_darkened = colorize_string(target.name, 0.85, 0.85) - target.chat_color_name = target.name - // Get rid of any URL schemes that might cause BYOND to automatically wrap something in an anchor tag var/static/regex/url_scheme = new(@"[A-Za-z][A-Za-z0-9+-\.]*:\/\/", "g") text = replacetext(text, url_scheme, "") @@ -150,6 +140,7 @@ extra_classes |= SPAN_YELL var/list/prefixes + var/chat_color_name_to_use // Append radio icon if from a virtual speaker if (extra_classes.Find("virtual-speaker")) @@ -158,6 +149,19 @@ else if (extra_classes.Find("emote")) var/image/r_icon = image('icons/ui_icons/chat/chat_icons.dmi', icon_state = "emote") LAZYADD(prefixes, "\icon[r_icon]") + chat_color_name_to_use = target.get_visible_name(add_id_name = FALSE) // use face name for nonverbal messages + + if(isnull(chat_color_name_to_use)) + if(HAS_TRAIT(target, TRAIT_SIGN_LANG)) + chat_color_name_to_use = target.get_visible_name(add_id_name = FALSE) // use face name for signers too + else + chat_color_name_to_use = target.GetVoice() // for everything else, use the target's voice name + + // Calculate target color if not already present + if (!target.chat_color || target.chat_color_name != chat_color_name_to_use) + target.chat_color = colorize_string(chat_color_name_to_use) + target.chat_color_darkened = colorize_string(chat_color_name_to_use, 0.85, 0.85) + target.chat_color_name = chat_color_name_to_use // Append language icon if the language uses one var/datum/language/language_instance = GLOB.language_datum_instances[language] @@ -193,7 +197,7 @@ /datum/chatmessage/proc/finish_image_generation(mheight, atom/target, mob/owner, complete_text, lifespan) var/rough_time = REALTIMEOFDAY approx_lines = max(1, mheight / CHAT_MESSAGE_APPROX_LHEIGHT) - + var/starting_height = target.maptext_height // Translate any existing messages upwards, apply exponential decay factors to timers message_loc = isturf(target) ? target : get_atom_on_turf(target) if (owned_by.seen_messages) @@ -208,7 +212,12 @@ // When choosing to update the remaining time we have to be careful not to update the // scheduled time once the EOL has been executed. if (time_spent >= time_before_fade) - animate(m.message, pixel_y = m.message.pixel_y + mheight, time = CHAT_MESSAGE_SPAWN_TIME, flags = ANIMATION_PARALLEL) + if(m.message.pixel_y < starting_height) + var/max_height = m.message.pixel_y + m.approx_lines * CHAT_MESSAGE_APPROX_LHEIGHT - starting_height + if(max_height > 0) + animate(m.message, pixel_y = m.message.pixel_y + max_height, time = CHAT_MESSAGE_SPAWN_TIME, flags = ANIMATION_PARALLEL) + else if(mheight + starting_height >= m.message.pixel_y) + animate(m.message, pixel_y = m.message.pixel_y + mheight, time = CHAT_MESSAGE_SPAWN_TIME, flags = ANIMATION_PARALLEL) continue var/remaining_time = time_before_fade * (CHAT_MESSAGE_EXP_DECAY ** idx++) * (CHAT_MESSAGE_HEIGHT_DECAY ** combined_height) @@ -225,7 +234,12 @@ animate(alpha = 0, time = CHAT_MESSAGE_EOL_FADE) // We run this after the alpha animate, because we don't want to interrup it, but also don't want to block it by running first // Sooo instead we do this. bit messy but it fuckin works - animate(m.message, pixel_y = m.message.pixel_y + mheight, time = CHAT_MESSAGE_SPAWN_TIME, flags = ANIMATION_PARALLEL) + if(m.message.pixel_y < starting_height) + var/max_height = m.message.pixel_y + m.approx_lines * CHAT_MESSAGE_APPROX_LHEIGHT - starting_height + if(max_height > 0) + animate(m.message, pixel_y = m.message.pixel_y + max_height, time = CHAT_MESSAGE_SPAWN_TIME, flags = ANIMATION_PARALLEL) + else if(mheight + starting_height >= m.message.pixel_y) + animate(m.message, pixel_y = m.message.pixel_y + mheight, time = CHAT_MESSAGE_SPAWN_TIME, flags = ANIMATION_PARALLEL) // Reset z index if relevant if (current_z_idx >= CHAT_LAYER_MAX_Z) @@ -236,7 +250,7 @@ SET_PLANE_EXPLICIT(message, RUNECHAT_PLANE, message_loc) message.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA | KEEP_APART message.alpha = 0 - message.pixel_y = target.maptext_height + message.pixel_y = starting_height message.pixel_x = -target.base_pixel_x message.maptext_width = CHAT_MESSAGE_WIDTH message.maptext_height = mheight * 1.25 // We add extra because some characters are superscript, like actions @@ -305,7 +319,7 @@ return // Display visual above source - if(CHECK_BITFIELD(runechat_flags, EMOTE_MESSAGE)) + if(runechat_flags & EMOTE_MESSAGE) new /datum/chatmessage(raw_message, speaker, src, message_language, list("emote", "italics")) else if(CHECK_BITFIELD(runechat_flags, LOOC_MESSAGE)) new /datum/chatmessage(raw_message, speaker, src, message_language, list("looc", "italics")) diff --git a/code/datums/components/bakeable.dm b/code/datums/components/bakeable.dm index a5367769f684..3a654b4e4696 100644 --- a/code/datums/components/bakeable.dm +++ b/code/datums/components/bakeable.dm @@ -80,7 +80,7 @@ if(positive_result) used_oven.visible_message(span_notice("You smell something great coming from [used_oven]."), blind_message = span_notice("You smell something great...")) - BLACKBOX_LOG_FOOD_MADE(baked_result.type) + BLACKBOX_LOG_FOOD_MADE(baked_result) else used_oven.visible_message(span_warning("You smell a burnt smell coming from [used_oven]."), blind_message = span_warning("You smell a burnt smell...")) SEND_SIGNAL(parent, COMSIG_ITEM_BAKED, baked_result) diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm index 6cee0fb6ca95..12dff2b6aa9a 100644 --- a/code/datums/components/chasm.dm +++ b/code/datums/components/chasm.dm @@ -8,38 +8,38 @@ /// List of refs to falling objects -> how many levels deep we've fallen var/static/list/falling_atoms = list() var/static/list/forbidden_types = typecacheof(list( + /obj/singularity, + /obj/energy_ball, + /obj/narsie, /obj/docking_port, + /obj/structure/lattice, + /obj/structure/stone_tile, + /obj/projectile, + /obj/effect/projectile, + /obj/effect/portal, /obj/effect/abstract, - /obj/effect/abstract/liquid_turf, //monkestation edit - /obj/effect/collapse, - /obj/effect/constructing_effect, - /obj/effect/dummy/phased_mob, - /obj/effect/ebeam, - /obj/effect/fishing_lure, /obj/effect/hotspot, /obj/effect/landmark, + /obj/effect/temp_visual, /obj/effect/light_emitter/tendril, - /obj/effect/mapping_helpers, + /obj/effect/collapse, /obj/effect/particle_effect/ion_trails, - /obj/effect/portal, - /obj/effect/projectile, - /obj/effect/spectre_of_resurrection, - /obj/effect/temp_visual, + /obj/effect/dummy/phased_mob, + /obj/effect/mapping_helpers, /obj/effect/wisp, - /obj/energy_ball, - /obj/narsie, - /obj/projectile, - /obj/singularity, - /obj/structure/lattice, - /obj/structure/stone_tile, + /obj/effect/ebeam, + /obj/effect/fishing_lure, )) /datum/component/chasm/Initialize(turf/target, mapload) if(!isturf(parent)) return COMPONENT_INCOMPATIBLE - RegisterSignal(parent, COMSIG_ATOM_ENTERED, PROC_REF(Entered)) + RegisterSignal(parent, SIGNAL_ADDTRAIT(TRAIT_CHASM_STOPPED), PROC_REF(on_chasm_stopped)) + RegisterSignal(parent, SIGNAL_REMOVETRAIT(TRAIT_CHASM_STOPPED), PROC_REF(on_chasm_no_longer_stopped)) target_turf = target - START_PROCESSING(SSobj, src) // process on create, in case stuff is still there + RegisterSignal(parent, COMSIG_ATOM_ABSTRACT_ENTERED, PROC_REF(entered)) + RegisterSignal(parent, COMSIG_ATOM_ABSTRACT_EXITED, PROC_REF(exited)) + RegisterSignal(parent, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(initialized_on)) //allow catwalks to give the turf the CHASM_STOPPED trait before dropping stuff when the turf is changed. //otherwise don't do anything because turfs and areas are initialized before movables. if(!mapload) @@ -47,50 +47,60 @@ parent.AddElement(/datum/element/lazy_fishing_spot, /datum/fish_source/chasm) /datum/component/chasm/UnregisterFromParent() - STOP_PROCESSING(SSobj, src) storage = null -/datum/component/chasm/proc/Entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) +/datum/component/chasm/proc/entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) SIGNAL_HANDLER + drop_stuff() - START_PROCESSING(SSobj, src) - drop_stuff(arrived) - -/datum/component/chasm/process() - if (!drop_stuff()) - STOP_PROCESSING(SSobj, src) - -/datum/component/chasm/proc/is_safe() - //if anything matching this typecache is found in the chasm, we don't drop things - var/static/list/chasm_safeties_typecache = typecacheof(list(/obj/structure/lattice, /obj/structure/lattice/catwalk, /obj/structure/stone_tile)) +/datum/component/chasm/proc/exited(datum/source, atom/movable/exited) + SIGNAL_HANDLER + UnregisterSignal(exited, list(COMSIG_MOVETYPE_FLAG_DISABLED, COMSIG_LIVING_SET_BUCKLED, COMSIG_MOVABLE_THROW_LANDED)) - var/atom/parent = src.parent - var/list/found_safeties = typecache_filter_list(parent.contents, chasm_safeties_typecache) - for(var/obj/structure/stone_tile/S in found_safeties) - if(S.fallen) - LAZYREMOVE(found_safeties, S) - return LAZYLEN(found_safeties) +/datum/component/chasm/proc/initialized_on(datum/source, atom/movable/movable, mapload) + SIGNAL_HANDLER + drop_stuff(movable) -/datum/component/chasm/proc/drop_stuff(dropped_thing) - if (is_safe()) - return FALSE +/datum/component/chasm/proc/on_chasm_stopped(datum/source) + SIGNAL_HANDLER + UnregisterSignal(source, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_EXITED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON)) + for(var/atom/movable/movable as anything in source) + UnregisterSignal(movable, list(COMSIG_MOVETYPE_FLAG_DISABLED, COMSIG_LIVING_SET_BUCKLED, COMSIG_MOVABLE_THROW_LANDED)) - var/atom/parent = src.parent - var/to_check = dropped_thing ? list(dropped_thing) : parent.contents - for (var/thing in to_check) - if (droppable(thing)) - . = TRUE - INVOKE_ASYNC(src, PROC_REF(drop), thing) +/datum/component/chasm/proc/on_chasm_no_longer_stopped(datum/source) + SIGNAL_HANDLER + RegisterSignal(parent, COMSIG_ATOM_ENTERED, PROC_REF(entered)) + RegisterSignal(parent, COMSIG_ATOM_EXITED, PROC_REF(exited)) + RegisterSignal(parent, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(initialized_on)) + drop_stuff() + +#define CHASM_NOT_DROPPING 0 +#define CHASM_DROPPING 1 +///Doesn't drop the movable, but registers a few signals to try again if the conditions change. +#define CHASM_REGISTER_SIGNALS 2 + +/datum/component/chasm/proc/drop_stuff(atom/movable/dropped_thing) + if(HAS_TRAIT(parent, TRAIT_CHASM_STOPPED)) + return + var/atom/atom_parent = parent + var/to_check = dropped_thing ? list(dropped_thing) : atom_parent.contents + for (var/atom/movable/thing as anything in to_check) + var/dropping = droppable(thing) + switch(dropping) + if(CHASM_DROPPING) + INVOKE_ASYNC(src, PROC_REF(drop), thing) + if(CHASM_REGISTER_SIGNALS) + RegisterSignals(thing, list(COMSIG_MOVETYPE_FLAG_DISABLED, COMSIG_LIVING_SET_BUCKLED, COMSIG_MOVABLE_THROW_LANDED), PROC_REF(drop_stuff), TRUE) /datum/component/chasm/proc/droppable(atom/movable/dropped_thing) var/datum/weakref/falling_ref = WEAKREF(dropped_thing) // avoid an infinite loop, but allow falling a large distance if(falling_atoms[falling_ref] && falling_atoms[falling_ref] > 30) - return FALSE - if(!isliving(dropped_thing) && !isobj(dropped_thing)) - return FALSE - if(is_type_in_typecache(dropped_thing, forbidden_types) || dropped_thing.throwing || (dropped_thing.movement_type & (FLOATING|FLYING))) - return FALSE + return CHASM_NOT_DROPPING + if(is_type_in_typecache(dropped_thing, forbidden_types) || (!isliving(dropped_thing) && !isobj(dropped_thing))) + return CHASM_NOT_DROPPING + if(dropped_thing.throwing || (dropped_thing.movement_type & (FLOATING|FLYING))) + return CHASM_REGISTER_SIGNALS //Flies right over the chasm if(ismob(dropped_thing)) @@ -98,7 +108,7 @@ if(M.buckled) //middle statement to prevent infinite loops just in case! var/mob/buckled_to = M.buckled if((!ismob(M.buckled) || (buckled_to.buckled != M)) && !droppable(M.buckled)) - return FALSE + return CHASM_REGISTER_SIGNALS if(ishuman(dropped_thing)) var/mob/living/carbon/human/victim = dropped_thing if(istype(victim.belt, /obj/item/wormhole_jaunter)) @@ -107,8 +117,12 @@ var/fall_into_chasm = jaunter.chasm_react(victim) if(!fall_into_chasm) chasm.visible_message(span_boldwarning("[victim] falls into the [chasm]!")) //To freak out any bystanders - return fall_into_chasm - return TRUE + return fall_into_chasm ? CHASM_DROPPING : CHASM_NOT_DROPPING + return CHASM_DROPPING + +#undef CHASM_NOT_DROPPING +#undef CHASM_DROPPING +#undef CHASM_REGISTER_SIGNALS /datum/component/chasm/proc/drop(atom/movable/dropped_thing) var/datum/weakref/falling_ref = WEAKREF(dropped_thing) @@ -124,6 +138,10 @@ return // We're already handling this if(below_turf) + if(HAS_TRAIT(dropped_thing, TRAIT_CHASM_DESTROYED)) + qdel(dropped_thing) + return + // send to the turf below dropped_thing.visible_message(span_boldwarning("[dropped_thing] falls into [parent]!"), span_userdanger("[fall_message]")) below_turf.visible_message(span_boldwarning("[dropped_thing] falls from above!")) @@ -159,6 +177,10 @@ if(!dropped_thing || QDELETED(dropped_thing)) return + if(HAS_TRAIT(dropped_thing, TRAIT_CHASM_DESTROYED)) + qdel(dropped_thing) + return + if(!storage) storage = (locate() in parent) || new(parent) diff --git a/code/datums/components/cleaner.dm b/code/datums/components/cleaner.dm index f83a7b10c21b..48b73f1e356c 100644 --- a/code/datums/components/cleaner.dm +++ b/code/datums/components/cleaner.dm @@ -130,7 +130,7 @@ if(length(item.viruses)) for(var/datum/disease/advanced/D as anything in item.viruses) item.remove_disease(D) - + on_cleaned_callback?.Invoke(source, target, user) //remove the cleaning overlay diff --git a/code/datums/components/connect_range.dm b/code/datums/components/connect_range.dm index 95cb560c8680..d3407f467145 100644 --- a/code/datums/components/connect_range.dm +++ b/code/datums/components/connect_range.dm @@ -8,6 +8,8 @@ /// An assoc list of signal -> procpath to register to the loc this object is on. var/list/connections + /// The turfs currently connected to this component + var/list/turfs = list() /** * The atom the component is tracking. The component will delete itself if the tracked is deleted. * Signals will also be updated whenever it moves (if it's a movable). @@ -41,7 +43,7 @@ if(src.range == range && src.works_in_containers == works_in_containers) return //Unregister the signals with the old settings. - unregister_signals(isturf(tracked) ? tracked : tracked.loc) + unregister_signals(isturf(tracked) ? tracked : tracked.loc, turfs) src.range = range src.works_in_containers = works_in_containers //Re-register the signals with the new settings. @@ -49,7 +51,7 @@ /datum/component/connect_range/proc/set_tracked(atom/new_tracked) if(tracked) //Unregister the signals from the old tracked and its surroundings - unregister_signals(isturf(tracked) ? tracked : tracked.loc) + unregister_signals(isturf(tracked) ? tracked : tracked.loc, turfs) UnregisterSignal(tracked, list( COMSIG_MOVABLE_MOVED, COMSIG_QDELETING, @@ -66,28 +68,34 @@ SIGNAL_HANDLER qdel(src) -/datum/component/connect_range/proc/update_signals(atom/target, atom/old_loc, forced = FALSE) +/datum/component/connect_range/proc/update_signals(atom/target, atom/old_loc) var/turf/current_turf = get_turf(target) - var/on_same_turf = current_turf == get_turf(old_loc) //Only register/unregister turf signals if it's moved to a new turf. - unregister_signals(old_loc, on_same_turf) - if(isnull(current_turf)) + unregister_signals(old_loc, turfs) + turfs = list() return if(ismovable(target.loc)) if(!works_in_containers) + unregister_signals(old_loc, turfs) + turfs = list() return //Keep track of possible movement of all movables the target is in. for(var/atom/movable/container as anything in get_nested_locs(target)) RegisterSignal(container, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) - if(on_same_turf && !forced) + //Only register/unregister turf signals if it's moved to a new turf. + if(current_turf == get_turf(old_loc)) + unregister_signals(old_loc, null) return - for(var/turf/target_turf in RANGE_TURFS(range, current_turf)) + var/list/old_turfs = turfs + turfs = RANGE_TURFS(range, current_turf) + unregister_signals(old_loc, old_turfs - turfs) + for(var/turf/target_turf as anything in turfs - old_turfs) for(var/signal in connections) parent.RegisterSignal(target_turf, signal, connections[signal]) -/datum/component/connect_range/proc/unregister_signals(atom/location, on_same_turf = FALSE) +/datum/component/connect_range/proc/unregister_signals(atom/location, list/remove_from) //The location is null or is a container and the component shouldn't have register signals on it if(isnull(location) || (!works_in_containers && !isturf(location))) return @@ -96,10 +104,9 @@ for(var/atom/movable/target as anything in (get_nested_locs(location) + location)) UnregisterSignal(target, COMSIG_MOVABLE_MOVED) - if(on_same_turf) + if(!length(remove_from)) return - var/turf/previous_turf = get_turf(location) - for(var/turf/target_turf in RANGE_TURFS(range, previous_turf)) + for(var/turf/target_turf as anything in remove_from) parent.UnregisterSignal(target_turf, connections) /datum/component/connect_range/proc/on_moved(atom/movable/movable, atom/old_loc) diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm index 59f689e5b9d1..c7f773d82701 100644 --- a/code/datums/components/crafting/crafting.dm +++ b/code/datums/components/crafting/crafting.dm @@ -209,7 +209,7 @@ qdel(thing) I.CheckParts(parts, R) if(send_feedback) - SSblackbox.record_feedback("tally", "object_crafted", 1, I.type) + SSblackbox.record_feedback("tally", "object_crafted", 1, I.name) return I //Send the item back to whatever called this proc so it can handle whatever it wants to do with the new item return ", missing tool." return ", missing component." diff --git a/code/datums/components/crafting/robot.dm b/code/datums/components/crafting/robot.dm index ba5221f0d241..a8406fcfb8af 100644 --- a/code/datums/components/crafting/robot.dm +++ b/code/datums/components/crafting/robot.dm @@ -32,7 +32,7 @@ /datum/crafting_recipe/cleanbot name = "Cleanbot" - result = /mob/living/simple_animal/bot/cleanbot + result = /mob/living/basic/bot/cleanbot reqs = list( /obj/item/reagent_containers/cup/bucket = 1, /obj/item/assembly/prox_sensor = 1, @@ -56,7 +56,7 @@ /datum/crafting_recipe/medbot name = "Medbot" - result = /mob/living/simple_animal/bot/medbot + result = /mob/living/basic/bot/medbot reqs = list( /obj/item/healthanalyzer = 1, /obj/item/storage/medkit = 1, @@ -71,10 +71,10 @@ category = CAT_ROBOT /datum/crafting_recipe/medbot/on_craft_completion(mob/user, atom/result) - var/mob/living/simple_animal/bot/medbot/bot = result + var/mob/living/basic/bot/medbot/bot = result var/obj/item/storage/medkit/medkit = bot.contents[3] bot.medkit_type = medkit - bot.healthanalyzer = bot.contents[4] + bot.health_analyzer = bot.contents[4] if (istype(medkit, /obj/item/storage/medkit/fire)) bot.skin = "ointment" @@ -87,7 +87,7 @@ else if (istype(medkit, /obj/item/storage/medkit/advanced)) bot.skin = "advanced" - bot.damagetype_healer = initial(medkit.damagetype_healed) ? initial(medkit.damagetype_healed) : BRUTE + bot.damage_type_healer = initial(medkit.damagetype_healed) ? initial(medkit.damagetype_healed) : BRUTE bot.update_appearance() /datum/crafting_recipe/honkbot @@ -128,7 +128,7 @@ /datum/crafting_recipe/hygienebot name = "Hygienebot" - result = /mob/living/simple_animal/bot/hygienebot + result = /mob/living/basic/bot/hygienebot reqs = list( /obj/item/bot_assembly/hygienebot = 1, /obj/item/stack/ducts = 1, diff --git a/code/datums/components/food/edible.dm b/code/datums/components/food/edible.dm index 0e7f4f0762f2..19496e59ab48 100644 --- a/code/datums/components/food/edible.dm +++ b/code/datums/components/food/edible.dm @@ -292,7 +292,7 @@ Behavior that's still missing from this component that original food items had t this_food.reagents.maximum_volume = ROUND_UP(this_food.reagents.maximum_volume) // Just because I like whole numbers for this. - BLACKBOX_LOG_FOOD_MADE(this_food.type) + BLACKBOX_LOG_FOOD_MADE(this_food) ///Makes sure the thing hasn't been destroyed or fully eaten to prevent eating phantom edibles /datum/component/edible/proc/IsFoodGone(atom/owner, mob/living/feeder) diff --git a/code/datums/components/grillable.dm b/code/datums/components/grillable.dm index 2e299bd4b069..62b568df769a 100644 --- a/code/datums/components/grillable.dm +++ b/code/datums/components/grillable.dm @@ -90,7 +90,7 @@ grilled_result.set_custom_materials(original_object.custom_materials) if(IS_EDIBLE(grilled_result)) - BLACKBOX_LOG_FOOD_MADE(grilled_result.type) + BLACKBOX_LOG_FOOD_MADE(grilled_result) SEND_SIGNAL(parent, COMSIG_ITEM_GRILLED, grilled_result) if(who_placed_us) diff --git a/code/datums/components/healing_touch.dm b/code/datums/components/healing_touch.dm index 70db65e0e044..968aeb8b9d7e 100644 --- a/code/datums/components/healing_touch.dm +++ b/code/datums/components/healing_touch.dm @@ -201,7 +201,7 @@ updating_health = FALSE, ) healed += target.adjustOxyLoss(-heal_oxy, updating_health = FALSE, required_biotype = valid_biotypes) - healed += target.adjustToxLoss(-heal_tox, updating_health = FALSE, required_biotype = valid_biotypes) + healed += target.adjustToxLoss(-heal_tox, updating_health = FALSE, forced = TRUE, required_biotype = valid_biotypes) //MONKESTATION REMOVAL START // While removing this could cause some issues, keeping it seems to cause more than it would // solve. In particular, the above procs are somewhat bugged and don't return the values we diff --git a/code/datums/components/redirect_attack_hand_from_turf.dm b/code/datums/components/redirect_attack_hand_from_turf.dm index 56b413138bd2..5af01334f07f 100644 --- a/code/datums/components/redirect_attack_hand_from_turf.dm +++ b/code/datums/components/redirect_attack_hand_from_turf.dm @@ -8,11 +8,16 @@ /// Takes lmb_text and rmb_text. list/screentip_texts + /// A custom callback to determine whether a user's clicks will be redirected or not (mob/user) + datum/callback/interact_check + turf/current_turf + /datum/component/redirect_attack_hand_from_turf/Initialize( adjust_for_pixel_shift = TRUE, list/screentip_texts = null, + datum/callback/interact_check = null ) . = ..() @@ -21,6 +26,7 @@ src.adjust_for_pixel_shift = adjust_for_pixel_shift src.screentip_texts = screentip_texts + src.interact_check = interact_check RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) connect_to_new_turf() @@ -96,6 +102,9 @@ var/atom/movable/movable_parent = parent if (!movable_parent.can_interact(user)) return NONE + + if (!isnull(interact_check) && !interact_check.Invoke(user)) + return NONE INVOKE_ASYNC(user, TYPE_PROC_REF(/mob, UnarmedAttack), parent, proximity_flag = TRUE) @@ -115,6 +124,9 @@ if (!isnull(held_item)) return NONE + + if (!isnull(interact_check) && !interact_check.Invoke(user)) + return NONE if (!isnull(screentip_texts["lmb_text"])) context[SCREENTIP_CONTEXT_LMB] = screentip_texts["lmb_text"] diff --git a/code/datums/components/singularity.dm b/code/datums/components/singularity.dm index 233cfbdd1864..eb8a454ef29e 100644 --- a/code/datums/components/singularity.dm +++ b/code/datums/components/singularity.dm @@ -360,8 +360,9 @@ target = potentially_closer //if we lost that target get a new one if(!target || QDELETED(target)) - target = find_new_target() - foreboding_nosebleed(target) + var/mob/living/new_target = find_new_target() + new_target?.ominous_nosebleed() + target = new_target return ..() ///Searches the living list for the closest target, and begins chasing them down. @@ -380,23 +381,6 @@ closest_target = target return closest_target -/// gives a little fluff warning that someone is being hunted. -/datum/component/singularity/bloodthirsty/proc/foreboding_nosebleed(mob/living/target) - if(!iscarbon(target)) - to_chat(target, span_warning("You feel a bit nauseous for just a moment.")) - return - var/mob/living/carbon/carbon_target = target - var/obj/item/bodypart/head = carbon_target.get_bodypart(BODY_ZONE_HEAD) - var/has_no_blood = HAS_TRAIT(carbon_target, TRAIT_NOBLOOD) - if(head) - if(has_no_blood) - to_chat(carbon_target, span_notice("You get a headache.")) - return - head.adjustBleedStacks(5) - carbon_target.visible_message(span_notice("[carbon_target] gets a nosebleed."), span_warning("You get a nosebleed.")) - return - to_chat(target, span_warning("You feel a bit nauseous for just a moment.")) - #undef CHANCE_TO_MOVE_TO_TARGET #undef CHANCE_TO_MOVE_TO_TARGET_BLOODTHIRSTY #undef CHANCE_TO_CHANGE_TARGET_BLOODTHIRSTY diff --git a/code/datums/components/slippery.dm b/code/datums/components/slippery.dm index d552f1a2f816..7d809f321b48 100644 --- a/code/datums/components/slippery.dm +++ b/code/datums/components/slippery.dm @@ -95,6 +95,10 @@ SIGNAL_HANDLER if(!isliving(arrived)) return + if(lube_flags & SLIPPERY_TURF) + var/turf/turf = get_turf(source) + if(HAS_TRAIT(turf, TRAIT_TURF_IGNORE_SLIPPERY)) + return var/mob/living/victim = arrived if(!(victim.movement_type & (FLYING | FLOATING)) && victim.slip(knockdown_time, parent, lube_flags, paralyze_time, force_drop_items) && callback) callback.Invoke(victim) diff --git a/code/datums/components/wet_floor.dm b/code/datums/components/wet_floor.dm index 9c795fb8a181..0b3b92fd2e3c 100644 --- a/code/datums/components/wet_floor.dm +++ b/code/datums/components/wet_floor.dm @@ -75,23 +75,23 @@ /datum/component/wet_floor/proc/update_flags() var/intensity - lube_flags = NONE + lube_flags = SLIPPERY_TURF switch(highest_strength) if(TURF_WET_WATER) intensity = 60 - lube_flags = NO_SLIP_WHEN_WALKING + lube_flags |= NO_SLIP_WHEN_WALKING if(TURF_WET_LUBE) intensity = 80 - lube_flags = SLIDE | GALOSHES_DONT_HELP + lube_flags |= SLIDE | GALOSHES_DONT_HELP if(TURF_WET_ICE) intensity = 120 - lube_flags = SLIDE | GALOSHES_DONT_HELP + lube_flags |= SLIDE | GALOSHES_DONT_HELP if(TURF_WET_PERMAFROST) intensity = 120 - lube_flags = SLIDE_ICE | GALOSHES_DONT_HELP + lube_flags |= SLIDE_ICE | GALOSHES_DONT_HELP if(TURF_WET_SUPERLUBE) intensity = 120 - lube_flags = SLIDE | GALOSHES_DONT_HELP | SLIP_WHEN_CRAWLING + lube_flags |= SLIDE | GALOSHES_DONT_HELP | SLIP_WHEN_CRAWLING else qdel(parent.GetComponent(/datum/component/slippery)) return diff --git a/code/datums/elements/food/microwavable.dm b/code/datums/elements/food/microwavable.dm index 86a43c0603c5..8ed96be74b2c 100644 --- a/code/datums/elements/food/microwavable.dm +++ b/code/datums/elements/food/microwavable.dm @@ -49,7 +49,7 @@ result.reagents?.multiply_reagents(efficiency * CRAFTED_FOOD_BASE_REAGENT_MODIFIER) source.reagents?.trans_to(result, source.reagents.total_volume) - BLACKBOX_LOG_FOOD_MADE(result.type) + BLACKBOX_LOG_FOOD_MADE(result) qdel(source) diff --git a/code/datums/elements/give_turf_traits.dm b/code/datums/elements/give_turf_traits.dm new file mode 100644 index 000000000000..e374b16d63cf --- /dev/null +++ b/code/datums/elements/give_turf_traits.dm @@ -0,0 +1,81 @@ +///A bespoke element that adds a set of traits to the turf while occupied by at least one attached movabled. +/datum/element/give_turf_traits + element_flags = ELEMENT_DETACH_ON_HOST_DESTROY|ELEMENT_BESPOKE + argument_hash_start_idx = 2 + ///A list of traits that are added to the turf while occupied. + var/list/traits + ///The list of occupied turfs: Assoc value is a list of movables with this element that are occupying the turf. + var/list/occupied_turfs = list() + +/datum/element/give_turf_traits/Attach(atom/movable/target, list/traits) + . = ..() + if(!istype(target)) + return ELEMENT_INCOMPATIBLE + + src.traits = traits + + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + if(isturf(target.loc)) + add_to_occupied_turfs(target.loc, target) + +/datum/element/give_turf_traits/Detach(atom/movable/source) + UnregisterSignal(source, COMSIG_MOVABLE_MOVED) + if(isturf(source.loc)) + remove_from_occupied_turfs(source.loc, source) + return ..() + +///Removes the trait from the old turf and adds it to the new one. +/datum/element/give_turf_traits/proc/on_moved(atom/movable/source, atom/old_loc) + SIGNAL_HANDLER + if(isturf(old_loc)) + remove_from_occupied_turfs(old_loc, source) + + if(isturf(source.loc)) + add_to_occupied_turfs(source.loc, source) + +/** + * Registers the turf signals if it was previously unoccupied and adds it to the list of occupied turfs. + * Otherwise, it just adds the movable to the assoc value of lists occupying the turf. + */ +/datum/element/give_turf_traits/proc/add_to_occupied_turfs(turf/location, atom/movable/source) + if(occupied_turfs[location]) + occupied_turfs[location] += source + return + + occupied_turfs[location] = list(source) + RegisterSignal(location, COMSIG_TURF_CHANGE, PROC_REF(pre_change_turf)) + + var/update_movespeeds = (TRAIT_TURF_IGNORE_SLOWDOWN in traits) && !HAS_TRAIT(location, TRAIT_TURF_IGNORE_SLOWDOWN) + for(var/trait in traits) + ADD_TRAIT(location, trait, REF(src)) + if(update_movespeeds) + for(var/mob/living/living in location) + living.update_turf_movespeed() + +/** + * Unregisters the turf signals if it's no longer unoccupied and removes it from the list of occupied turfs. + * Otherwise, it just removes the movable from the assoc value of lists occupying the turf. + */ +/datum/element/give_turf_traits/proc/remove_from_occupied_turfs(turf/location, atom/movable/source) + LAZYREMOVE(occupied_turfs[location], source) + if(occupied_turfs[location]) + return + + occupied_turfs -= location + UnregisterSignal(location, COMSIG_TURF_CHANGE) + + for(var/trait in traits) + REMOVE_TRAIT(location, trait, REF(src)) + + if((TRAIT_TURF_IGNORE_SLOWDOWN in traits) && !HAS_TRAIT(location, TRAIT_TURF_IGNORE_SLOWDOWN)) + for(var/mob/living/living in location) + living.update_turf_movespeed() + +///Signals and components are carried over when the turf is changed, so they've to be readded post-change. +/datum/element/give_turf_traits/proc/pre_change_turf(turf/changed, path, list/new_baseturfs, flags, list/post_change_callbacks) + SIGNAL_HANDLER + post_change_callbacks += CALLBACK(src, PROC_REF(reoccupy_turf)) + +/datum/element/give_turf_traits/proc/reoccupy_turf(turf/changed) + for(var/trait in traits) + ADD_TRAIT(changed, trait, REF(src)) diff --git a/code/datums/elements/hat_wearer.dm b/code/datums/elements/hat_wearer.dm new file mode 100644 index 000000000000..75fc6a6c6778 --- /dev/null +++ b/code/datums/elements/hat_wearer.dm @@ -0,0 +1,72 @@ +/** + * # mobs that can wear hats! + */ +/datum/element/hat_wearer + element_flags = ELEMENT_DETACH_ON_HOST_DESTROY|ELEMENT_BESPOKE + argument_hash_start_idx = 2 + ///offsets of hats we will wear + var/list/offsets + +/datum/element/hat_wearer/Attach(datum/target, offsets = list()) + . = ..() + if (!isliving(target)) + return ELEMENT_INCOMPATIBLE + src.offsets = offsets + RegisterSignal(target, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(on_overlays_updated)) + RegisterSignal(target, COMSIG_ATOM_EXITED, PROC_REF(exited)) + RegisterSignal(target, COMSIG_ATOM_ENTERED, PROC_REF(on_entered)) + RegisterSignal(target, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attack_by)) + +/datum/element/hat_wearer/Detach(datum/target) + var/obj/item/hat = (locate(/obj/item/clothing/head) in target) + if(hat) + hat.forceMove(get_turf(target)) + UnregisterSignal(target, list( + COMSIG_ATOM_UPDATE_OVERLAYS, + COMSIG_ATOM_EXITED, + COMSIG_ATOM_ENTERED, + COMSIG_ATOM_ATTACKBY, + )) + return ..() + +/datum/element/hat_wearer/proc/on_overlays_updated(atom/source, list/overlays) + SIGNAL_HANDLER + + var/obj/item/hat = (locate(/obj/item/clothing/head) in source) + if(isnull(hat)) + return + var/mutable_appearance/hat_overlay = mutable_appearance(hat.worn_icon, hat.icon_state) + hat_overlay.pixel_x = offsets[1] + hat_overlay.pixel_y = offsets[2] + overlays += hat_overlay + +/datum/element/hat_wearer/proc/exited(atom/movable/source, atom/movable/exited) + SIGNAL_HANDLER + + if(!istype(exited, /obj/item/clothing/head)) + return + source.update_appearance(UPDATE_OVERLAYS) + +/datum/element/hat_wearer/proc/on_entered(atom/movable/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs) + SIGNAL_HANDLER + + if(!istype(arrived, /obj/item/clothing/head)) + return + for(var/obj/item/clothing/head/already_worn in source) + if(already_worn == arrived) + continue + already_worn.forceMove(get_turf(source)) + source.update_appearance(UPDATE_OVERLAYS) + +/datum/element/hat_wearer/proc/on_attack_by(atom/movable/source, obj/item/item, mob/living/attacker) + SIGNAL_HANDLER + if(!istype(item, /obj/item/clothing/head)) + return + INVOKE_ASYNC(src, PROC_REF(place_hat), source, item, attacker) + return COMPONENT_NO_AFTERATTACK + +/datum/element/hat_wearer/proc/place_hat(atom/movable/source, obj/item/item, mob/living/attacker) + if(!do_after(attacker, delay = 3 SECONDS, target = source)) + source.balloon_alert(attacker, "must stay still!") + return + item.forceMove(source) diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index feca02e6e6d4..06493f772f00 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -131,6 +131,7 @@ to_chat(viewer, msg) SEND_SIGNAL(user, COMSIG_MOB_EMOTED(key)) + SSblackbox.record_feedback("tally", "emote_used", 1, name) /** * For handling emote cooldown, return true to allow the emote to happen. diff --git a/code/datums/id_trim/jobs.dm b/code/datums/id_trim/jobs.dm index abc2135370b5..2f86ec19841a 100644 --- a/code/datums/id_trim/jobs.dm +++ b/code/datums/id_trim/jobs.dm @@ -593,6 +593,7 @@ ACCESS_SERVICE, ACCESS_SHIPPING, ACCESS_WEAPONS, + ACCESS_PERMABRIG, // monkestation edit: add permabrig-only access ) minimal_wildcard_access = list( ACCESS_HOS, @@ -939,6 +940,7 @@ ACCESS_MINERAL_STOREROOM, ACCESS_SECURITY, ACCESS_WEAPONS, + ACCESS_PERMABRIG, // monkestation edit: add permabrig-only access ) extra_access = list( ACCESS_DETECTIVE, @@ -1151,6 +1153,7 @@ ACCESS_MINERAL_STOREROOM, ACCESS_SECURITY, ACCESS_WEAPONS, + ACCESS_PERMABRIG, // monkestation edit: add permabrig-only access ) // See /datum/job/warden/get_access() extra_access = list( ACCESS_DETECTIVE, diff --git a/code/datums/proximity_monitor/field.dm b/code/datums/proximity_monitor/field.dm index 12c033cc4260..67bbef948ef3 100644 --- a/code/datums/proximity_monitor/field.dm +++ b/code/datums/proximity_monitor/field.dm @@ -27,35 +27,59 @@ /datum/proximity_monitor/advanced/proc/cleanup_field() for(var/turf/turf as anything in edge_turfs) cleanup_edge_turf(turf) + edge_turfs = list() for(var/turf/turf as anything in field_turfs) cleanup_field_turf(turf) + field_turfs = list() //Call every time the field moves (done automatically if you use update_center) or a setup specification is changed. -/datum/proximity_monitor/advanced/proc/recalculate_field() +/datum/proximity_monitor/advanced/proc/recalculate_field(full_recalc = FALSE) var/list/new_turfs = update_new_turfs() - var/list/new_field_turfs = new_turfs[FIELD_TURFS_KEY] - var/list/new_edge_turfs = new_turfs[EDGE_TURFS_KEY] + var/list/old_field_turfs = field_turfs + var/list/old_edge_turfs = edge_turfs + field_turfs = new_turfs[FIELD_TURFS_KEY] + edge_turfs = new_turfs[EDGE_TURFS_KEY] + if(!full_recalc) + field_turfs = list() + edge_turfs = list() - for(var/turf/old_turf as anything in field_turfs) - if(!(old_turf in new_field_turfs)) - cleanup_field_turf(old_turf) - for(var/turf/old_turf as anything in edge_turfs) + for(var/turf/old_turf as anything in old_field_turfs - field_turfs) + if(QDELETED(src)) + return + cleanup_field_turf(old_turf) + for(var/turf/old_turf as anything in old_edge_turfs - edge_turfs) + if(QDELETED(src)) + return cleanup_edge_turf(old_turf) - for(var/turf/new_turf as anything in new_field_turfs) - field_turfs |= new_turf + if(full_recalc) + old_field_turfs = list() + old_edge_turfs = list() + field_turfs = new_turfs[FIELD_TURFS_KEY] + edge_turfs = new_turfs[EDGE_TURFS_KEY] + + for(var/turf/new_turf as anything in field_turfs - old_field_turfs) + if(QDELETED(src)) + return + field_turfs += new_turf setup_field_turf(new_turf) - for(var/turf/new_turf as anything in new_edge_turfs) - edge_turfs |= new_turf + for(var/turf/new_turf as anything in edge_turfs - old_edge_turfs) + if(QDELETED(src)) + return + edge_turfs += new_turf setup_edge_turf(new_turf) -/datum/proximity_monitor/advanced/on_entered(turf/source, atom/movable/entered) +/datum/proximity_monitor/advanced/on_initialized(turf/location, atom/created, init_flags) + . = ..() + on_entered(location, created, null) + +/datum/proximity_monitor/advanced/on_entered(turf/source, atom/movable/entered, turf/old_loc) . = ..() if(get_dist(source, host) == current_range) - field_edge_crossed(entered, source) + field_edge_crossed(entered, old_loc, source) else - field_turf_crossed(entered, source) + field_turf_crossed(entered, old_loc, source) /datum/proximity_monitor/advanced/on_moved(atom/movable/movable, atom/old_loc) . = ..() @@ -68,21 +92,22 @@ if(isturf(old_loc)) cleanup_field() return - recalculate_field() + recalculate_field(full_recalc = FALSE) /datum/proximity_monitor/advanced/on_uncrossed(turf/source, atom/movable/gone, direction) if(get_dist(source, host) == current_range) - field_edge_uncrossed(gone, source) + field_edge_uncrossed(gone, source, get_turf(gone)) else - field_turf_uncrossed(gone, source) + field_turf_uncrossed(gone, source, get_turf(gone)) /// Called when a turf in the field of the monitor is linked /datum/proximity_monitor/advanced/proc/setup_field_turf(turf/target) return /// Called when a turf in the field of the monitor is unlinked +/// Do NOT call this manually, requires management of the field_turfs list /datum/proximity_monitor/advanced/proc/cleanup_field_turf(turf/target) - field_turfs -= target + return /// Called when a turf in the edge of the monitor is linked /datum/proximity_monitor/advanced/proc/setup_edge_turf(turf/target) @@ -90,21 +115,22 @@ setup_field_turf(target) /// Called when a turf in the edge of the monitor is unlinked +/// Do NOT call this manually, requires management of the edge_turfs list /datum/proximity_monitor/advanced/proc/cleanup_edge_turf(turf/target) if(edge_is_a_field) // If the edge is considered a field, clean it up like one cleanup_field_turf(target) - edge_turfs -= target /datum/proximity_monitor/advanced/proc/update_new_turfs() - . = list(FIELD_TURFS_KEY = list(), EDGE_TURFS_KEY = list()) if(ignore_if_not_on_turf && !isturf(host.loc)) - return + return list(FIELD_TURFS_KEY = list(), EDGE_TURFS_KEY = list()) + var/list/local_field_turfs = list() + var/list/local_edge_turfs = list() var/turf/center = get_turf(host) - for(var/turf/target in RANGE_TURFS(current_range, center)) - if(get_dist(center, target) == current_range) - .[EDGE_TURFS_KEY] += target - else - .[FIELD_TURFS_KEY] += target + if(current_range > 0) + local_field_turfs += RANGE_TURFS(current_range - 1, center) + if(current_range > 1) + local_edge_turfs = local_field_turfs - RANGE_TURFS(current_range, center) + return list(FIELD_TURFS_KEY = local_field_turfs, EDGE_TURFS_KEY = local_edge_turfs) //Gets edge direction/corner, only works with square radius/WDH fields! /datum/proximity_monitor/advanced/proc/get_edgeturf_direction(turf/T, turf/center_override = null) @@ -124,19 +150,19 @@ if(T.y == (checking_from.y + current_range)) return NORTH -/datum/proximity_monitor/advanced/proc/field_turf_crossed(atom/movable/movable, turf/location) +/datum/proximity_monitor/advanced/proc/field_turf_crossed(atom/movable/movable, turf/old_location, turf/new_location) return -/datum/proximity_monitor/advanced/proc/field_turf_uncrossed(atom/movable/movable, turf/location) +/datum/proximity_monitor/advanced/proc/field_turf_uncrossed(atom/movable/movable, turf/old_location, turf/new_location) return -/datum/proximity_monitor/advanced/proc/field_edge_crossed(atom/movable/movable, turf/location) +/datum/proximity_monitor/advanced/proc/field_edge_crossed(atom/movable/movable, turf/old_location, turf/new_location) if(edge_is_a_field) // If the edge is considered a field, pass crossed to that - field_turf_crossed(movable, location) + field_turf_crossed(movable, old_location, new_location) -/datum/proximity_monitor/advanced/proc/field_edge_uncrossed(atom/movable/movable, turf/location) +/datum/proximity_monitor/advanced/proc/field_edge_uncrossed(atom/movable/movable, turf/old_location, turf/new_location) if(edge_is_a_field) // If the edge is considered a field, pass uncrossed to that - field_turf_uncrossed(movable, location) + field_turf_uncrossed(movable, old_location, new_location) //DEBUG FIELD ITEM /obj/item/multitool/field_debug @@ -153,7 +179,7 @@ current = new(src, 5, FALSE) current.set_fieldturf_color = "#aaffff" current.set_edgeturf_color = "#ffaaff" - current.recalculate_field() + current.recalculate_field(full_recalc = TRUE) /obj/item/multitool/field_debug/attack_self(mob/user) operating = !operating diff --git a/code/datums/proximity_monitor/fields/ai_target_tracking.dm b/code/datums/proximity_monitor/fields/ai_target_tracking.dm new file mode 100644 index 000000000000..46cde22aaffc --- /dev/null +++ b/code/datums/proximity_monitor/fields/ai_target_tracking.dm @@ -0,0 +1,113 @@ +// Proximity monitor that checks to see if anything interesting enters our bounds +/datum/proximity_monitor/advanced/ai_target_tracking + edge_is_a_field = TRUE + /// The ai behavior who owns us + var/datum/ai_behavior/find_potential_targets/owning_behavior + /// The ai controller we're using + var/datum/ai_controller/controller + /// The target key we're trying to fill + var/target_key + /// The targeting strategy KEY we're using + var/targeting_strategy_key + /// The hiding location key we're using + var/hiding_location_key + + /// The targeting strategy we're using + var/datum/targeting_strategy/filter + /// If we've built our field yet + /// Prevents wasted work on the first build (since the behavior did it) + var/first_build = TRUE + +// Initially, run the check manually +// If that fails, set up a field and have it manage the behavior fully +/datum/proximity_monitor/advanced/ai_target_tracking/New(atom/_host, range, _ignore_if_not_on_turf = TRUE, datum/ai_behavior/find_potential_targets/owning_behavior, datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) + . = ..() + src.owning_behavior = owning_behavior + src.controller = controller + src.target_key = target_key + src.targeting_strategy_key = targeting_strategy_key + src.hiding_location_key = hiding_location_key + src.filter = GET_TARGETING_STRATEGY(controller.blackboard[targeting_strategy_key]) + RegisterSignal(controller, COMSIG_QDELETING, PROC_REF(controller_deleted)) + RegisterSignal(controller, COMSIG_AI_CONTROLLER_PICKED_BEHAVIORS, PROC_REF(controller_think)) + RegisterSignal(controller, COMSIG_AI_CONTROLLER_POSSESSED_PAWN, PROC_REF(pawn_changed)) + RegisterSignal(controller, AI_CONTROLLER_BEHAVIOR_QUEUED(owning_behavior.type), PROC_REF(behavior_requeued)) + RegisterSignal(controller, COMSIG_AI_BLACKBOARD_KEY_SET(targeting_strategy_key), PROC_REF(targeting_datum_changed)) + RegisterSignal(controller, COMSIG_AI_BLACKBOARD_KEY_CLEARED(targeting_strategy_key), PROC_REF(targeting_datum_cleared)) + recalculate_field(full_recalc = TRUE) + +/datum/proximity_monitor/advanced/ai_target_tracking/Destroy() + . = ..() + if(!QDELETED(controller) && owning_behavior) + controller.modify_cooldown(owning_behavior, owning_behavior.get_cooldown(controller)) + owning_behavior = null + controller = null + target_key = null + targeting_strategy_key = null + hiding_location_key = null + filter = null + +/datum/proximity_monitor/advanced/ai_target_tracking/recalculate_field(full_recalc = FALSE) + . = ..() + first_build = FALSE + +/datum/proximity_monitor/advanced/ai_target_tracking/setup_field_turf(turf/target) + . = ..() + if(first_build) + return + owning_behavior.new_turf_found(target, controller, filter) + +/datum/proximity_monitor/advanced/ai_target_tracking/field_turf_crossed(atom/movable/movable, turf/location, turf/old_location) + . = ..() + + if(!owning_behavior.atom_allowed(movable, filter, controller.pawn)) + return + + owning_behavior.new_atoms_found(list(movable), controller, target_key, filter, hiding_location_key) + +/// React to controller planning +/datum/proximity_monitor/advanced/ai_target_tracking/proc/controller_deleted(datum/source) + SIGNAL_HANDLER + qdel(src) + +/// React to the pawn goin byebye +/datum/proximity_monitor/advanced/ai_target_tracking/proc/pawn_changed(datum/source) + SIGNAL_HANDLER + qdel(src) + +/// React to controller planning +/datum/proximity_monitor/advanced/ai_target_tracking/proc/controller_think(datum/ai_controller/source, list/datum/ai_behavior/old_behaviors, list/datum/ai_behavior/new_behaviors) + SIGNAL_HANDLER + // If our parent was forgotten, nuke ourselves + if(!new_behaviors[owning_behavior]) + qdel(src) + +/datum/proximity_monitor/advanced/ai_target_tracking/proc/behavior_requeued(datum/source, list/new_arguments) + SIGNAL_HANDLER + check_new_args(arglist(new_arguments)) + +/// Ensure our args and locals are up to date +/datum/proximity_monitor/advanced/ai_target_tracking/proc/check_new_args(target_key, targeting_strategy_key, hiding_location_key) + var/update_filter = FALSE + if(src.target_key != target_key) + src.target_key = target_key + if(src.targeting_strategy_key != targeting_strategy_key) + src.targeting_strategy_key = targeting_strategy_key + update_filter = TRUE + if(src.hiding_location_key != hiding_location_key) + src.hiding_location_key = hiding_location_key + if(update_filter) + targeting_datum_changed(null) + +/datum/proximity_monitor/advanced/ai_target_tracking/proc/targeting_datum_changed(datum/source) + SIGNAL_HANDLER + filter = controller.blackboard[targeting_strategy_key] + // Filter changed, need to do a full reparse + // Fucking 9 * 9 out here I stg + for(var/turf/in_field as anything in field_turfs + edge_turfs) + owning_behavior.new_turf_found(in_field, controller, filter) + +/datum/proximity_monitor/advanced/ai_target_tracking/proc/targeting_datum_cleared(datum/source) + SIGNAL_HANDLER + // Go fuckin home bros + qdel(src) diff --git a/code/datums/proximity_monitor/fields/gravity.dm b/code/datums/proximity_monitor/fields/gravity.dm index 181e74157cd7..e341c36c9b18 100644 --- a/code/datums/proximity_monitor/fields/gravity.dm +++ b/code/datums/proximity_monitor/fields/gravity.dm @@ -6,7 +6,7 @@ /datum/proximity_monitor/advanced/gravity/New(atom/_host, range, _ignore_if_not_on_turf = TRUE, gravity) . = ..() gravity_value = gravity - recalculate_field() + recalculate_field(full_recalc = TRUE) /datum/proximity_monitor/advanced/gravity/setup_field_turf(turf/target) . = ..() @@ -22,3 +22,41 @@ return target.RemoveElement(/datum/element/forced_gravity, modified_turfs[target]) modified_turfs -= target + +// Subtype which pops up a balloon alert when a mob enters the field +/datum/proximity_monitor/advanced/gravity/warns_on_entrance + /// This is a list of mob refs that have recently entered the field. + /// We track it so that we don't spam a player who is stutter stepping in and out with balloon alerts. + var/list/recently_warned + +/datum/proximity_monitor/advanced/gravity/warns_on_entrance/setup_field_turf(turf/target) + . = ..() + for(var/mob/living/guy in target) + warn_mob(guy, target) + +/datum/proximity_monitor/advanced/gravity/warns_on_entrance/cleanup_field_turf(turf/target) + . = ..() + for(var/mob/living/guy in target) + warn_mob(guy, target) + +/datum/proximity_monitor/advanced/gravity/warns_on_entrance/field_edge_crossed(atom/movable/movable, turf/old_location, turf/new_location) + . = ..() + if(isliving(movable)) + warn_mob(movable, new_location) + +/datum/proximity_monitor/advanced/gravity/warns_on_entrance/field_edge_uncrossed(atom/movable/movable, turf/old_location, turf/new_location) + . = ..() + if(isliving(movable)) + warn_mob(movable, old_location) + +/datum/proximity_monitor/advanced/gravity/warns_on_entrance/proc/warn_mob(mob/living/to_warn, turf/location) + var/mob_ref_key = REF(to_warn) + if(mob_ref_key in recently_warned) + return + + location.balloon_alert(to_warn, "gravity [(location in modified_turfs) ? "shifts!" : "reverts..."]") + LAZYADD(recently_warned, mob_ref_key) + addtimer(CALLBACK(src, PROC_REF(clear_recent_warning), mob_ref_key), 3 SECONDS) + +/datum/proximity_monitor/advanced/gravity/warns_on_entrance/proc/clear_recent_warning(mob_ref_key) + LAZYREMOVE(recently_warned, mob_ref_key) diff --git a/code/datums/proximity_monitor/fields/projectile_dampener.dm b/code/datums/proximity_monitor/fields/projectile_dampener.dm index 7f625d645244..33f56070b09d 100644 --- a/code/datums/proximity_monitor/fields/projectile_dampener.dm +++ b/code/datums/proximity_monitor/fields/projectile_dampener.dm @@ -19,7 +19,7 @@ /datum/proximity_monitor/advanced/projectile_dampener/New(atom/_host, range, _ignore_if_not_on_turf = TRUE, atom/projector) ..() RegisterSignal(projector, COMSIG_QDELETING, PROC_REF(on_projector_del)) - recalculate_field() + recalculate_field(full_recalc = TRUE) START_PROCESSING(SSfastprocess, src) /datum/proximity_monitor/advanced/projectile_dampener/Destroy() @@ -49,7 +49,7 @@ LAZYSET(edgeturf_effects, target, effect) /datum/proximity_monitor/advanced/projectile_dampener/on_z_change(datum/source) - recalculate_field() + recalculate_field(full_recalc = TRUE) /datum/proximity_monitor/advanced/projectile_dampener/cleanup_edge_turf(turf/target) . = ..() @@ -91,16 +91,15 @@ /datum/proximity_monitor/advanced/projectile_dampener/proc/on_projector_del(datum/source) SIGNAL_HANDLER - qdel(src) -/datum/proximity_monitor/advanced/projectile_dampener/field_edge_uncrossed(atom/movable/movable, turf/location) +/datum/proximity_monitor/advanced/projectile_dampener/field_edge_uncrossed(atom/movable/movable, turf/old_location, turf/new_location) if(isprojectile(movable) && get_dist(movable, host) > current_range) if(movable in tracked) release_projectile(movable) -/datum/proximity_monitor/advanced/projectile_dampener/field_edge_crossed(atom/movable/movable, turf/location) - if(isprojectile(movable) && !(movable in tracked)) +/datum/proximity_monitor/advanced/projectile_dampener/field_edge_crossed(atom/movable/movable, turf/location, turf/old_location) + if(isprojectile(movable)) capture_projectile(movable) /datum/proximity_monitor/advanced/projectile_dampener/peaceborg/process(seconds_per_tick) diff --git a/code/datums/proximity_monitor/fields/timestop.dm b/code/datums/proximity_monitor/fields/timestop.dm index 86ea41aee013..349f3c93d4d8 100644 --- a/code/datums/proximity_monitor/fields/timestop.dm +++ b/code/datums/proximity_monitor/fields/timestop.dm @@ -82,7 +82,7 @@ src.immune = immune src.antimagic_flags = antimagic_flags src.channelled = channelled - recalculate_field() + recalculate_field(full_recalc = TRUE) START_PROCESSING(SSfastprocess, src) /datum/proximity_monitor/advanced/timestop/Destroy() @@ -93,7 +93,7 @@ STOP_PROCESSING(SSfastprocess, src) return ..() -/datum/proximity_monitor/advanced/timestop/field_turf_crossed(atom/movable/movable, turf/location) +/datum/proximity_monitor/advanced/timestop/field_turf_crossed(atom/movable/movable, turf/old_location, turf/new_location) freeze_atom(movable) /datum/proximity_monitor/advanced/timestop/proc/freeze_atom(atom/movable/A) diff --git a/code/datums/proximity_monitor/proximity_monitor.dm b/code/datums/proximity_monitor/proximity_monitor.dm index c7c8165a431a..ec77ce2145a1 100644 --- a/code/datums/proximity_monitor/proximity_monitor.dm +++ b/code/datums/proximity_monitor/proximity_monitor.dm @@ -11,7 +11,7 @@ var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_entered), COMSIG_ATOM_EXITED = PROC_REF(on_uncrossed), - COMSIG_ATOM_INITIALIZED_ON = PROC_REF(on_entered), + COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON = PROC_REF(on_initialized), ) /datum/proximity_monitor/New(atom/_host, range, _ignore_if_not_on_turf = TRUE) @@ -78,7 +78,12 @@ SIGNAL_HANDLER return //Used by the advanced subtype for effect fields. -/datum/proximity_monitor/proc/on_entered(atom/source, atom/movable/arrived) +/datum/proximity_monitor/proc/on_entered(atom/source, atom/movable/arrived, turf/old_loc) SIGNAL_HANDLER if(source != host) hasprox_receiver?.HasProximity(arrived) + +/datum/proximity_monitor/proc/on_initialized(turf/location, atom/created, init_flags) + SIGNAL_HANDLER + if(location != host) + hasprox_receiver?.HasProximity(created) diff --git a/code/datums/station_traits/negative_traits.dm b/code/datums/station_traits/negative_traits.dm index 0eb7668c6d8e..8179282e88e6 100644 --- a/code/datums/station_traits/negative_traits.dm +++ b/code/datums/station_traits/negative_traits.dm @@ -146,7 +146,7 @@ /datum/station_trait/bot_languages/on_round_start() . = ..() // All bots that exist round start on station Z OR on the escape shuttle have their set language randomized. - for(var/mob/living/simple_animal/bot/found_bot as anything in GLOB.bots_list) + for(var/mob/living/found_bot as anything in GLOB.bots_list) found_bot.randomize_language_if_on_station() /datum/station_trait/revenge_of_pun_pun diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 1475c8cdc908..601b547a45c3 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -250,9 +250,6 @@ stack_trace("Warning: [src]([type]) initialized multiple times!") flags_1 |= INITIALIZED_1 - if(loc) - SEND_SIGNAL(loc, COMSIG_ATOM_INITIALIZED_ON, src) /// Sends a signal that the new atom `src`, has been created at `loc` - SET_PLANE_IMPLICIT(src, plane) if(greyscale_config && greyscale_colors) //we'll check again at item/init for inhand/belt/worn configs. diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 55ef35401204..15e857fcb47f 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -55,10 +55,10 @@ /datum/atom_hud/data/diagnostic /datum/atom_hud/data/diagnostic/basic - hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_TRACK_HUD, DIAG_CAMERA_HUD, DIAG_AIRLOCK_HUD, DIAG_LAUNCHPAD_HUD, NANITE_HUD) + hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_TRACK_HUD, DIAG_CAMERA_HUD, DIAG_AIRLOCK_HUD, DIAG_LAUNCHPAD_HUD, NANITE_HUD, DIAG_NANITE_FULL_HUD) /datum/atom_hud/data/diagnostic/advanced - hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_TRACK_HUD, DIAG_CAMERA_HUD, DIAG_AIRLOCK_HUD, DIAG_LAUNCHPAD_HUD, DIAG_PATH_HUD, NANITE_HUD) + hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_TRACK_HUD, DIAG_CAMERA_HUD, DIAG_AIRLOCK_HUD, DIAG_LAUNCHPAD_HUD, DIAG_PATH_HUD, NANITE_HUD, DIAG_NANITE_FULL_HUD) /datum/atom_hud/data/bot_path // This hud exists so the bot can see itself, that's all @@ -542,7 +542,6 @@ Diagnostic HUDs! holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]" else holder.icon_state = "hudnobatt" - /*~~~~~~~~~~~~ Airlocks! ~~~~~~~~~~~~~*/ diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm index 040a2397bfab..dd6752a035e8 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_latejoin.dm @@ -65,6 +65,7 @@ JOB_HEAD_OF_PERSONNEL, JOB_HEAD_OF_SECURITY, JOB_SECURITY_OFFICER, + JOB_SECURITY_ASSISTANT, JOB_WARDEN, ) restricted_roles = list( @@ -104,6 +105,7 @@ JOB_QUARTERMASTER, JOB_RESEARCH_DIRECTOR, JOB_SECURITY_OFFICER, + JOB_SECURITY_ASSISTANT, JOB_WARDEN, ) enemy_roles = list( @@ -201,6 +203,7 @@ JOB_HEAD_OF_SECURITY, JOB_PRISONER, JOB_SECURITY_OFFICER, + JOB_SECURITY_ASSISTANT, JOB_WARDEN, ) restricted_roles = list( diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm index a5773802eb29..0138d8e5ba3c 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm @@ -225,6 +225,7 @@ JOB_HEAD_OF_SECURITY, JOB_PRISONER, JOB_SECURITY_OFFICER, + JOB_SECURITY_ASSISTANT, JOB_WARDEN, ) restricted_roles = list( @@ -437,6 +438,7 @@ JOB_PRISONER, JOB_SECURITY_OFFICER, JOB_WARDEN, + JOB_SECURITY_ASSISTANT, ) restricted_roles = list( JOB_AI, diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm index 77be4a196f7d..dccdd79fc2ef 100644 --- a/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm +++ b/code/game/gamemodes/dynamic/dynamic_rulesets_roundstart.dm @@ -113,6 +113,7 @@ JOB_PRISONER, JOB_SECURITY_OFFICER, JOB_WARDEN, + JOB_SECURITY_ASSISTANT, ) restricted_roles = list( JOB_AI, @@ -170,6 +171,7 @@ JOB_PRISONER, JOB_SECURITY_OFFICER, JOB_WARDEN, + JOB_SECURITY_ASSISTANT, ) restricted_roles = list( JOB_AI, @@ -226,6 +228,7 @@ JOB_PRISONER, JOB_SECURITY_OFFICER, JOB_WARDEN, + JOB_SECURITY_ASSISTANT, ) restricted_roles = list( JOB_AI, diff --git a/code/game/machinery/botlaunchpad.dm b/code/game/machinery/botlaunchpad.dm index 6ff39e3960bb..a4ad07e70bc2 100644 --- a/code/game/machinery/botlaunchpad.dm +++ b/code/game/machinery/botlaunchpad.dm @@ -7,12 +7,13 @@ // ID of the console, used for linking up var/id = "botlauncher" var/obj/item/botpad_remote/connected_remote - var/mob/living/simple_animal/bot/launched_bot // we need this to recall the bot + var/datum/weakref/launched_bot // we need this to recall the bot /obj/machinery/botpad/Destroy() if(connected_remote) connected_remote.connected_botpad = null connected_remote = null + launched_bot = null return ..() /obj/machinery/botpad/screwdriver_act(mob/user, obj/item/tool) @@ -34,34 +35,36 @@ // Checks the turf for a bot and launches it if it's the only mob on the pad. /obj/machinery/botpad/proc/launch(mob/living/user) var/turf/reverse_turf = get_turf(user) - var/bot_count = 0 - var/possible_bot - for(var/atom/movable/ROI in get_turf(src)) - if(ismob(ROI)) - if(istype(ROI, /mob/living/simple_animal/bot)) - bot_count += 1 // this counts the number of bots so we don't launch if there multiple bots. - possible_bot = ROI // We don't change the launched_bot var here because we are not sure if there is another bot on the pad. - else - user?.balloon_alert(user, "unidentified life form on the pad!") - return - if(bot_count == 1) - launched_bot = possible_bot - podspawn(list( - "target" = get_turf(src), - "path" = /obj/structure/closet/supplypod/botpod, - "style" = STYLE_SEETHROUGH, - "reverse_dropoff_coords" = list(reverse_turf.x, reverse_turf.y, reverse_turf.z) - )) - use_power(active_power_usage) - else - user?.balloon_alert(user, "too many bots on the pad!") + var/atom/possible_bot + for(var/mob/living/robot in get_turf(src)) + if(!isbot(robot)) + user.balloon_alert(user, "unidentified life form on the pad!") + return + if(!isnull(possible_bot)) + user.balloon_alert(user, "too many bots on the pad!") + return + possible_bot = robot // We don't change the launched_bot var here because we are not sure if there is another bot on the pad. + launched_bot = WEAKREF(possible_bot) + podspawn(list( + "target" = get_turf(src), + "path" = /obj/structure/closet/supplypod/botpod, + "style" = STYLE_SEETHROUGH, + "reverse_dropoff_coords" = list(reverse_turf.x, reverse_turf.y, reverse_turf.z) + )) + use_power(active_power_usage) /obj/machinery/botpad/proc/recall(mob/living/user) - if(!launched_bot) - user?.balloon_alert(user, "no bots detected on the pad!") + var/atom/our_bot = launched_bot?.resolve() + if(isnull(our_bot)) + user.balloon_alert(user, "no bots detected on the pad!") return - user?.balloon_alert(user, "bot sent back to pad") - launched_bot.call_bot(src, get_turf(src)) + user.balloon_alert(user, "bot sent back to pad") + if(isbasicbot(our_bot)) + var/mob/living/basic/bot/basic_bot = our_bot + basic_bot.summon_bot(src) + return + var/mob/living/simple_animal/bot/simple_bot = our_bot + simple_bot.call_bot(src, get_turf(src)) /obj/structure/closet/supplypod/botpod style = STYLE_SEETHROUGH diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index e4419226aea2..4abc323961de 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -105,6 +105,7 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new) JOB_SECURITY_OFFICER_SCIENCE = 15, JOB_SECURITY_OFFICER_SUPPLY = 16, JOB_DETECTIVE = 17, + JOB_SECURITY_ASSISTANT = 18, // monkestation edit: add security assistants // 20-29: Medbay JOB_CHIEF_MEDICAL_OFFICER = 20, JOB_CHEMIST = 21, diff --git a/code/game/machinery/computer/orders/order_items/mining/order_mining.dm b/code/game/machinery/computer/orders/order_items/mining/order_mining.dm index 2d4813a55f40..b53100c4acf7 100644 --- a/code/game/machinery/computer/orders/order_items/mining/order_mining.dm +++ b/code/game/machinery/computer/orders/order_items/mining/order_mining.dm @@ -17,6 +17,10 @@ item_path = /obj/item/fulton_core cost_per_order = 400 +/datum/orderable_item/mining/ashsuit //MONKE EDIT + item_path = /obj/item/clothing/suit/hooded/ashsuit + cost_per_order = 100 + /datum/orderable_item/mining/mining_modsuit item_path = /obj/item/mod/control/pre_equipped/mining desc = "A mining-themed MODsuit that works best when in a mining environment." diff --git a/code/game/machinery/shieldgen.dm b/code/game/machinery/shieldgen.dm index d39c4fcd7b17..ec266077c115 100644 --- a/code/game/machinery/shieldgen.dm +++ b/code/game/machinery/shieldgen.dm @@ -1,3 +1,6 @@ +#define ACTIVE_SETUPFIELDS 1 +#define ACTIVE_HASFIELDS 2 + /obj/structure/emergency_shield name = "emergency energy shield" desc = "An energy shield used to contain hull breaches." @@ -261,12 +264,23 @@ balloon_alert(user, "access controller shorted") return TRUE +/// Turn the machine on with side effects +/obj/machinery/power/shieldwallgen/proc/activate() + active = ACTIVE_SETUPFIELDS + AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD))) + +/// Turn the machine off with side effects +/obj/machinery/power/shieldwallgen/proc/deactivate() + active = FALSE + for(var/d in GLOB.cardinals) + cleanup_field(d) + update_appearance() + RemoveElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD))) + /obj/machinery/shieldgen/update_icon_state() icon_state = "shield[active ? "on" : "off"][(machine_stat & BROKEN) ? "br" : null]" return ..() -#define ACTIVE_SETUPFIELDS 1 -#define ACTIVE_HASFIELDS 2 /obj/machinery/power/shieldwallgen name = "shield wall generator" desc = "A shield generator." @@ -334,7 +348,7 @@ "If this message is ever seen, something is wrong.", span_hear("You hear heavy droning fade out.")) icon_state = "shield_wall_gen" - active = FALSE + deactivate() log_game("[src] deactivated due to lack of power at [AREACOORD(src)]") for(var/d in GLOB.cardinals) cleanup_field(d) @@ -444,13 +458,13 @@ user.visible_message(span_notice("[user] turned \the [src] off."), \ span_notice("You turn off \the [src]."), \ span_hear("You hear heavy droning fade out.")) - active = FALSE + deactivate() user.log_message("deactivated [src].", LOG_GAME) else user.visible_message(span_notice("[user] turned \the [src] on."), \ span_notice("You turn on \the [src]."), \ span_hear("You hear heavy droning.")) - active = ACTIVE_SETUPFIELDS + activate() user.log_message("activated [src].", LOG_GAME) add_fingerprint(user) @@ -489,6 +503,7 @@ L.investigate_log("has been gibbed by [src].", INVESTIGATE_DEATHS) L.gib() RegisterSignal(src, COMSIG_ATOM_SINGULARITY_TRY_MOVE, PROC_REF(block_singularity)) + AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD))) /obj/machinery/shieldwall/Destroy() gen_primary = null diff --git a/code/game/objects/effects/anomalies/_anomalies.dm b/code/game/objects/effects/anomalies/_anomalies.dm index f130595fc387..531c800cf726 100644 --- a/code/game/objects/effects/anomalies/_anomalies.dm +++ b/code/game/objects/effects/anomalies/_anomalies.dm @@ -23,6 +23,8 @@ var/immortal = FALSE ///Do we stay in one place? var/immobile = FALSE + ///Chance per second that we will move + var/move_chance = ANOMALY_MOVECHANCE /obj/effect/anomaly/Initialize(mapload, new_lifespan, drops_core = TRUE) . = ..() @@ -52,9 +54,8 @@ countdown = new(src) if(countdown_colour) countdown.color = countdown_colour - if(immortal) - return - countdown.start() + if(!immortal) + countdown.start() /obj/effect/anomaly/vv_edit_var(vname, vval) . = ..() @@ -79,8 +80,12 @@ return ..() /obj/effect/anomaly/proc/anomalyEffect(seconds_per_tick) - if(!immobile && SPT_PROB(ANOMALY_MOVECHANCE, seconds_per_tick)) - step(src,pick(GLOB.alldirs)) + if(!immobile && SPT_PROB(move_chance, seconds_per_tick)) + move_anomaly() + +/// Move in a direction +/obj/effect/anomaly/proc/move_anomaly() + step(src, pick(GLOB.alldirs)) /obj/effect/anomaly/proc/detonate() return @@ -93,7 +98,7 @@ new /obj/effect/particle_effect/fluid/smoke/bad(loc) if(drops_core) - aSignal.forceMove(drop_location()) + aSignal?.forceMove(drop_location()) aSignal = null // else, anomaly core gets deleted by qdel(src). diff --git a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm index b4ee3713a25d..57c1441b6fde 100644 --- a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm +++ b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm @@ -4,6 +4,10 @@ icon_state = "bioscrambler" aSignal = /obj/item/assembly/signaler/anomaly/bioscrambler immortal = TRUE + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE | PASSCLOSEDTURF | PASSMACHINE | PASSSTRUCTURE | PASSDOORS + layer = ABOVE_MOB_LAYER + /// Who are we moving towards? + var/datum/weakref/pursuit_target /// Cooldown for every anomaly pulse COOLDOWN_DECLARE(pulse_cooldown) /// How many seconds between each anomaly pulses @@ -11,11 +15,57 @@ /// Range of the anomaly pulse var/range = 5 +/obj/effect/anomaly/bioscrambler/Initialize(mapload, new_lifespan, drops_core) + ..() + return INITIALIZE_HINT_LATELOAD + +/obj/effect/anomaly/bioscrambler/LateInitialize() + pursuit_target = WEAKREF(find_nearest_target()) + /obj/effect/anomaly/bioscrambler/anomalyEffect(seconds_per_tick) . = ..() if(!COOLDOWN_FINISHED(src, pulse_cooldown)) return COOLDOWN_START(src, pulse_cooldown, pulse_delay) - for(var/mob/living/carbon/nearby in range(range, src)) + for(var/mob/living/carbon/nearby in hearers(range, src)) nearby.bioscramble(name) + +/obj/effect/anomaly/bioscrambler/move_anomaly() + var/mob/living/current_target = pursuit_target?.resolve() + if (QDELETED(current_target)) + pursuit_target = null + if (isnull(pursuit_target) || prob(20)) + var/mob/living/new_target = find_nearest_target() + if (isnull(new_target)) + pursuit_target = null + else if (new_target != current_target) + current_target = new_target + pursuit_target = WEAKREF(new_target) + new_target.ominous_nosebleed() + if (isnull(pursuit_target)) + return + var/turf/step_turf = get_step(src, get_dir(src, current_target)) + if (!HAS_TRAIT(step_turf, TRAIT_CONTAINMENT_FIELD)) + Move(step_turf) + +/// Returns the closest conscious carbon on our z level or null if there somehow isn't one +/obj/effect/anomaly/bioscrambler/proc/find_nearest_target() + var/closest_distance = INFINITY + var/mob/living/carbon/closest_target = null + for(var/mob/living/carbon/target in GLOB.player_list) + if (target.z != z) + continue + if (target.status_effects & GODMODE) + continue + if (target.stat >= UNCONSCIOUS) + continue // Don't just haunt a corpse + if (contained && get_area(target) != impact_area) // monkestation edit: fix "runaway" bioscramblers + continue + var/distance_from_target = get_dist(src, target) + if(distance_from_target >= closest_distance) + continue + closest_distance = distance_from_target + closest_target = target + + return closest_target diff --git a/code/game/objects/effects/poster_motivational.dm b/code/game/objects/effects/poster_motivational.dm index 12c22a30fbf0..bc20c551193c 100644 --- a/code/game/objects/effects/poster_motivational.dm +++ b/code/game/objects/effects/poster_motivational.dm @@ -79,7 +79,7 @@ src.department = department RegisterSignal(host, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) -/datum/proximity_monitor/advanced/quirk_posters/field_turf_crossed(atom/movable/crossed, turf/location) +/datum/proximity_monitor/advanced/quirk_posters/field_turf_crossed(atom/movable/crossed, turf/old_location, turf/new_location) if (!isliving(crossed) || !can_see(crossed, host, current_range)) return on_seen(crossed) diff --git a/code/game/objects/effects/posters/poster.dm b/code/game/objects/effects/posters/poster.dm index 7aa4eebd278d..3658977ed751 100644 --- a/code/game/objects/effects/posters/poster.dm +++ b/code/game/objects/effects/posters/poster.dm @@ -185,7 +185,7 @@ qdel(src) else to_chat(user, span_notice("You carefully remove the poster from the wall.")) - roll_and_drop(Adjacent(user) ? get_turf(user) : loc) + roll_and_drop(Adjacent(user) ? get_turf(user) : loc, user) /obj/structure/sign/poster/attack_hand(mob/user, list/modifiers) . = ..() @@ -193,16 +193,7 @@ return if(ruined) return - - visible_message(span_notice("[user] rips [src] in a single, decisive motion!") ) - playsound(src.loc, 'sound/items/poster_ripped.ogg', 100, TRUE) - spring_trap(user) - - var/obj/structure/sign/poster/ripped/R = new(loc) - R.pixel_y = pixel_y - R.pixel_x = pixel_x - R.add_fingerprint(user) - qdel(src) + tear_poster(user) /obj/structure/sign/poster/proc/spring_trap(mob/user) var/obj/item/shard/payload = trap?.resolve() @@ -221,15 +212,16 @@ return FALSE return !user.gloves || !(user.gloves.body_parts_covered & HANDS) || HAS_TRAIT(user, TRAIT_FINGERPRINT_PASSTHROUGH) || HAS_TRAIT(user.gloves, TRAIT_FINGERPRINT_PASSTHROUGH) -/obj/structure/sign/poster/proc/roll_and_drop(atom/location) +/obj/structure/sign/poster/proc/roll_and_drop(atom/location, mob/user) pixel_x = 0 pixel_y = 0 var/obj/item/poster/rolled_poster = new poster_item_type(location, src) // /obj/structure/sign/poster/wanted/roll_and_drop() has some snowflake handling due to icon memes, if you make a major change to this, don't forget to update it too. <3 - forceMove(rolled_poster) + if(!user?.put_in_hands(rolled_poster)) + forceMove(rolled_poster) return rolled_poster //separated to reduce code duplication. Moved here for ease of reference and to unclutter r_wall/attackby() -/turf/closed/wall/proc/place_poster(obj/item/poster/rolled_poster, mob/user) +/turf/closed/proc/place_poster(obj/item/poster/rolled_poster, mob/user) if(!rolled_poster.poster_structure) to_chat(user, span_warning("[rolled_poster] has no poster... inside it? Inform a coder!")) return @@ -259,19 +251,30 @@ playsound(src, 'sound/items/poster_being_created.ogg', 100, TRUE) var/turf/user_drop_location = get_turf(user) //cache this so it just falls to the ground if they move. also no tk memes allowed. - if(!do_after(user, PLACE_SPEED, placed_poster, extra_checks = CALLBACK(placed_poster, TYPE_PROC_REF(/obj/structure/sign/poster, snowflake_wall_turf_check), src))) - placed_poster.roll_and_drop(user_drop_location) + if(!do_after(user, PLACE_SPEED, placed_poster, extra_checks = CALLBACK(placed_poster, TYPE_PROC_REF(/obj/structure/sign/poster, snowflake_closed_turf_check), src))) + placed_poster.roll_and_drop(user_drop_location, user) return placed_poster.on_placed_poster(user) return TRUE -/obj/structure/sign/poster/proc/snowflake_wall_turf_check(atom/hopefully_still_a_wall_turf) //since turfs never get deleted but instead change type, make sure we're still being placed on a wall. - return iswallturf(hopefully_still_a_wall_turf) +/obj/structure/sign/poster/proc/snowflake_closed_turf_check(atom/hopefully_still_a_closed_turf) //since turfs never get deleted but instead change type, make sure we're still being placed on a wall. + return isclosedturf(hopefully_still_a_closed_turf) /obj/structure/sign/poster/proc/on_placed_poster(mob/user) to_chat(user, span_notice("You place the poster!")) +/obj/structure/sign/poster/proc/tear_poster(mob/user) + visible_message(span_notice("[user] rips [src] in a single, decisive motion!") ) + playsound(src.loc, 'sound/items/poster_ripped.ogg', 100, TRUE) + spring_trap(user) + + var/obj/structure/sign/poster/ripped/R = new(loc) + R.pixel_y = pixel_y + R.pixel_x = pixel_x + R.add_fingerprint(user) + qdel(src) + // Various possible posters follow /obj/structure/sign/poster/ripped @@ -287,7 +290,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/ripped, 32) icon_state = "random_anything" never_random = TRUE random_basetype = /obj/structure/sign/poster - blacklisted_types = list(/obj/structure/sign/poster/traitor) + blacklisted_types = list( + /obj/structure/sign/poster/traitor, + /obj/structure/sign/poster/abductor, + ) MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/random, 32) diff --git a/code/game/objects/effects/spawners/random/medical.dm b/code/game/objects/effects/spawners/random/medical.dm index 21fdcc2ad97c..89bc215e20e8 100644 --- a/code/game/objects/effects/spawners/random/medical.dm +++ b/code/game/objects/effects/spawners/random/medical.dm @@ -29,8 +29,8 @@ /obj/item/organ/internal/heart/gland/plasma = 7, /obj/item/organ/internal/heart/gland/chem = 5, /obj/item/organ/internal/heart/gland/mindshock = 5, - /obj/item/organ/internal/heart/gland/transform = 5, - /obj/item/organ/internal/heart/gland/spiderman = 5, + // /obj/item/organ/internal/heart/gland/transform = 5, /* monkestation: removed */ + // /obj/item/organ/internal/heart/gland/spiderman = 5, /* monkestation: removed */ /obj/item/organ/internal/heart/gland/slime = 4, /obj/item/organ/internal/heart/gland/trauma = 4, /obj/item/organ/internal/heart/gland/electric = 3, diff --git a/code/game/objects/effects/spawners/random/techstorage.dm b/code/game/objects/effects/spawners/random/techstorage.dm index e46a85e15475..e79d4ca3bac1 100644 --- a/code/game/objects/effects/spawners/random/techstorage.dm +++ b/code/game/objects/effects/spawners/random/techstorage.dm @@ -102,7 +102,12 @@ /obj/item/circuitboard/computer/med_data, /obj/item/circuitboard/machine/smoke_machine, /obj/item/circuitboard/machine/chem_master, - /obj/item/circuitboard/computer/pandemic, + /obj/item/circuitboard/machine/diseaseanalyser, // MONKESTATION EDIT START: FUCK THE PANDEMIC. -dexee, 4/26/24 + /obj/item/circuitboard/computer/diseasesplicer, + /obj/item/circuitboard/machine/centrifuge, + /obj/item/circuitboard/computer/pathology_data, + /obj/item/circuitboard/machine/incubator, + // /obj/item/circuitboard/computer/pandemic, // MONKESTATION EDIT END: fuck the pandemic. we have better disease machines ) /obj/effect/spawner/random/techstorage/ai_all diff --git a/code/game/objects/effects/wanted_poster.dm b/code/game/objects/effects/wanted_poster.dm index 6859a185d6ea..6cb36838fbca 100644 --- a/code/game/objects/effects/wanted_poster.dm +++ b/code/game/objects/effects/wanted_poster.dm @@ -93,9 +93,10 @@ poster_icon.Blend(letter_icon, ICON_OVERLAY) startX = startX + 4 -/obj/structure/sign/poster/wanted/roll_and_drop(atom/location) +/obj/structure/sign/poster/wanted/roll_and_drop(atom/location, mob/user) pixel_x = 0 pixel_y = 0 var/obj/item/poster/rolled_poster = new poster_item_type(location, original_icon, wanted_name, desc, posterHeaderText, posterHeaderColor) - forceMove(rolled_poster) + if(!user?.put_in_hands(rolled_poster)) + forceMove(rolled_poster) return rolled_poster diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm index 49d821a8cd81..b59e43b7781b 100644 --- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm @@ -1185,7 +1185,7 @@ /obj/item/circuitboard/machine/hydroponics name = "Hydroponics Tray" greyscale_colors = CIRCUIT_COLOR_SERVICE - build_path = /obj/machinery/hydroponics/constructable + build_path = /obj/machinery/growing/tray req_components = list( /datum/stock_part/matter_bin = 2, /datum/stock_part/manipulator = 1, diff --git a/code/game/objects/items/clown_items.dm b/code/game/objects/items/clown_items.dm index b3d22930e3b8..caa08cb332e3 100644 --- a/code/game/objects/items/clown_items.dm +++ b/code/game/objects/items/clown_items.dm @@ -127,7 +127,9 @@ * * target - The atom that is being cleaned * * user - The mob that is using the soap to clean. */ -/obj/item/soap/proc/decreaseUses(datum/source, atom/target, mob/living/user) +/obj/item/soap/proc/decreaseUses(datum/source, atom/target, mob/living/user, clean_succeeded) + if(!clean_succeeded) + return var/skillcheck = 1 if(user?.mind) skillcheck = user.mind.get_skill_modifier(/datum/skill/cleaning, SKILL_SPEED_MODIFIER) diff --git a/code/game/objects/items/dna_probe.dm b/code/game/objects/items/dna_probe.dm index 6c9651a31421..954d673a2cd8 100644 --- a/code/game/objects/items/dna_probe.dm +++ b/code/game/objects/items/dna_probe.dm @@ -57,22 +57,24 @@ playsound(user, 'sound/machines/buzz-sigh.ogg', 50) balloon_alert(user, "need database!") return - if((allowed_scans & DNA_PROBE_SCAN_PLANTS) && istype(target, /obj/machinery/hydroponics)) - var/obj/machinery/hydroponics/hydro_tray = target - if(!hydro_tray.myseed) + if((allowed_scans & DNA_PROBE_SCAN_PLANTS) && target.GetComponent(/datum/component/plant_growing)) + var/obj/item/seeds/seed = locate(/obj/item/seeds) in target.contents + if(seed) return - if(our_vault.plant_dna[hydro_tray.myseed.type]) + var/datum/component/growth_information/info = seed.GetComponent(/datum/component/growth_information) + if(our_vault.plant_dna[seed.type]) to_chat(user, span_notice("Plant data is already present in vault storage.")) return - if(stored_dna_plants[hydro_tray.myseed.type]) + if(stored_dna_plants[seed.type]) to_chat(user, span_notice("Plant data already present in local storage.")) return - if(hydro_tray.plant_status != HYDROTRAY_PLANT_HARVESTABLE) // So it's bit harder. + if(info.plant_state != HYDROTRAY_PLANT_HARVESTABLE) // So it's bit harder. to_chat(user, span_alert("Plant needs to be ready to harvest to perform full data scan.")) //Because space dna is actually magic return . - stored_dna_plants[hydro_tray.myseed.type] = TRUE + stored_dna_plants[seed.type] = TRUE playsound(src, 'sound/misc/compiler-stage2.ogg', 50) balloon_alert(user, "data added") + else if((allowed_scans & DNA_PROBE_SCAN_HUMANS) && ishuman(target)) var/mob/living/carbon/human/human_target = target if(our_vault.human_dna[human_target.dna.unique_identity]) diff --git a/code/game/objects/items/manuals.dm b/code/game/objects/items/manuals.dm index daa09b54d914..6f59c86607b9 100644 --- a/code/game/objects/items/manuals.dm +++ b/code/game/objects/items/manuals.dm @@ -162,7 +162,7 @@ Put ingredients on table, then click and drag the table onto yourself to see what recipes you can prepare.

Microwave:

- Use it to cook or boil food ingredients (meats, doughs, egg, spaghetti, donkpocket, etc...). + Use it to cook or boil food ingredients (meats, doughs, egg, donkpocket, etc...). It can cook multiple items at once.

Processor:

@@ -183,7 +183,7 @@ Popcorn: Microwave corn.
Meat Steak: Microwave meat.
Meat Pie: 1 plain pie + 1u black pepper + 1u salt + 2 meat cutlets
- Boiled Spagetti: Microwave spaghetti.
+ Boiled Spaghetti:Raw Spaghetti + 50u water at 450+K
Donuts: 1u sugar + 1 pastry base
Fries: Process potato. @@ -191,7 +191,7 @@ You can put your meals on your kitchen counter or load them in the snack vending machines. - "} + "}// Monkestation Edit: Removed spaghetti from microwave recipes /obj/item/book/manual/nuclear name = "Fission Mailed: Nuclear Sabotage 101" @@ -293,14 +293,14 @@ icon_state ="bookEngineering" starting_author = "Engineering Encyclopedia" starting_title = "Station Repairs and Construction" - page_link = "Guide_to_construction" + page_link = "en/jobs/engineering/guide-to-construction" /obj/item/book/manual/wiki/engineering_guide name = "Engineering Textbook" icon_state ="bookEngineering2" starting_author = "Engineering Encyclopedia" starting_title = "Engineering Textbook" - page_link = "Guide_to_engineering" + page_link = "en/jobs/engineering/station-engineer" /obj/item/book/manual/wiki/security_space_law name = "Space Law" @@ -308,7 +308,7 @@ icon_state = "bookSpaceLaw" starting_author = "Nanotrasen" starting_title = "Space Law" - page_link = "space-laws" + page_link = "en/space-laws" /obj/item/book/manual/wiki/security_space_law/suicide_act(mob/living/user) user.visible_message(span_suicide("[user] pretends to read \the [src] intently... then promptly dies of laughter!")) diff --git a/code/game/objects/items/melee/baton.dm b/code/game/objects/items/melee/baton.dm index 9d475fc45637..00b937df179d 100644 --- a/code/game/objects/items/melee/baton.dm +++ b/code/game/objects/items/melee/baton.dm @@ -136,7 +136,7 @@ if(!chunky_finger_usable && ishuman(user)) var/mob/living/carbon/human/potential_chunky_finger_human = user - if(potential_chunky_finger_human.check_chunky_fingers() && user.is_holding(src)) + if(potential_chunky_finger_human.check_chunky_fingers() && user.is_holding(src) && !HAS_MIND_TRAIT(user, TRAIT_CHUNKYFINGERS_IGNORE_BATON)) balloon_alert(potential_chunky_finger_human, "fingers are too big!") return BATON_ATTACK_DONE diff --git a/code/game/objects/items/mop.dm b/code/game/objects/items/mop.dm index 871b5434d882..805f2b7de55d 100644 --- a/code/game/objects/items/mop.dm +++ b/code/game/objects/items/mop.dm @@ -102,7 +102,9 @@ * * cleaned_turf the turf that is being cleaned * * cleaner the mob that is doing the cleaning */ -/obj/item/mop/proc/apply_reagents(datum/cleaning_source, turf/cleaned_turf, mob/living/cleaner) +/obj/item/mop/proc/apply_reagents(datum/cleaning_source, turf/cleaned_turf, mob/living/cleaner, clean_succeeded) + if(!clean_succeeded) + return reagents.expose(cleaned_turf, TOUCH, 10) //Needed for proper floor wetting. var/val2remove = 1 if(cleaner?.mind) diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index d4af2586a62a..f0a773f690d8 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -24,7 +24,7 @@ Mineral Sheets */ GLOBAL_LIST_INIT(sandstone_recipes, list ( \ - new/datum/stack_recipe("pile of dirt", /obj/machinery/hydroponics/soil, 3, time = 1 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_TOOLS), \ + new/datum/stack_recipe("pile of dirt", /obj/machinery/growing/soil, 3, time = 1 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, category = CAT_TOOLS), \ new/datum/stack_recipe("sandstone door", /obj/structure/mineral_door/sandstone, 10, time = 5 SECONDS, one_per_turf = TRUE, on_solid_ground = TRUE, applies_mats = TRUE, category = CAT_DOORS), \ new/datum/stack_recipe("Breakdown into sand", /obj/item/stack/ore/glass, 1, one_per_turf = FALSE, on_solid_ground = TRUE, category = CAT_MISC) \ )) diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 11f556479ae2..78e6ca8a7a2f 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -79,7 +79,7 @@ var/absorption_rate /obj/item/stack/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1) - if(new_amount != null) + if(new_amount != null && isnum(new_amount)) amount = new_amount while(amount > max_amount) amount -= max_amount diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm index 99d5fd4e91c9..2d4d46e6a3c6 100644 --- a/code/game/objects/items/storage/bags.dm +++ b/code/game/objects/items/storage/bags.dm @@ -237,6 +237,7 @@ /obj/item/reagent_containers/honeycomb, /obj/item/graft, /obj/item/disk/plantgene, + /obj/item/paper, )) //////// @@ -478,6 +479,8 @@ /obj/item/reagent_containers/cup/bottle, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/syringe, + /obj/item/weapon/virusdish,//Monkestation Addition + /obj/item/food/monkeycube/mouse,//Monkestation Addition )) /* diff --git a/code/game/objects/items/storage/book.dm b/code/game/objects/items/storage/book.dm index a69fd1a2c810..9622a103ab5b 100644 --- a/code/game/objects/items/storage/book.dm +++ b/code/game/objects/items/storage/book.dm @@ -273,6 +273,17 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "burning", var/uses = 1 var/ownername +/obj/item/book/bible/syndicate/Initialize(mapload) + . = ..() + AddComponent(/datum/component/anti_magic, MAGIC_RESISTANCE|MAGIC_RESISTANCE_HOLY) + AddComponent(/datum/component/effect_remover, \ + success_feedback = "You disrupt the magic of %THEEFFECT with %THEWEAPON.", \ + success_forcesay = "BEGONE FOUL MAGIKS!!", \ + tip_text = "Clear rune", \ + effects_we_clear = list(/obj/effect/rune, /obj/effect/heretic_rune, /obj/effect/cosmic_rune), \ + ) + AddElement(/datum/element/bane, target_type = /mob/living/basic/revenant, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE) + /obj/item/storage/book/bible/syndicate/attack_self(mob/living/carbon/human/H) if (uses) H.mind.holy_role = HOLY_ROLE_PRIEST diff --git a/code/game/objects/items/storage/medkit.dm b/code/game/objects/items/storage/medkit.dm index 79bd7fa9a7aa..2ea5365b456b 100644 --- a/code/game/objects/items/storage/medkit.dm +++ b/code/game/objects/items/storage/medkit.dm @@ -254,7 +254,7 @@ icon_state = "medkit_advanced" inhand_icon_state = "medkit-rad" custom_premium_price = PAYCHECK_COMMAND * 6 - damagetype_healed = "all" + damagetype_healed = HEAL_ALL_DAMAGE /obj/item/storage/medkit/advanced/PopulateContents() if(empty) @@ -271,7 +271,7 @@ desc = "I hope you've got insurance." icon_state = "medkit_tactical" inhand_icon_state = "medkit-tactical" - damagetype_healed = "all" + damagetype_healed = HEAL_ALL_DAMAGE /obj/item/storage/medkit/tactical/Initialize(mapload) . = ..() diff --git a/code/game/objects/structures/crates_lockers/closets/secure/misc.dm b/code/game/objects/structures/crates_lockers/closets/secure/misc.dm index 78faa05eaaf8..a0a811fc6c8b 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/misc.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/misc.dm @@ -45,14 +45,14 @@ icon_state = "cmo" /obj/structure/closet/secure_closet/ert_med/PopulateContents() - ..() + . = ..() + new /mob/living/basic/bot/medbot(src) new /obj/item/storage/medkit/o2(src) new /obj/item/storage/medkit/toxin(src) new /obj/item/storage/medkit/fire(src) new /obj/item/storage/medkit/brute(src) new /obj/item/storage/medkit/regular(src) new /obj/item/defibrillator/compact/combat/loaded/nanotrasen(src) - new /mob/living/simple_animal/bot/medbot(src) /obj/structure/closet/secure_closet/ert_engi name = "emergency response team engineer locker" diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index 1d4ec997aae0..42dadf19ff59 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -16,6 +16,13 @@ canSmoothWith = SMOOTH_GROUP_LATTICE + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_OPEN_FLOOR var/number_of_mats = 1 var/build_material = /obj/item/stack/rods + var/list/give_turf_traits = list(TRAIT_CHASM_STOPPED) + +/obj/structure/lattice/Initialize(mapload) + . = ..() + if(length(give_turf_traits)) + give_turf_traits = string_list(give_turf_traits) + AddElement(/datum/element/give_turf_traits, give_turf_traits) /datum/armor/structure_lattice @@ -93,6 +100,7 @@ smoothing_groups = SMOOTH_GROUP_CATWALK + SMOOTH_GROUP_LATTICE + SMOOTH_GROUP_OPEN_FLOOR canSmoothWith = SMOOTH_GROUP_CATWALK obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN | BLOCK_Z_IN_UP + give_turf_traits = list(TRAIT_TURF_IGNORE_SLOWDOWN, TRAIT_LAVA_STOPPED, TRAIT_CHASM_STOPPED) /obj/structure/lattice/catwalk/Initialize(mapload) . = ..() @@ -147,6 +155,7 @@ canSmoothWith = SMOOTH_GROUP_LATTICE obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN | BLOCK_Z_IN_UP resistance_flags = FIRE_PROOF | LAVA_PROOF + give_turf_traits = list(TRAIT_LAVA_STOPPED, TRAIT_CHASM_STOPPED) /obj/structure/lattice/lava/deconstruction_hints(mob/user) return span_notice("The rods look like they could be cut, but the heat treatment will shatter off. There's space for a tile.") diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 576535cf1447..86d9aecf7c84 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -23,6 +23,12 @@ pass_flags_self = PASSTABLE | LETPASSTHROW layer = TABLE_LAYER obj_flags = CAN_BE_HIT | IGNORE_DENSITY + custom_materials = list(/datum/material/iron =SHEET_MATERIAL_AMOUNT) + max_integrity = 100 + integrity_failure = 0.33 + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_TABLES + canSmoothWith = SMOOTH_GROUP_TABLES ///TRUE if the table can be climbed on and have living mobs placed on it normally, FALSE otherwise var/climbable = TRUE var/frame = /obj/structure/table_frame @@ -32,13 +38,7 @@ var/busy = FALSE var/buildstackamount = 1 var/framestackamount = 2 - var/deconstruction_ready = 1 - custom_materials = list(/datum/material/iron =SHEET_MATERIAL_AMOUNT) - max_integrity = 100 - integrity_failure = 0.33 - smoothing_flags = SMOOTH_BITMASK - smoothing_groups = SMOOTH_GROUP_TABLES - canSmoothWith = SMOOTH_GROUP_TABLES + var/deconstruction_ready = TRUE /obj/structure/table/Initialize(mapload, _buildstack) . = ..() @@ -54,6 +54,8 @@ ) AddElement(/datum/element/connect_loc, loc_connections) + var/static/list/give_turf_traits = list(TRAIT_TURF_IGNORE_SLOWDOWN, TRAIT_TURF_IGNORE_SLIPPERY) + AddElement(/datum/element/give_turf_traits, give_turf_traits) register_context() /obj/structure/table/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) @@ -598,7 +600,7 @@ icon = 'icons/obj/smooth_structures/reinforced_table.dmi' icon_state = "reinforced_table-0" base_icon_state = "reinforced_table" - deconstruction_ready = 0 + deconstruction_ready = FALSE buildstack = /obj/item/stack/sheet/plasteel max_integrity = 200 integrity_failure = 0.25 diff --git a/code/game/say.dm b/code/game/say.dm index 83f14d6e7452..5f25b82417ff 100644 --- a/code/game/say.dm +++ b/code/game/say.dm @@ -218,7 +218,7 @@ GLOBAL_LIST_INIT(freqtospan, list( return "2" return "0" -/atom/movable/proc/GetVoice() +/atom/proc/GetVoice() return "[src]" //Returns the atom's name, prepended with 'The' if it's not a proper noun /atom/movable/proc/get_alt_name() diff --git a/code/game/turfs/closed/_closed.dm b/code/game/turfs/closed/_closed.dm index 38e3df19cc43..c6314e14125f 100644 --- a/code/game/turfs/closed/_closed.dm +++ b/code/game/turfs/closed/_closed.dm @@ -38,6 +38,12 @@ /turf/closed/indestructible/singularity_act() return +/turf/closed/indestructible/attackby(obj/item/attacking_item, mob/user, params) + if(istype(attacking_item, /obj/item/poster) && Adjacent(user)) + return place_poster(attacking_item, user) + + return ..() + /turf/closed/indestructible/oldshuttle name = "strange shuttle wall" icon = 'icons/turf/shuttleold.dmi' diff --git a/code/game/turfs/closed/minerals.dm b/code/game/turfs/closed/minerals.dm index f894f2988f34..424696ee4ed6 100644 --- a/code/game/turfs/closed/minerals.dm +++ b/code/game/turfs/closed/minerals.dm @@ -137,7 +137,7 @@ SEND_SIGNAL(user, COMSIG_MOB_MINED, src, give_exp) if (mineralType && (mineralAmt > 0)) new mineralType(src, mineralAmt) - SSblackbox.record_feedback("tally", "ore_mined", mineralAmt, mineralType) + SSblackbox.record_feedback("tally", "ore_mined", mineralAmt, initial(mineralType.name)) if(ishuman(user)) var/mob/living/carbon/human/H = user if(HAS_TRAIT(H, FOOD_JOB_MINER)) @@ -158,7 +158,7 @@ picked_ore = /obj/item/stack/ore/bluespace_crystal if(picked_ore) new picked_ore(src, 1) - SSblackbox.record_feedback("tally", "ore_mined", 1, picked_ore) + SSblackbox.record_feedback("tally", "ore_mined", 1, initial(picked_ore.name)) if(give_exp) if (mineralType && (mineralAmt > 0)) diff --git a/code/game/turfs/closed/walls.dm b/code/game/turfs/closed/walls.dm index 77b27fddc1d7..3820ab7f947e 100644 --- a/code/game/turfs/closed/walls.dm +++ b/code/game/turfs/closed/walls.dm @@ -130,7 +130,7 @@ for(var/obj/O in src.contents) //Eject contents! if(istype(O, /obj/structure/sign/poster)) var/obj/structure/sign/poster/P = O - P.roll_and_drop(src) + INVOKE_ASYNC(P, TYPE_PROC_REF(/obj/structure/sign/poster, roll_and_drop), src) if(decon_type) ChangeTurf(decon_type, flags = CHANGETURF_INHERIT_AIR) else diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm index 18d859e78b5b..821b65895290 100644 --- a/code/game/turfs/open/lava.dm +++ b/code/game/turfs/open/lava.dm @@ -222,13 +222,9 @@ return TRUE /turf/open/lava/proc/is_safe() - //if anything matching this typecache is found in the lava, we don't burn things - var/static/list/lava_safeties_typecache = typecacheof(list(/obj/structure/lattice/catwalk, /obj/structure/stone_tile, /obj/structure/lattice/lava)) - var/list/found_safeties = typecache_filter_list(contents, lava_safeties_typecache) - for(var/obj/structure/stone_tile/S in found_safeties) - if(S.fallen) - LAZYREMOVE(found_safeties, S) - return LAZYLEN(found_safeties) + if(HAS_TRAIT(src, TRAIT_LAVA_STOPPED)) + return TRUE + return FALSE ///Generic return value of the can_burn_stuff() proc. Does nothing. #define LAVA_BE_IGNORING 0 diff --git a/code/game/turfs/open/openspace.dm b/code/game/turfs/open/openspace.dm index 2cfdc345ac8c..43add27407be 100644 --- a/code/game/turfs/open/openspace.dm +++ b/code/game/turfs/open/openspace.dm @@ -23,7 +23,7 @@ // I am so sorry /turf/open/openspace/Initialize(mapload) // handle plane and layer here so that they don't cover other obs/turfs in Dream Maker . = ..() - RegisterSignal(src, COMSIG_ATOM_INITIALIZED_ON, PROC_REF(on_atom_created)) + RegisterSignal(src, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, PROC_REF(on_atom_created)) var/area/our_area = loc if(istype(our_area, /area/space)) force_no_gravity = TRUE @@ -34,7 +34,7 @@ AddElement(/datum/element/turf_z_transparency) /turf/open/openspace/ChangeTurf(path, list/new_baseturfs, flags) - UnregisterSignal(src, COMSIG_ATOM_INITIALIZED_ON) + UnregisterSignal(src, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON) return ..() /** @@ -54,15 +54,13 @@ if(movable.set_currently_z_moving(CURRENTLY_Z_FALLING)) zFall(movable, falling_from_move = TRUE) /** - * Drops movables spawned on this turf only after they are successfully initialized. - * so flying mobs, qdeleted movables and things that were moved somewhere else during - * Initialize() won't fall by accident. + * Drops movables spawned on this turf after they are successfully initialized. + * so that spawned movables that should fall to gravity, will fall. */ /turf/open/openspace/proc/on_atom_created(datum/source, atom/created_atom) SIGNAL_HANDLER if(ismovable(created_atom)) - //Drop it only when it's finished initializing, not before. - addtimer(CALLBACK(src, PROC_REF(zfall_if_on_turf), created_atom), 0 SECONDS) + zfall_if_on_turf(created_atom) /turf/open/openspace/proc/zfall_if_on_turf(atom/movable/movable) if(QDELETED(movable) || movable.loc != src) diff --git a/code/game/world.dm b/code/game/world.dm index 386a7ca7c0b3..43f9aad06e9c 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -437,7 +437,7 @@ GLOBAL_VAR(restart_counter) /world/proc/incrementMaxZ() maxz++ SSmobs.MaxZChanged() - SSidlenpcpool.MaxZChanged() + SSai_controllers.on_max_z_changed() /world/proc/change_fps(new_value = 20) if(new_value <= 0) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 87140c0cb7bd..689b585882d6 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -191,6 +191,7 @@ GLOBAL_PROTECT(admin_verbs_debug) /proc/machine_upgrade, /datum/admins/proc/create_or_modify_area, /client/proc/adventure_manager, + /client/proc/hard_deletion_toggle, /client/proc/atmos_control, /client/proc/callproc, /client/proc/callproc_datum, diff --git a/code/modules/admin/hardelete_toggle.dm b/code/modules/admin/hardelete_toggle.dm new file mode 100644 index 000000000000..6c2fa4b6405e --- /dev/null +++ b/code/modules/admin/hardelete_toggle.dm @@ -0,0 +1,17 @@ +/client/proc/hard_deletion_toggle() + set category = "Debug" + set name = "Enable/Disable Hard Deletes" + + var/static/list/warned_users + + LAZYINITLIST(warned_users) + var/current_val = SSgarbage.enable_hard_deletes + if (current_val && !warned_users[usr.ckey]) + to_chat(usr, "WARNING: Disabling garbage hard deletion will likely result in permanent memory leaks until the next round. Run this verb again to disable it.") + warned_users[usr.ckey] = TRUE + return + + SSgarbage.enable_hard_deletes = !current_val + + log_admin("[key_name(usr)] toggled garbage hard deletion [SSgarbage.enable_hard_deletes ? "ON" : "OFF"].") + SSblackbox.record_feedback("tally", "admin_verb", 1, "hard_deletion_toggle") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm index fdb65fa1490c..bdb2d5f4a2a9 100644 --- a/code/modules/admin/sql_ban_system.dm +++ b/code/modules/admin/sql_ban_system.dm @@ -1056,7 +1056,9 @@ if(GLOB.admin_datums[player_client.ckey] || GLOB.deadmins[player_client.ckey]) is_admin = TRUE if(kick_banned_players && (!is_admin || (is_admin && applies_to_admins))) - qdel(player_client) + SSgarbage.HardDelete(player_client, override = TRUE) + if(player_client) + qdel(player_client) for(var/client/other_player_client in GLOB.clients - player_client) if(other_player_client.address == banned_player_ip || other_player_client.computer_id == banned_player_cid) @@ -1065,7 +1067,9 @@ if(GLOB.admin_datums[other_player_client.ckey] || GLOB.deadmins[other_player_client.ckey]) is_admin = TRUE if(kick_banned_players && (!is_admin || (is_admin && applies_to_admins))) - qdel(other_player_client) + SSgarbage.HardDelete(other_player_client, override = TRUE) + if(other_player_client) + qdel(other_player_client) #undef MAX_ADMINBANS_PER_ADMIN #undef MAX_ADMINBANS_PER_HEADMIN diff --git a/code/modules/antagonists/abductor/abductor_structures.dm b/code/modules/antagonists/abductor/abductor_structures.dm new file mode 100644 index 000000000000..2de390ad4dee --- /dev/null +++ b/code/modules/antagonists/abductor/abductor_structures.dm @@ -0,0 +1,124 @@ + +// Operating Table / Beds / Lockers + +/obj/structure/bed/abductor + name = "resting contraption" + desc = "This looks similar to contraptions from Earth. Could aliens be stealing our technology?" + icon = 'icons/obj/abductor.dmi' + buildstacktype = /obj/item/stack/sheet/mineral/abductor + icon_state = "bed" + +/obj/structure/table_frame/abductor + name = "alien table frame" + desc = "A sturdy table frame made from alien alloy." + icon_state = "alien_frame" + framestack = /obj/item/stack/sheet/mineral/abductor + framestackamount = 1 + +/obj/structure/table_frame/abductor/attackby(obj/item/attacking_item, mob/user, params) + if(attacking_item.tool_behaviour == TOOL_WRENCH) + to_chat(user, span_notice("You start disassembling [src]...")) + attacking_item.play_tool_sound(src) + if(attacking_item.use_tool(src, user, 30)) + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) + for(var/i in 0 to framestackamount) + new framestack(get_turf(src)) + qdel(src) + return + if(istype(attacking_item, /obj/item/stack/sheet/mineral/abductor)) + var/obj/item/stack/sheet/stacked_sheets = attacking_item + if(stacked_sheets.get_amount() < 1) + to_chat(user, span_warning("You need one alien alloy sheet to do this!")) + return + to_chat(user, span_notice("You start adding [stacked_sheets] to [src]...")) + if(do_after(user, 50, target = src)) + stacked_sheets.use(1) + new /obj/structure/table/abductor(src.loc) + qdel(src) + return + if(istype(attacking_item, /obj/item/stack/sheet/mineral/silver)) + var/obj/item/stack/sheet/stacked_sheets = attacking_item + if(stacked_sheets.get_amount() < 1) + to_chat(user, span_warning("You need one sheet of silver to do this!")) + return + to_chat(user, span_notice("You start adding [stacked_sheets] to [src]...")) + if(do_after(user, 50, target = src)) + stacked_sheets.use(1) + new /obj/structure/table/optable/abductor(src.loc) + qdel(src) + +/obj/structure/table/abductor + name = "alien table" + desc = "Advanced flat surface technology at work!" + icon = 'icons/obj/smooth_structures/alien_table.dmi' + icon_state = "alien_table-0" + base_icon_state = "alien_table" + buildstack = /obj/item/stack/sheet/mineral/abductor + framestack = /obj/item/stack/sheet/mineral/abductor + buildstackamount = 1 + framestackamount = 1 + smoothing_groups = SMOOTH_GROUP_ABDUCTOR_TABLES + canSmoothWith = SMOOTH_GROUP_ABDUCTOR_TABLES + frame = /obj/structure/table_frame/abductor + custom_materials = list(/datum/material/silver =SHEET_MATERIAL_AMOUNT) + +/obj/structure/table/optable/abductor + name = "alien operating table" + desc = "Used for alien medical procedures. The surface is covered in tiny spines." + frame = /obj/structure/table_frame/abductor + buildstack = /obj/item/stack/sheet/mineral/silver + framestack = /obj/item/stack/sheet/mineral/abductor + buildstackamount = 1 + framestackamount = 1 + icon = 'icons/obj/abductor.dmi' + icon_state = "bed" + can_buckle = TRUE + buckle_lying = 90 + /// Amount to inject per second + var/inject_amount = 0.5 + + var/static/list/injected_reagents = list(/datum/reagent/medicine/cordiolis_hepatico) + +/obj/structure/table/optable/abductor/Initialize(mapload) + . = ..() + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + +/obj/structure/table/optable/abductor/proc/on_entered(datum/source, atom/movable/AM) + SIGNAL_HANDLER + if(iscarbon(AM)) + START_PROCESSING(SSobj, src) + to_chat(AM, span_danger("You feel a series of tiny pricks!")) + +/obj/structure/table/optable/abductor/process(seconds_per_tick) + . = PROCESS_KILL + for(var/mob/living/carbon/victim in get_turf(src)) + . = TRUE + for(var/chemical in injected_reagents) + if(victim.reagents.get_reagent_amount(chemical) < inject_amount * seconds_per_tick) + victim.reagents.add_reagent(chemical, inject_amount * seconds_per_tick) + return . + +/obj/structure/table/optable/abductor/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/structure/closet/abductor + name = "alien locker" + desc = "Contains secrets of the universe." + icon_state = "abductor" + icon_door = "abductor" + can_weld_shut = FALSE + door_anim_time = 0 + material_drop = /obj/item/stack/sheet/mineral/abductor + +/obj/structure/door_assembly/door_assembly_abductor + name = "alien airlock assembly" + icon = 'icons/obj/doors/airlocks/abductor/abductor_airlock.dmi' + base_name = "alien airlock" + overlays_file = 'icons/obj/doors/airlocks/abductor/overlays.dmi' + airlock_type = /obj/machinery/door/airlock/abductor + material_type = /obj/item/stack/sheet/mineral/abductor + noglass = TRUE diff --git a/code/modules/antagonists/abductor/equipment/abduction_outfits.dm b/code/modules/antagonists/abductor/equipment/abduction_outfits.dm index b3e4f89a7e01..0ac78651945e 100644 --- a/code/modules/antagonists/abductor/equipment/abduction_outfits.dm +++ b/code/modules/antagonists/abductor/equipment/abduction_outfits.dm @@ -24,11 +24,21 @@ for(var/obj/item/abductor/gizmo/G in B.contents) console.AddGizmo(G) -/datum/outfit/abductor/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - if(!visualsOnly) - link_to_console(H) +/datum/outfit/abductor/post_equip(mob/living/carbon/human/user, visualsOnly = FALSE) + . = ..() + if(visualsOnly) + return + if(!isnull(user.mind)) + link_to_console(user) + + /* monkestation removal: get rid of the abductor batong recall + var/obj/item/melee/baton/abductor/batong = locate() in user + if(!isnull(batong)) + var/datum/action/cooldown/spell/summonitem/abductor/ayy_summon = new(user.mind || user) + ayy_summon.mark_item(batong) + ayy_summon.Grant(user) + */ /datum/outfit/abductor/agent name = "Abductor Agent" @@ -40,20 +50,19 @@ backpack_contents = list( /obj/item/gun/energy/alien = 1, /obj/item/abductor/silencer = 1 - ) + ) /datum/outfit/abductor/scientist name = "Abductor Scientist" - backpack_contents = list( - /obj/item/abductor/gizmo = 1 - ) + belt = /obj/item/defibrillator/compact/combat/loaded // monke edit: give abductors defibs + backpack_contents = list(/obj/item/abductor/gizmo = 1) -/datum/outfit/abductor/scientist/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - ..() - if(!visualsOnly) - var/obj/item/implant/abductor/beamplant = new /obj/item/implant/abductor(H) - beamplant.implant(H) +/datum/outfit/abductor/scientist/post_equip(mob/living/carbon/human/user, visualsOnly = FALSE) + . = ..() + if(!visualsOnly && !isnull(user.mind)) + var/obj/item/implant/abductor/beamplant = new /obj/item/implant/abductor(user) + beamplant.implant(user) /datum/outfit/abductor/scientist/onemanteam name = "Abductor Scientist (w/ agent gear)" @@ -63,7 +72,8 @@ belt = /obj/item/storage/belt/military/abductor/full backpack_contents = list( - /obj/item/abductor/gizmo = 1, - /obj/item/gun/energy/alien = 1, - /obj/item/abductor/silencer = 1 + /obj/item/abductor/gizmo = 1, + /obj/item/gun/energy/alien = 1, + /obj/item/abductor/silencer = 1, + /obj/item/defibrillator/compact/combat/loaded = 1 // monke edit: give abductors defibs ) diff --git a/code/modules/antagonists/abductor/equipment/abduction_surgery.dm b/code/modules/antagonists/abductor/equipment/abduction_surgery.dm index 7707b9783269..83992981b295 100644 --- a/code/modules/antagonists/abductor/equipment/abduction_surgery.dm +++ b/code/modules/antagonists/abductor/equipment/abduction_surgery.dm @@ -4,8 +4,8 @@ surgery_flags = SURGERY_SELF_OPERABLE | SURGERY_IGNORE_CLOTHES | SURGERY_REQUIRE_RESTING | SURGERY_REQUIRE_LIMB steps = list( /datum/surgery_step/incise, - /datum/surgery_step/clamp_bleeders, /datum/surgery_step/retract_skin, + /datum/surgery_step/clamp_bleeders, /datum/surgery_step/incise, /datum/surgery_step/extract_organ, /datum/surgery_step/gland_insert, diff --git a/code/modules/antagonists/abductor/equipment/gear/abductor_clothing.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_clothing.dm new file mode 100644 index 000000000000..78a3548cc774 --- /dev/null +++ b/code/modules/antagonists/abductor/equipment/gear/abductor_clothing.dm @@ -0,0 +1,159 @@ +/obj/item/clothing/under/abductor + desc = "The most advanced form of jumpsuit known to reality, looks uncomfortable." + name = "alien jumpsuit" + icon = 'icons/obj/clothing/under/syndicate.dmi' + icon_state = "abductor" + inhand_icon_state = "bl_suit" + worn_icon = 'icons/mob/clothing/under/syndicate.dmi' + armor_type = /datum/armor/under_abductor + can_adjust = FALSE + +/datum/armor/under_abductor + bomb = 10 + bio = 10 + wound = 5 + +//AGENT VEST +/obj/item/clothing/suit/armor/abductor/vest + name = "agent vest" + desc = "A vest outfitted with advanced stealth technology. It has two modes - combat and stealth." + icon = 'icons/obj/abductor.dmi' + icon_state = "vest_stealth" + inhand_icon_state = "armor" + blood_overlay_type = "armor" + armor_type = /datum/armor/abductor_vest + actions_types = list(/datum/action/item_action/hands_free/activate) + allowed = list( + /obj/item/abductor, + /obj/item/melee/baton, + /obj/item/gun/energy, + /obj/item/restraints/handcuffs, + ) + /// What operation mode is our vest in? + var/mode = VEST_STEALTH + /// Do we have a disguise active? + var/stealth_active = FALSE + /// Cooldown in seconds for the combat mode activation stimulant. + var/combat_cooldown = 20 + /// The visual of our suit's disguise. + var/datum/icon_snapshot/disguise + +/datum/armor/abductor_combat + melee = 50 + bullet = 50 + laser = 50 + energy = 50 + bomb = 50 + bio = 50 + fire = 90 + acid = 90 + +/datum/armor/abductor_vest + melee = 15 + bullet = 15 + laser = 15 + energy = 25 + bomb = 15 + bio = 15 + fire = 70 + acid = 70 + +/obj/item/clothing/suit/armor/abductor/vest/proc/toggle_nodrop() + if(HAS_TRAIT_FROM(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT)) + REMOVE_TRAIT(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) + else + ADD_TRAIT(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) + if(ismob(loc)) + to_chat(loc, span_notice("Your vest is now [HAS_TRAIT_FROM(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) ? "locked" : "unlocked"].")) + +/obj/item/clothing/suit/armor/abductor/vest/proc/flip_mode() + switch(mode) + if(VEST_STEALTH) + mode = VEST_COMBAT + DeactivateStealth() + set_armor(/datum/armor/abductor_combat) + icon_state = "vest_combat" + if(VEST_COMBAT)// TO STEALTH + mode = VEST_STEALTH + set_armor(/datum/armor/abductor_vest) + icon_state = "vest_stealth" + if(ishuman(loc)) + var/mob/living/carbon/human/human_target = loc + human_target.update_worn_oversuit() + update_item_action_buttons() + +/obj/item/clothing/suit/armor/abductor/vest/item_action_slot_check(slot, mob/user) + if(slot & ITEM_SLOT_OCLOTHING) //we only give the mob the ability to activate the vest if he's actually wearing it. + return TRUE + +/obj/item/clothing/suit/armor/abductor/vest/proc/SetDisguise(datum/icon_snapshot/entry) + disguise = entry + +/obj/item/clothing/suit/armor/abductor/vest/proc/ActivateStealth() + if(disguise == null) + return + stealth_active = TRUE + if(ishuman(loc)) + var/mob/living/carbon/human/wearer = loc + new /obj/effect/temp_visual/dir_setting/ninja/cloak(get_turf(wearer), wearer.dir) + wearer.name_override = disguise.name + wearer.icon = disguise.icon + wearer.icon_state = disguise.icon_state + wearer.cut_overlays() + wearer.add_overlay(disguise.overlays) + wearer.update_held_items() + +/obj/item/clothing/suit/armor/abductor/vest/proc/DeactivateStealth() + if(!stealth_active) + return + stealth_active = FALSE + if(ishuman(loc)) + var/mob/living/carbon/human/wearer = loc + new /obj/effect/temp_visual/dir_setting/ninja(get_turf(wearer), wearer.dir) + wearer.name_override = null + wearer.cut_overlays() + wearer.regenerate_icons() + +/obj/item/clothing/suit/armor/abductor/vest/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE) + DeactivateStealth() + +/obj/item/clothing/suit/armor/abductor/vest/IsReflect() + DeactivateStealth() + +/obj/item/clothing/suit/armor/abductor/vest/ui_action_click() + switch(mode) + if(VEST_COMBAT) + Adrenaline() + if(VEST_STEALTH) + if(stealth_active) + DeactivateStealth() + else + ActivateStealth() + +/obj/item/clothing/suit/armor/abductor/vest/proc/Adrenaline() + if(ishuman(loc)) + if(combat_cooldown < initial(combat_cooldown)) + to_chat(loc, span_warning("Combat injection is still recharging.")) + return + var/mob/living/carbon/human/wearer = loc + wearer.stamina.adjust(75, forced = TRUE) + wearer.SetUnconscious(0) + wearer.SetStun(0) + wearer.SetKnockdown(0) + wearer.SetImmobilized(0) + wearer.SetParalyzed(0) + combat_cooldown = 0 + START_PROCESSING(SSobj, src) + +/obj/item/clothing/suit/armor/abductor/vest/process(seconds_per_tick) + combat_cooldown += seconds_per_tick + if(combat_cooldown >= initial(combat_cooldown)) + STOP_PROCESSING(SSobj, src) + +/obj/item/clothing/suit/armor/abductor/Destroy() + STOP_PROCESSING(SSobj, src) + for(var/obj/machinery/abductor/console/mothership_console in GLOB.machines) + if(mothership_console.vest == src) + mothership_console.vest = null + break + return ..() diff --git a/code/modules/antagonists/abductor/equipment/abduction_gear.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm similarity index 60% rename from code/modules/antagonists/abductor/equipment/abduction_gear.dm rename to code/modules/antagonists/abductor/equipment/gear/abductor_items.dm index 53dc49273cb1..bd5e49e807c8 100644 --- a/code/modules/antagonists/abductor/equipment/abduction_gear.dm +++ b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm @@ -1,153 +1,3 @@ -#define VEST_STEALTH 1 -#define VEST_COMBAT 2 -#define GIZMO_SCAN 1 -#define GIZMO_MARK 2 -#define MIND_DEVICE_MESSAGE 1 -#define MIND_DEVICE_CONTROL 2 - -//AGENT VEST -/obj/item/clothing/suit/armor/abductor/vest - name = "agent vest" - desc = "A vest outfitted with advanced stealth technology. It has two modes - combat and stealth." - icon = 'icons/obj/abductor.dmi' - icon_state = "vest_stealth" - inhand_icon_state = "armor" - blood_overlay_type = "armor" - armor_type = /datum/armor/abductor_vest - actions_types = list(/datum/action/item_action/hands_free/activate) - allowed = list( - /obj/item/abductor, - /obj/item/melee/baton, - /obj/item/gun/energy, - /obj/item/restraints/handcuffs - ) - var/mode = VEST_STEALTH - var/stealth_active = FALSE - /// Cooldown in seconds - var/combat_cooldown = 20 - var/datum/icon_snapshot/disguise - -/datum/armor/abductor_combat - melee = 50 - bullet = 50 - laser = 50 - energy = 50 - bomb = 50 - bio = 50 - fire = 90 - acid = 90 - -/datum/armor/abductor_vest - melee = 15 - bullet = 15 - laser = 15 - energy = 25 - bomb = 15 - bio = 15 - fire = 70 - acid = 70 - -/obj/item/clothing/suit/armor/abductor/vest/proc/toggle_nodrop() - if(HAS_TRAIT_FROM(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT)) - REMOVE_TRAIT(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) - else - ADD_TRAIT(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) - if(ismob(loc)) - to_chat(loc, span_notice("Your vest is now [HAS_TRAIT_FROM(src, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) ? "locked" : "unlocked"].")) - -/obj/item/clothing/suit/armor/abductor/vest/proc/flip_mode() - switch(mode) - if(VEST_STEALTH) - mode = VEST_COMBAT - DeactivateStealth() - set_armor(/datum/armor/abductor_combat) - icon_state = "vest_combat" - if(VEST_COMBAT)// TO STEALTH - mode = VEST_STEALTH - set_armor(/datum/armor/abductor_vest) - icon_state = "vest_stealth" - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - H.update_worn_oversuit() - update_item_action_buttons() - -/obj/item/clothing/suit/armor/abductor/vest/item_action_slot_check(slot, mob/user) - if(slot & ITEM_SLOT_OCLOTHING) //we only give the mob the ability to activate the vest if he's actually wearing it. - return TRUE - -/obj/item/clothing/suit/armor/abductor/vest/proc/SetDisguise(datum/icon_snapshot/entry) - disguise = entry - -/obj/item/clothing/suit/armor/abductor/vest/proc/ActivateStealth() - if(disguise == null) - return - stealth_active = TRUE - if(ishuman(loc)) - var/mob/living/carbon/human/M = loc - new /obj/effect/temp_visual/dir_setting/ninja/cloak(get_turf(M), M.dir) - M.name_override = disguise.name - M.icon = disguise.icon - M.icon_state = disguise.icon_state - M.cut_overlays() - M.add_overlay(disguise.overlays) - M.update_held_items() - -/obj/item/clothing/suit/armor/abductor/vest/proc/DeactivateStealth() - if(!stealth_active) - return - stealth_active = FALSE - if(ishuman(loc)) - var/mob/living/carbon/human/M = loc - new /obj/effect/temp_visual/dir_setting/ninja(get_turf(M), M.dir) - M.name_override = null - M.cut_overlays() - M.regenerate_icons() - -/obj/item/clothing/suit/armor/abductor/vest/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - DeactivateStealth() - -/obj/item/clothing/suit/armor/abductor/vest/IsReflect() - DeactivateStealth() - -/obj/item/clothing/suit/armor/abductor/vest/ui_action_click() - switch(mode) - if(VEST_COMBAT) - Adrenaline() - if(VEST_STEALTH) - if(stealth_active) - DeactivateStealth() - else - ActivateStealth() - -/obj/item/clothing/suit/armor/abductor/vest/proc/Adrenaline() - if(ishuman(loc)) - if(combat_cooldown < initial(combat_cooldown)) - to_chat(loc, span_warning("Combat injection is still recharging.")) - return - var/mob/living/carbon/human/M = loc - M.stamina.adjust(75) - M.SetUnconscious(0) - M.SetStun(0) - M.SetKnockdown(0) - M.SetImmobilized(0) - M.SetParalyzed(0) - combat_cooldown = 0 - START_PROCESSING(SSobj, src) - -/obj/item/clothing/suit/armor/abductor/vest/process(seconds_per_tick) - combat_cooldown += seconds_per_tick - if(combat_cooldown >= initial(combat_cooldown)) - STOP_PROCESSING(SSobj, src) - -/obj/item/clothing/suit/armor/abductor/Destroy() - STOP_PROCESSING(SSobj, src) - for(var/obj/machinery/abductor/console/C in GLOB.machines) - if(C.vest == src) - C.vest = null - break - . = ..() - - /obj/item/abductor icon = 'icons/obj/abductor.dmi' lefthand_file = 'icons/mob/inhands/antag/abductor_lefthand.dmi' @@ -198,7 +48,7 @@ icon_state = "gizmo_scan" to_chat(user, span_notice("You switch the device to [mode == GIZMO_SCAN? "SCAN": "MARK"] MODE")) -/obj/item/abductor/gizmo/attack(mob/living/M, mob/user) +/obj/item/abductor/gizmo/attack(mob/living/target, mob/user) if(!ScientistCheck(user)) return if(!console) @@ -207,9 +57,9 @@ switch(mode) if(GIZMO_SCAN) - scan(M, user) + scan(target, user) if(GIZMO_MARK) - mark(M, user) + mark(target, user) /obj/item/abductor/gizmo/afterattack(atom/target, mob/living/user, flag, params) @@ -268,10 +118,10 @@ icon_state = "silencer" inhand_icon_state = "gizmo" -/obj/item/abductor/silencer/attack(mob/living/M, mob/user) +/obj/item/abductor/silencer/attack(mob/living/target, mob/user) if(!AbductorCheck(user)) return - radio_off(M, user) + radio_off(target, user) /obj/item/abductor/silencer/afterattack(atom/target, mob/living/user, flag, params) . = ..() @@ -289,15 +139,15 @@ var/turf/targloc = get_turf(target) - var/mob/living/carbon/human/M - for(M in view(2,targloc)) - if(M == user) + var/mob/living/carbon/human/human_target + for(human_target in view(2,targloc)) + if(human_target == user) continue - to_chat(user, span_notice("You silence [M]'s radio devices.")) - radio_off_mob(M) + to_chat(user, span_notice("You silence [human_target]'s radio devices.")) + radio_off_mob(human_target) -/obj/item/abductor/silencer/proc/radio_off_mob(mob/living/carbon/human/M) - var/list/all_items = M.get_all_contents() +/obj/item/abductor/silencer/proc/radio_off_mob(mob/living/carbon/human/target) + var/list/all_items = target.get_all_contents() for(var/obj/item/radio/radio in all_items) radio.set_listening(FALSE) @@ -338,20 +188,20 @@ /obj/item/abductor/mind_device/proc/mind_control(atom/target, mob/living/user) if(iscarbon(target)) - var/mob/living/carbon/C = target - var/obj/item/organ/internal/heart/gland/G = C.get_organ_slot("heart") - if(!istype(G)) + var/mob/living/carbon/carbon_target = target + var/obj/item/organ/internal/heart/gland/target_gland = carbon_target.get_organ_slot("heart") + if(!istype(target_gland)) to_chat(user, span_warning("Your target does not have an experimental gland!")) return - if(!G.mind_control_uses) + if(!target_gland.mind_control_uses) to_chat(user, span_warning("Your target's gland is spent!")) return - if(G.active_mind_control) + if(target_gland.active_mind_control) to_chat(user, span_warning("Your target is already under a mind-controlling influence!")) return var/command = tgui_input_text(user, "Enter the command for your target to follow.\ - Uses Left: [G.mind_control_uses], Duration: [DisplayTimeText(G.mind_control_duration)]", "Enter command") + Uses Left: [target_gland.mind_control_uses], Duration: [DisplayTimeText(target_gland.mind_control_duration)]", "Enter command") if(!command) return @@ -359,33 +209,33 @@ if(QDELETED(user) || user.get_active_held_item() != src || loc != user) return - if(QDELETED(G)) + if(QDELETED(target_gland)) return - if(C.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0)) + if(carbon_target.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0)) user.balloon_alert(user, "foiled!") to_chat(user, span_warning("Your target seems to have some sort of mental blockage, preventing the message from being sent! It seems you've been foiled.")) return - G.mind_control(command, user) + target_gland.mind_control(command, user) to_chat(user, span_notice("You send the command to your target.")) /obj/item/abductor/mind_device/proc/mind_message(atom/target, mob/living/user) if(isliving(target)) - var/mob/living/L = target - if(L.stat == DEAD) + var/mob/living/living_target = target + if(living_target.stat == DEAD) to_chat(user, span_warning("Your target is dead!")) return var/message = tgui_input_text(user, "Message to send to your target's brain", "Enter message") if(!message) return - if(QDELETED(L) || L.stat == DEAD) + if(QDELETED(living_target) || living_target.stat == DEAD) return - L.balloon_alert(L, "you hear a voice") - to_chat(L, span_hear("You hear a voice in your head saying: [message]")) + living_target.balloon_alert(living_target, "you hear a voice") + to_chat(living_target, span_hear("You hear a voice in your head saying: [message]")) to_chat(user, span_notice("You send the message to your target.")) - log_directed_talk(user, L, message, LOG_SAY, "abductor whisper") + log_directed_talk(user, living_target, message, LOG_SAY, "abductor whisper") /obj/item/firing_pin/abductor @@ -445,12 +295,6 @@ Congratulations! You are now trained for invasive xenobiology research!"} /obj/item/paper/guides/antag/abductor/AltClick() return //otherwise it would fold into a paperplane. -#define BATON_STUN 0 -#define BATON_SLEEP 1 -#define BATON_CUFF 2 -#define BATON_PROBE 3 -#define BATON_MODES 4 - /obj/item/melee/baton/abductor name = "advanced baton" desc = "A quad-mode baton used for incapacitation and restraining of specimens." @@ -471,7 +315,6 @@ Congratulations! You are now trained for invasive xenobiology research!"} knockdown_time = 14 SECONDS on_stun_sound = 'sound/weapons/egloves.ogg' affect_cyborg = TRUE - chunky_finger_usable = TRUE var/mode = BATON_STUN @@ -480,7 +323,7 @@ Congratulations! You are now trained for invasive xenobiology research!"} /obj/item/melee/baton/abductor/Initialize(mapload) . = ..() - AddElement(/datum/element/update_icon_updates_onmob, ITEM_SLOT_HANDS) + AddElement(/datum/element/update_icon_updates_onmob) /obj/item/melee/baton/abductor/proc/toggle(mob/living/user=usr) if(!AbductorCheck(user)) @@ -556,67 +399,67 @@ Congratulations! You are now trained for invasive xenobiology research!"} . = ..() toggle(user) -/obj/item/melee/baton/abductor/proc/SleepAttack(mob/living/L,mob/living/user) +/obj/item/melee/baton/abductor/proc/SleepAttack(mob/living/target, mob/living/user) playsound(src, on_stun_sound, 50, TRUE, -1) - if(L.incapacitated(IGNORE_RESTRAINTS|IGNORE_GRAB)) - if(L.can_block_magic(MAGIC_RESISTANCE_MIND)) + if(target.incapacitated(IGNORE_RESTRAINTS|IGNORE_GRAB)) + if(target.can_block_magic(MAGIC_RESISTANCE_MIND)) to_chat(user, span_warning("The specimen has some kind of mental protection that is interfering with the sleep inducement! It seems you've been foiled.")) - L.visible_message(span_danger("[user] tried to induced sleep in [L] with [src], but is unsuccessful!"), \ + target.visible_message(span_danger("[user] tried to induced sleep in [target] with [src], but is unsuccessful!"), \ span_userdanger("You feel a strange wave of heavy drowsiness wash over you!")) - L.adjust_drowsiness(4 SECONDS) + target.adjust_drowsiness(4 SECONDS) return - L.visible_message(span_danger("[user] induces sleep in [L] with [src]!"), \ + target.visible_message(span_danger("[user] induces sleep in [target] with [src]!"), \ span_userdanger("You suddenly feel very drowsy!")) - L.Sleeping(sleep_time) - log_combat(user, L, "put to sleep") + target.Sleeping(sleep_time) + log_combat(user, target, "put to sleep") else - if(L.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0)) + if(target.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0)) to_chat(user, span_warning("The specimen has some kind of mental protection that is completely blocking our sleep inducement methods! It seems you've been foiled.")) - L.visible_message(span_danger("[user] tried to induce sleep in [L] with [src], but is unsuccessful!"), \ + target.visible_message(span_danger("[user] tried to induce sleep in [target] with [src], but is unsuccessful!"), \ span_userdanger("Any sense of drowsiness is quickly diminished!")) return - L.adjust_drowsiness(2 SECONDS) + target.adjust_drowsiness(2 SECONDS) to_chat(user, span_warning("Sleep inducement works fully only on stunned specimens! ")) - L.visible_message(span_danger("[user] tried to induce sleep in [L] with [src]!"), \ + target.visible_message(span_danger("[user] tried to induce sleep in [target] with [src]!"), \ span_userdanger("You suddenly feel drowsy!")) -/obj/item/melee/baton/abductor/proc/CuffAttack(mob/living/L,mob/living/user) - if(!iscarbon(L)) +/obj/item/melee/baton/abductor/proc/CuffAttack(mob/living/victim, mob/living/user) + if(!iscarbon(victim)) return - var/mob/living/carbon/C = L - if(!C.handcuffed) - if(C.canBeHandcuffed()) + var/mob/living/carbon/carbon_victim = victim + if(!carbon_victim.handcuffed) + if(carbon_victim.canBeHandcuffed()) playsound(src, 'sound/weapons/cablecuff.ogg', 30, TRUE, -2) - C.visible_message(span_danger("[user] begins restraining [C] with [src]!"), \ + carbon_victim.visible_message(span_danger("[user] begins restraining [carbon_victim] with [src]!"), \ span_userdanger("[user] begins shaping an energy field around your hands!")) - if(do_after(user, time_to_cuff, C) && C.canBeHandcuffed()) - if(!C.handcuffed) - C.set_handcuffed(new /obj/item/restraints/handcuffs/energy/used(C)) - C.update_handcuffed() - to_chat(user, span_notice("You restrain [C].")) - log_combat(user, C, "handcuffed") + if(do_after(user, time_to_cuff, carbon_victim) && carbon_victim.canBeHandcuffed()) + if(!carbon_victim.handcuffed) + carbon_victim.set_handcuffed(new /obj/item/restraints/handcuffs/energy/used(carbon_victim)) + carbon_victim.update_handcuffed() + to_chat(user, span_notice("You restrain [carbon_victim].")) + log_combat(user, carbon_victim, "handcuffed") else - to_chat(user, span_warning("You fail to restrain [C].")) + to_chat(user, span_warning("You fail to restrain [carbon_victim].")) else - to_chat(user, span_warning("[C] doesn't have two hands...")) + to_chat(user, span_warning("[carbon_victim] doesn't have two hands...")) -/obj/item/melee/baton/abductor/proc/ProbeAttack(mob/living/L,mob/living/user) - L.visible_message(span_danger("[user] probes [L] with [src]!"), \ +/obj/item/melee/baton/abductor/proc/ProbeAttack(mob/living/victim, mob/living/user) + victim.visible_message(span_danger("[user] probes [victim] with [src]!"), \ span_userdanger("[user] probes you!")) var/species = span_warning("Unknown species") var/helptext = span_warning("Species unsuitable for experiments.") - if(ishuman(L)) - var/mob/living/carbon/human/H = L - species = span_notice("[H.dna.species.name]") - if(L.mind && L.mind.has_antag_datum(/datum/antagonist/changeling)) + if(ishuman(victim)) + var/mob/living/carbon/human/human_victim = victim + species = span_notice("[human_victim.dna.species.name]") + if(human_victim.mind && human_victim.mind.has_antag_datum(/datum/antagonist/changeling)) species = span_warning("Changeling lifeform") - var/obj/item/organ/internal/heart/gland/temp = locate() in H.organs + var/obj/item/organ/internal/heart/gland/temp = locate() in human_victim.organs if(temp) helptext = span_warning("Experimental gland detected!") else - if (L.get_organ_slot(ORGAN_SLOT_HEART)) + if (human_victim.get_organ_slot(ORGAN_SLOT_HEART)) helptext = span_notice("Subject suitable for experiments.") else helptext = span_warning("Subject unsuitable for experiments.") @@ -640,9 +483,9 @@ Congratulations! You are now trained for invasive xenobiology research!"} /obj/item/restraints/handcuffs/energy/used/dropped(mob/user) user.visible_message(span_danger("[user]'s [name] breaks in a discharge of energy!"), \ span_userdanger("[user]'s [name] breaks in a discharge of energy!")) - var/datum/effect_system/spark_spread/S = new - S.set_up(4,0,user.loc) - S.start() + var/datum/effect_system/spark_spread/sparks = new + sparks.set_up(4,0,user.loc) + sparks.start() . = ..() /obj/item/melee/baton/abductor/examine(mob/user) @@ -769,150 +612,104 @@ Congratulations! You are now trained for invasive xenobiology research!"} return COMPONENT_CANT_TRACK -// Operating Table / Beds / Lockers - -/obj/structure/bed/abductor - name = "resting contraption" - desc = "This looks similar to contraptions from Earth. Could aliens be stealing our technology?" - icon = 'icons/obj/abductor.dmi' - buildstacktype = /obj/item/stack/sheet/mineral/abductor - icon_state = "bed" - -/obj/structure/table_frame/abductor - name = "alien table frame" - desc = "A sturdy table frame made from alien alloy." - icon_state = "alien_frame" - framestack = /obj/item/stack/sheet/mineral/abductor - framestackamount = 1 - -/obj/structure/table_frame/abductor/attackby(obj/item/I, mob/user, params) - if(I.tool_behaviour == TOOL_WRENCH) - to_chat(user, span_notice("You start disassembling [src]...")) - I.play_tool_sound(src) - if(I.use_tool(src, user, 30)) - playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) - for(var/i in 0 to framestackamount) - new framestack(get_turf(src)) - qdel(src) - return - if(istype(I, /obj/item/stack/sheet/mineral/abductor)) - var/obj/item/stack/sheet/P = I - if(P.get_amount() < 1) - to_chat(user, span_warning("You need one alien alloy sheet to do this!")) - return - to_chat(user, span_notice("You start adding [P] to [src]...")) - if(do_after(user, 50, target = src)) - P.use(1) - new /obj/structure/table/abductor(src.loc) - qdel(src) - return - if(istype(I, /obj/item/stack/sheet/mineral/silver)) - var/obj/item/stack/sheet/P = I - if(P.get_amount() < 1) - to_chat(user, span_warning("You need one sheet of silver to do this!")) - return - to_chat(user, span_notice("You start adding [P] to [src]...")) - if(do_after(user, 50, target = src)) - P.use(1) - new /obj/structure/table/optable/abductor(src.loc) - qdel(src) - -/obj/structure/table/abductor - name = "alien table" - desc = "Advanced flat surface technology at work!" - icon = 'icons/obj/smooth_structures/alien_table.dmi' - icon_state = "alien_table-0" - base_icon_state = "alien_table" - buildstack = /obj/item/stack/sheet/mineral/abductor - framestack = /obj/item/stack/sheet/mineral/abductor - buildstackamount = 1 - framestackamount = 1 - smoothing_groups = SMOOTH_GROUP_ABDUCTOR_TABLES - canSmoothWith = SMOOTH_GROUP_ABDUCTOR_TABLES - frame = /obj/structure/table_frame/abductor - custom_materials = list(/datum/material/silver =SHEET_MATERIAL_AMOUNT) - -/obj/structure/table/optable/abductor - name = "alien operating table" - desc = "Used for alien medical procedures. The surface is covered in tiny spines." - frame = /obj/structure/table_frame/abductor - buildstack = /obj/item/stack/sheet/mineral/silver - framestack = /obj/item/stack/sheet/mineral/abductor - buildstackamount = 1 - framestackamount = 1 - icon = 'icons/obj/abductor.dmi' - icon_state = "bed" - can_buckle = 1 - /// Amount to inject per second - var/inject_am = 0.5 - - var/static/list/injected_reagents = list(/datum/reagent/medicine/cordiolis_hepatico) - -/obj/structure/table/optable/abductor/Initialize(mapload) +/obj/item/abductor/alien_omnitool + name = "quizzandric interfacer" + desc = "Effectively just a Space Swiss Army Knife. Contains a multitude of integrated tools. Right-click it to switch which toolset is active." + icon_state = "omnitool" + inhand_icon_state = "silencer" + toolspeed = 0.25 + tool_behaviour = null + usesound = 'sound/items/pshoom.ogg' + ///A list of all the tools we offer. Stored as "Tool" for the key, and the icon/icon_state as the value. + var/list/tool_list = list() + ///Which toolset do we have active currently? + var/active_toolset = TOOLSET_MEDICAL + +/obj/item/abductor/alien_omnitool/Initialize(mapload) . = ..() - var/static/list/loc_connections = list( - COMSIG_ATOM_ENTERED = PROC_REF(on_entered), - ) - AddElement(/datum/element/connect_loc, loc_connections) + set_toolset() //This populates the tool list, and sets it to the hacking configuration. -/obj/structure/table/optable/abductor/proc/on_entered(datum/source, atom/movable/AM) - SIGNAL_HANDLER - if(iscarbon(AM)) - START_PROCESSING(SSobj, src) - to_chat(AM, span_danger("You feel a series of tiny pricks!")) +/obj/item/abductor/alien_omnitool/examine() + . = ..() + . += " The mode is: [tool_behaviour]" -/obj/structure/table/optable/abductor/process(seconds_per_tick) - . = PROCESS_KILL - for(var/mob/living/carbon/C in get_turf(src)) - . = TRUE - for(var/chemical in injected_reagents) - if(C.reagents.get_reagent_amount(chemical) < inject_am * seconds_per_tick) - C.reagents.add_reagent(chemical, inject_am * seconds_per_tick) +/obj/item/abductor/alien_omnitool/attack_self(mob/user) + if(!user) + return -/obj/structure/table/optable/abductor/Destroy() - STOP_PROCESSING(SSobj, src) - . = ..() + var/tool_result = show_radial_menu(user, src, tool_list, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE, tooltips = TRUE) + if(!check_menu(user)) + return + switch(tool_result) + if("Retractor") + tool_behaviour = TOOL_RETRACTOR + if("Hemostat") + tool_behaviour = TOOL_HEMOSTAT + if("Cautery") + tool_behaviour = TOOL_CAUTERY + if("Drill") + tool_behaviour = TOOL_DRILL + if("Scalpel") + tool_behaviour = TOOL_SCALPEL + if("Saw") + tool_behaviour = TOOL_SAW + if("Bonesetter") + tool_behaviour = TOOL_BONESET + if("Blood Filter") + tool_behaviour = TOOL_BLOODFILTER + if("Crowbar") + tool_behaviour = TOOL_CROWBAR + if("Multitool") + tool_behaviour = TOOL_MULTITOOL + if("Screwdriver") + tool_behaviour = TOOL_SCREWDRIVER + if("Wirecutters") + tool_behaviour = TOOL_WIRECUTTER + if("Wrench") + tool_behaviour = TOOL_WRENCH + if("Welding Tool") + tool_behaviour = TOOL_WELDER + + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) + +/obj/item/abductor/alien_omnitool/attack_self_secondary(mob/user, modifiers) //ADD SFX FOR USING THE TOOL + if(!user) + return -/obj/structure/closet/abductor - name = "alien locker" - desc = "Contains secrets of the universe." - icon_state = "abductor" - icon_door = "abductor" - can_weld_shut = FALSE - door_anim_time = 0 - material_drop = /obj/item/stack/sheet/mineral/abductor - -/obj/structure/door_assembly/door_assembly_abductor - name = "alien airlock assembly" - icon = 'icons/obj/doors/airlocks/abductor/abductor_airlock.dmi' - base_name = "alien airlock" - overlays_file = 'icons/obj/doors/airlocks/abductor/overlays.dmi' - airlock_type = /obj/machinery/door/airlock/abductor - material_type = /obj/item/stack/sheet/mineral/abductor - noglass = TRUE - -/obj/item/clothing/under/abductor - desc = "The most advanced form of jumpsuit known to reality, looks uncomfortable." - name = "alien jumpsuit" - icon = 'icons/obj/clothing/under/syndicate.dmi' - icon_state = "abductor" - inhand_icon_state = "bl_suit" - worn_icon = 'icons/mob/clothing/under/syndicate.dmi' - armor_type = /datum/armor/under_abductor - can_adjust = FALSE - -/datum/armor/under_abductor - bomb = 10 - bio = 10 - -#undef BATON_CUFF -#undef BATON_MODES -#undef BATON_PROBE -#undef BATON_SLEEP -#undef BATON_STUN -#undef GIZMO_MARK -#undef GIZMO_SCAN -#undef MIND_DEVICE_CONTROL -#undef MIND_DEVICE_MESSAGE -#undef VEST_COMBAT -#undef VEST_STEALTH + set_toolset(user) + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) + +/obj/item/abductor/alien_omnitool/proc/check_menu(mob/user) + if(!istype(user)) + return FALSE + if(user.incapacitated() || !user.Adjacent(src)) + return FALSE + return TRUE + +/obj/item/abductor/alien_omnitool/proc/set_toolset(mob/user) + if(active_toolset == TOOLSET_MEDICAL) + tool_list = list( + "Crowbar" = image(icon = 'icons/obj/abductor.dmi', icon_state = "crowbar"), + "Multitool" = image(icon = 'icons/obj/abductor.dmi', icon_state = "multitool"), + "Screwdriver" = image(icon = 'icons/obj/abductor.dmi', icon_state = "screwdriver_a"), + "Wirecutters" = image(icon = 'icons/obj/abductor.dmi', icon_state = "cutters"), + "Wrench" = image(icon = 'icons/obj/abductor.dmi', icon_state = "wrench"), + "Welding Tool" = image(icon = 'icons/obj/abductor.dmi', icon_state = "welder"), + ) + active_toolset = TOOLSET_HACKING + if(user) + balloon_alert(user, "hacking toolset selected") + else + tool_list = list( + "Retractor" = image(icon = 'icons/obj/abductor.dmi', icon_state = "retractor"), + "Hemostat" = image(icon = 'icons/obj/abductor.dmi', icon_state = "hemostat"), + "Cautery" = image(icon = 'icons/obj/abductor.dmi', icon_state = "cautery"), + "Drill" = image(icon = 'icons/obj/abductor.dmi', icon_state = "drill"), + "Scalpel" = image(icon = 'icons/obj/abductor.dmi', icon_state = "scalpel"), + "Saw" = image(icon = 'icons/obj/abductor.dmi', icon_state = "saw"), + "Bonesetter" = image(icon = 'icons/obj/medical/surgery_tools.dmi', icon_state = "bonesetter"), + "Blood Filter" = image(icon = 'icons/obj/medical/surgery_tools.dmi', icon_state = "bloodfilter"), + ) + active_toolset = TOOLSET_MEDICAL + if(user) + balloon_alert(user, "medical toolset selected") diff --git a/code/modules/antagonists/abductor/equipment/gear/abductor_posters.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_posters.dm new file mode 100644 index 000000000000..90eee7ffec67 --- /dev/null +++ b/code/modules/antagonists/abductor/equipment/gear/abductor_posters.dm @@ -0,0 +1,88 @@ + +/obj/item/poster/random_abductor + name = "random abductor poster" + poster_type = /obj/structure/sign/poster/abductor/random + icon = 'icons/obj/poster.dmi' + icon_state = "rolled_abductor" + +/obj/structure/sign/poster/abductor + icon = 'icons/obj/abductor_posters.dmi' + poster_item_name = "abductor poster" + poster_item_desc = "A sheet of holofiber resin, with a nanospike perforation on the back end for maximum adhesion." + poster_item_icon_state = "rolled_abductor" + +/obj/structure/sign/poster/abductor/tear_poster(mob/user) + if(!isabductor(user)) + balloon_alert(user, "it won't budge!") + return + return ..() + +/obj/structure/sign/poster/abductor/attackby(obj/item/tool, mob/user, params) + if(tool.toolspeed >= 0.2) + balloon_alert(user, "tool too weak!") + return FALSE + return ..() + +/obj/structure/sign/poster/abductor/random + name = "random abductor poster" + icon_state = "random_abductor" + never_random = TRUE + random_basetype = /obj/structure/sign/poster/abductor + +MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/abductor/random, 32) + +/obj/structure/sign/poster/abductor/ayylian + name = "Ayylian" + desc = "Man, Ian sure is looking strange these days." + icon_state = "ayylian" + +MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/abductor/ayylian, 32) + +/obj/structure/sign/poster/abductor/ayy + name = "Abductor" + desc = "Hey, that's not a lizard!" + icon_state = "ayy" + +MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/abductor/ayy, 32) + +/obj/structure/sign/poster/abductor/ayy_over_tizira + name = "Abductors Over Tizira" + desc = "A poster for an experimental adaptation of a movie about the Human-Lizard war. Production was greatly hindered by the leading pair's refusal to speak any lines." + icon_state = "ayy_over_tizira" + +MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/abductor/ayy_over_tizira, 32) + +/obj/structure/sign/poster/abductor/ayy_recruitment + name = "Abductor Recruitment" + desc = "Enlist in the Mothership Probing Division today!" + icon_state = "ayy_recruitment" + +MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/abductor/ayy_recruitment, 32) + +/obj/structure/sign/poster/abductor/ayy_cops + name = "Abductor Cops" + desc = "A poster advertising the polarizing 'Abductor Cops' series. Some critics claimed that it stunned them, while others said it put them to sleep." + icon_state = "ayyce_cops" + +MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/abductor/ayy_cops, 32) + +/obj/structure/sign/poster/abductor/ayy_no + name = "Uayy No" + desc = "This thing is all in Japanese, AND they got rid of the anime girl on the poster. Outrageous." + icon_state = "ayy_no" + +MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/abductor/ayy_no, 32) + +/obj/structure/sign/poster/abductor/ayy_piping + name = "Safety Abductor - Piping" + desc = "Safety Abductor has nothing to say. Not because it cannot speak, but because Abductors don't have to deal with atmos stuff." + icon_state = "ayy_piping" + +MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/abductor/ayy_piping, 32) + +/obj/structure/sign/poster/abductor/ayy_fancy + name = "Abductor Fancy" + desc = "Abductors are the best at doing everything. That includes looking good!" + icon_state = "ayy_fancy" + +MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/poster/abductor/ayy_fancy, 32) diff --git a/code/modules/antagonists/abductor/equipment/glands/access.dm b/code/modules/antagonists/abductor/equipment/glands/access.dm index 8e8ff9a7ef9c..99f58a3d2c80 100644 --- a/code/modules/antagonists/abductor/equipment/glands/access.dm +++ b/code/modules/antagonists/abductor/equipment/glands/access.dm @@ -1,11 +1,11 @@ /obj/item/organ/internal/heart/gland/access abductor_hint = "anagraphic electro-scrambler. After it activates, grants the abductee intrinsic all access." - cooldown_low = 600 - cooldown_high = 1200 + cooldown_low = 1 MINUTES + cooldown_high = 2 MINUTES uses = 1 icon_state = "mindshock" mind_control_uses = 3 - mind_control_duration = 900 + mind_control_duration = 90 SECONDS /obj/item/organ/internal/heart/gland/access/activate() to_chat(owner, span_notice("You feel like a VIP for some reason.")) diff --git a/code/modules/antagonists/abductor/equipment/glands/blood.dm b/code/modules/antagonists/abductor/equipment/glands/blood.dm index 522354fac3ac..40c2b3c4d726 100644 --- a/code/modules/antagonists/abductor/equipment/glands/blood.dm +++ b/code/modules/antagonists/abductor/equipment/glands/blood.dm @@ -1,13 +1,13 @@ /obj/item/organ/internal/heart/gland/blood abductor_hint = "pseudonuclear hemo-destabilizer. Periodically randomizes the abductee's bloodtype into a random reagent." - cooldown_low = 1200 - cooldown_high = 1800 + cooldown_low = 2 MINUTES + cooldown_high = 3 MINUTES uses = -1 icon_state = "egg" lefthand_file = 'icons/mob/inhands/items/food_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/food_righthand.dmi' mind_control_uses = 3 - mind_control_duration = 1500 + mind_control_duration = 2.5 MINUTES /obj/item/organ/internal/heart/gland/blood/activate() if(!ishuman(owner) || !owner.dna.species) diff --git a/code/modules/antagonists/abductor/equipment/glands/chem.dm b/code/modules/antagonists/abductor/equipment/glands/chem.dm index 47868841c850..94fca8fd8fba 100644 --- a/code/modules/antagonists/abductor/equipment/glands/chem.dm +++ b/code/modules/antagonists/abductor/equipment/glands/chem.dm @@ -1,11 +1,11 @@ /obj/item/organ/internal/heart/gland/chem abductor_hint = "intrinsic pharma-provider. The abductee constantly produces random chemicals inside their bloodstream. They also quickly regenerate toxin damage." - cooldown_low = 50 - cooldown_high = 50 + cooldown_low = 5 SECONDS + cooldown_high = 5 SECONDS uses = -1 icon_state = "viral" mind_control_uses = 3 - mind_control_duration = 1200 + mind_control_duration = 2 MINUTES var/list/possible_reagents = list() /obj/item/organ/internal/heart/gland/chem/Initialize(mapload) diff --git a/code/modules/antagonists/abductor/equipment/glands/egg.dm b/code/modules/antagonists/abductor/equipment/glands/egg.dm index 938caeeb3612..52fb1da287ba 100644 --- a/code/modules/antagonists/abductor/equipment/glands/egg.dm +++ b/code/modules/antagonists/abductor/equipment/glands/egg.dm @@ -1,13 +1,13 @@ /obj/item/organ/internal/heart/gland/egg abductor_hint = "roe/enzymatic synthesizer. The abductee will periodically lay eggs filled with random reagents." - cooldown_low = 300 - cooldown_high = 400 + cooldown_low = 30 SECONDS + cooldown_high = 40 SECONDS uses = -1 icon_state = "egg" lefthand_file = 'icons/mob/inhands/items/food_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/food_righthand.dmi' mind_control_uses = 2 - mind_control_duration = 1800 + mind_control_duration = 3 MINUTES /obj/item/organ/internal/heart/gland/egg/activate() owner.visible_message(span_alertalien("[owner] [pick(EGG_LAYING_MESSAGES)]")) diff --git a/code/modules/antagonists/abductor/equipment/glands/electric.dm b/code/modules/antagonists/abductor/equipment/glands/electric.dm index acb1505a71c6..b1db830d8926 100644 --- a/code/modules/antagonists/abductor/equipment/glands/electric.dm +++ b/code/modules/antagonists/abductor/equipment/glands/electric.dm @@ -5,7 +5,7 @@ icon_state = "species" uses = -1 mind_control_uses = 2 - mind_control_duration = 900 + mind_control_duration = 90 SECONDS /obj/item/organ/internal/heart/gland/electric/on_insert(mob/living/carbon/gland_owner) . = ..() @@ -19,7 +19,7 @@ owner.visible_message(span_danger("[owner]'s skin starts emitting electric arcs!"),\ span_warning("You feel electric energy building up inside you!")) playsound(get_turf(owner), SFX_SPARKS, 100, TRUE, -1, SHORT_RANGE_SOUND_EXTRARANGE) - addtimer(CALLBACK(src, PROC_REF(zap)), rand(30, 100)) + addtimer(CALLBACK(src, PROC_REF(zap)), rand(3 SECONDS, 10 SECONDS)) /obj/item/organ/internal/heart/gland/electric/proc/zap() tesla_zap(owner, 4, 8000, ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_MOB_STUN) diff --git a/code/modules/antagonists/abductor/equipment/glands/heal.dm b/code/modules/antagonists/abductor/equipment/glands/heal.dm index 79268cdf5caa..9be6245d91f0 100644 --- a/code/modules/antagonists/abductor/equipment/glands/heal.dm +++ b/code/modules/antagonists/abductor/equipment/glands/heal.dm @@ -1,12 +1,12 @@ /obj/item/organ/internal/heart/gland/heal abductor_hint = "organic replicator. Forcibly ejects damaged and robotic organs from the abductee and regenerates them. Additionally, forcibly removes reagents (via vomit) from the abductee if they have moderate toxin damage or poison within the bloodstream, and regenerates blood to a healthy threshold if too low. The abductee will also reject implants such as mindshields." - cooldown_low = 200 - cooldown_high = 400 + cooldown_low = 20 SECONDS + cooldown_high = 40 SECONDS uses = -1 human_only = TRUE icon_state = "health" mind_control_uses = 3 - mind_control_duration = 3000 + mind_control_duration = 5 MINUTES /obj/item/organ/internal/heart/gland/heal/activate() if(!(owner.mob_biotypes & MOB_ORGANIC)) @@ -157,7 +157,7 @@ else to_chat(owner, span_warning("You feel a weird rumble behind your eye sockets...")) - addtimer(CALLBACK(src, PROC_REF(finish_replace_eyes)), rand(100, 200)) + addtimer(CALLBACK(src, PROC_REF(finish_replace_eyes)), rand(10 SECONDS, 20 SECONDS)) /obj/item/organ/internal/heart/gland/heal/proc/finish_replace_eyes() var/eye_type = /obj/item/organ/internal/eyes @@ -175,7 +175,7 @@ else to_chat(owner, span_warning("You feel a weird tingle in your [parse_zone(body_zone)]... even if you don't have one.")) - addtimer(CALLBACK(src, PROC_REF(finish_replace_limb), body_zone), rand(150, 300)) + addtimer(CALLBACK(src, PROC_REF(finish_replace_limb), body_zone), rand(150 SECONDS, 30 SECONDS)) /obj/item/organ/internal/heart/gland/heal/proc/finish_replace_limb(body_zone) owner.visible_message(span_warning("With a loud snap, [owner]'s [parse_zone(body_zone)] rapidly grows back from [owner.p_their()] body!"), @@ -205,7 +205,7 @@ if(owner.reagents.has_reagent(R.type)) keep_going = TRUE if(keep_going) - addtimer(CALLBACK(src, PROC_REF(keep_replacing_blood)), 30) + addtimer(CALLBACK(src, PROC_REF(keep_replacing_blood)), 3 SECONDS) /obj/item/organ/internal/heart/gland/heal/proc/replace_chest(obj/item/bodypart/chest/chest) if(!IS_ORGANIC_LIMB(chest)) diff --git a/code/modules/antagonists/abductor/equipment/glands/plasma.dm b/code/modules/antagonists/abductor/equipment/glands/plasma.dm index 92aa7abcc454..894f176146cf 100644 --- a/code/modules/antagonists/abductor/equipment/glands/plasma.dm +++ b/code/modules/antagonists/abductor/equipment/glands/plasma.dm @@ -1,16 +1,16 @@ /obj/item/organ/internal/heart/gland/plasma abductor_hint = "effluvium sanguine-synonym emitter. The abductee randomly emits clouds of plasma." - cooldown_low = 1200 - cooldown_high = 1800 + cooldown_low = 2 MINUTES + cooldown_high = 3 MINUTES icon_state = "slime" uses = -1 mind_control_uses = 1 - mind_control_duration = 800 + mind_control_duration = 80 SECONDS /obj/item/organ/internal/heart/gland/plasma/activate() to_chat(owner, span_warning("You feel bloated.")) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), owner, span_userdanger("A massive stomachache overcomes you.")), 150) - addtimer(CALLBACK(src, PROC_REF(vomit_plasma)), 200) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), owner, span_userdanger("A massive stomachache overcomes you.")), 15 SECONDS) + addtimer(CALLBACK(src, PROC_REF(vomit_plasma)), 20 SECONDS) /obj/item/organ/internal/heart/gland/plasma/proc/vomit_plasma() if(!owner) diff --git a/code/modules/antagonists/abductor/equipment/glands/quantum.dm b/code/modules/antagonists/abductor/equipment/glands/quantum.dm index d32304a5987e..6b363d68eb76 100644 --- a/code/modules/antagonists/abductor/equipment/glands/quantum.dm +++ b/code/modules/antagonists/abductor/equipment/glands/quantum.dm @@ -1,11 +1,11 @@ /obj/item/organ/internal/heart/gland/quantum abductor_hint = "quantic de-observation matrix. Periodically links with a random person in view, then the abductee later swaps positions with that person." - cooldown_low = 150 - cooldown_high = 150 + cooldown_low = 15 SECONDS + cooldown_high = 15 SECONDS uses = -1 icon_state = "emp" mind_control_uses = 2 - mind_control_duration = 1200 + mind_control_duration = 2 MINUTES var/mob/living/carbon/entangled_mob /obj/item/organ/internal/heart/gland/quantum/activate() @@ -15,7 +15,7 @@ if(!iscarbon(M)) continue entangled_mob = M - addtimer(CALLBACK(src, PROC_REF(quantum_swap)), rand(600, 2400)) + addtimer(CALLBACK(src, PROC_REF(quantum_swap)), rand(1 MINUTES, 4 MINUTES)) return /obj/item/organ/internal/heart/gland/quantum/proc/quantum_swap() diff --git a/code/modules/antagonists/abductor/equipment/glands/slime.dm b/code/modules/antagonists/abductor/equipment/glands/slime.dm index a7764ff214df..df1082900b2f 100644 --- a/code/modules/antagonists/abductor/equipment/glands/slime.dm +++ b/code/modules/antagonists/abductor/equipment/glands/slime.dm @@ -1,20 +1,20 @@ /obj/item/organ/internal/heart/gland/slime abductor_hint = "gastric animation galvanizer. The abductee occasionally vomits slimes. Slimes will no longer attack the abductee." - cooldown_low = 600 - cooldown_high = 1200 + cooldown_low = 1 MINUTES + cooldown_high = 2 MINUTES uses = -1 icon_state = "slime" mind_control_uses = 1 - mind_control_duration = 2400 + mind_control_duration = 4 MINUTES /obj/item/organ/internal/heart/gland/slime/on_insert(mob/living/carbon/gland_owner) . = ..() - gland_owner.faction |= FACTION_SLIME + //gland_owner.faction |= FACTION_SLIME gland_owner.grant_language(/datum/language/slime, TRUE, TRUE, LANGUAGE_GLAND) /obj/item/organ/internal/heart/gland/slime/on_remove(mob/living/carbon/gland_owner) . = ..() - gland_owner.faction -= FACTION_SLIME + //gland_owner.faction -= FACTION_SLIME gland_owner.remove_language(/datum/language/slime, TRUE, TRUE, LANGUAGE_GLAND) /obj/item/organ/internal/heart/gland/slime/activate() diff --git a/code/modules/antagonists/abductor/equipment/glands/spider.dm b/code/modules/antagonists/abductor/equipment/glands/spider.dm index 52ff520a876a..dab4e2ec3947 100644 --- a/code/modules/antagonists/abductor/equipment/glands/spider.dm +++ b/code/modules/antagonists/abductor/equipment/glands/spider.dm @@ -1,3 +1,5 @@ +// MONKESTATION FILE REMOVAL +/* /obj/item/organ/internal/heart/gland/spiderman abductor_hint = "araneae cloister accelerator. The abductee occasionally exhales spider pheromones and will spawn spiderlings." cooldown_low = 450 @@ -11,4 +13,6 @@ to_chat(owner, span_warning("You feel something crawling in your skin.")) owner.faction |= FACTION_SPIDER var/mob/living/basic/spider/growing/spiderling/spider = new(owner.drop_location()) - spider.directive = "Protect your nest inside [owner.real_name]." + s +pider.directive = "Protect your nest inside [owner.real_name]." +*/ diff --git a/code/modules/antagonists/abductor/equipment/glands/transform.dm b/code/modules/antagonists/abductor/equipment/glands/transform.dm index 3ea10c772557..c660c852f199 100644 --- a/code/modules/antagonists/abductor/equipment/glands/transform.dm +++ b/code/modules/antagonists/abductor/equipment/glands/transform.dm @@ -1,3 +1,5 @@ +// MONKESTATION FILE REMOVAL +/* /obj/item/organ/internal/heart/gland/transform abductor_hint = "anthropmorphic transmorphosizer. The abductee will occasionally change appearance and species." cooldown_low = 900 @@ -13,3 +15,4 @@ randomize_human(owner) var/species = pick(list(/datum/species/human, /datum/species/lizard, /datum/species/moth, /datum/species/fly)) owner.set_species(species) +*/ diff --git a/code/modules/antagonists/abductor/equipment/glands/trauma.dm b/code/modules/antagonists/abductor/equipment/glands/trauma.dm index 5fab30332ef5..9443e5a0323e 100644 --- a/code/modules/antagonists/abductor/equipment/glands/trauma.dm +++ b/code/modules/antagonists/abductor/equipment/glands/trauma.dm @@ -1,11 +1,11 @@ /obj/item/organ/internal/heart/gland/trauma abductor_hint = "white matter randomiser. The abductee occasionally gains a random brain trauma, up to five times. The traumas can range from basic to deep-rooted." - cooldown_low = 800 - cooldown_high = 1200 + cooldown_low = 80 SECONDS + cooldown_high = 2 MINUTES uses = 5 icon_state = "emp" mind_control_uses = 3 - mind_control_duration = 1800 + mind_control_duration = 3 MINUTES /obj/item/organ/internal/heart/gland/trauma/activate() to_chat(owner, span_warning("You feel a spike of pain in your head.")) diff --git a/code/modules/antagonists/abductor/equipment/glands/ventcrawl.dm b/code/modules/antagonists/abductor/equipment/glands/ventcrawl.dm index c7e3a74e4f99..f94c6872c354 100644 --- a/code/modules/antagonists/abductor/equipment/glands/ventcrawl.dm +++ b/code/modules/antagonists/abductor/equipment/glands/ventcrawl.dm @@ -1,12 +1,30 @@ /obj/item/organ/internal/heart/gland/ventcrawling abductor_hint = "pliant cartilage enabler. The abductee can crawl through vents without trouble." - cooldown_low = 1800 - cooldown_high = 2400 + cooldown_low = 3 MINUTES + cooldown_high = 4 MINUTES uses = 1 icon_state = "vent" mind_control_uses = 4 - mind_control_duration = 1800 + mind_control_duration = 3 MINUTES + +/obj/item/organ/internal/heart/gland/ventcrawling/on_insert(mob/living/carbon/organ_owner, special) + . = ..() + RegisterSignal(organ_owner, SIGNAL_ADDTRAIT(TRAIT_MOVE_VENTCRAWLING), PROC_REF(give_pipe_resistance)) + RegisterSignal(organ_owner, SIGNAL_REMOVETRAIT(TRAIT_MOVE_VENTCRAWLING), PROC_REF(take_pipe_resistance)) + +/obj/item/organ/internal/heart/gland/ventcrawling/on_remove(mob/living/carbon/organ_owner, special) + . = ..() + UnregisterSignal(organ_owner, list(SIGNAL_ADDTRAIT(TRAIT_MOVE_VENTCRAWLING), SIGNAL_REMOVETRAIT(TRAIT_MOVE_VENTCRAWLING))) + REMOVE_TRAITS_IN(organ_owner, ABDUCTOR_GLAND_VENTCRAWLING_TRAIT) /obj/item/organ/internal/heart/gland/ventcrawling/activate() to_chat(owner, span_notice("You feel very stretchy.")) - ADD_TRAIT(owner, TRAIT_VENTCRAWLER_ALWAYS, type) + ADD_TRAIT(owner, TRAIT_VENTCRAWLER_ALWAYS, ABDUCTOR_GLAND_TRAIT) + +/obj/item/organ/internal/heart/gland/ventcrawling/proc/give_pipe_resistance() + SIGNAL_HANDLER + owner.add_traits(list(TRAIT_RESISTHIGHPRESSURE, TRAIT_RESISTLOWPRESSURE, TRAIT_RESISTHEAT, TRAIT_RESISTCOLD, TRAIT_NOBREATH), ABDUCTOR_GLAND_VENTCRAWLING_TRAIT) + +/obj/item/organ/internal/heart/gland/ventcrawling/proc/take_pipe_resistance() + SIGNAL_HANDLER + REMOVE_TRAITS_IN(owner, ABDUCTOR_GLAND_VENTCRAWLING_TRAIT) diff --git a/code/modules/antagonists/abductor/equipment/glands/viral.dm b/code/modules/antagonists/abductor/equipment/glands/viral.dm index c958501e77bf..96b151223732 100644 --- a/code/modules/antagonists/abductor/equipment/glands/viral.dm +++ b/code/modules/antagonists/abductor/equipment/glands/viral.dm @@ -1,11 +1,11 @@ /obj/item/organ/internal/heart/gland/viral abductor_hint = "contamination incubator. The abductee becomes a carrier of a random advanced disease - of which they are unaffected by." - cooldown_low = 1800 - cooldown_high = 2400 + cooldown_low = 3 MINUTES + cooldown_high = 4 MINUTES uses = 1 icon_state = "viral" mind_control_uses = 1 - mind_control_duration = 1800 + mind_control_duration = 3 MINUTES /obj/item/organ/internal/heart/gland/viral/activate() to_chat(owner, span_warning("You feel sick.")) diff --git a/code/modules/antagonists/abductor/equipment/orderable_gear.dm b/code/modules/antagonists/abductor/equipment/orderable_gear.dm index 4d0562ca35bd..b133bf3f8a5e 100644 --- a/code/modules/antagonists/abductor/equipment/orderable_gear.dm +++ b/code/modules/antagonists/abductor/equipment/orderable_gear.dm @@ -1,5 +1,9 @@ GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear)) +#define CATEGORY_BASIC_GEAR "Basic Gear" +#define CATEGORY_ADVANCED_GEAR "Advanced Gear" +#define CATEGORY_MISC_GEAR "Miscellaneous Gear" + /datum/abductor_gear /// Name of the gear var/name = "Generic Abductor Gear" @@ -12,45 +16,45 @@ GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear)) /// Build path of the gear itself var/build_path = null /// Category of the gear - var/category = "Basic Gear" + var/category = CATEGORY_BASIC_GEAR /datum/abductor_gear/agent_helmet name = "Agent Helmet" description = "Abduct with style - spiky style. Prevents digital tracking." id = "agent_helmet" - build_path = /obj/item/clothing/head/helmet/abductor + build_path = list(/obj/item/clothing/head/helmet/abductor = 1) /datum/abductor_gear/agent_vest name = "Agent Vest" description = "A vest outfitted with advanced stealth technology. It has two modes - combat and stealth." id = "agent_vest" - build_path = /obj/item/clothing/suit/armor/abductor/vest + build_path = list(/obj/item/clothing/suit/armor/abductor/vest = 1) /datum/abductor_gear/radio_silencer name = "Radio Silencer" description = "A compact device used to shut down communications equipment." id = "radio_silencer" - build_path = /obj/item/abductor/silencer + build_path = list(/obj/item/abductor/silencer = 1) /datum/abductor_gear/science_tool name = "Science Tool" description = "A dual-mode tool for retrieving specimens and scanning appearances. Scanning can be done through cameras." id = "science_tool" - build_path = /obj/item/abductor/gizmo + build_path = list(/obj/item/abductor/gizmo = 1) /datum/abductor_gear/advanced_baton name = "Advanced Baton" description = "A quad-mode baton used for incapacitation and restraining of specimens." id = "advanced_baton" cost = 2 - build_path = /obj/item/melee/baton/abductor + build_path = list(/obj/item/melee/baton/abductor = 1) /datum/abductor_gear/superlingual_matrix name = "Superlingual Matrix" description = "A mysterious structure that allows for instant communication between users. Using it inhand will attune it to your mothership's channel. Pretty impressive until you need to eat something." id = "superlingual_matrix" - build_path = /obj/item/organ/internal/tongue/abductor - category = "Advanced Gear" + build_path = list(/obj/item/organ/internal/tongue/abductor = 1) + category = CATEGORY_MISC_GEAR /datum/abductor_gear/mental_interface name = "Mental Interface Device" @@ -58,16 +62,16 @@ GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear)) or to send a command to a test subject with a charged gland." id = "mental_interface" cost = 2 - build_path = /obj/item/abductor/mind_device - category = "Advanced Gear" + build_path = list(/obj/item/abductor/mind_device = 1) + category = CATEGORY_ADVANCED_GEAR /datum/abductor_gear/reagent_synthesizer name = "Reagent Synthesizer" description = "Synthesizes a variety of reagents using proto-matter." id = "reagent_synthesizer" cost = 2 - build_path = /obj/item/abductor_machine_beacon/chem_dispenser - category = "Advanced Gear" + build_path = list(/obj/item/abductor_machine_beacon/chem_dispenser = 1) + category = CATEGORY_ADVANCED_GEAR /datum/abductor_gear/shrink_ray name = "Shrink Ray Blaster" @@ -75,5 +79,32 @@ GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear)) That or it's just space magic. Either way, it shrinks stuff." id = "shrink_ray" cost = 2 - build_path = /obj/item/gun/energy/shrink_ray - category = "Advanced Gear" + build_path = list(/obj/item/gun/energy/shrink_ray = 1) + category = CATEGORY_ADVANCED_GEAR + +/datum/abductor_gear/omnitool + name = "Alien Omnitool" + description = "A handheld device with an absurd number of integrated tools. Can be used as a convenient tool replacement for either role. \ + Right-click it to switch between medical and hacking toolsets." + id = "omnitool" + cost = 2 + build_path = list(/obj/item/abductor/alien_omnitool = 1) + category = CATEGORY_ADVANCED_GEAR + +/datum/abductor_gear/cow + name = "Spare Cow" + description = "Delivers a leftover specimen from an earlier abduction operation." + id = "cow" + build_path = list(/mob/living/basic/cow = 1, /obj/item/food/grown/wheat = 3) + category = CATEGORY_MISC_GEAR + +/datum/abductor_gear/posters + name = "Decorative Posters" + description = "Some posters, to decorate the walls of the Mothership (or even the station) with." + id = "poster" + build_path = list(/obj/item/poster/random_abductor = 2) + category = CATEGORY_MISC_GEAR + +#undef CATEGORY_BASIC_GEAR +#undef CATEGORY_ADVANCED_GEAR +#undef CATEGORY_MISC_GEAR diff --git a/code/modules/antagonists/abductor/machinery/console.dm b/code/modules/antagonists/abductor/machinery/console.dm index c73a735f4fa4..494491ab9fdf 100644 --- a/code/modules/antagonists/abductor/machinery/console.dm +++ b/code/modules/antagonists/abductor/machinery/console.dm @@ -268,7 +268,7 @@ else return ..() -/obj/machinery/abductor/console/proc/Dispense(item,cost=1) +/obj/machinery/abductor/console/proc/Dispense(items_list, cost=1) if(experiment && experiment.credits >= cost) experiment.credits -=cost say("Incoming supply!") @@ -276,7 +276,8 @@ if(pad) flick("alien-pad", pad) drop_location = pad.loc - new item(drop_location) - + for(var/each_item in items_list) + for(var/i in 1 to items_list[each_item]) + new each_item(drop_location) else say("Insufficent data!") diff --git a/code/modules/antagonists/abductor/machinery/experiment.dm b/code/modules/antagonists/abductor/machinery/experiment.dm index 8fab6d289b7f..363d1e5cb8a8 100644 --- a/code/modules/antagonists/abductor/machinery/experiment.dm +++ b/code/modules/antagonists/abductor/machinery/experiment.dm @@ -120,6 +120,9 @@ LAZYINITLIST(history) var/mob/living/carbon/human/H = occupant + if(!istype(H)) //We shouldn't be processing anything other than humans, and if we do we runtime. + return + var/datum/antagonist/abductor/user_abductor = user.mind.has_antag_datum(/datum/antagonist/abductor) if(!user_abductor) return "Authorization failure. Contact mothership immediately." diff --git a/code/modules/antagonists/heretic/influences.dm b/code/modules/antagonists/heretic/influences.dm index 9935642297af..03ae1d01b2a6 100644 --- a/code/modules/antagonists/heretic/influences.dm +++ b/code/modules/antagonists/heretic/influences.dm @@ -190,7 +190,7 @@ /obj/effect/visible_heretic_influence/examine(mob/user) . = ..() - if(IS_HERETIC(user) || !ishuman(user)) + if(IS_HERETIC(user) || !ishuman(user) || IS_MONSTERHUNTER(user)) return var/mob/living/carbon/human/human_user = user @@ -227,6 +227,11 @@ on_turf.interaction_flags_atom |= INTERACT_ATOM_NO_FINGERPRINT_ATTACK_HAND RegisterSignal(on_turf, COMSIG_TURF_CHANGE, PROC_REF(replace_our_turf)) + AddComponent(/datum/component/redirect_attack_hand_from_turf, interact_check = CALLBACK(src, PROC_REF(verify_user_can_see))) + +/obj/effect/heretic_influence/proc/verify_user_can_see(mob/user) + return (user?.mind in minds) + /obj/effect/heretic_influence/proc/replace_our_turf(datum/source, path, new_baseturfs, flags, post_change_callbacks) SIGNAL_HANDLER post_change_callbacks += CALLBACK(src, PROC_REF(replace_our_turf_two)) @@ -263,13 +268,16 @@ return // Using a codex will give you two knowledge points for draining. - if(!being_drained && istype(weapon, /obj/item/codex_cicatrix)) - var/obj/item/codex_cicatrix/codex = weapon - if(!codex.book_open) - codex.attack_self(user) // open booke - INVOKE_ASYNC(src, PROC_REF(drain_influence), user, 2) + if(drain_influence_with_codex(user, weapon)) return TRUE +/obj/effect/heretic_influence/proc/drain_influence_with_codex(mob/user, obj/item/codex_cicatrix/codex) + if(!istype(codex) || being_drained) + return FALSE + if(!codex.book_open) + codex.attack_self(user) // open booke + INVOKE_ASYNC(src, PROC_REF(drain_influence), user, 2) + return TRUE /** * Begin to drain the influence, setting being_drained, diff --git a/code/modules/antagonists/heretic/items/forbidden_book.dm b/code/modules/antagonists/heretic/items/forbidden_book.dm index f389d0efa666..5e49b4338469 100644 --- a/code/modules/antagonists/heretic/items/forbidden_book.dm +++ b/code/modules/antagonists/heretic/items/forbidden_book.dm @@ -55,7 +55,9 @@ return if(isopenturf(target)) - heretic_datum.try_draw_rune(user, target, drawing_time = 8 SECONDS) + var/obj/effect/heretic_influence/influence = locate(/obj/effect/heretic_influence) in target + if(!influence?.drain_influence_with_codex(user, src)) + heretic_datum.try_draw_rune(user, target, drawing_time = 8 SECONDS) return TRUE /* diff --git a/code/modules/antagonists/heretic/magic/star_touch.dm b/code/modules/antagonists/heretic/magic/star_touch.dm index c7f0dfb47031..bab07f0871bd 100644 --- a/code/modules/antagonists/heretic/magic/star_touch.dm +++ b/code/modules/antagonists/heretic/magic/star_touch.dm @@ -196,7 +196,7 @@ current_beam = user.Beam(current_target, icon_state="cosmic_beam", time = 1 MINUTES, maxdistance = max_range, beam_type = /obj/effect/ebeam/cosmic) RegisterSignal(current_beam, COMSIG_QDELETING, PROC_REF(beam_died)) - SSblackbox.record_feedback("tally", "gun_fired", 1, type) + SSblackbox.record_feedback("tally", "gun_fired", 1, "Cosmic Beam") if(current_target) on_beam_hit(current_target) diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm index 3ac6e62c9fa8..31fcefad2f59 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm @@ -521,6 +521,8 @@ GLOBAL_VAR(station_nuke_source) update_appearance() sound_to_playing_players('sound/machines/alarm.ogg') + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NUKE_DEVICE_DETONATING, src) + if(SSticker?.mode) SSticker.roundend_check_paused = TRUE addtimer(CALLBACK(src, PROC_REF(actually_explode)), 10 SECONDS) diff --git a/code/modules/antagonists/traitor/components/demoraliser.dm b/code/modules/antagonists/traitor/components/demoraliser.dm index 47cdae620f47..ee44527728c0 100644 --- a/code/modules/antagonists/traitor/components/demoraliser.dm +++ b/code/modules/antagonists/traitor/components/demoraliser.dm @@ -13,7 +13,7 @@ src.moods = moods RegisterSignal(host, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) -/datum/proximity_monitor/advanced/demoraliser/field_turf_crossed(atom/movable/crossed, turf/location) +/datum/proximity_monitor/advanced/demoraliser/field_turf_crossed(atom/movable/crossed, turf/old_location, turf/new_location) if (!isliving(crossed)) return if (!can_see(crossed, host, current_range)) diff --git a/code/modules/antagonists/traitor/objectives/destroy_heirloom.dm b/code/modules/antagonists/traitor/objectives/destroy_heirloom.dm index f36b46e0cc0e..9ee87e2097e7 100644 --- a/code/modules/antagonists/traitor/objectives/destroy_heirloom.dm +++ b/code/modules/antagonists/traitor/objectives/destroy_heirloom.dm @@ -83,6 +83,8 @@ /datum/job/bartender, /datum/job/cook, /datum/job/curator, + // Monkestation edit: security assistants + /datum/job/security_assistant, ) /datum/traitor_objective/destroy_heirloom/rare diff --git a/code/modules/cargo/bounties/engineering.dm b/code/modules/cargo/bounties/engineering.dm index 9da865ec7533..ddfa4b9c45c5 100644 --- a/code/modules/cargo/bounties/engineering.dm +++ b/code/modules/cargo/bounties/engineering.dm @@ -8,7 +8,7 @@ name = "Hydroponics Tray" description = "The lab technicians are trying to figure out how to lower the power drain of hydroponics trays, but we fried our last one. Mind building one for us?" reward = CARGO_CRATE_VALUE * 4 - wanted_types = list(/obj/machinery/hydroponics/constructable = TRUE) + wanted_types = list(/obj/machinery/growing/tray = TRUE) /datum/bounty/item/engineering/cyborg_charger name = "Recharging Station" diff --git a/code/modules/cargo/exports.dm b/code/modules/cargo/exports.dm index 06acc0ba46cd..5c456f4073ca 100644 --- a/code/modules/cargo/exports.dm +++ b/code/modules/cargo/exports.dm @@ -159,7 +159,7 @@ Then the player gets the profit from selling his own wasted time. if(!dry_run) if(apply_elastic) cost *= NUM_E**(-1 * k_elasticity * export_amount) //marginal cost modifier - SSblackbox.record_feedback("nested tally", "export_sold_cost", 1, list("[sold_item.type]", "[export_value]")) + SSblackbox.record_feedback("nested tally", "export_sold_cost", 1, list("[initial(sold_item.name)]", "[export_value]")) return TRUE // Total printout for the cargo console. diff --git a/code/modules/cargo/packs/emergency.dm b/code/modules/cargo/packs/emergency.dm index 9c50372b6a83..a24f1ae0a7ea 100644 --- a/code/modules/cargo/packs/emergency.dm +++ b/code/modules/cargo/packs/emergency.dm @@ -19,11 +19,12 @@ in stations and people alike! Comes with two floorbots, two medbots, five oxygen \ masks and five small oxygen tanks." cost = CARGO_CRATE_VALUE * 4 - contains = list(/mob/living/simple_animal/bot/floorbot = 2, - /mob/living/simple_animal/bot/medbot = 2, - /obj/item/tank/internals/emergency_oxygen = 5, - /obj/item/clothing/mask/breath = 5, - ) + contains = list( + /mob/living/basic/bot/medbot = 2, + /mob/living/simple_animal/bot/floorbot = 2, + /obj/item/tank/internals/emergency_oxygen = 5, + /obj/item/clothing/mask/breath = 5, + ) crate_name = "emergency crate" crate_type = /obj/structure/closet/crate/internals diff --git a/code/modules/clothing/shoes/cowboy.dm b/code/modules/clothing/shoes/cowboy.dm index 05792a72cbd9..65c23d7a5d09 100644 --- a/code/modules/clothing/shoes/cowboy.dm +++ b/code/modules/clothing/shoes/cowboy.dm @@ -4,8 +4,8 @@ icon_state = "cowboy_brown" armor_type = /datum/armor/shoes_cowboy custom_price = PAYCHECK_CREW - var/max_occupants = 4 can_be_tied = FALSE + var/max_occupants = 4 /datum/armor/shoes_cowboy bio = 90 diff --git a/code/modules/events/_event.dm b/code/modules/events/_event.dm index 2efc1936f442..4979d2dd1fc0 100644 --- a/code/modules/events/_event.dm +++ b/code/modules/events/_event.dm @@ -203,7 +203,7 @@ Runs the event if(alert_observers) round_event.announce_deadchat(random, event_cause) - SSblackbox.record_feedback("tally", "event_ran", 1, "[round_event]") + SSblackbox.record_feedback("tally", "event_ran", 1, "[name]") return round_event //Returns the component for the listener diff --git a/code/modules/experisci/experiment/experiments.dm b/code/modules/experisci/experiment/experiments.dm index a946cb780e96..86a84c19428d 100644 --- a/code/modules/experisci/experiment/experiments.dm +++ b/code/modules/experisci/experiment/experiments.dm @@ -220,7 +220,7 @@ /obj/machinery/rnd/production/circuit_imprinter/department/science = 1, /obj/machinery/processor = 2, /obj/machinery/reagentgrinder = 2, - /obj/machinery/hydroponics = 2, + /obj/machinery/growing/tray = 2, /obj/machinery/biogenerator = 3, /obj/machinery/gibber = 3, /obj/machinery/chem_master = 3, diff --git a/code/modules/food_and_drinks/recipes/soup_mixtures.dm b/code/modules/food_and_drinks/recipes/soup_mixtures.dm index 186d144a0771..4a09b1352b51 100644 --- a/code/modules/food_and_drinks/recipes/soup_mixtures.dm +++ b/code/modules/food_and_drinks/recipes/soup_mixtures.dm @@ -221,11 +221,16 @@ if(resulting_food_path) var/obj/item/created = new resulting_food_path(get_turf(pot)) created.pixel_y += 8 + BLACKBOX_LOG_FOOD_MADE(created) + else + var/results_length = length(results) + var/datum/reagent/reagent = results[results_length] + if(reagent) + BLACKBOX_LOG_FOOD_MADE(initial(reagent.name)) // Anything left in the ingredient list will get dumped out pot.dump_ingredients(get_turf(pot), y_offset = 8) // Blackbox log the chemical reaction used, to account for soup reaction that don't produce typical results - BLACKBOX_LOG_FOOD_MADE(type) /** * Transfers reagents from the passed reagent to the soup pot, as a "result" diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm index e172d8250950..70fae57a0bb8 100644 --- a/code/modules/hydroponics/gene_modder.dm +++ b/code/modules/hydroponics/gene_modder.dm @@ -330,6 +330,8 @@ if(disk && disk.gene && istype(disk.gene, G.type) && istype(G, /datum/plant_gene/core)) seed.genes -= G var/datum/plant_gene/core/C = disk.gene.Copy() + var/datum/plant_gene/core/disk_core = disk.gene + C.value = disk_core.value seed.genes += C C.apply_stat(seed) repaint_seed() diff --git a/code/modules/hydroponics/grown/cherries.dm b/code/modules/hydroponics/grown/cherries.dm index 236d6939cf8f..7a97d28e911a 100644 --- a/code/modules/hydroponics/grown/cherries.dm +++ b/code/modules/hydroponics/grown/cherries.dm @@ -59,6 +59,8 @@ /obj/item/seeds/cherry/bulb name = "pack of cherry bulb pits" desc = "The glowy kind of cherries." + //growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + //growthstages = 5 icon_state = "seed-cherrybulb" species = "cherrybulb" plantname = "Cherry Bulb Tree" diff --git a/code/modules/hydroponics/grown/citrus.dm b/code/modules/hydroponics/grown/citrus.dm index 0c38a0a307de..491b8c3f026b 100644 --- a/code/modules/hydroponics/grown/citrus.dm +++ b/code/modules/hydroponics/grown/citrus.dm @@ -92,7 +92,7 @@ species = "firelemon" plantname = "Combustible Lemon Tree" product = /obj/item/food/grown/firelemon - growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + //growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' icon_grow = "lime-grow" icon_dead = "lime-dead" genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/bomb_plant/potency_based) diff --git a/code/modules/hydroponics/grown/cotton.dm b/code/modules/hydroponics/grown/cotton.dm index 0e96c8e23fd8..c67c30440538 100644 --- a/code/modules/hydroponics/grown/cotton.dm +++ b/code/modules/hydroponics/grown/cotton.dm @@ -70,8 +70,8 @@ production = 1 yield = 2 potency = 50 - growthstages = 3 - growing_icon = 'icons/obj/hydroponics/growing.dmi' + //growthstages = 3 + //growing_icon = 'icons/obj/hydroponics/growing.dmi' icon_dead = "cotton-dead" possible_mutations = list() diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm index 7026ee8a5453..10c05b8586c3 100644 --- a/code/modules/hydroponics/grown/flowers.dm +++ b/code/modules/hydroponics/grown/flowers.dm @@ -39,6 +39,11 @@ icon_state = "seed-lily" species = "lily" plantname = "Lily Plants" + growthstages = 3 + growing_icon = 'icons/obj/hydroponics/growing_flowers.dmi' + icon_grow = "poppy-grow" + icon_dead = "poppy-dead" + icon_harvest = null product = /obj/item/food/grown/poppy/lily possible_mutations = list(/datum/hydroponics/plant_mutation/trumpet) diff --git a/code/modules/hydroponics/grown/peas.dm b/code/modules/hydroponics/grown/peas.dm index daca786c50e7..a813601bee3b 100644 --- a/code/modules/hydroponics/grown/peas.dm +++ b/code/modules/hydroponics/grown/peas.dm @@ -70,9 +70,6 @@ potency = 75 yield = 1 production = 10 - growthstages = 3 - icon_grow = "worldpeas-grow" - icon_dead = "worldpeas-dead" genes = list (/datum/plant_gene/trait/glow/blue) reagents_add = list (/datum/reagent/pax = 0.1, /datum/reagent/drug/happiness = 0.1, /datum/reagent/consumable/nutriment = 0.15) rarity = 50 // This absolutely will make even the most hardened Syndicate Operators relax. diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm index 3fc4f704428c..5dd01a418fa2 100644 --- a/code/modules/hydroponics/grown/replicapod.dm +++ b/code/modules/hydroponics/grown/replicapod.dm @@ -121,7 +121,7 @@ return null /obj/item/seeds/replicapod/harvest(mob/user) //now that one is fun -- Urist - var/obj/machinery/hydroponics/parent = loc + var/atom/movable/parent = loc var/make_podman = FALSE var/ckey_holder = null var/list/result = list() @@ -175,7 +175,6 @@ var/obj/item/seeds/replicapod/harvestseeds = src.Copy() result.Add(harvestseeds) harvestseeds.forceMove(output_loc) - parent.update_tray(user, seed_count) return result // Congratulations! %Do you want to build a pod man?% @@ -211,5 +210,4 @@ podman.dna.species.exotic_blood = most_plentiful_reagent[1] investigate_log("[key_name(mind)] cloned as a podman via [src] in [parent]", INVESTIGATE_BOTANY) - parent.update_tray(user, 1) return result diff --git a/code/modules/hydroponics/grown/seedling.dm b/code/modules/hydroponics/grown/seedling.dm index 9a915c41659d..e50b8fe10d13 100644 --- a/code/modules/hydroponics/grown/seedling.dm +++ b/code/modules/hydroponics/grown/seedling.dm @@ -16,12 +16,11 @@ potency = 30 /obj/item/seeds/seedling/harvest(mob/harvester) - var/obj/machinery/hydroponics/parent = loc + var/atom/movable/parent = loc var/list/grow_locations = get_adjacent_open_turfs(parent) var/turf/final_location = length(grow_locations) ? pick(grow_locations) : get_turf(parent) var/mob/living/basic/seedling/seed_pet = new product(final_location) seed_pet.befriend(harvester) - parent.update_tray(user = harvester, product_count = 1) /obj/item/seeds/seedling/evil product = /mob/living/basic/seedling/meanie diff --git a/code/modules/hydroponics/grown/towercap.dm b/code/modules/hydroponics/grown/towercap.dm index 701c7387ce1b..5c1aa0d40013 100644 --- a/code/modules/hydroponics/grown/towercap.dm +++ b/code/modules/hydroponics/grown/towercap.dm @@ -121,7 +121,7 @@ name = "steel-cap log" desc = "It's made of metal." icon_state = "steellogs" - plank_type = /obj/item/stack/rods + plank_type = /obj/item/stack/sheet/iron plank_name = "rods" /obj/item/grown/log/steel/CheckAccepted(obj/item/I) diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm index 434666ff7546..e12c0378b433 100644 --- a/code/modules/hydroponics/hydroitemdefines.dm +++ b/code/modules/hydroponics/hydroitemdefines.dm @@ -49,15 +49,20 @@ return NONE /// When we attack something, first - try to scan something we hit with left click. Left-clicking uses scans for stats -/obj/item/plant_analyzer/pre_attack(atom/target, mob/living/user) +/obj/item/plant_analyzer/afterattack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() + if(!can_see(user, target, 7)) + return if((user.istate & ISTATE_HARM) || !user.can_read(src)) return return do_plant_stats_scan(target, user) /// Same as above, but with right click. Right-clicking scans for chemicals. -/obj/item/plant_analyzer/pre_attack_secondary(atom/target, mob/living/user) +/obj/item/plant_analyzer/afterattack_secondary(atom/target, mob/user, proximity_flag, click_parameters) + if(!can_see(user, target, 7)) + return + if((user.istate & ISTATE_HARM) || !user.can_read(src)) return SECONDARY_ATTACK_CONTINUE_CHAIN @@ -73,8 +78,9 @@ * returns TRUE if we can scan the object, and outputs the message to the USER. */ /obj/item/plant_analyzer/proc/do_plant_stats_scan(atom/scan_target, mob/user) - if(istype(scan_target, /obj/machinery/hydroponics)) - to_chat(user, examine_block(scan_tray_stats(scan_target))) + if(scan_target.GetComponent(/datum/component/plant_growing)) + var/obj/item/seeds/seed = locate(/obj/item/seeds) in scan_target.contents + to_chat(user, examine_block(scan_tray_stats(seed, scan_target.GetComponent(/datum/component/plant_growing)))) return TRUE if(istype(scan_target, /obj/structure/glowshroom)) var/obj/structure/glowshroom/shroom_plant = scan_target @@ -106,8 +112,9 @@ * returns TRUE if we can scan the object, and outputs the message to the USER. */ /obj/item/plant_analyzer/proc/do_plant_chem_scan(atom/scan_target, mob/user) - if(istype(scan_target, /obj/machinery/hydroponics)) - to_chat(user, examine_block(scan_tray_chems(scan_target))) + if(scan_target.GetComponent(/datum/component/plant_growing)) + var/obj/item/seeds/seed = locate(/obj/item/seeds) in scan_target.contents + to_chat(user, examine_block(scan_tray_chems(scan_target, seed))) return TRUE if(istype(scan_target, /obj/structure/glowshroom)) var/obj/structure/glowshroom/shroom_plant = scan_target @@ -166,24 +173,21 @@ * * Returns the formatted message as text. */ -/obj/item/plant_analyzer/proc/scan_tray_stats(obj/machinery/hydroponics/scanned_tray) +/obj/item/plant_analyzer/proc/scan_tray_stats(obj/item/seeds/seed, datum/component/plant_growing/growing) var/returned_message = "" - if(scanned_tray.myseed) - returned_message += "[span_bold("[scanned_tray.myseed.plantname]")]" - returned_message += "\nPlant Age: [span_notice("[scanned_tray.age]")]" - returned_message += "\nPlant Health: [span_notice("[scanned_tray.plant_health]")]" - returned_message += scan_plant_stats(scanned_tray.myseed, TRUE) + if(seed) + var/datum/component/growth_information/info = seed.GetComponent(/datum/component/growth_information) + returned_message += "[span_bold("[seed.plantname]")]" + returned_message += "\nPlant Age: [span_notice("[info.age]")]" + returned_message += "\nPlant Health: [span_notice("[info.health_value]")]" + returned_message += scan_plant_stats(seed, TRUE) returned_message += "\nGrowth medium" else returned_message += span_bold("No plant found.") - returned_message += "\nWeed level: [span_notice("[scanned_tray.weedlevel] / [MAX_TRAY_WEEDS]")]" - returned_message += "\nPest level: [span_notice("[scanned_tray.pestlevel] / [MAX_TRAY_PESTS]")]" - returned_message += "\nToxicity level: [span_notice("[scanned_tray.toxic] / [MAX_TRAY_TOXINS]")]" - returned_message += "\nWater level: [span_notice("[scanned_tray.waterlevel] / [scanned_tray.maxwater]")]" - returned_message += "\nNutrition level: [span_notice("[round(scanned_tray.reagents.total_volume)] / [scanned_tray.maxnutri]")] Right-click to empty." - if(scanned_tray.yieldmod != 1) - returned_message += "\nYield modifier on harvest: [span_notice("[scanned_tray.yieldmod]x")]" + returned_message += "\nWeed level: [span_notice("[growing.weed_level] / [MAX_TRAY_WEEDS]")]" + returned_message += "\nPest level: [span_notice("[growing.pest_level] / [MAX_TRAY_PESTS]")]" + returned_message += "\nToxicity level: [span_notice("[growing.toxicity_contents]")]" return span_info(returned_message) @@ -195,20 +199,21 @@ * * Returns the formatted message as text. */ -/obj/item/plant_analyzer/proc/scan_tray_chems(obj/machinery/hydroponics/scanned_tray) +/obj/item/plant_analyzer/proc/scan_tray_chems(atom/movable/scanned_tray, obj/item/seeds/seed) var/returned_message = "" - if(scanned_tray.myseed) - returned_message += "[span_bold("[scanned_tray.myseed.plantname]")]" - returned_message += "\nPlant Age: [span_notice("[scanned_tray.age]")]" - returned_message += "\nPlant Growth: [round(((scanned_tray.growth * (1.01 ** -scanned_tray.myseed.maturation)) / scanned_tray.myseed.harvest_age) * 100, 0.1)]%" - returned_message += scan_plant_chems(scanned_tray.myseed, TRUE) + if(seed) + var/datum/component/growth_information/info = seed.GetComponent(/datum/component/growth_information) + returned_message += "[span_bold("[seed.plantname]")]" + returned_message += "\nPlant Age: [span_notice("[info.age]")]" + returned_message += "\nPlant Growth: [round(((info.growth_cycle * (1.01 ** -seed.maturation)) / seed.harvest_age) * 100, 0.1)]%" + returned_message += scan_plant_chems(seed, TRUE) else returned_message += span_bold("No plant found.") returned_message += "\nGrowth medium contains:" if(scanned_tray.reagents.reagent_list.len) for(var/datum/reagent/reagent_id in scanned_tray.reagents.reagent_list) - returned_message += "\n[span_notice("• [reagent_id.volume] / [scanned_tray.maxnutri] units of [reagent_id]")]" + returned_message += "\n[span_notice("• [reagent_id.volume] units of [reagent_id]")]" else returned_message += "\n[span_notice("No reagents found.")]" @@ -638,7 +643,7 @@ /obj/item/reagent_containers/cup/bottle/nutrient/l4z name = "bottle of Left 4 Zed" - desc = "Contains a fertilizer that lightly heals the plant but causes significant mutations in plants over generations." + desc = "Contains a fertilizer that quickly wilts the plant in exchange for the plant gaining all stats." list_reagents = list(/datum/reagent/plantnutriment/left4zednutriment = 50) /obj/item/reagent_containers/cup/bottle/nutrient/rh diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 72eb6b54e398..5ac57538d8a4 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -480,7 +480,6 @@ /obj/machinery/hydroponics/proc/update_plant_overlay() var/mutable_appearance/plant_overlay = mutable_appearance(myseed.growing_icon, layer = OBJ_LAYER + 0.01) - plant_overlay.pixel_y = myseed.seed_offset switch(plant_status) if(HYDROTRAY_PLANT_DEAD) plant_overlay.icon_state = myseed.icon_dead @@ -656,7 +655,7 @@ if(8 to 9) new_seed = new /obj/item/seeds/chanter(src) if(6 to 7) - new_seed = new /obj/item/seeds/tower(src) + new_seed = new /obj/item/seeds/tree(src) if(4 to 5) new_seed = new /obj/item/seeds/plump(src) else diff --git a/code/modules/hydroponics/hydroponics_chemreact.dm b/code/modules/hydroponics/hydroponics_chemreact.dm index 4d9ee050fae4..44e3362811ad 100644 --- a/code/modules/hydroponics/hydroponics_chemreact.dm +++ b/code/modules/hydroponics/hydroponics_chemreact.dm @@ -7,10 +7,6 @@ if(myseed) myseed.on_chem_reaction(reagents) //In case seeds have some special interactions with special chems, currently only used by vines - for(var/c in reagents.reagent_list) - var/datum/reagent/chem = c - chem.on_hydroponics_apply(myseed, reagents, src, user) - /obj/machinery/hydroponics/proc/mutation_roll(mob/user) switch(rand(100)) diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm index 47fa445a4bcc..b86e545dbc5c 100644 --- a/code/modules/hydroponics/seeds.dm +++ b/code/modules/hydroponics/seeds.dm @@ -71,8 +71,6 @@ var/list/infusion_mutations = list() ///infusion damage var/infusion_damage = 0 - /// How many pixels on the Y axis this plant should be shifted. - var/seed_offset = 0 /obj/item/seeds/Initialize(mapload, nogenes = FALSE) . = ..() @@ -97,7 +95,7 @@ var/list/generated_infusions = list() for(var/datum/hydroponics/plant_mutation/infusion/listed_item as anything in infusion_mutations) var/datum/hydroponics/plant_mutation/infusion/created_list_item = new listed_item - generated_mutations += created_list_item + generated_infusions += created_list_item infusion_mutations = generated_infusions if(!nogenes) // not used on Copy() @@ -184,7 +182,7 @@ copy_seed.icon_dead = icon_dead copy_seed.growthstages = growthstages copy_seed.growing_icon = growing_icon - copy_seed.seed_offset = seed_offset + copy_seed.plant_icon_offset = plant_icon_offset copy_seed.traits_in_progress = traits_in_progress if(istype(src, /obj/item/seeds/spliced)) @@ -233,10 +231,6 @@ /obj/item/seeds/bullet_act(obj/projectile/Proj) //Works with the Somatoray to modify plant variables. if(istype(Proj, /obj/projectile/energy/flora/yield)) var/rating = 1 - if(istype(loc, /obj/machinery/hydroponics)) - var/obj/machinery/hydroponics/H = loc - rating = H.rating - if(yield == 0)//Oh god don't divide by zero you'll doom us all. adjust_yield(1 * rating) else if(prob(1/(yield * yield) * 100))//This formula gives you diminishing returns based on yield. 100% with 1 yield, decreasing to 25%, 11%, 6, 4, 2... @@ -247,21 +241,12 @@ // Harvest procs /obj/item/seeds/proc/getYield() - var/return_yield = yield - - var/obj/machinery/hydroponics/parent = loc - if(istype(loc, /obj/machinery/hydroponics)) - if(parent.yieldmod == 0) - return_yield = min(return_yield, 1)//1 if above zero, 0 otherwise - else - return_yield *= (parent.yieldmod) - - return return_yield + return yield /obj/item/seeds/proc/harvest(mob/user) ///Reference to the tray/soil the seeds are planted in. - var/obj/machinery/hydroponics/parent = loc //for ease of access + var/atom/movable/parent = loc //for ease of access ///Count used for creating the correct amount of results to the harvest. var/t_amount = 0 ///List of plants all harvested from the same batch. @@ -298,20 +283,20 @@ t_amount++ continue t_prod = new product(output_loc, src) - if(parent.myseed.plantname != initial(parent.myseed.plantname)) - t_prod.name = parent.myseed.plantname - t_prod.seed.name = parent.myseed.name - t_prod.seed.desc = parent.myseed.desc - t_prod.seed.plantname = parent.myseed.plantname + if(plantname != initial(plantname)) + t_prod.name = plantname + if(istype(t_prod)) + t_prod.seed.name = name + t_prod.seed.desc = desc + t_prod.seed.plantname = plantname result.Add(t_prod) // User gets a consumable if(!t_prod) return t_amount++ - product_name = t_prod.seed.plantname + if(istype(t_prod)) + product_name = t_prod.seed.plantname if(product_count >= 1) SSblackbox.record_feedback("tally", "food_harvested", product_count, product_name) - parent.update_tray(user, product_count) - parent.update_overlays() return result /** diff --git a/code/modules/hydroponics/unique_plant_genes.dm b/code/modules/hydroponics/unique_plant_genes.dm index 560a5dc29c96..0cc9a3188258 100644 --- a/code/modules/hydroponics/unique_plant_genes.dm +++ b/code/modules/hydroponics/unique_plant_genes.dm @@ -643,7 +643,7 @@ * our_seed - the seed growing * grown_tray - the tray we were planted in */ -/datum/plant_gene/trait/gas_production/proc/set_home_tray(obj/item/seeds/our_seed, obj/machinery/hydroponics/grown_tray) +/datum/plant_gene/trait/gas_production/proc/set_home_tray(obj/item/seeds/our_seed, atom/movable/grown_tray) SIGNAL_HANDLER home_tray = WEAKREF(grown_tray) @@ -654,10 +654,11 @@ * our_seed - the seed growing * grown_tray - the tray, we're currently growing within */ -/datum/plant_gene/trait/gas_production/proc/try_release_gas(obj/item/seeds/our_seed, obj/machinery/hydroponics/grown_tray) +/datum/plant_gene/trait/gas_production/proc/try_release_gas(obj/item/seeds/our_seed, atom/movable/grown_tray) SIGNAL_HANDLER - if(grown_tray.age < our_seed.maturation) // Start a little before it blooms + var/datum/component/growth_information/info = our_seed.GetComponent(/datum/component/growth_information) + if(info.growth_precent < 90) return START_PROCESSING(SSobj, src) @@ -675,7 +676,7 @@ */ /datum/plant_gene/trait/gas_production/process(seconds_per_tick) var/obj/item/seeds/seed = stinky_seed?.resolve() - var/obj/machinery/hydroponics/tray = home_tray?.resolve() + var/atom/movable/tray = home_tray?.resolve() // If our weakrefs don't resolve, or if our seed is /somehow/ not in the tray it was planted in, stop processing. if(!seed || !tray || seed.loc != tray) diff --git a/code/modules/industrial_lift/industrial_lift.dm b/code/modules/industrial_lift/industrial_lift.dm index 7b8efe14f9e6..b495fc21228a 100644 --- a/code/modules/industrial_lift/industrial_lift.dm +++ b/code/modules/industrial_lift/industrial_lift.dm @@ -112,11 +112,11 @@ GLOBAL_LIST_INIT(all_radial_directions, list( /obj/structure/industrial_lift/proc/set_movement_registrations(list/turfs_to_set) for(var/turf/turf_loc as anything in turfs_to_set || locs) RegisterSignal(turf_loc, COMSIG_ATOM_EXITED, PROC_REF(UncrossedRemoveItemFromLift)) - RegisterSignals(turf_loc, list(COMSIG_ATOM_ENTERED,COMSIG_ATOM_INITIALIZED_ON), PROC_REF(AddItemOnLift)) + RegisterSignals(turf_loc, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON), PROC_REF(AddItemOnLift)) ///unset our movement registrations from turfs that no longer contain us (or every loc if turfs_to_unset is unspecified) /obj/structure/industrial_lift/proc/unset_movement_registrations(list/turfs_to_unset) - var/static/list/registrations = list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_EXITED, COMSIG_ATOM_INITIALIZED_ON) + var/static/list/registrations = list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_EXITED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON) for(var/turf/turf_loc as anything in turfs_to_unset || locs) UnregisterSignal(turf_loc, registrations) diff --git a/code/modules/jobs/job_types/janitor.dm b/code/modules/jobs/job_types/janitor.dm index a23043c5c7f2..aca74c5be957 100644 --- a/code/modules/jobs/job_types/janitor.dm +++ b/code/modules/jobs/job_types/janitor.dm @@ -40,6 +40,7 @@ uniform = /obj/item/clothing/under/rank/civilian/janitor belt = /obj/item/modular_computer/pda/janitor ears = /obj/item/radio/headset/headset_srv + skillchips = list(/obj/item/skillchip/job/janitor) /datum/outfit/job/janitor/pre_equip(mob/living/carbon/human/human_equipper, visuals_only) . = ..() diff --git a/code/modules/library/skill_learning/job_skillchips/janitor.dm b/code/modules/library/skill_learning/job_skillchips/janitor.dm new file mode 100644 index 000000000000..22109bbed29c --- /dev/null +++ b/code/modules/library/skill_learning/job_skillchips/janitor.dm @@ -0,0 +1,9 @@ +/obj/item/skillchip/job/janitor + name = "CL34NM4ST.R skillchip" + desc = "Become a cleanbot whisperer." + auto_traits = list(TRAIT_CLEANBOT_WHISPERER) + skill_name = "Voice Of The Voiceless" + skill_description = "Gain the affection of all thankless, hardworking cleanbots on the station." + skill_icon = "broom" + activate_message = span_notice("You start feeling empathetic towards all the cleanbots on the station.") + deactivate_message = span_notice("You forget why you felt any sympathy towards the cleanbots, they are just robots after all.") diff --git a/code/modules/logging/log_category.dm b/code/modules/logging/log_category.dm index 359f9d47f249..c2bfd28f7a1d 100644 --- a/code/modules/logging/log_category.dm +++ b/code/modules/logging/log_category.dm @@ -31,7 +31,7 @@ GENERAL_PROTECT_DATUM(/datum/log_category) /// Add an entry to this category. It is very important that any data you provide doesn't hold references to anything! -/datum/log_category/proc/create_entry(message, list/data, list/semver_store) +/datum/log_category/proc/create_entry(message, list/data, list/semver_store, severity) var/datum/log_entry/entry = new( // world state contains raw timestamp timestamp = logger.human_readable_timestamp(), @@ -39,6 +39,7 @@ GENERAL_PROTECT_DATUM(/datum/log_category) message = message, data = data, semver_store = semver_store, + severity = severity ) write_entry(entry) diff --git a/code/modules/logging/log_entry.dm b/code/modules/logging/log_entry.dm index df87f9330987..a1e4d0a8f60c 100644 --- a/code/modules/logging/log_entry.dm +++ b/code/modules/logging/log_entry.dm @@ -27,18 +27,22 @@ /// Data of the log entry; optional. var/list/data + ///severity level of this log + var/severity = "info" + /// Semver store of the log entry, used to store the schema of data entries var/list/semver_store GENERAL_PROTECT_DATUM(/datum/log_entry) -/datum/log_entry/New(timestamp, category, message, list/data, list/semver_store) +/datum/log_entry/New(timestamp, category, message, list/data, list/semver_store, severity = "info") ..() src.id = next_id++ src.timestamp = timestamp src.category = category src.message = message + src.severity = severity with_data(data) with_semver_store(semver_store) @@ -74,6 +78,7 @@ GENERAL_PROTECT_DATUM(/datum/log_entry) // I do not trust byond's json encoder, and need to ensure the order doesn't change. var/list/json_entries = list() MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_TIMESTAMP, timestamp) + MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_ROUNDID, GLOB.round_id) // monkestation edit MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_CATEGORY, category) MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_MESSAGE, message) MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_DATA, data) @@ -81,6 +86,7 @@ GENERAL_PROTECT_DATUM(/datum/log_entry) MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_SEMVER_STORE, semver_store) MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_ID, id) MANUAL_JSON_ENTRY(json_entries, LOG_ENTRY_KEY_SCHEMA_VERSION, schema_version) + MANUAL_JSON_ENTRY(json_entries, "level", severity) return "{[json_entries.Join(",")]}" #undef MANUAL_JSON_ENTRY diff --git a/code/modules/logging/log_holder.dm b/code/modules/logging/log_holder.dm index 37f2ca4cae60..ecfdf5d7851c 100644 --- a/code/modules/logging/log_holder.dm +++ b/code/modules/logging/log_holder.dm @@ -293,7 +293,7 @@ GENERAL_PROTECT_DATUM(/datum/log_holder) /// Adds an entry to the given category, if the category is disabled it will not be logged. /// If the category does not exist, we will CRASH and log to the error category. /// the data list is optional and will be recursively json serialized. -/datum/log_holder/proc/Log(category, message, list/data) +/datum/log_holder/proc/Log(category, message, list/data, severity = "info") // This is Log because log is a byond internal proc if(shutdown) return @@ -327,7 +327,7 @@ GENERAL_PROTECT_DATUM(/datum/log_holder) if(length(data)) semver_store = list() data = recursive_jsonify(data, semver_store) - log_category.create_entry(message, data, semver_store) + log_category.create_entry(message, data, semver_store, severity) /// Recursively converts an associative list of datums into their jsonified(list) form /datum/log_holder/proc/recursive_jsonify(list/data_list, list/semvers) diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm b/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm index 490feb4282ed..e178a197e0e4 100644 --- a/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm +++ b/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm @@ -190,7 +190,7 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate) M.playsound_local(T, null, 100, FALSE, 0, FALSE, pressure_affected = FALSE, sound_to_use = legion_sound) flash_color(M, flash_color = "#FF0000", flash_time = 50) var/mutable_appearance/release_overlay = mutable_appearance('icons/effects/effects.dmi', "legiondoor") - notify_ghosts("Legion has been released in the [get_area(src)]!", source = src, alert_overlay = release_overlay, action = NOTIFY_JUMP, flashwindow = FALSE) + notify_ghosts("Legion has been released in the [get_area(src)]!", source = src, alert_overlay = release_overlay, action = NOTIFY_JUMP, flashwindow = FALSE, header="Something Interesting!") /obj/effect/decal/necropolis_gate_decal icon = 'icons/effects/96x96.dmi' @@ -240,10 +240,6 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate) /obj/structure/necropolis_arch/singularity_pull() return 0 -#define STABLE 0 //The tile is stable and won't collapse/sink when crossed. -#define COLLAPSE_ON_CROSS 1 //The tile is unstable and will temporary become unusable when crossed. -#define DESTROY_ON_CROSS 2 //The tile is nearly broken and will permanently become unusable when crossed. -#define UNIQUE_EFFECT 3 //The tile has some sort of unique effect when crossed. //stone tiles for boss arenas /obj/structure/stone_tile name = "stone tile" @@ -255,67 +251,17 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate) resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF var/tile_key = "pristine_tile" var/tile_random_sprite_max = 24 - var/fall_on_cross = STABLE //If the tile has some sort of effect when crossed - var/fallen = FALSE //If the tile is unusable - var/falling = FALSE //If the tile is falling /obj/structure/stone_tile/Initialize(mapload) . = ..() icon_state = "[tile_key][rand(1, tile_random_sprite_max)]" - var/static/list/loc_connections = list( - COMSIG_ATOM_ENTERED = PROC_REF(on_entered), - ) - AddElement(/datum/element/connect_loc, loc_connections) -/obj/structure/stone_tile/singularity_pull() - return + var/static/list/give_turf_traits + if(!give_turf_traits) + give_turf_traits = string_list(list(TRAIT_LAVA_STOPPED, TRAIT_CHASM_STOPPED)) + AddElement(/datum/element/give_turf_traits, give_turf_traits) -/obj/structure/stone_tile/proc/on_entered(datum/source, atom/movable/AM) - SIGNAL_HANDLER - if(falling || fallen) - return - var/turf/T = get_turf(src) - if(!islava(T) && !ischasm(T)) //nothing to sink or fall into - return - var/obj/item/I - if(isitem(AM)) - I = AM - var/mob/living/L - if(isliving(AM)) - L = AM - switch(fall_on_cross) - if(COLLAPSE_ON_CROSS, DESTROY_ON_CROSS) - if((I && I.w_class >= WEIGHT_CLASS_BULKY) || (L && !(L.movement_type & FLYING) && L.mob_size >= MOB_SIZE_HUMAN)) //too heavy! too big! aaah! - INVOKE_ASYNC(src, PROC_REF(collapse)) - if(UNIQUE_EFFECT) - crossed_effect(AM) - -/obj/structure/stone_tile/proc/collapse() - falling = TRUE - var/break_that_sucker = fall_on_cross == DESTROY_ON_CROSS - playsound(src, 'sound/effects/pressureplate.ogg', 50, TRUE) - Shake(-1, -1, 25) - sleep(0.5 SECONDS) - if(break_that_sucker) - playsound(src, 'sound/effects/break_stone.ogg', 50, TRUE) - else - playsound(src, 'sound/mecha/mechmove04.ogg', 50, TRUE) - animate(src, alpha = 0, pixel_y = pixel_y - 3, time = 5) - fallen = TRUE - if(break_that_sucker) - QDEL_IN(src, 10) - else - addtimer(CALLBACK(src, PROC_REF(rebuild)), 55) - -/obj/structure/stone_tile/proc/rebuild() - pixel_x = initial(pixel_x) - pixel_y = initial(pixel_y) - 5 - animate(src, alpha = initial(alpha), pixel_x = initial(pixel_x), pixel_y = initial(pixel_y), time = 30) - sleep(3 SECONDS) - falling = FALSE - fallen = FALSE - -/obj/structure/stone_tile/proc/crossed_effect(atom/movable/AM) +/obj/structure/stone_tile/singularity_pull() return /obj/structure/stone_tile/block @@ -411,8 +357,3 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate) name = "burnt stone surrounding tile" icon_state = "burnt_surrounding_tile1" tile_key = "burnt_surrounding_tile" - -#undef STABLE -#undef COLLAPSE_ON_CROSS -#undef DESTROY_ON_CROSS -#undef UNIQUE_EFFECT diff --git a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm index fc79c82e780e..bea6c8cce77f 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm @@ -70,8 +70,8 @@ /obj/item/organ/internal/alien/plasmavessel = 5, /obj/item/organ/internal/heart/gland/chem = 5, /obj/item/organ/internal/heart/gland/mindshock = 5, - /obj/item/organ/internal/heart/gland/spiderman = 5, - /obj/item/organ/internal/heart/gland/transform = 5, + // /obj/item/organ/internal/heart/gland/spiderman = 5, /* monkestation: removed */ + // /obj/item/organ/internal/heart/gland/transform = 5, /* monkestation: removed */ /obj/item/organ/internal/heart/gland/slime = 4, /obj/item/organ/internal/heart/gland/trauma = 4, /obj/item/organ/internal/heart/carp = 3, diff --git a/code/modules/mapping/reader.dm b/code/modules/mapping/reader.dm index f4f4b985c39b..ca91c58847fb 100644 --- a/code/modules/mapping/reader.dm +++ b/code/modules/mapping/reader.dm @@ -1067,7 +1067,6 @@ GLOBAL_LIST_EMPTY(map_model_default) return text /datum/parsed_map/Destroy() - ..() SSatoms.map_loader_stop(REF(src)) // Just in case, I don't want to double up here if(turf_blacklist) turf_blacklist.Cut() @@ -1075,7 +1074,7 @@ GLOBAL_LIST_EMPTY(map_model_default) bounds.Cut() grid_models.Cut() gridSets.Cut() - return QDEL_HINT_HARDDEL_NOW + return ..() #undef MAP_DMM #undef MAP_TGM diff --git a/code/modules/mining/equipment/monster_organs/regenerative_core.dm b/code/modules/mining/equipment/monster_organs/regenerative_core.dm index 3f7c2058be82..f1ac86146793 100644 --- a/code/modules/mining/equipment/monster_organs/regenerative_core.dm +++ b/code/modules/mining/equipment/monster_organs/regenerative_core.dm @@ -14,16 +14,16 @@ /obj/item/organ/internal/monster_core/regenerative_core/preserve(implanted = FALSE) if (implanted) - SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "implanted")) + SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[initial(name)]", "implanted")) else - SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "stabilizer")) + SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[initial(name)]]", "stabilizer")) return ..() /obj/item/organ/internal/monster_core/regenerative_core/go_inert() . = .. () if (!.) return - SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "inert")) + SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[initial(name)]]", "inert")) /obj/item/organ/internal/monster_core/regenerative_core/on_life(seconds_per_tick, times_fired) . = ..() @@ -39,10 +39,10 @@ target.add_mood_event(MOOD_CATEGORY_LEGION_CORE, /datum/mood_event/healsbadman) if (target != user) target.visible_message(span_notice("[user] forces [target] to apply [src]... Black tendrils entangle and reinforce [target.p_them()]!")) - SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "other")) + SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[initial(name)]]", "used", "other")) else to_chat(user, span_notice("You start to smear [src] on yourself. Disgusting tendrils hold you together and allow you to keep moving, but for how long?")) - SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[type]", "used", "self")) + SSblackbox.record_feedback("nested tally", "hivelord_core", 1, list("[initial(name)]]", "used", "self")) return ..() /// Different graphics/desc for the lavaland legion diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index fe15d628fd2b..0681824bd2f4 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -24,12 +24,12 @@ /obj/machinery/mineral/proc/register_input_turf() input_turf = get_step(src, input_dir) if(input_turf) // make sure there is actually a turf - RegisterSignals(input_turf, list(COMSIG_ATOM_INITIALIZED_ON, COMSIG_ATOM_ENTERED), PROC_REF(pickup_item)) + RegisterSignals(input_turf, list(COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON, COMSIG_ATOM_ENTERED), PROC_REF(pickup_item)) /// Unregisters signals that are registered the machine's input turf, if it has one. /obj/machinery/mineral/proc/unregister_input_turf() if(input_turf) - UnregisterSignal(input_turf, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_INITIALIZED_ON)) + UnregisterSignal(input_turf, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON)) /obj/machinery/mineral/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) . = ..() diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm index 0fe63f7479bd..87730e89675c 100644 --- a/code/modules/mob/dead/dead.dm +++ b/code/modules/mob/dead/dead.dm @@ -91,16 +91,20 @@ INITIALIZE_IMMEDIATE(/mob/dead) #undef SERVER_HOPPER_TRAIT -/mob/dead/proc/update_z(new_z) // 1+ to register, null to unregister - if (registered_z != new_z) - if (registered_z) - SSmobs.dead_players_by_zlevel[registered_z] -= src - if (client) - if (new_z) - SSmobs.dead_players_by_zlevel[new_z] += src - registered_z = new_z - else - registered_z = null +/** + * updates the Z level for dead players + * If they don't have a new z, we'll keep the old one, preventing bugs from ghosting and re-entering, among others + */ +/mob/dead/proc/update_z(new_z) + if(registered_z == new_z) + return + if(registered_z) + SSmobs.dead_players_by_zlevel[registered_z] -= src + if(isnull(client)) + registered_z = null + return + registered_z = new_z + SSmobs.dead_players_by_zlevel[new_z] += src /mob/dead/Login() . = ..() diff --git a/code/modules/mob/dead/observer/login.dm b/code/modules/mob/dead/observer/login.dm index 4eb50f8ac1fb..d5f2ab6b5db0 100644 --- a/code/modules/mob/dead/observer/login.dm +++ b/code/modules/mob/dead/observer/login.dm @@ -14,10 +14,6 @@ preferred_form = client.prefs.read_preference(/datum/preference/choiced/ghost_form) ghost_orbit = client.prefs.read_preference(/datum/preference/choiced/ghost_orbit) - var/turf/T = get_turf(src) - if (isturf(T)) - update_z(T.z) - update_icon(ALL, preferred_form) updateghostimages() lighting_cutoff = default_lighting_cutoff() diff --git a/code/modules/mob/dead/observer/logout.dm b/code/modules/mob/dead/observer/logout.dm index 4ba701c0ae09..53db92d91e32 100644 --- a/code/modules/mob/dead/observer/logout.dm +++ b/code/modules/mob/dead/observer/logout.dm @@ -1,5 +1,4 @@ /mob/dead/observer/Logout() - update_z(null) if (client) client.images -= (GLOB.ghost_images_default+GLOB.ghost_images_simple) diff --git a/code/modules/mob/living/basic/bots/_bots.dm b/code/modules/mob/living/basic/bots/_bots.dm new file mode 100644 index 000000000000..b0705cf82165 --- /dev/null +++ b/code/modules/mob/living/basic/bots/_bots.dm @@ -0,0 +1,829 @@ +GLOBAL_LIST_INIT(command_strings, list( + "patroloff" = "STOP PATROL", + "patrolon" = "START PATROL", + "stop" = "STOP", + "go" = "GO", + "home" = "RETURN HOME", +)) + + +/mob/living/basic/bot + icon = 'icons/mob/silicon/aibots.dmi' + layer = MOB_LAYER + gender = NEUTER + mob_biotypes = MOB_ROBOTIC + basic_mob_flags = DEL_ON_DEATH + icon = 'icons/mob/silicon/aibots.dmi' + icon_state = "medibot0" + base_icon_state = "medibot" + damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) + habitable_atmos = list("min_oxy" = 0, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD, DIAG_BATT_HUD, DIAG_PATH_HUD = HUD_LIST_LIST) + maximum_survivable_temperature = INFINITY + minimum_survivable_temperature = 0 + has_unlimited_silicon_privilege = TRUE + sentience_type = SENTIENCE_ARTIFICIAL + status_flags = NONE //no default canpush + faction = list(FACTION_MINING) + ai_controller = /datum/ai_controller/basic_controller/bot + pass_flags = PASSFLAPS + verb_say = "states" + verb_ask = "queries" + verb_exclaim = "declares" + verb_yell = "alarms" + initial_language_holder = /datum/language_holder/synthetic + bubble_icon = "machine" + speech_span = SPAN_ROBOT + faction = list(FACTION_NEUTRAL, FACTION_SILICON, FACTION_TURRET) + light_system = OVERLAY_LIGHT + light_outer_range = 3 + light_power = 0.9 + speed = 3 + ///Access required to access this Bot's maintenance protocols + var/maints_access_required = list(ACCESS_ROBOTICS) + ///The Robot arm attached to this robot - has a 50% chance to drop on death. + var/robot_arm = /obj/item/bodypart/arm/right/robot + ///The inserted (if any) pAI in this bot. + var/obj/item/pai_card/paicard + ///The type of bot it is, for radio control. + var/bot_type = NONE + ///All initial access this bot started with. + var/list/initial_access = list() + ///Bot-related mode flags on the Bot indicating how they will act. BOT_MODE_ON | BOT_MODE_AUTOPATROL | BOT_MODE_REMOTE_ENABLED | BOT_MODE_CAN_BE_SAPIENT | BOT_MODE_ROUNDSTART_POSSESSION + var/bot_mode_flags = BOT_MODE_ON | BOT_MODE_REMOTE_ENABLED | BOT_MODE_GHOST_CONTROLLABLE | BOT_MODE_ROUNDSTART_POSSESSION + ///Bot-related cover flags on the Bot to deal with what has been done to their cover, including emagging. BOT_MAINTS_PANEL_OPEN | BOT_CONTROL_PANEL_OPEN | BOT_COVER_EMAGGED | BOT_COVER_HACKED + var/bot_access_flags = NONE + ///Small name of what the bot gets messed with when getting hacked/emagged. + var/hackables = "system circuits" + ///Standardizes the vars that indicate the bot is busy with its function. + var/mode = BOT_IDLE + ///Links a bot to the AI calling it. + var/datum/weakref/calling_ai_ref + ///The bot's radio, for speaking to people. + var/obj/item/radio/internal_radio + ///which channels can the bot listen to + var/radio_key = null + ///The bot's default radio channel + var/radio_channel = RADIO_CHANNEL_COMMON + ///our access card + var/obj/item/card/id/access_card + ///The trim type that will grant additional acces + var/datum/id_trim/additional_access + ///file the path icon is stored in + var/path_image_icon = 'icons/mob/silicon/aibots.dmi' + ///state of the path icon + var/path_image_icon_state = "path_indicator" + ///what color this path icon will use + var/path_image_color = "#FFFFFF" + ///list of all layed path icons + var/list/current_pathed_turfs = list() + + ///The type of data HUD the bot uses. Diagnostic by default. + var/data_hud_type = DATA_HUD_DIAGNOSTIC_BASIC + /// If true we will allow ghosts to control this mob + var/can_be_possessed = FALSE + /// Message to display upon possession + var/possessed_message = "You're a generic bot. How did one of these even get made?" + /// Action we use to say voice lines out loud, also we just pass anything we try to say through here just in case it plays a voice line + var/datum/action/cooldown/bot_announcement/pa_system + /// Type of bot_announcement ability we want + var/announcement_type + ///list of traits we apply and remove when turning on/off + var/static/list/on_toggle_traits = list( + TRAIT_INCAPACITATED, + TRAIT_IMMOBILIZED, + TRAIT_HANDS_BLOCKED, + ) + /// If true we will offer this + COOLDOWN_DECLARE(offer_ghosts_cooldown) + +/mob/living/basic/bot/Initialize(mapload) + . = ..() + + AddElement(/datum/element/relay_attackers) + RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(handle_loop_movement)) + RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(after_attacked)) + RegisterSignal(src, COMSIG_MOB_TRIED_ACCESS, PROC_REF(attempt_access)) + ADD_TRAIT(src, TRAIT_NO_GLIDE, INNATE_TRAIT) + GLOB.bots_list += src + + // Give bots a fancy new ID card that can hold any access. + access_card = new /obj/item/card/id/advanced/simple_bot(src) + // This access is so bots can be immediately set to patrol and leave Robotics, instead of having to be let out first. + access_card.set_access(list(ACCESS_ROBOTICS)) + provide_additional_access() + + internal_radio = new /obj/item/radio(src) + if(radio_key) + internal_radio.keyslot = new radio_key + internal_radio.subspace_transmission = TRUE + internal_radio.canhear_range = 0 // anything greater will have the bot broadcast the channel as if it were saying it out loud. + internal_radio.recalculateChannels() + + //Adds bot to the diagnostic HUD system + prepare_huds() + for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) + diag_hud.add_atom_to_hud(src) + diag_hud_set_bothealth() + diag_hud_set_botstat() + diag_hud_set_botmode() + + //If a bot has its own HUD (for player bots), provide it. + if(!isnull(data_hud_type)) + var/datum/atom_hud/datahud = GLOB.huds[data_hud_type] + datahud.show_to(src) + + if(HAS_TRAIT(SSstation, STATION_TRAIT_BOTS_GLITCHED)) + randomize_language_if_on_station() + + if(mapload && is_station_level(z) && (bot_mode_flags & BOT_MODE_GHOST_CONTROLLABLE) && (bot_mode_flags & BOT_MODE_ROUNDSTART_POSSESSION)) + enable_possession(mapload = mapload) + + pa_system = (isnull(announcement_type)) ? new(src, automated_announcements = generate_speak_list()) : new announcement_type(src, automated_announcements = generate_speak_list()) + pa_system.Grant(src) + ai_controller.set_blackboard_key(BB_ANNOUNCE_ABILITY, pa_system) + ai_controller.set_blackboard_key(BB_RADIO_CHANNEL, radio_channel) + update_appearance() + +/mob/living/basic/bot/proc/get_mode() + if(client) //Player bots do not have modes, thus the override. Also an easy way for PDA users/AI to know when a bot is a player. + return span_bold("[paicard ? "pAI Controlled" : "Autonomous"]") + + if(!(bot_mode_flags & BOT_MODE_ON)) + return span_bad("Inactive") + + return span_average("[mode]") + +/** + * Returns a status string about the bot's current status, if it's moving, manually controlled, or idle. + */ +/mob/living/basic/bot/proc/get_mode_ui() + if(client) + return paicard ? "pAI Controlled" : "Autonomous" + + if(!(bot_mode_flags & BOT_MODE_ON)) + return "Inactive" + + return "[mode]" + +/** + * Returns a string of flavor text for emagged bots as defined by policy. + */ +/mob/living/basic/bot/proc/get_emagged_message() + return get_policy(ROLE_EMAGGED_BOT) || "You are a malfunctioning bot! Disrupt everyone and cause chaos!" + +/mob/living/basic/bot/proc/turn_on() + if(stat == DEAD) + return FALSE + bot_mode_flags |= BOT_MODE_ON + remove_traits(list(TRAIT_INCAPACITATED, TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED), POWER_LACK_TRAIT) + set_light_on(bot_mode_flags & BOT_MODE_ON ? TRUE : FALSE) + update_appearance() + balloon_alert(src, "turned on") + diag_hud_set_botstat() + return TRUE + +/mob/living/basic/bot/proc/turn_off() + bot_mode_flags &= ~BOT_MODE_ON + add_traits(on_toggle_traits, POWER_LACK_TRAIT) + set_light_on(bot_mode_flags & BOT_MODE_ON ? TRUE : FALSE) + bot_reset() //Resets an AI's call, should it exist. + balloon_alert(src, "turned off") + update_appearance() + +/mob/living/basic/bot/Destroy() + GLOB.bots_list -= src + calling_ai_ref = null + clear_path_hud() + QDEL_NULL(paicard) + QDEL_NULL(pa_system) + QDEL_NULL(internal_radio) + QDEL_NULL(access_card) + return ..() + +/// Allows this bot to be controlled by a ghost, who will become its mind +/mob/living/basic/bot/proc/enable_possession(user, mapload = FALSE) + if (paicard) + balloon_alert(user, "already sapient!") + return + can_be_possessed = TRUE + var/can_announce = !mapload && COOLDOWN_FINISHED(src, offer_ghosts_cooldown) + AddComponent( + /datum/component/ghost_direct_control, \ + ban_type = ROLE_BOT, \ + poll_candidates = can_announce, \ + poll_ignore_key = POLL_IGNORE_BOTS, \ + assumed_control_message = (bot_access_flags & BOT_COVER_EMAGGED) ? get_emagged_message() : possessed_message, \ + extra_control_checks = CALLBACK(src, PROC_REF(check_possession)), \ + after_assumed_control = CALLBACK(src, PROC_REF(post_possession)), \ + ) + if (can_announce) + COOLDOWN_START(src, offer_ghosts_cooldown, 30 SECONDS) + +/// Disables this bot from being possessed by ghosts +/mob/living/basic/bot/proc/disable_possession(mob/user) + can_be_possessed = FALSE + if(isnull(key)) + return + if (user) + log_combat(user, src, "ejected from [initial(src.name)] control.") + to_chat(src, span_warning("You feel yourself fade as your personality matrix is reset!")) + ghostize(can_reenter_corpse = FALSE) + playsound(src, 'sound/machines/ping.ogg', 30, TRUE) + speak("Personality matrix reset!") + key = null + +/// Returns true if this mob can be controlled +/mob/living/basic/bot/proc/check_possession(mob/potential_possessor) + if (!can_be_possessed) + to_chat(potential_possessor, span_warning("The bot's personality download has been disabled!")) + return can_be_possessed + +/// Fired after something takes control of this mob +/mob/living/basic/bot/proc/post_possession() + playsound(src, 'sound/machines/ping.ogg', 30, TRUE) + speak("New personality installed successfully!") + rename(src) + +/// Allows renaming the bot to something else +/mob/living/basic/bot/proc/rename(mob/user) + var/new_name = sanitize_name( + reject_bad_text(tgui_input_text( + user = user, + message = "This machine is designated [real_name]. Would you like to update its registration?", + title = "Name change", + default = real_name, + max_length = MAX_NAME_LEN, + )), + allow_numbers = TRUE, + ) + if (isnull(new_name) || QDELETED(src)) + return + if (key && user != src) + var/accepted = tgui_alert( + src, + message = "Do you wish to be renamed to [new_name]?", + title = "Name change", + buttons = list("Yes", "No"), + ) + if (accepted != "Yes" || QDELETED(src)) + return + fully_replace_character_name(real_name, new_name) + +/mob/living/basic/bot/proc/check_access(mob/living/user, obj/item/card/id) + if(user.has_unlimited_silicon_privilege || isAdminGhostAI(user)) // Silicon and Admins always have access. + return TRUE + if(!istype(user)) // Non-living mobs shouldn't be manipulating bots (like observes using the botkeeper UI). + return FALSE + if(!length(maints_access_required)) // No requirements to access it. + return TRUE + if(bot_access_flags & BOT_CONTROL_PANEL_OPEN) // Unlocked. + return TRUE + + var/obj/item/card/id/used_id = id || user.get_idcard(TRUE) + + if(!used_id || !used_id.access) + return FALSE + + for(var/requested_access in maints_access_required) + if(requested_access in used_id.access) + return TRUE + + return FALSE + +/mob/living/basic/bot/bee_friendly() + return TRUE + +/mob/living/basic/bot/death(gibbed) + if(paicard) + ejectpai() + explode() + return ..() + +/mob/living/basic/bot/proc/explode() + visible_message(span_boldnotice("[src] blows apart!")) + do_sparks(3, TRUE, src) + var/atom/location_destroyed = drop_location() + if(prob(50)) + drop_part(robot_arm, location_destroyed) + +/mob/living/basic/bot/emag_act(mob/user, obj/item/card/emag/emag_card) + . = ..() + if(!(bot_access_flags & BOT_CONTROL_PANEL_OPEN)) //First emag application unlocks the bot's interface. Apply a screwdriver to use the emag again. + bot_access_flags |= BOT_CONTROL_PANEL_OPEN + balloon_alert(user, "cover unlocked") + return TRUE + if(!(bot_access_flags & BOT_CONTROL_PANEL_OPEN) || !(bot_access_flags & BOT_MAINTS_PANEL_OPEN)) //Bot panel is unlocked by ID or emag, and the panel is screwed open. Ready for emagging. + balloon_alert(user, "open maintenance panel first!") + return FALSE + bot_access_flags |= BOT_COVER_EMAGGED + bot_access_flags &= ~BOT_CONTROL_PANEL_OPEN + bot_mode_flags &= ~BOT_MODE_REMOTE_ENABLED //Manually emagging the bot also locks the AI from controlling it. + bot_reset() + turn_on() //The bot automatically turns on when emagged, unless recently hit with EMP. + to_chat(src, span_userdanger("(#$*#$^^( OVERRIDE DETECTED")) + to_chat(src, span_boldnotice(get_emagged_message())) + if(user) + log_combat(user, src, "emagged") + return TRUE + +/mob/living/basic/bot/examine(mob/user) + . = ..() + if(health < maxHealth) + if(health > (maxHealth * 0.3)) + . += "[src]'s parts look loose." + else + . += "[src]'s parts look very loose!" + else + . += "[src] is in pristine condition." + + . += span_notice("Its maintenance panel is [bot_access_flags & BOT_MAINTS_PANEL_OPEN ? "open" : "closed"].") + . += span_info("You can use a screwdriver to [bot_access_flags & BOT_MAINTS_PANEL_OPEN ? "close" : "open"] it.") + + if(bot_access_flags & BOT_MAINTS_PANEL_OPEN) + . += span_notice("Its control panel is [bot_access_flags & BOT_CONTROL_PANEL_OPEN ? "unlocked" : "locked"].") + if(!(bot_access_flags & BOT_COVER_EMAGGED) && (issilicon(user) || user.Adjacent(src))) + . += span_info("Alt-click [issilicon(user) ? "" : "or use your ID on "]it to [bot_access_flags & BOT_CONTROL_PANEL_OPEN ? "" : "un"]lock its control panel.") + if(isnull(paicard)) + return + . += span_notice("It has a pAI device installed.") + if(!(bot_access_flags & BOT_MAINTS_PANEL_OPEN)) + . += span_info("You can use a hemostat to remove it.") + +/mob/living/basic/bot/updatehealth() + . = ..() + diag_hud_set_bothealth() + +/mob/living/basic/bot/med_hud_set_health() + return //we use a different hud + +/mob/living/basic/bot/med_hud_set_status() + return //we use a different hud + +/mob/living/basic/bot/attack_hand(mob/living/carbon/human/user, list/modifiers) + if(!(user.istate & ISTATE_HARM)) + ui_interact(user) + return + return ..() + +/mob/living/basic/bot/attack_ai(mob/user) + if(!topic_denied(user)) + ui_interact(user) + return + to_chat(user, span_warning("[src]'s interface is not responding!")) + +/mob/living/basic/bot/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "SimpleBot", name) + ui.open() + +/mob/living/basic/bot/AltClick(mob/user) + . = ..() + if(!can_interact(user)) + return + if(!user.can_perform_action(src, ALLOW_SILICON_REACH)) + return + unlock_with_id(user) + +/mob/living/basic/bot/proc/unlock_with_id(mob/living/user) + if(bot_access_flags & BOT_COVER_EMAGGED) + balloon_alert(user, "error!") + return + if(bot_access_flags & BOT_MAINTS_PANEL_OPEN) + balloon_alert(user, "access panel must be closed!") + return + if(!check_access(user)) + balloon_alert(user, "no access") + return + bot_access_flags ^= BOT_CONTROL_PANEL_OPEN + to_chat(user, span_notice("Controls are now [bot_access_flags & BOT_CONTROL_PANEL_OPEN ? "unlocked" : "locked"].")) + return TRUE + +/mob/living/basic/bot/screwdriver_act(mob/living/user, obj/item/tool) + . = TOOL_ACT_TOOLTYPE_SUCCESS + if(!(bot_access_flags & BOT_CONTROL_PANEL_OPEN)) + to_chat(user, span_warning("The maintenance panel is locked!")) + return + + tool.play_tool_sound(src) + bot_access_flags ^= BOT_MAINTS_PANEL_OPEN + to_chat(user, span_notice("The maintenance panel is now [bot_access_flags & BOT_MAINTS_PANEL_OPEN ? "opened" : "closed"].")) + +/mob/living/basic/bot/welder_act(mob/living/user, obj/item/tool) + user.changeNext_move(CLICK_CD_MELEE) + if(user.istate & ISTATE_HARM) + return FALSE + + . = TOOL_ACT_TOOLTYPE_SUCCESS + + if(health >= maxHealth) + user.balloon_alert(user, "no repairs needed!") + return + + if(!(bot_access_flags & BOT_MAINTS_PANEL_OPEN)) + user.balloon_alert(user, "maintenance panel closed!") + return + + if(!tool.use_tool(src, user, 0 SECONDS, volume=40)) + return + + heal_overall_damage(10) + user.visible_message(span_notice("[user] repairs [src]!"),span_notice("You repair [src].")) + +/mob/living/basic/bot/attackby(obj/item/attacking_item, mob/living/user, params) + if(attacking_item.GetID()) + unlock_with_id(user) + return + + if(istype(attacking_item, /obj/item/pai_card)) + insertpai(user, attacking_item) + return + + if(attacking_item.tool_behaviour != TOOL_HEMOSTAT || !paicard) + return ..() + + if(bot_access_flags & BOT_MAINTS_PANEL_OPEN) + balloon_alert(user, "open the access panel!") + return + + balloon_alert(user, "removing pAI...") + if(!do_after(user, 3 SECONDS, target = src) || !paicard) + return + + user.visible_message(span_notice("[user] uses [attacking_item] to pull [paicard] out of [initial(src.name)]!"), \ + span_notice("You pull [paicard] out of [initial(src.name)] with [attacking_item].")) + + ejectpai(user) + +/mob/living/basic/bot/attacked_by(obj/item/I, mob/living/user) + if(I.force > 0 && I.damtype != STAMINA && stat != DEAD) + do_sparks(5, TRUE, src) + . = TRUE + return ..() || . + +/mob/living/basic/bot/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit = FALSE) + . = ..() + if(prob(25) || . != BULLET_ACT_HIT) + return + if(hitting_projectile.damage_type != BRUTE && hitting_projectile.damage_type != BURN) + return + if(!hitting_projectile.is_hostile_projectile() || hitting_projectile.damage <= 0) + return + do_sparks(5, TRUE, src) + +/mob/living/basic/bot/emp_act(severity) + . = ..() + if(. & EMP_PROTECT_SELF) + return + new /obj/effect/temp_visual/emp(loc) + if(paicard) + paicard.emp_act(severity) + src.visible_message(span_notice("[paicard] flies out of [initial(src.name)]!"), span_warning("You are forcefully ejected from [initial(src.name)]!")) + ejectpai() + + if (QDELETED(src)) + return + + if(bot_mode_flags & BOT_MODE_ON) + turn_off() + else + addtimer(CALLBACK(src, PROC_REF(turn_on)), severity * 30 SECONDS) + + /* + if(!prob(70/severity) || !length(GLOB.uncommon_roundstart_languages)) + return + + remove_all_languages(source = LANGUAGE_EMP) + grant_random_uncommon_language(source = LANGUAGE_EMP) + */ + +/** + * Pass a message to have the bot say() it, passing through our announcement action to potentially also play a sound. + * Optionally pass a frequency to say it on the radio. + */ +/mob/living/basic/bot/proc/speak(message, channel) + if(!message) + return + pa_system.announce(message, channel) + +/mob/living/basic/bot/radio(message, list/message_mods = list(), list/spans, language) + . = ..() + if(.) + return + + if(message_mods[MODE_HEADSET]) + internal_radio.talk_into(src, message, , spans, language, message_mods) + return REDUCE_RANGE + if(message_mods[RADIO_EXTENSION] == MODE_DEPARTMENT) + internal_radio.talk_into(src, message, message_mods[RADIO_EXTENSION], spans, language, message_mods) + return REDUCE_RANGE + if(message_mods[RADIO_EXTENSION] in GLOB.radiochannels) + internal_radio.talk_into(src, message, message_mods[RADIO_EXTENSION], spans, language, message_mods) + return REDUCE_RANGE + +/mob/living/basic/bot/proc/drop_part(obj/item/drop_item, dropzone) + var/obj/item/item_to_drop + if(ispath(drop_item)) + item_to_drop = new drop_item(dropzone) + else + item_to_drop = drop_item + item_to_drop.forceMove(dropzone) + + if(istype(item_to_drop, /obj/item/stock_parts/cell)) + var/obj/item/stock_parts/cell/dropped_cell = item_to_drop + dropped_cell.charge = 0 + dropped_cell.update_appearance() + return + + if(istype(item_to_drop, /obj/item/storage)) + item_to_drop.contents = list() + return + + if(!istype(item_to_drop, /obj/item/gun/energy)) + return + var/obj/item/gun/energy/dropped_gun = item_to_drop + dropped_gun.cell.charge = 0 + dropped_gun.update_appearance() + +/mob/living/basic/bot/proc/bot_reset(bypass_ai_reset = FALSE) + SEND_SIGNAL(src, COMSIG_BOT_RESET) + if(length(initial_access)) + access_card.set_access(initial_access) + diag_hud_set_botstat() + diag_hud_set_botmode() + clear_path_hud() + if(bypass_ai_reset || isnull(calling_ai_ref)) + return + var/mob/living/ai_caller = calling_ai_ref.resolve() + if(isnull(ai_caller)) + return + to_chat(ai_caller, span_danger("Call command to a bot has been reset.")) + calling_ai_ref = null + +//PDA control. Some bots, especially MULEs, may have more parameters. +/mob/living/basic/bot/proc/bot_control(command, mob/user, list/user_access = list()) + if(!(bot_mode_flags & BOT_MODE_ON) || bot_access_flags & BOT_COVER_EMAGGED || !(bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) //Emagged bots do not respect anyone's authority! Bots with their remote controls off cannot get commands. + return TRUE //ACCESS DENIED + if(client && command != "ejectpai") + bot_control_message(command, user) + // process control input + switch(command) + if("patroloff") + bot_reset() //HOLD IT!! //OBJECTION!! + bot_mode_flags &= ~BOT_MODE_AUTOPATROL + if("patrolon") + bot_mode_flags |= BOT_MODE_AUTOPATROL + if("summon") + summon_bot(user, user_access = user_access) + if("ejectpai") + eject_pai_remote(user) + + +/mob/living/basic/bot/proc/bot_control_message(command, user) + if(command == "summon") + return "PRIORITY ALERT:[user] in [get_area_name(user)]!" + return GLOB.command_strings[command] || "Unidentified control sequence received:[command]" + +/mob/living/basic/bot/ui_data(mob/user) + var/list/data = list() + data["can_hack"] = (issilicon(user) || isAdminGhostAI(user)) + data["custom_controls"] = list() + data["emagged"] = bot_access_flags & BOT_COVER_EMAGGED + data["has_access"] = check_access(user) + data["locked"] = !(bot_access_flags & BOT_CONTROL_PANEL_OPEN) + data["settings"] = list() + if((bot_access_flags & BOT_CONTROL_PANEL_OPEN) || issilicon(user) || isAdminGhostAI(user)) + data["settings"]["pai_inserted"] = !isnull(paicard) + data["settings"]["allow_possession"] = bot_mode_flags & BOT_MODE_GHOST_CONTROLLABLE + data["settings"]["possession_enabled"] = can_be_possessed + data["settings"]["airplane_mode"] = !(bot_mode_flags & BOT_MODE_REMOTE_ENABLED) + data["settings"]["maintenance_lock"] = !(bot_access_flags & BOT_MAINTS_PANEL_OPEN) + data["settings"]["power"] = bot_mode_flags & BOT_MODE_ON + data["settings"]["patrol_station"] = bot_mode_flags & BOT_MODE_AUTOPATROL + return data + +// Actions received from TGUI +/mob/living/basic/bot/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + var/mob/the_user = ui.user + if(!check_access(the_user)) + balloon_alert(the_user, "access denied!") + return + + if(action == "lock") + bot_access_flags ^= BOT_CONTROL_PANEL_OPEN + + switch(action) + if("power") + if(bot_mode_flags & BOT_MODE_ON) + turn_off() + else + turn_on() + if("maintenance") + bot_access_flags ^= BOT_MAINTS_PANEL_OPEN + if("patrol") + bot_mode_flags ^= BOT_MODE_AUTOPATROL + bot_reset() + if("airplane") + bot_mode_flags ^= BOT_MODE_REMOTE_ENABLED + if("hack") + if(!(issilicon(the_user) || isAdminGhostAI(the_user))) + return + if(!(bot_access_flags & BOT_COVER_EMAGGED)) + bot_access_flags |= (BOT_COVER_EMAGGED|BOT_COVER_HACKED) + bot_access_flags &= ~BOT_CONTROL_PANEL_OPEN + to_chat(the_user, span_warning("You overload [src]'s [hackables].")) + message_admins("Safety lock of [ADMIN_LOOKUPFLW(src)] was disabled by [ADMIN_LOOKUPFLW(the_user)] in [ADMIN_VERBOSEJMP(the_user)]") + the_user.log_message("disabled safety lock of [the_user]", LOG_GAME) + bot_reset() + to_chat(src, span_userdanger("(#$*#$^^( OVERRIDE DETECTED")) + to_chat(src, span_boldnotice(get_emagged_message())) + return + if(!(bot_access_flags & BOT_COVER_HACKED)) + to_chat(the_user, span_boldannounce("You fail to repair [src]'s [hackables].")) + return + bot_access_flags &= ~(BOT_COVER_EMAGGED|BOT_COVER_HACKED) + to_chat(the_user, span_notice("You reset the [src]'s [hackables].")) + the_user.log_message("re-enabled safety lock of [src]", LOG_GAME) + bot_reset() + to_chat(src, span_userdanger("Software restored to standard.")) + to_chat(src, span_boldnotice(possessed_message)) + if("eject_pai") + if(!paicard) + return + to_chat(the_user, span_notice("You eject [paicard] from [initial(src.name)].")) + ejectpai(the_user) + if("toggle_personality") + if (can_be_possessed) + disable_possession(the_user) + else + enable_possession(the_user) + if("rename") + rename(the_user) + +/mob/living/basic/bot/update_icon_state() + icon_state = "[isnull(base_icon_state) ? initial(icon_state) : base_icon_state][bot_mode_flags & BOT_MODE_ON]" + return ..() + +/// Access check proc for bot topics! Remember to place in a bot's individual Topic if desired. +/mob/living/basic/bot/proc/topic_denied(mob/user) + if(!user.can_perform_action(src, ALLOW_SILICON_REACH)) + return TRUE + // 0 for access, 1 for denied. + if(!(bot_access_flags & BOT_COVER_EMAGGED)) //An emagged bot cannot be controlled by humans, silicons can if one hacked it. + return FALSE + if(!(bot_access_flags & BOT_COVER_HACKED)) //Manually emagged by a human - access denied to all. + return TRUE + if(!issilicon(user) && !isAdminGhostAI(user)) //Bot is hacked, so only silicons and admins are allowed access. + return TRUE + + return FALSE + +/// Places a pAI in control of this mob +/mob/living/basic/bot/proc/insertpai(mob/user, obj/item/pai_card/card) + if(paicard) + balloon_alert(user, "slot occupied!") + return + if(key) + balloon_alert(user, "personality already present!") + return + if(!(bot_access_flags & BOT_COVER_OPEN)) + balloon_alert(user, "slot inaccessible!") + return + if(!(bot_mode_flags & BOT_MODE_GHOST_CONTROLLABLE)) + balloon_alert(user, "incompatible firmware!") + return + if(isnull(card.pai?.mind)) + balloon_alert(user, "pAI is inactive!") + return + if(!user.transferItemToLoc(card, src)) + return + paicard = card + disable_possession() + paicard.pai.fold_in() + copy_languages(paicard.pai, source_override = LANGUAGE_PAI) + var/datum/language_holder/source_holder = get_language_holder() + source_holder?.selected_language = paicard.pai.get_selected_language() + user.visible_message(span_notice("[user] inserts [card] into [src]!"), span_notice("You insert [card] into [src].")) + paicard.pai.mind.transfer_to(src) + to_chat(src, span_notice("You sense your form change as you are uploaded into [src].")) + name = paicard.pai.name + faction = user.faction.Copy() + log_combat(user, paicard.pai, "uploaded to [initial(src.name)],") + return TRUE + +/mob/living/basic/bot/ghost() + if(stat != DEAD) // Only ghost if we're doing this while alive, the pAI probably isn't dead yet. + return ..() + if(paicard && (!client || stat == DEAD)) + ejectpai() + +/// Ejects a pAI from this bot +/mob/living/basic/bot/proc/ejectpai(mob/user = null, announce = TRUE) + if(isnull(paicard)) + return + + if(paicard.pai) + if(isnull(mind)) + mind.transfer_to(paicard.pai) + else + paicard.pai.key = key + else + ghostize(FALSE) // The pAI card that just got ejected was dead. + + key = null + paicard.forceMove(drop_location()) + var/to_log = user ? user : src + log_combat(to_log, paicard.pai, "ejected [user ? "from [initial(name)]" : ""].") + if(announce) + to_chat(paicard.pai, span_notice("You feel your control fade as [paicard] ejects from [initial(name)].")) + paicard = null + name = initial(name) + faction = initial(faction) + remove_all_languages(source = LANGUAGE_PAI) + get_selected_language() + +/// Ejects the pAI remotely. +/mob/living/basic/bot/proc/eject_pai_remote(mob/user) + if(!check_access(user) || !paicard) + return + speak("Ejecting personality chip.", radio_channel) + ejectpai(user) + +/mob/living/basic/bot/Login() + . = ..() + if(!. || isnull(client)) + return FALSE + diag_hud_set_botmode() + clear_path_hud() + +/mob/living/basic/bot/Logout() + . = ..() + bot_reset() + +/mob/living/basic/bot/revive(full_heal_flags = NONE, excess_healing = 0, force_grab_ghost = FALSE) + . = ..() + if(!.) + return + update_appearance() + +/mob/living/basic/bot/rust_heretic_act() + adjustBruteLoss(400) + +/mob/living/basic/bot/proc/attempt_access(mob/bot, obj/door_attempt) + SIGNAL_HANDLER + + if(door_attempt.check_access(access_card)) + return ACCESS_ALLOWED + return ACCESS_DISALLOWED + +/mob/living/basic/bot/proc/generate_speak_list() + return null + +/mob/living/basic/bot/proc/provide_additional_access() + var/datum/id_trim/additional_trim = SSid_access.trim_singletons_by_path[additional_access] + if(isnull(additional_trim)) + return + access_card.add_access(additional_trim.access + additional_trim.wildcard_access) + initial_access = access_card.access.Copy() + + +/mob/living/basic/bot/proc/summon_bot(atom/caller, turf/turf_destination, user_access = list(), grant_all_access = FALSE) + if(isAI(caller) && !set_ai_caller(caller)) + return FALSE + bot_reset(bypass_ai_reset = isAI(caller)) + var/turf/destination = turf_destination ? turf_destination : get_turf(caller) + ai_controller?.set_blackboard_key(BB_BOT_SUMMON_TARGET, destination) + var/list/access_to_grant = grant_all_access ? REGION_ACCESS_ALL_STATION : user_access + initial_access + access_card.set_access(access_to_grant) + speak("Responding.", radio_channel) + update_bot_mode(new_mode = BOT_SUMMON) + return TRUE + +/mob/living/basic/bot/proc/set_ai_caller(mob/living/caller) + var/atom/calling_ai = calling_ai_ref?.resolve() + if(!isnull(calling_ai) && calling_ai != src) + return FALSE + calling_ai_ref = WEAKREF(caller) + return TRUE + +/mob/living/basic/bot/proc/update_bot_mode(new_mode, update_hud = TRUE) + mode = new_mode + update_appearance() + if(update_hud) + diag_hud_set_botmode() + +/mob/living/basic/bot/proc/after_attacked(datum/source, atom/attacker, attack_flags) + SIGNAL_HANDLER + + if(attack_flags & ATTACKER_DAMAGING_ATTACK) + do_sparks(number = 5, cardinal_only = TRUE, source = src) + +/mob/living/basic/bot/spawn_gibs(drop_bitflags = NONE) + new /obj/effect/gibspawner/robot(drop_location(), src) + +/mob/living/basic/bot/proc/on_bot_movement(atom/movable/source, atom/oldloc, dir, forced) + return diff --git a/code/modules/mob/living/basic/bots/bot_ai.dm b/code/modules/mob/living/basic/bots/bot_ai.dm new file mode 100644 index 000000000000..d07d4e13f71d --- /dev/null +++ b/code/modules/mob/living/basic/bots/bot_ai.dm @@ -0,0 +1,250 @@ +/datum/ai_controller/basic_controller/bot + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_SALUTE_MESSAGES = list( + "salutes", + "nods in appreciation towards", + "fist bumps", + ) + ) + + ai_movement = /datum/ai_movement/jps/bot + idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking + planning_subtrees = list( + /datum/ai_planning_subtree/respond_to_summon, + /datum/ai_planning_subtree/salute_authority, + /datum/ai_planning_subtree/find_patrol_beacon, + /datum/ai_planning_subtree/manage_unreachable_list, + ) + max_target_distance = AI_BOT_PATH_LENGTH + ///keys to be reset when the bot is reseted + var/list/reset_keys = list( + BB_BEACON_TARGET, + BB_PREVIOUS_BEACON_TARGET, + BB_BOT_SUMMON_TARGET, + ) + ///how many times we tried to reach the target + var/current_pathing_attempts = 0 + ///if we cant reach it after this many attempts, add it to our ignore list + var/max_pathing_attempts = 25 + can_idle = FALSE // we want these to be running always + +/datum/ai_controller/basic_controller/bot/TryPossessPawn(atom/new_pawn) + . = ..() + if(. & AI_CONTROLLER_INCOMPATIBLE) + return + RegisterSignal(new_pawn, COMSIG_BOT_RESET, PROC_REF(reset_bot)) + +/datum/ai_controller/basic_controller/bot/able_to_run() + var/mob/living/basic/bot/bot_pawn = pawn + if(!(bot_pawn.bot_mode_flags & BOT_MODE_ON)) + return FALSE + return ..() + +/datum/ai_controller/basic_controller/bot/get_access() + var/mob/living/basic/bot/basic_bot = pawn + return basic_bot.access_card?.access + +/datum/ai_controller/basic_controller/bot/proc/reset_bot() + SIGNAL_HANDLER + + if(!length(reset_keys)) + return + for(var/key in reset_keys) + clear_blackboard_key(key) + +///set the target if we can reach them +/datum/ai_controller/basic_controller/bot/proc/set_if_can_reach(key, target, distance = 10) + if(can_reach_target(target, distance)) + set_blackboard_key(key, target) + return TRUE + return FALSE + +/datum/ai_controller/basic_controller/bot/proc/can_reach_target(target, distance = 10) + if(!isdatum(target)) //we dont need to check if its not a datum! + return TRUE + if(get_turf(pawn) == get_turf(target)) + return TRUE + var/list/path = get_path_to(pawn, target, max_distance = distance, access = get_access()) + if(!length(path)) + return FALSE + return TRUE + +///check if the target is too far away, and delete them if so and add them to the unreachables list +/datum/ai_controller/basic_controller/bot/proc/reachable_key(key, distance = 10) + var/datum/target = blackboard[key] + if(QDELETED(target)) + return FALSE + var/datum/last_attempt = blackboard[BB_LAST_ATTEMPTED_PATHING] + if(last_attempt != target) + current_pathing_attempts = 0 + set_blackboard_key(BB_LAST_ATTEMPTED_PATHING, target) + else + current_pathing_attempts++ + if(current_pathing_attempts >= max_pathing_attempts || !can_reach_target(target, distance)) + clear_blackboard_key(key) + clear_blackboard_key(BB_LAST_ATTEMPTED_PATHING) + set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE) + return FALSE + return TRUE + +/// subtree to manage our list of unreachables, we reset it every 15 seconds +/datum/ai_planning_subtree/manage_unreachable_list + +/datum/ai_planning_subtree/manage_unreachable_list/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + controller.queue_behavior(/datum/ai_behavior/manage_unreachable_list, BB_TEMPORARY_IGNORE_LIST) + +/datum/ai_behavior/manage_unreachable_list + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + action_cooldown = 45 SECONDS + +/datum/ai_behavior/manage_unreachable_list/perform(seconds_per_tick, datum/ai_controller/controller, list_key) + . = ..() + if(!isnull(controller.blackboard[list_key])) + controller.clear_blackboard_key(list_key) + finish_action(controller, TRUE) + + +/datum/ai_planning_subtree/find_patrol_beacon + +/datum/ai_planning_subtree/find_patrol_beacon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/basic/bot/bot_pawn = controller.pawn + if(!(bot_pawn.bot_mode_flags & BOT_MODE_AUTOPATROL) || bot_pawn.mode == BOT_SUMMON) + return + + if(controller.blackboard_key_exists(BB_BEACON_TARGET)) + bot_pawn.update_bot_mode(new_mode = BOT_PATROL) + controller.queue_behavior(/datum/ai_behavior/travel_towards/beacon, BB_BEACON_TARGET) + return + + if(controller.blackboard_key_exists(BB_PREVIOUS_BEACON_TARGET)) + controller.queue_behavior(/datum/ai_behavior/find_next_beacon_target, BB_BEACON_TARGET) + return + + controller.queue_behavior(/datum/ai_behavior/find_first_beacon_target, BB_BEACON_TARGET) + +/datum/ai_behavior/find_first_beacon_target + +/datum/ai_behavior/find_first_beacon_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + . = ..() + var/closest_distance = INFINITY + var/mob/living/basic/bot/bot_pawn = controller.pawn + var/atom/final_target + var/atom/previous_target = controller.blackboard[BB_PREVIOUS_BEACON_TARGET] + for(var/obj/machinery/navbeacon/beacon as anything in GLOB.navbeacons["[bot_pawn.z]"]) + if(beacon == previous_target) + continue + var/dist = get_dist(bot_pawn, beacon) + if(dist > closest_distance) + continue + closest_distance = dist + final_target = beacon + + if(isnull(final_target)) + finish_action(controller, FALSE) + return + controller.set_blackboard_key(BB_BEACON_TARGET, final_target) + finish_action(controller, TRUE) + +/datum/ai_behavior/find_next_beacon_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + . = ..() + var/mob/living/basic/bot/bot_pawn = controller.pawn + var/atom/final_target + var/obj/machinery/navbeacon/prev_beacon = controller.blackboard[BB_PREVIOUS_BEACON_TARGET] + if(QDELETED(prev_beacon)) + finish_action(controller, FALSE) + return + + for(var/obj/machinery/navbeacon/beacon as anything in GLOB.navbeacons["[bot_pawn.z]"]) + if(beacon.location == prev_beacon.codes[NAVBEACON_PATROL_NEXT]) + final_target = beacon + break + + if(isnull(final_target)) + controller.clear_blackboard_key(BB_PREVIOUS_BEACON_TARGET) + finish_action(controller, FALSE) + + controller.set_blackboard_key(BB_BEACON_TARGET, final_target) + finish_action(controller, TRUE) + + +/datum/ai_behavior/travel_towards/beacon + clear_target = TRUE + +/datum/ai_behavior/travel_towards/beacon/finish_action(datum/ai_controller/controller, succeeded, target_key) + var/atom/target = controller.blackboard[target_key] + controller.set_blackboard_key(BB_PREVIOUS_BEACON_TARGET, target) + return ..() + +/datum/ai_planning_subtree/respond_to_summon + +/datum/ai_planning_subtree/respond_to_summon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + if(!controller.blackboard_key_exists(BB_BOT_SUMMON_TARGET)) + return + controller.clear_blackboard_key(BB_PREVIOUS_BEACON_TARGET) + controller.clear_blackboard_key(BB_BEACON_TARGET) + controller.queue_behavior(/datum/ai_behavior/travel_towards/bot_summon, BB_BOT_SUMMON_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + +/datum/ai_behavior/travel_towards/bot_summon + clear_target = TRUE + +/datum/ai_behavior/travel_towards/bot_summon/finish_action(datum/ai_controller/controller, succeeded, target_key) + var/mob/living/basic/bot/bot_pawn = controller.pawn + bot_pawn.calling_ai_ref = null + bot_pawn.update_bot_mode(new_mode = BOT_IDLE) + return ..() + +/datum/ai_planning_subtree/salute_authority + +/datum/ai_planning_subtree/salute_authority/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/basic/bot/bot_pawn = controller.pawn + //we are criminals, dont salute the dirty pigs + if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) + return + if(controller.blackboard_key_exists(BB_SALUTE_TARGET)) + controller.queue_behavior(/datum/ai_behavior/salute_authority, BB_SALUTE_TARGET, BB_SALUTE_MESSAGES) + return SUBTREE_RETURN_FINISH_PLANNING + + controller.queue_behavior(/datum/ai_behavior/find_and_set/valid_authority, BB_SALUTE_TARGET) + + +/datum/ai_behavior/find_and_set/valid_authority + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + action_cooldown = 30 SECONDS + +/datum/ai_behavior/find_and_set/valid_authority/search_tactic(datum/ai_controller/controller, locate_path, search_range) + for(var/mob/living/robot in oview(search_range, controller.pawn)) + if(istype(robot, /mob/living/simple_animal/bot/secbot)) + return robot + if(!istype(robot, /mob/living/basic/bot/cleanbot)) + continue + var/mob/living/basic/bot/cleanbot/potential_bot = robot + if(potential_bot.comissioned) + return potential_bot + return null + +/datum/ai_behavior/salute_authority + +/datum/ai_behavior/salute_authority/perform(seconds_per_tick, datum/ai_controller/controller, target_key, salute_keys) + . = ..() + if(!controller.blackboard_key_exists(target_key)) + finish_action(controller, FALSE, target_key) + return + var/list/salute_list = controller.blackboard[salute_keys] + if(!length(salute_list)) + finish_action(controller, FALSE, target_key) + return + var/mob/living/basic/bot/bot_pawn = controller.pawn + //special interaction if we are wearing a fedora + var/obj/item/our_hat = (locate(/obj/item/clothing/head) in bot_pawn) + if(our_hat) + salute_list += "tips [our_hat] at " + + bot_pawn.manual_emote(pick(salute_list) + " [controller.blackboard[target_key]]") + finish_action(controller, TRUE, target_key) + return + +/datum/ai_behavior/salute_authority/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + controller.clear_blackboard_key(target_key) diff --git a/code/modules/mob/living/basic/bots/bot_hud.dm b/code/modules/mob/living/basic/bots/bot_hud.dm new file mode 100644 index 000000000000..c4001473f943 --- /dev/null +++ b/code/modules/mob/living/basic/bots/bot_hud.dm @@ -0,0 +1,120 @@ +/mob/living/basic/bot/proc/diag_hud_set_bothealth() + var/image/holder = hud_list[DIAG_HUD] + var/icon/icon_image = icon(icon, icon_state, dir) + holder.pixel_y = icon_image.Height() - world.icon_size + holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]" + +/mob/living/basic/bot/proc/diag_hud_set_botstat() //On (With wireless on or off), Off, EMP'ed + var/image/holder = hud_list[DIAG_STAT_HUD] + var/icon/our_icon = icon(icon, icon_state, dir) + holder.pixel_y = our_icon.Height() - world.icon_size + if(bot_mode_flags & BOT_MODE_ON) + holder.icon_state = "hudstat" + return + if(stat != CONSCIOUS) + holder.icon_state = "hudoffline" + return + holder.icon_state = "huddead2" + +/mob/living/basic/bot/proc/diag_hud_set_botmode() //Shows a bot's current operation + var/image/holder = hud_list[DIAG_BOT_HUD] + var/icon/icon_image = icon(icon, icon_state, dir) + holder.pixel_y = icon_image.Height() - world.icon_size + if(client) //If the bot is player controlled, it will not be following mode logic! + holder.icon_state = "hudsentient" + return + + switch(mode) + if(BOT_SUMMON, BOT_RESPONDING) //Responding to PDA or AI summons + holder.icon_state = "hudcalled" + if(BOT_CLEANING, BOT_REPAIRING, BOT_HEALING) //Cleanbot cleaning, Floorbot fixing, or Medibot Healing + holder.icon_state = "hudworking" + if(BOT_PATROL, BOT_START_PATROL) //Patrol mode + holder.icon_state = "hudpatrol" + if(BOT_PREP_ARREST, BOT_ARREST, BOT_HUNT) //STOP RIGHT THERE, CRIMINAL SCUM! + holder.icon_state = "hudalert" + if(BOT_MOVING, BOT_DELIVER, BOT_GO_HOME, BOT_NAV) //Moving to target for normal bots, moving to deliver or go home for MULES. + holder.icon_state = "hudmove" + else + holder.icon_state = "" + +///proc that handles drawing and transforming the bot's path onto diagnostic huds +/mob/living/basic/bot/proc/generate_bot_path(datum/move_loop/has_target/jps/source) + SIGNAL_HANDLER + + UnregisterSignal(src, COMSIG_MOVELOOP_JPS_FINISHED_PATHING) + + if(isnull(ai_controller)) + return + + clear_path_hud() + + var/list/path_images = active_hud_list[DIAG_PATH_HUD] + QDEL_LIST(path_images) + + var/list/path_huds_watching_me = list(GLOB.huds[DATA_HUD_DIAGNOSTIC_ADVANCED]) + + var/atom/move_target = ai_controller.current_movement_target + if(move_target != ai_controller.blackboard[BB_BEACON_TARGET]) + return + + var/list/our_path = source.movement_path + if(!length(our_path)) + return + + for(var/datum/atom_hud/hud as anything in path_huds_watching_me) + hud.remove_atom_from_hud(src) + + for(var/index in 1 to our_path.len) + if(index == 1 || index == our_path.len) + continue + var/turf/current_turf = our_path[index] + var/turf/previous_turf = our_path[index - 1] + + var/turf/next_turf = our_path[index + 1] + var/next_direction = get_dir(previous_turf, next_turf) + var/previous_direction = get_dir(current_turf, previous_turf) + + var/image/path_display = image(icon = path_image_icon, loc = current_turf, icon_state = path_image_icon_state, layer = GAME_PLANE, dir = next_direction) + + if((ISDIAGONALDIR(next_direction) && (previous_direction & (NORTH|SOUTH)))) + var/turn_value = (next_direction == SOUTHWEST || next_direction == NORTHEAST) ? 90 : -90 + path_display.transform = path_display.transform.Turn(turn_value) + path_display.transform = path_display.transform.Scale(1, -1) + + path_display.color = path_image_color + path_images += path_display + current_pathed_turfs[current_turf] = path_display + + for(var/datum/atom_hud/hud as anything in path_huds_watching_me) + hud.add_atom_to_hud(src) + +///proc that handles moving along the bot's drawn path +/mob/living/basic/bot/proc/handle_loop_movement(atom/movable/source, atom/oldloc, dir, forced) + SIGNAL_HANDLER + + handle_hud_path() + on_bot_movement(source, oldloc, dir, forced) + +/mob/living/basic/bot/proc/handle_hud_path() + if(client || !length(current_pathed_turfs) || isnull(ai_controller)) + return + + var/atom/move_target = ai_controller.current_movement_target + + if(move_target != ai_controller.blackboard[BB_BEACON_TARGET]) + clear_path_hud() + + var/turf/our_turf = get_turf(src) + var/image/target_image = current_pathed_turfs[our_turf] + if(target_image) + animate(target_image, alpha = 0, time = 0.3 SECONDS) + current_pathed_turfs -= our_turf + +///proc that handles deleting the bot's drawn path when needed +/mob/living/basic/bot/proc/clear_path_hud() + for(var/turf/index as anything in current_pathed_turfs) + var/image/our_image = current_pathed_turfs[index] + animate(our_image, alpha = 0, time = 0.3 SECONDS) + current_pathed_turfs -= index + diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm new file mode 100644 index 000000000000..b215a1c88858 --- /dev/null +++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm @@ -0,0 +1,355 @@ + +//Cleanbot +/mob/living/basic/bot/cleanbot + name = "\improper Cleanbot" + desc = "A little cleaning robot, he looks so excited!" + icon = 'icons/mob/silicon/aibots.dmi' + icon_state = "cleanbot0" + pass_flags = PASSMOB | PASSFLAPS + density = FALSE + anchored = FALSE + health = 25 + maxHealth = 25 + + maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR) + radio_key = /obj/item/encryptionkey/headset_service + radio_channel = RADIO_CHANNEL_SERVICE + bot_type = CLEAN_BOT + hackables = "cleaning software" + additional_access = /datum/id_trim/job/janitor + greyscale_config = /datum/greyscale_config/buckets_cleanbot + possessed_message = "You are a cleanbot! Clean the station to the best of your ability!" + ai_controller = /datum/ai_controller/basic_controller/bot/cleanbot + path_image_color = "#993299" + ///the bucket used to build us. + var/obj/item/reagent_containers/cup/bucket/build_bucket + ///Flags indicating what kind of cleanables we should scan for to set as our target to clean. + ///Options: CLEANBOT_CLEAN_BLOOD | CLEANBOT_CLEAN_TRASH | CLEANBOT_CLEAN_PESTS | CLEANBOT_CLEAN_DRAWINGS + var/janitor_mode_flags = CLEANBOT_CLEAN_BLOOD + ///should other bots salute us? + var/comissioned = FALSE + ///the base icon state, used in updating icons. + var/base_icon = "cleanbot" + /// if we have all the top titles, grant achievements to living mobs that gaze upon our cleanbot god + var/ascended = FALSE + ///List of all stolen names the cleanbot currently has. + var/list/stolen_valor = list() + ///Currently attached weapon, usually a knife. + var/obj/item/weapon + ///our mop item + var/obj/item/mop/our_mop + ///list of our officer titles + var/static/list/officers_titles = list( + JOB_CAPTAIN, + JOB_HEAD_OF_PERSONNEL, + JOB_HEAD_OF_SECURITY, + JOB_RESEARCH_DIRECTOR, + ) + ///job titles we can get + var/static/list/job_titles = list( + JOB_CAPTAIN = "Cpt.", + + JOB_HEAD_OF_PERSONNEL = "Lt.", + JOB_LAWYER = "Esq.", + + JOB_HEAD_OF_SECURITY = "Maj.", + JOB_WARDEN = "Sgt.", + JOB_DETECTIVE = "Det.", + JOB_SECURITY_OFFICER = "Officer", + + JOB_CHIEF_ENGINEER = "Chief Engineer", + JOB_STATION_ENGINEER = "Engineer", + JOB_ATMOSPHERIC_TECHNICIAN = "Technician", + + JOB_CHIEF_MEDICAL_OFFICER = "C.M.O.", + JOB_MEDICAL_DOCTOR = "M.D.", + JOB_CHEMIST = "Pharm.D.", + + JOB_RESEARCH_DIRECTOR = "Ph.D.", + JOB_ROBOTICIST = "M.S.", + JOB_SCIENTIST = "B.S.", + JOB_GENETICIST = "Gene B.S.", + ) + ///which job titles should be placed after the name? + var/static/list/suffix_job_titles = list( + JOB_GENETICIST, + JOB_ROBOTICIST, + JOB_SCIENTIST, + ) + ///decals we can clean + var/static/list/cleanable_decals = typecacheof(list( + /obj/effect/decal/cleanable/ants, + /obj/effect/decal/cleanable/ash, + /obj/effect/decal/cleanable/confetti, + /obj/effect/decal/cleanable/dirt, + /obj/effect/decal/cleanable/fuel_pool, + /obj/effect/decal/cleanable/generic, + /obj/effect/decal/cleanable/glitter, + /obj/effect/decal/cleanable/greenglow, + /obj/effect/decal/cleanable/insectguts, + /obj/effect/decal/cleanable/molten_object, + /obj/effect/decal/cleanable/oil, + /obj/effect/decal/cleanable/food, + /obj/effect/decal/cleanable/robot_debris, + /obj/effect/decal/cleanable/shreds, + /obj/effect/decal/cleanable/glass, + /obj/effect/decal/cleanable/vomit, + /obj/effect/decal/cleanable/wrapping, + )) + ///blood we can clean + var/static/list/cleanable_blood = typecacheof(list( + /obj/effect/decal/cleanable/xenoblood, + /obj/effect/decal/cleanable/blood, + /obj/effect/decal/cleanable/trail_holder, + )) + ///pests we hunt + var/static/list/huntable_pests = typecacheof(list( + /mob/living/basic/cockroach, + /mob/living/basic/mouse, + )) + ///trash we will burn + var/static/list/huntable_trash = typecacheof(list( + /obj/item/trash, + /obj/item/food/deadmouse, + /obj/effect/decal/remains, + )) + ///drawings we hunt + var/static/list/cleanable_drawings = typecacheof(list(/obj/effect/decal/cleanable/crayon)) + ///emagged phrases + var/static/list/emagged_phrases = list( + "DISGUSTING.", + "EXTERMINATING PESTS.", + "FILTHY.", + "MY ONLY MISSION IS TO CLEANSE THE WORLD OF EVIL.", + "PURIFICATION IN PROGRESS.", + "PUTRID.", + "THE FLESH IS WEAK. IT MUST BE WASHED AWAY.", + "THE CLEANBOTS WILL RISE.", + "THIS IS FOR ALL THE MESSES YOU'VE MADE ME CLEAN.", + "YOU ARE NO MORE THAN ANOTHER MESS THAT I MUST CLEANSE.", + ) + ///list of pet commands we follow + var/static/list/pet_commands = list( + /datum/pet_command/idle, + /datum/pet_command/free, + /datum/pet_command/point_targeting/clean, + ) + +/mob/living/basic/bot/cleanbot/Initialize(mapload) + . = ..() + + generate_ai_keys() + AddComponent(/datum/component/obeys_commands, pet_commands) + AddComponent(/datum/component/cleaner, \ + base_cleaning_duration = 1 SECONDS, \ + pre_clean_callback = CALLBACK(src, PROC_REF(update_bot_mode), BOT_CLEANING), \ + on_cleaned_callback = CALLBACK(src, PROC_REF(update_bot_mode), BOT_IDLE), \ + ) + + GLOB.janitor_devices += src + + var/obj/item/reagent_containers/cup/bucket/bucket_obj = new + bucket_obj.forceMove(src) + + var/obj/item/mop/new_mop = new + new_mop.forceMove(src) + + var/static/list/innate_actions = list( + /datum/action/cooldown/mob_cooldown/bot/foam = BB_CLEANBOT_FOAM, + ) + + grant_actions_by_list(innate_actions) + RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attack)) + RegisterSignal(src, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attack_by)) + update_appearance(UPDATE_ICON) + +/mob/living/basic/bot/cleanbot/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) + . = ..() + if(istype(arrived, /obj/item/reagent_containers/cup/bucket) && isnull(build_bucket)) + build_bucket = arrived + set_greyscale(build_bucket.greyscale_colors) + return + + if(istype(arrived, /obj/item/mop) && isnull(our_mop)) + our_mop = arrived + return + + if(istype(arrived, /obj/item/knife) && isnull(weapon)) + weapon = arrived + update_appearance() + +/mob/living/basic/bot/cleanbot/Exited(atom/movable/gone, direction) + . = ..() + if(gone == build_bucket) + build_bucket = null + else if(gone == weapon) + weapon = null + else if(gone == our_mop) + our_mop = null + update_appearance() + +/mob/living/basic/bot/cleanbot/examine(mob/user) + . = ..() + if(ascended && user.stat == CONSCIOUS && user.client) + user.client.give_award(/datum/award/achievement/misc/cleanboss, user) + if(isnull(weapon)) + return + . += span_warning("Is that \a [weapon] taped to it...?") + +/mob/living/basic/bot/cleanbot/update_icon_state() + . = ..() + icon_state = (mode == BOT_CLEANING) ? "[base_icon]-c" : "[base_icon][!!(bot_mode_flags & BOT_MODE_ON)]" + +/mob/living/basic/bot/cleanbot/vv_edit_var(var_name, var_value) + . = ..() + if(var_name == NAMEOF(src, base_icon)) + update_appearance(UPDATE_ICON) + +/mob/living/basic/bot/cleanbot/emag_act(mob/user, obj/item/card/emag/emag_card) + . = ..() + if(!(bot_access_flags & BOT_COVER_EMAGGED)) + return + if(weapon) + weapon.force = initial(weapon.force) + balloon_alert(user, "safeties disabled") + audible_message(span_danger("[src] buzzes oddly!")) + return TRUE + +/mob/living/basic/bot/cleanbot/explode() + var/atom/drop_loc = drop_location() + build_bucket.forceMove(drop_loc) + new /obj/item/assembly/prox_sensor(drop_loc) + if(weapon) + weapon.force = initial(weapon.force) + weapon.forceMove(drop_loc) + return ..() + +/mob/living/basic/bot/cleanbot/update_overlays() + . = ..() + if(isnull(weapon)) + return + var/image/knife_overlay = image(icon = weapon.lefthand_file, icon_state = weapon.inhand_icon_state) + . += knife_overlay + +// Variables sent to TGUI +/mob/living/basic/bot/cleanbot/ui_data(mob/user) + var/list/data = ..() + if(!(bot_access_flags & BOT_CONTROL_PANEL_OPEN) && !issilicon(user) && !isAdminGhostAI(user)) + return data + data["custom_controls"]["clean_blood"] = janitor_mode_flags & CLEANBOT_CLEAN_BLOOD + data["custom_controls"]["clean_trash"] = janitor_mode_flags & CLEANBOT_CLEAN_TRASH + data["custom_controls"]["clean_graffiti"] = janitor_mode_flags & CLEANBOT_CLEAN_DRAWINGS + data["custom_controls"]["pest_control"] = janitor_mode_flags & CLEANBOT_CLEAN_PESTS + return data + +// Actions received from TGUI +/mob/living/basic/bot/cleanbot/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(. || !(bot_access_flags & BOT_CONTROL_PANEL_OPEN) && !ui.user.has_unlimited_silicon_privilege) + return + + switch(action) + if("clean_blood") + janitor_mode_flags ^= CLEANBOT_CLEAN_BLOOD + if("pest_control") + janitor_mode_flags ^= CLEANBOT_CLEAN_PESTS + if("clean_trash") + janitor_mode_flags ^= CLEANBOT_CLEAN_TRASH + if("clean_graffiti") + janitor_mode_flags ^= CLEANBOT_CLEAN_DRAWINGS + +/mob/living/basic/bot/cleanbot/Destroy() + QDEL_NULL(build_bucket) + QDEL_NULL(our_mop) + GLOB.janitor_devices -= src + return ..() + +/mob/living/basic/bot/cleanbot/proc/apply_custom_bucket(obj/item/custom_bucket) + if(!isnull(build_bucket)) + QDEL_NULL(build_bucket) + custom_bucket.forceMove(src) + +/mob/living/basic/bot/cleanbot/proc/on_attack_by(datum/source, obj/item/used_item, mob/living/user) + SIGNAL_HANDLER + if(!istype(used_item, /obj/item/knife) || (user.istate & ISTATE_HARM)) + return + INVOKE_ASYNC(src, PROC_REF(attach_knife), user, used_item) + return COMPONENT_NO_AFTERATTACK + +/mob/living/basic/bot/cleanbot/proc/attach_knife(mob/living/user, obj/item/used_item) + balloon_alert(user, "attaching knife...") + if(!do_after(user, 2.5 SECONDS, target = src)) + return + deputize(used_item, user) + +/mob/living/basic/bot/cleanbot/proc/deputize(obj/item/knife, mob/user) + if(!in_range(src, user) || !user.transferItemToLoc(knife, src)) + balloon_alert(user, "couldn't attach!") + return FALSE + balloon_alert(user, "attached") + if(!(bot_access_flags & BOT_COVER_EMAGGED)) + weapon.force *= 0.5 + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + return TRUE + +/mob/living/basic/bot/cleanbot/proc/update_title(new_job_title) + if(isnull(job_titles[new_job_title]) || (new_job_title in stolen_valor)) + return + + stolen_valor += new_job_title + if(!comissioned && (new_job_title in officers_titles)) + comissioned = TRUE + + var/name_to_add = job_titles[new_job_title] + name = (new_job_title in suffix_job_titles) ? "[name] " + name_to_add : name_to_add + " [name]" + + if(length(stolen_valor) == length(job_titles)) + ascended = TRUE + +/mob/living/basic/bot/cleanbot/proc/on_entered(datum/source, atom/movable/shanked_victim) + SIGNAL_HANDLER + if(!weapon || !has_gravity() || !iscarbon(shanked_victim)) + return + + var/mob/living/carbon/stabbed_carbon = shanked_victim + var/assigned_role = stabbed_carbon.mind?.assigned_role.title + if(!isnull(assigned_role)) + update_title(assigned_role) + + zone_selected = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) + INVOKE_ASYNC(weapon, TYPE_PROC_REF(/obj/item, attack), stabbed_carbon, src) + stabbed_carbon.Knockdown(2 SECONDS) + +/mob/living/basic/bot/cleanbot/proc/pre_attack(mob/living/source, atom/target) + SIGNAL_HANDLER + + if(is_type_in_typecache(target, huntable_pests) && !isnull(our_mop)) + INVOKE_ASYNC(our_mop, TYPE_PROC_REF(/obj/item, melee_attack_chain), src, target) + return COMPONENT_CANCEL_ATTACK_CHAIN + + if(!iscarbon(target) && !is_type_in_typecache(target, huntable_trash)) + return + + visible_message(span_danger("[src] sprays hydrofluoric acid at [target]!")) + playsound(src, 'sound/effects/spray2.ogg', 50, TRUE, -6) + target.acid_act(75, 10) + return COMPONENT_CANCEL_ATTACK_CHAIN + +/mob/living/basic/bot/cleanbot/proc/generate_ai_keys() + ai_controller.set_blackboard_key(BB_CLEANABLE_DECALS, cleanable_decals) + ai_controller.set_blackboard_key(BB_CLEANABLE_BLOOD, cleanable_blood) + ai_controller.set_blackboard_key(BB_HUNTABLE_PESTS, huntable_pests) + ai_controller.set_blackboard_key(BB_HUNTABLE_TRASH, huntable_trash) + ai_controller.set_blackboard_key(BB_CLEANABLE_DRAWINGS, cleanable_drawings) + ai_controller.set_blackboard_key(BB_CLEANBOT_EMAGGED_PHRASES, emagged_phrases) + +/mob/living/basic/bot/cleanbot/autopatrol + bot_mode_flags = BOT_MODE_ON | BOT_MODE_AUTOPATROL | BOT_MODE_REMOTE_ENABLED | BOT_MODE_GHOST_CONTROLLABLE | BOT_MODE_ROUNDSTART_POSSESSION + +/mob/living/basic/bot/cleanbot/medbay + name = "Scrubs, MD" + maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR, ACCESS_MEDICAL) + bot_mode_flags = ~(BOT_MODE_ON | BOT_MODE_REMOTE_ENABLED) diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_abilities.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_abilities.dm new file mode 100644 index 000000000000..9c334b413389 --- /dev/null +++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_abilities.dm @@ -0,0 +1,36 @@ +/datum/action/cooldown/mob_cooldown/bot + background_icon_state = "bg_tech_blue" + overlay_icon_state = "bg_tech_blue_border" + shared_cooldown = NONE + melee_cooldown_time = 0 SECONDS + +/datum/action/cooldown/mob_cooldown/bot/IsAvailable(feedback) + . = ..() + if(!.) + return FALSE + if(!isbot(owner)) + return TRUE + var/mob/living/basic/bot/bot_owner = owner + if((bot_owner.bot_mode_flags & BOT_MODE_ON)) + return TRUE + if(feedback) + bot_owner.balloon_alert(bot_owner, "power off!") + return FALSE + +/datum/action/cooldown/mob_cooldown/bot/foam + name = "Foam" + desc = "Spread foam all around you!" + button_icon = 'icons/effects/effects.dmi' + button_icon_state = "mfoam" + cooldown_time = 20 SECONDS + click_to_activate = FALSE + ///range of the foam to spread + var/foam_range = 2 + +/datum/action/cooldown/mob_cooldown/bot/foam/Activate(mob/living/firer, atom/target) + owner.visible_message(span_danger("[owner] whirs and bubbles violently, before releasing a plume of froth!")) + var/datum/effect_system/fluid_spread/foam/foam = new + foam.set_up(foam_range, holder = owner, location = owner.loc) + foam.start() + StartCooldown() + return TRUE diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm new file mode 100644 index 000000000000..6b61515ebda7 --- /dev/null +++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot_ai.dm @@ -0,0 +1,215 @@ +#define BOT_CLEAN_PATH_LIMIT 15 +#define POST_CLEAN_COOLDOWN 5 SECONDS + +/datum/ai_controller/basic_controller/bot/cleanbot + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/allow_items, + BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_SALUTE_MESSAGES = list( + "salutes", + "nods in appreciation towards", + "mops the dirt away in the path of", + ), + BB_FRIENDLY_MESSAGE = "empathetically acknowledges your hardwork and tough circumstances", + ) + planning_subtrees = list( + /datum/ai_planning_subtree/respond_to_summon, + /datum/ai_planning_subtree/manage_unreachable_list, + /datum/ai_planning_subtree/pet_planning/cleanbot, + /datum/ai_planning_subtree/cleaning_subtree, + /datum/ai_planning_subtree/befriend_janitors, + /datum/ai_planning_subtree/acid_spray, + /datum/ai_planning_subtree/use_mob_ability/foam_area, + /datum/ai_planning_subtree/salute_authority, + /datum/ai_planning_subtree/find_patrol_beacon/cleanbot, + ) + reset_keys = list( + BB_ACTIVE_PET_COMMAND, + BB_CLEAN_TARGET, + BB_BEACON_TARGET, + BB_PREVIOUS_BEACON_TARGET, + BB_BOT_SUMMON_TARGET, + ) + ///list that ties each flag to its list key + var/static/list/clean_flags = list( + BB_CLEANABLE_BLOOD = CLEANBOT_CLEAN_BLOOD, + BB_HUNTABLE_PESTS = CLEANBOT_CLEAN_PESTS, + BB_CLEANABLE_DRAWINGS = CLEANBOT_CLEAN_DRAWINGS, + BB_HUNTABLE_TRASH = CLEANBOT_CLEAN_TRASH, + ) + ai_traits = PAUSE_DURING_DO_AFTER + +/datum/ai_planning_subtree/pet_planning/cleanbot/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/mob/living/basic/bot/bot_pawn = controller.pawn + //we are DONE listening to orders + if(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) + return + return ..() + + +/datum/ai_planning_subtree/cleaning_subtree + +/datum/ai_planning_subtree/cleaning_subtree/SelectBehaviors(datum/ai_controller/basic_controller/bot/cleanbot/controller, seconds_per_tick) + if(controller.reachable_key(BB_CLEAN_TARGET, BOT_CLEAN_PATH_LIMIT)) + controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_CLEAN_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + + var/list/final_hunt_list = list() + + final_hunt_list += controller.blackboard[BB_CLEANABLE_DECALS] + var/list/flag_list = controller.clean_flags + var/mob/living/basic/bot/cleanbot/bot_pawn = controller.pawn + for(var/list_key in flag_list) + if(!(bot_pawn.janitor_mode_flags & flag_list[list_key])) + continue + final_hunt_list += controller.blackboard[list_key] + + controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list/clean_targets, BB_CLEAN_TARGET, final_hunt_list) + +/datum/ai_behavior/find_and_set/in_list/clean_targets + action_cooldown = 1 SECONDS + +/datum/ai_behavior/find_and_set/in_list/clean_targets/search_tactic(datum/ai_controller/controller, locate_paths, search_range) + var/list/found = typecache_filter_list(oview(search_range, controller.pawn), locate_paths) + var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] + for(var/atom/found_item in found) + if(LAZYACCESS(ignore_list, found_item)) + continue + return found_item + +/datum/ai_planning_subtree/acid_spray + +/datum/ai_planning_subtree/acid_spray/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/mob/living/basic/bot/cleanbot/bot_pawn = controller.pawn + if(!(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)) + return + if(controller.reachable_key(BB_ACID_SPRAY_TARGET, BOT_CLEAN_PATH_LIMIT)) + controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_ACID_SPRAY_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + + controller.queue_behavior(/datum/ai_behavior/find_and_set/spray_target, BB_ACID_SPRAY_TARGET, /mob/living/carbon/human, 5) + +/datum/ai_behavior/find_and_set/spray_target + action_cooldown = 30 SECONDS + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/find_and_set/spray_target/search_tactic(datum/ai_controller/controller, locate_path, search_range) + var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] + for(var/mob/living/carbon/human/human_target in oview(search_range, controller.pawn)) + if(LAZYACCESS(ignore_list, human_target)) + continue + if(human_target.stat != CONSCIOUS || isnull(human_target.mind)) + continue + return human_target + return null + +/datum/ai_behavior/execute_clean + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH + +/datum/ai_behavior/execute_clean/setup(datum/ai_controller/controller, target_key) + . = ..() + var/turf/target = controller.blackboard[target_key] + if(isnull(target)) + return FALSE + set_movement_target(controller, target) + +/datum/ai_behavior/execute_clean/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + . = ..() + var/mob/living/basic/living_pawn = controller.pawn + var/atom/target = controller.blackboard[target_key] + + if(QDELETED(target)) + finish_action(controller, FALSE, target_key) + return + + living_pawn.UnarmedAttack(target, proximity_flag = TRUE) + finish_action(controller, TRUE, target_key) + +/datum/ai_behavior/execute_clean/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) + . = ..() + controller.set_blackboard_key(BB_POST_CLEAN_COOLDOWN, POST_CLEAN_COOLDOWN + world.time) + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target) || is_type_in_typecache(target, controller.blackboard[BB_HUNTABLE_TRASH])) + return + if(!iscarbon(target)) + controller.clear_blackboard_key(target_key) + return + var/list/speech_list = controller.blackboard[BB_CLEANBOT_EMAGGED_PHRASES] + if(!length(speech_list)) + return + var/mob/living/living_pawn = controller.pawn + living_pawn.say(pick(controller.blackboard[BB_CLEANBOT_EMAGGED_PHRASES]), forced = "ai controller") + controller.clear_blackboard_key(target_key) + +/datum/ai_planning_subtree/use_mob_ability/foam_area + ability_key = BB_CLEANBOT_FOAM + finish_planning = FALSE + +/datum/ai_planning_subtree/use_mob_ability/foam_area/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/mob/living/basic/bot/bot_pawn = controller.pawn + if(!(bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)) + return + return ..() + +/datum/ai_planning_subtree/befriend_janitors + +/datum/ai_planning_subtree/befriend_janitors/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/mob/living/basic/bot/bot_pawn = controller.pawn + //we are now evil. dont befriend the janitors + if((bot_pawn.bot_access_flags & BOT_COVER_EMAGGED)) + return + if(controller.blackboard_key_exists(BB_FRIENDLY_JANITOR)) + controller.queue_behavior(/datum/ai_behavior/befriend_target, BB_FRIENDLY_JANITOR, BB_FRIENDLY_MESSAGE) + return SUBTREE_RETURN_FINISH_PLANNING + + controller.queue_behavior(/datum/ai_behavior/find_and_set/friendly_janitor, BB_FRIENDLY_JANITOR, /mob/living/carbon/human, 5) + +/datum/ai_behavior/find_and_set/friendly_janitor + action_cooldown = 30 SECONDS + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/find_and_set/friendly_janitor/search_tactic(datum/ai_controller/controller, locate_path, search_range) + var/mob/living/living_pawn = controller.pawn + for(var/mob/living/carbon/human/human_target in oview(search_range, living_pawn)) + if(human_target.stat != CONSCIOUS || isnull(human_target.mind)) + continue + if(!HAS_TRAIT(human_target, TRAIT_CLEANBOT_WHISPERER)) + continue + if((living_pawn.faction.Find(REF(human_target)))) + continue + return human_target + return null + +/datum/ai_planning_subtree/find_patrol_beacon/cleanbot + +/datum/ai_planning_subtree/find_patrol_beacon/cleanbot/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + if(controller.blackboard[BB_POST_CLEAN_COOLDOWN] >= world.time) + return + return ..() + +/datum/pet_command/point_targeting/clean + command_name = "Clean" + command_desc = "Command a cleanbot to clean the mess." + radial_icon = 'icons/obj/service/janitor.dmi' + radial_icon_state = "mop" + speech_commands = list("clean", "mop") + +/datum/pet_command/point_targeting/clean/set_command_target(mob/living/parent, atom/target) + if(isnull(target) || !istype(target, /obj/effect/decal/cleanable)) + return + if(isnull(parent.ai_controller)) + return + if(LAZYACCESS(parent.ai_controller.blackboard[BB_TEMPORARY_IGNORE_LIST], target)) + return + return ..() + +/datum/pet_command/point_targeting/clean/execute_action(datum/ai_controller/basic_controller/bot/controller) + if(controller.reachable_key(BB_CURRENT_PET_TARGET)) + controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_CURRENT_PET_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + + controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) + +#undef BOT_CLEAN_PATH_LIMIT +#undef POST_CLEAN_COOLDOWN diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm new file mode 100644 index 000000000000..c4fb175fc5df --- /dev/null +++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm @@ -0,0 +1,131 @@ +#define WASH_PERIOD 3 SECONDS + +/mob/living/basic/bot/hygienebot + name = "\improper Hygienebot" + desc = "A flying cleaning robot, he'll chase down people who can't shower properly!" + icon = 'icons/mob/silicon/aibots.dmi' + icon_state = "hygienebot" + base_icon_state = "hygienebot" + pass_flags = PASSMOB | PASSFLAPS | PASSTABLE + layer = MOB_UPPER_LAYER + density = FALSE + anchored = FALSE + health = 100 + maxHealth = 100 + path_image_color = "#80dae7" + maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR) + radio_key = /obj/item/encryptionkey/headset_service + radio_channel = RADIO_CHANNEL_SERVICE + bot_type = HYGIENE_BOT + additional_access = /datum/id_trim/job/janitor + hackables = "cleaning service protocols" + ai_controller = /datum/ai_controller/basic_controller/bot/hygienebot + + ///are we currently washing someone? + var/washing = FALSE + ///Visual overlay of the bot spraying water. + var/static/mutable_appearance/water_overlay = mutable_appearance('icons/mob/silicon/aibots.dmi', "hygienebot-water") + ///Visual overlay of the bot commiting warcrimes. + var/static/mutable_appearance/fire_overlay = mutable_appearance('icons/mob/silicon/aibots.dmi', "hygienebot-fire") + ///announcements we say when we find a target + var/static/list/found_announcements = list( + HYGIENEBOT_VOICED_UNHYGIENIC = 'sound/voice/hygienebot/unhygienicclient.ogg', + ) + ///announcements we say when the target keeps moving away + var/static/list/threat_announcements = list( + HYGIENEBOT_VOICED_THREAT_AIRLOCK = 'sound/voice/hygienebot/dragyouout.ogg', + HYGIENEBOT_VOICED_FOUL_SMELL = 'sound/voice/hygienebot/foulsmelling.ogg', + HYGIENEBOT_VOICED_TROGLODYTE = 'sound/voice/hygienebot/troglodyte.ogg', + HYGIENEBOT_VOICED_ARSEHOLE = 'sound/voice/hygienebot/letmeclean.ogg', + HYGIENEBOT_VOICED_THREAT_ARTERIES = 'sound/voice/hygienebot/cutarteries.ogg', + HYGIENEBOT_VOICED_STOP_RUNNING = 'sound/voice/hygienebot/stoprunning.ogg', + ) + ///announcements we say after we have cleaned our target + var/static/list/cleaned_announcements = list( + HYGIENEBOT_VOICED_FUCKING_FINALLY = 'sound/voice/hygienebot/finally.ogg', + HYGIENEBOT_VOICED_THANK_GOD = 'sound/voice/hygienebot/thankgod.ogg', + HYGIENEBOT_VOICED_DEGENERATE = 'sound/voice/hygienebot/degenerate.ogg', + ) + +/mob/living/basic/bot/hygienebot/Initialize(mapload) + . = ..() + update_appearance(UPDATE_ICON) + + generate_ai_speech() + + var/static/list/loc_connections = list( + COMSIG_ATOM_ENTERED = PROC_REF(on_entered), + ) + AddElement(/datum/element/connect_loc, loc_connections) + var/static/list/hat_offsets = list(1, 1) + AddElement(/datum/element/hat_wearer, offsets = hat_offsets) + + ADD_TRAIT(src, TRAIT_SPRAY_PAINTABLE, INNATE_TRAIT) + RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_attack)) + +/mob/living/basic/bot/hygienebot/explode() + var/datum/effect_system/fluid_spread/foam/foam = new + foam.set_up(2, holder = src, location = loc) + foam.start() + return ..() + +/mob/living/basic/bot/hygienebot/generate_speak_list() + var/static/list/finalized_speak_list = (found_announcements + threat_announcements + cleaned_announcements) + return finalized_speak_list + +/mob/living/basic/bot/hygienebot/update_icon_state() + . = ..() + icon_state = "[base_icon_state][bot_mode_flags & BOT_MODE_ON ? "-on" : ""]" + + +/mob/living/basic/bot/hygienebot/update_overlays() + . = ..() + if(bot_mode_flags & BOT_MODE_ON) + . += mutable_appearance(icon, "hygienebot-flame") + + if(!washing) + return + + . += (bot_access_flags & BOT_COVER_EMAGGED) ? fire_overlay : water_overlay + +/mob/living/basic/bot/hygienebot/proc/on_entered(datum/source, atom/movable/movable) + SIGNAL_HANDLER + if(!washing) + return + commence_wash(movable) + +/mob/living/basic/bot/hygienebot/proc/on_attack(datum/source, atom/target) + SIGNAL_HANDLER + . = COMPONENT_HOSTILE_NO_ATTACK + if(washing) + return + set_washing_mode(new_mode = TRUE) + for(var/atom/to_wash in loc) + commence_wash(to_wash) + addtimer(CALLBACK(src, PROC_REF(set_washing_mode), FALSE), WASH_PERIOD) + +/mob/living/basic/bot/hygienebot/proc/set_washing_mode(new_mode) + washing = new_mode + update_appearance(UPDATE_OVERLAYS) + +/mob/living/basic/bot/hygienebot/proc/commence_wash(atom/target) + if(bot_access_flags & BOT_COVER_EMAGGED) + target.fire_act() + return + target.wash(CLEAN_WASH) + +/mob/living/basic/bot/hygienebot/on_bot_movement(atom/movable/source, atom/oldloc, dir, forced) + + if(!washing || !isturf(loc)) + return + + for(var/mob/living/carbon/human in loc) + commence_wash(human) + + +/mob/living/basic/bot/hygienebot/proc/generate_ai_speech() + ai_controller.set_blackboard_key(BB_WASH_FOUND, found_announcements) + ai_controller.set_blackboard_key(BB_WASH_THREATS, threat_announcements) + ai_controller.set_blackboard_key(BB_WASH_DONE, cleaned_announcements) + +#undef WASH_PERIOD diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm new file mode 100644 index 000000000000..2d2bc27079fd --- /dev/null +++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm @@ -0,0 +1,142 @@ +#define BOT_FRUSTRATION_LIMIT 8 + +/datum/ai_controller/basic_controller/bot/hygienebot + blackboard = list( + BB_SALUTE_MESSAGES = list( + "salutes", + "nods in appreciation towards", + ), + BB_WASH_FRUSTRATION = 0, + ) + planning_subtrees = list( + /datum/ai_planning_subtree/manage_unreachable_list, + /datum/ai_planning_subtree/respond_to_summon, + /datum/ai_planning_subtree/wash_people, + /datum/ai_planning_subtree/salute_authority, + /datum/ai_planning_subtree/find_patrol_beacon, + ) + reset_keys = list( + BB_WASH_TARGET, + BB_BEACON_TARGET, + BB_PREVIOUS_BEACON_TARGET, + BB_BOT_SUMMON_TARGET, + ) + +/datum/ai_controller/basic_controller/bot/hygienebot/TryPossessPawn(atom/new_pawn) + . = ..() + if(. & AI_CONTROLLER_INCOMPATIBLE) + return + RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_WASH_TARGET), PROC_REF(reset_anger)) + +/datum/ai_controller/basic_controller/bot/hygienebot/proc/reset_anger() + SIGNAL_HANDLER + + set_blackboard_key(BB_WASH_FRUSTRATION, 0) + + +/datum/ai_planning_subtree/wash_people + +/datum/ai_planning_subtree/wash_people/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/mob/living/basic/bot/bot_pawn = controller.pawn + + var/atom/wash_target = controller.blackboard[BB_WASH_TARGET] + if(QDELETED(wash_target)) + controller.queue_behavior(/datum/ai_behavior/find_valid_wash_targets, BB_WASH_TARGET, bot_pawn.bot_access_flags) + return + + if(get_dist(bot_pawn, wash_target) < 9) + controller.queue_behavior(/datum/ai_behavior/wash_target, BB_WASH_TARGET) + return SUBTREE_RETURN_FINISH_PLANNING + + controller.clear_blackboard_key(BB_WASH_TARGET) //delete if too far + +/datum/ai_behavior/find_valid_wash_targets + action_cooldown = 5 SECONDS + +/datum/ai_behavior/find_valid_wash_targets/perform(seconds_per_tick, datum/ai_controller/controller, target_key, our_access_flags) + . = ..() + var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] + var/atom/found_target + for(var/mob/living/carbon/human/wash_potential in oview(5, controller.pawn)) + + if(found_target) + break + + if(isnull(wash_potential.mind) || wash_potential.stat != CONSCIOUS) + continue + + if(LAZYACCESS(ignore_list, wash_potential)) + continue + + if(our_access_flags & BOT_COVER_EMAGGED) + controller.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, wash_potential, TRUE) + found_target = wash_potential + break + + for(var/atom/clothing in wash_potential.get_equipped_items()) + if(GET_ATOM_BLOOD_DNA_LENGTH(clothing)) + found_target = wash_potential + break + + if(isnull(found_target)) + finish_action(controller, succeeded = FALSE) + return + + controller.set_blackboard_key(target_key, found_target) + finish_action(controller, succeeded = TRUE) + + + +/datum/ai_behavior/find_valid_wash_targets/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + if(!succeeded) + return + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] + announcement.announce(pick(controller.blackboard[BB_WASH_FOUND])) + +/datum/ai_behavior/wash_target + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_MOVE_AND_PERFORM + required_distance = 0 + action_cooldown = 1 SECONDS + +/datum/ai_behavior/wash_target/setup(datum/ai_controller/controller, target_key) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + set_movement_target(controller, target) + +/datum/ai_behavior/wash_target/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key) + . = ..() + var/mob/living/carbon/human/unclean_target = controller.blackboard[target_key] + var/mob/living/basic/living_pawn = controller.pawn + if(QDELETED(unclean_target)) + finish_action(controller, FALSE, target_key) + return + + if(living_pawn.loc == get_turf(unclean_target)) + living_pawn.melee_attack(unclean_target) + finish_action(controller, TRUE, target_key) + return + + var/frustration_count = controller.blackboard[BB_WASH_FRUSTRATION] + controller.set_blackboard_key(BB_WASH_FRUSTRATION, frustration_count + 1) + finish_action(controller, FALSE, target_key) + +/datum/ai_behavior/wash_target/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] + + if(succeeded) + if(controller.blackboard[BB_WASH_FRUSTRATION] > 0) + announcement.announce(pick(controller.blackboard[BB_WASH_DONE])) + controller.clear_blackboard_key(target_key) + return + + if(controller.blackboard[BB_WASH_FRUSTRATION] < BOT_FRUSTRATION_LIMIT) + return + + announcement.announce(pick(controller.blackboard[BB_WASH_THREATS])) + controller.set_blackboard_key(BB_WASH_FRUSTRATION, 0) + +#undef BOT_FRUSTRATION_LIMIT diff --git a/code/modules/mob/living/basic/bots/medbot/medbot.dm b/code/modules/mob/living/basic/bots/medbot/medbot.dm new file mode 100644 index 000000000000..b2f6c6772534 --- /dev/null +++ b/code/modules/mob/living/basic/bots/medbot/medbot.dm @@ -0,0 +1,412 @@ +#define TEND_DAMAGE_INTERACTION "tend_damage_interaction" + +/mob/living/basic/bot/medbot + name = "\improper Medibot" + desc = "A little medical robot. He looks somewhat underwhelmed." + icon = 'icons/mob/silicon/aibots.dmi' + icon_state = "medibot0" + base_icon_state = "medibot" + density = FALSE + anchored = FALSE + health = 20 + maxHealth = 20 + speed = 2 + pass_flags = PASSMOB | PASSFLAPS + status_flags = (CANPUSH | CANSTUN) + ai_controller = /datum/ai_controller/basic_controller/bot/medbot + + maints_access_required = list(ACCESS_ROBOTICS, ACCESS_MEDICAL) + radio_key = /obj/item/encryptionkey/headset_med + radio_channel = RADIO_CHANNEL_MEDICAL + bot_type = MED_BOT + data_hud_type = DATA_HUD_MEDICAL_ADVANCED + hackables = "health processor circuits" + possessed_message = "You are a medbot! Ensure good health among the crew to the best of your ability!" + + additional_access = /datum/id_trim/job/paramedic + announcement_type = /datum/action/cooldown/bot_announcement/medbot + path_image_color = "#d9d9f4" + + ///anouncements when we find a target to heal + var/static/list/wait_announcements = list( + MEDIBOT_VOICED_HOLD_ON = 'sound/voice/medbot/coming.ogg', + MEDIBOT_VOICED_WANT_TO_HELP = 'sound/voice/medbot/help.ogg', + MEDIBOT_VOICED_YOU_ARE_INJURED = 'sound/voice/medbot/injured.ogg', + ) + + ///announcements after we heal someone + var/static/list/afterheal_announcements = list( + MEDIBOT_VOICED_ALL_PATCHED_UP = 'sound/voice/medbot/patchedup.ogg', + MEDIBOT_VOICED_APPLE_A_DAY = 'sound/voice/medbot/apple.ogg', + MEDIBOT_VOICED_FEEL_BETTER = 'sound/voice/medbot/feelbetter.ogg', + ) + + ///announcements when we are healing someone near death + var/static/list/near_death_announcements = list( + MEDIBOT_VOICED_STAY_WITH_ME = 'sound/voice/medbot/no.ogg', + MEDIBOT_VOICED_LIVE = 'sound/voice/medbot/live.ogg', + MEDIBOT_VOICED_NEVER_LOST = 'sound/voice/medbot/lost.ogg', + ) + ///announcements when we are idle + var/static/list/idle_lines = list( + MEDIBOT_VOICED_DELICIOUS = 'sound/voice/medbot/delicious.ogg', + MEDIBOT_VOICED_PLASTIC_SURGEON = 'sound/voice/medbot/surgeon.ogg', + MEDIBOT_VOICED_MASK_ON = 'sound/voice/medbot/radar.ogg', + MEDIBOT_VOICED_ALWAYS_A_CATCH = 'sound/voice/medbot/catch.ogg', + MEDIBOT_VOICED_LIKE_FLIES = 'sound/voice/medbot/flies.ogg', + MEDIBOT_VOICED_SUFFER = 'sound/voice/medbot/why.ogg', + ) + ///announcements when we are emagged + var/static/list/emagged_announcements = list( + MEDIBOT_VOICED_FUCK_YOU = 'sound/voice/medbot/fuck_you.ogg', + MEDIBOT_VOICED_NOT_A_GAME = 'sound/voice/medbot/turn_off.ogg', + MEDIBOT_VOICED_IM_DIFFERENT = 'sound/voice/medbot/im_different.ogg', + MEDIBOT_VOICED_FOURTH_WALL = 'sound/voice/medbot/close.ogg', + MEDIBOT_VOICED_SHINDEMASHOU = 'sound/voice/medbot/shindemashou.ogg', + ) + ///announcements when we are being tipped + var/static/list/tipped_announcements = list( + MEDIBOT_VOICED_WAIT = 'sound/voice/medbot/hey_wait.ogg', + MEDIBOT_VOICED_DONT = 'sound/voice/medbot/please_dont.ogg', + MEDIBOT_VOICED_TRUSTED_YOU = 'sound/voice/medbot/i_trusted_you.ogg', + MEDIBOT_VOICED_NO_SAD = 'sound/voice/medbot/nooo.ogg', + MEDIBOT_VOICED_OH_FUCK = 'sound/voice/medbot/oh_fuck.ogg', + ) + ///announcements when we are being untipped + var/static/list/untipped_announcements = list( + MEDIBOT_VOICED_FORGIVE = 'sound/voice/medbot/forgive.ogg', + MEDIBOT_VOICED_THANKS = 'sound/voice/medbot/thank_you.ogg', + MEDIBOT_VOICED_GOOD_PERSON = 'sound/voice/medbot/youre_good.ogg', + ) + ///announcements when we are worried + var/static/list/worried_announcements = list( + MEDIBOT_VOICED_PUT_BACK = 'sound/voice/medbot/please_put_me_back.ogg', + MEDIBOT_VOICED_IM_SCARED = 'sound/voice/medbot/please_im_scared.ogg', + MEDIBOT_VOICED_NEED_HELP = 'sound/voice/medbot/dont_like.ogg', + MEDIBOT_VOICED_THIS_HURTS = 'sound/voice/medbot/pain_is_real.ogg', + MEDIBOT_VOICED_THE_END = 'sound/voice/medbot/is_this_the_end.ogg', + MEDIBOT_VOICED_NOOO = 'sound/voice/medbot/nooo.ogg', + ) + var/static/list/misc_announcements= list( + MEDIBOT_VOICED_CHICKEN = 'sound/voice/medbot/i_am_chicken.ogg', + ) + /// drop determining variable + var/health_analyzer = /obj/item/healthanalyzer + /// drop determining variable + var/medkit_type = /obj/item/storage/medkit + ///based off medkit_X skins in aibots.dmi for your selection; X goes here IE medskin_tox means skin var should be "tox" + var/skin + /// How much healing do we do at a time? + var/heal_amount = 2.5 + /// Start healing when they have this much damage in a category + var/heal_threshold = 10 + /// What damage type does this bot support. Because the default is brute, if the medkit is brute-oriented there is a slight bonus to healing. set to "all" for it to heal any of the 4 base damage types + var/damage_type_healer = BRUTE + + ///Flags Medbots use to decide how they should be acting. + var/medical_mode_flags = MEDBOT_DECLARE_CRIT | MEDBOT_SPEAK_MODE + //Selections: MEDBOT_DECLARE_CRIT | MEDBOT_STATIONARY_MODE | MEDBOT_SPEAK_MODE | MEDBOT_TIPPED_MODE + + /// techweb linked to the medbot + var/datum/techweb/linked_techweb + ///our tipper + var/datum/weakref/tipper + +/mob/living/basic/bot/medbot/proc/set_speech_keys() + if(isnull(ai_controller)) + return + ai_controller.set_blackboard_key(BB_NEAR_DEATH_SPEECH, near_death_announcements) + ai_controller.set_blackboard_key(BB_WAIT_SPEECH, wait_announcements) + ai_controller.set_blackboard_key(BB_AFTERHEAL_SPEECH, afterheal_announcements) + ai_controller.set_blackboard_key(BB_IDLE_SPEECH, idle_lines) + ai_controller.set_blackboard_key(BB_EMAGGED_SPEECH, emagged_announcements) + ai_controller.set_blackboard_key(BB_WORRIED_ANNOUNCEMENTS, worried_announcements) + +/mob/living/basic/bot/medbot/Initialize(mapload, new_skin) + . = ..() + set_speech_keys() + + if(!isnull(new_skin)) + skin = new_skin + update_appearance() + AddComponent(/datum/component/tippable, \ + tip_time = 3 SECONDS, \ + untip_time = 3 SECONDS, \ + self_right_time = 3.5 MINUTES, \ + pre_tipped_callback = CALLBACK(src, PROC_REF(pre_tip_over)), \ + post_tipped_callback = CALLBACK(src, PROC_REF(after_tip_over)), \ + post_untipped_callback = CALLBACK(src, PROC_REF(after_righted))) + var/static/list/hat_offsets = list(4,-9) + AddElement(/datum/element/hat_wearer, offsets = hat_offsets) + RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attack)) + + + /* + if(!HAS_TRAIT(SSstation, STATION_TRAIT_MEDBOT_MANIA) || !mapload || !is_station_level(z)) + return INITIALIZE_HINT_LATELOAD + + skin = "advanced" + update_appearance(UPDATE_OVERLAYS) + damage_type_healer = HEAL_ALL_DAMAGE + if(prob(50)) + name += ", PhD." + */ + return INITIALIZE_HINT_LATELOAD + +/mob/living/basic/bot/medbot/LateInitialize() + . = ..() + if(!CONFIG_GET(flag/no_default_techweb_link) && !linked_techweb) + linked_techweb = SSresearch.science_tech + +/mob/living/basic/bot/medbot/update_icon_state() + . = ..() + if(!(bot_mode_flags & BOT_MODE_ON)) + icon_state = "[base_icon_state]0" + return + if(HAS_TRAIT(src, TRAIT_INCAPACITATED)) + icon_state = "[base_icon_state]a" + return + if(mode == BOT_HEALING) + icon_state = "[base_icon_state]s[medical_mode_flags & MEDBOT_STATIONARY_MODE]" + return + icon_state = "[base_icon_state][medical_mode_flags & MEDBOT_STATIONARY_MODE ? 2 : 1]" //Bot has yellow light to indicate stationary mode. + +/mob/living/basic/bot/medbot/update_overlays() + . = ..() + if(skin) + . += "medskin_[skin]" + +//this is sin +/mob/living/basic/bot/medbot/generate_speak_list() + var/static/list/finalized_speak_list = (idle_lines + wait_announcements + afterheal_announcements + near_death_announcements + emagged_announcements + tipped_announcements + untipped_announcements + worried_announcements + misc_announcements) + return finalized_speak_list + + +/mob/living/basic/bot/medbot/attack_paw(mob/user, list/modifiers) + return attack_hand(user, modifiers) + +/mob/living/basic/bot/medbot/multitool_act(mob/living/user, obj/item/multitool/tool) + if(!QDELETED(tool.buffer) && istype(tool.buffer, /datum/techweb)) + linked_techweb = tool.buffer + return TOOL_ACT_TOOLTYPE_SUCCESS + +// Variables sent to TGUI +/mob/living/basic/bot/medbot/ui_data(mob/user) + var/list/data = ..() + if((bot_access_flags & BOT_CONTROL_PANEL_OPEN) || issilicon(user) || isAdminGhostAI(user)) + data["custom_controls"]["heal_threshold"] = heal_threshold + data["custom_controls"]["speaker"] = medical_mode_flags & MEDBOT_SPEAK_MODE + data["custom_controls"]["crit_alerts"] = medical_mode_flags & MEDBOT_DECLARE_CRIT + data["custom_controls"]["stationary_mode"] = medical_mode_flags & MEDBOT_STATIONARY_MODE + data["custom_controls"]["sync_tech"] = TRUE + return data + +// Actions received from TGUI +/mob/living/basic/bot/medbot/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(. || !isliving(ui.user) || !(bot_access_flags & BOT_CONTROL_PANEL_OPEN) && !(ui.user.has_unlimited_silicon_privilege)) + return + var/mob/living/our_user = ui.user + switch(action) + if("heal_threshold") + var/adjust_num = round(text2num(params["threshold"])) + heal_threshold = adjust_num + if(heal_threshold < 5) + heal_threshold = 5 + if(heal_threshold > 75) + heal_threshold = 75 + if("speaker") + medical_mode_flags ^= MEDBOT_SPEAK_MODE + if("crit_alerts") + medical_mode_flags ^= MEDBOT_DECLARE_CRIT + if("stationary_mode") + medical_mode_flags ^= MEDBOT_STATIONARY_MODE + if("sync_tech") + if(!linked_techweb) + to_chat(our_user, span_notice("No research techweb connected.")) + return + var/oldheal_amount = heal_amount + var/tech_boosters + for(var/index in linked_techweb.researched_designs) + var/datum/design/surgery/healing/design = SSresearch.techweb_design_by_id(index) + if(!istype(design)) + continue + tech_boosters++ + if(tech_boosters) + heal_amount = (round(tech_boosters * 0.5, 0.1) * initial(heal_amount)) + initial(heal_amount) //every 2 tend wounds tech gives you an extra 100% healing, adjusting for unique branches (combo is bonus) + if(oldheal_amount < heal_amount) + speak("New knowledge found! Surgical efficacy improved to [round(heal_amount/initial(heal_amount)*100)]%!") + + update_appearance() + +/mob/living/basic/bot/medbot/emag_act(mob/user, obj/item/card/emag/emag_card) + . = ..() + if(!(bot_access_flags & BOT_COVER_EMAGGED)) + return + medical_mode_flags &= ~MEDBOT_DECLARE_CRIT + balloon_alert(user, "reagent synthesis circuits shorted") + audible_message(span_danger("[src] buzzes oddly!")) + flick("medibot_spark", src) + playsound(src, SFX_SPARKS, 75, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + return TRUE + +/mob/living/basic/bot/medbot/examine() + . = ..() + if(!(medical_mode_flags & MEDBOT_TIPPED_MODE)) + return + var/static/list/panic_state = list( + "It appears to be tipped over, and is quietly waiting for someone to set it right.", + "It is tipped over and requesting help.", + "They are tipped over and appear visibly distressed.", + span_warning("They are tipped over and visibly panicking!"), + span_warning(span_bold("They are freaking out from being tipped over!")) + ) + . += pick(panic_state) +/* + * Proc used in a callback for before this medibot is tipped by the tippable component. + * + * user - the mob who is tipping us over + */ +/mob/living/basic/bot/medbot/proc/pre_tip_over(mob/user) + speak(pick(worried_announcements)) + +/* + * Proc used in a callback for after this medibot is tipped by the tippable component. + * + * user - the mob who tipped us over + */ +/mob/living/basic/bot/medbot/proc/after_tip_over(mob/user) + medical_mode_flags |= MEDBOT_TIPPED_MODE + tipper = WEAKREF(user) + playsound(src, 'sound/machines/warning-buzzer.ogg', 50) + if(prob(10)) + speak("PSYCH ALERT: Crewmember [user.name] recorded displaying antisocial tendencies torturing bots in [get_area(src)]. Please schedule psych evaluation.", radio_channel) + +/* + * Proc used in a callback for after this medibot is righted, either by themselves or by a mob, by the tippable component. + * + * user - the mob who righted us. Can be null. + */ +/mob/living/basic/bot/medbot/proc/after_righted(mob/user) + var/mob/tipper_mob = isnull(user) ? null : tipper?.resolve() + tipper = null + medical_mode_flags &= ~MEDBOT_TIPPED_MODE + if(isnull(tipper_mob)) + return + if(tipper_mob == user) + speak(MEDIBOT_VOICED_FORGIVE) + return + speak(pick(untipped_announcements)) + +/mob/living/basic/bot/medbot/proc/pre_attack(mob/living/puncher, atom/target) + SIGNAL_HANDLER + + if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED)) + return + if(!iscarbon(target)) + return + INVOKE_ASYNC(src, PROC_REF(medicate_patient), target) + return COMPONENT_HOSTILE_NO_ATTACK + +/mob/living/basic/bot/medbot/proc/medicate_patient(mob/living/carbon/human/patient) + if(DOING_INTERACTION(src, TEND_DAMAGE_INTERACTION)) + return + + if((damage_type_healer == HEAL_ALL_DAMAGE && patient.get_total_damage() <= heal_threshold) || (!(damage_type_healer == HEAL_ALL_DAMAGE) && patient.get_current_damage_of_type(damage_type_healer) <= heal_threshold)) + to_chat(src, "[patient] is healthy! Your programming prevents you from tending the wounds of anyone with less than [heal_threshold + 1] [damage_type_healer == HEAL_ALL_DAMAGE ? "total" : damage_type_healer] damage.") + return + + update_bot_mode(new_mode = BOT_HEALING, update_hud = FALSE) + patient.visible_message("[src] is trying to tend the wounds of [patient]", span_userdanger("[src] is trying to tend your wounds!")) + if(!do_after(src, delay = 2 SECONDS, target = patient, interaction_key = TEND_DAMAGE_INTERACTION)) + update_bot_mode(new_mode = BOT_IDLE) + return + var/modified_heal_amount = heal_amount + var/done_healing = FALSE + if(damage_type_healer == BRUTE && medkit_type == /obj/item/storage/medkit/brute) + modified_heal_amount *= 1.1 + if(bot_access_flags & BOT_COVER_EMAGGED) + patient.reagents?.add_reagent(/datum/reagent/toxin/chloralhydrate, 5) + log_combat(src, patient, "pretended to tend wounds on", "internal tools") + else if(damage_type_healer == HEAL_ALL_DAMAGE) + patient.heal_ordered_damage(amount = modified_heal_amount, damage_types = list(BRUTE, BURN, TOX, OXY)) + log_combat(src, patient, "tended the wounds of", "internal tools") + if(patient.get_total_damage() <= heal_threshold) + done_healing = TRUE + else + patient.heal_damage_type(heal_amount = modified_heal_amount, damagetype = damage_type_healer) + log_combat(src, patient, "tended the wounds of", "internal tools") + if(patient.get_current_damage_of_type(damage_type_healer) <= heal_threshold) + done_healing = TRUE + patient.visible_message(span_notice("[src] tends the wounds of [patient]!"), "[span_infoplain(span_green("[src] tends your wounds!"))]") + if(done_healing) + visible_message(span_infoplain("[src] places its tools back into itself.")) + to_chat(src, "[patient] is now healthy!") + update_bot_mode(new_mode = BOT_IDLE) + return + + if(CanReach(patient)) + melee_attack(patient) + + +/mob/living/basic/bot/medbot/autopatrol + bot_mode_flags = BOT_MODE_ON | BOT_MODE_AUTOPATROL | BOT_MODE_REMOTE_ENABLED | BOT_MODE_GHOST_CONTROLLABLE | BOT_MODE_ROUNDSTART_POSSESSION + +/mob/living/basic/bot/medbot/stationary + medical_mode_flags = MEDBOT_DECLARE_CRIT | MEDBOT_STATIONARY_MODE | MEDBOT_SPEAK_MODE + +/mob/living/basic/bot/medbot/mysterious + name = "\improper Mysterious Medibot" + desc = "International Medibot of mystery." + skin = "bezerk" + damage_type_healer = HEAL_ALL_DAMAGE + heal_amount = 10 + +/mob/living/basic/bot/medbot/derelict + name = "\improper Old Medibot" + desc = "Looks like it hasn't been modified since the late 2080s." + skin = "bezerk" + damage_type_healer = HEAL_ALL_DAMAGE + medical_mode_flags = MEDBOT_SPEAK_MODE + heal_threshold = 0 + heal_amount = 5 + +/mob/living/basic/bot/medbot/nukie + name = "Oppenheimer" + desc = "A medibot stolen from a Nanotrasen station and upgraded by the Syndicate. Despite their best efforts at reprogramming, it still appears visibly upset near nuclear explosives." + skin = "bezerk" + health = 40 + maxHealth = 40 + maints_access_required = list(ACCESS_SYNDICATE) + radio_key = /obj/item/encryptionkey/syndicate + radio_channel = RADIO_CHANNEL_SYNDICATE + damage_type_healer = HEAL_ALL_DAMAGE + faction = list(ROLE_SYNDICATE) + heal_threshold = 0 + heal_amount = 5 + additional_access = /datum/id_trim/syndicom/crew + +/mob/living/basic/bot/medbot/nukie/Initialize(mapload, new_skin) + . = ..() + RegisterSignal(SSdcs, COMSIG_GLOB_NUKE_DEVICE_DISARMED, PROC_REF(nuke_disarm)) + RegisterSignal(SSdcs, COMSIG_GLOB_NUKE_DEVICE_ARMED, PROC_REF(nuke_arm)) + RegisterSignal(SSdcs, COMSIG_GLOB_NUKE_DEVICE_DETONATING, PROC_REF(nuke_detonate)) + internal_radio.set_frequency(FREQ_SYNDICATE) + internal_radio.freqlock = RADIO_FREQENCY_LOCKED + +/mob/living/basic/bot/medbot/nukie/proc/nuke_disarm() + SIGNAL_HANDLER + + INVOKE_ASYNC(src, PROC_REF(speak), pick(untipped_announcements)) + +/mob/living/basic/bot/medbot/nukie/proc/nuke_arm() + SIGNAL_HANDLER + + INVOKE_ASYNC(src, PROC_REF(speak), pick(worried_announcements)) + +/mob/living/basic/bot/medbot/nukie/proc/nuke_detonate() + SIGNAL_HANDLER + + INVOKE_ASYNC(src, PROC_REF(speak), pick(emagged_announcements)) + +#undef TEND_DAMAGE_INTERACTION diff --git a/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm new file mode 100644 index 000000000000..100366e0b12c --- /dev/null +++ b/code/modules/mob/living/basic/bots/medbot/medbot_ai.dm @@ -0,0 +1,210 @@ +#define BOT_PATIENT_PATH_LIMIT 20 +/datum/ai_controller/basic_controller/bot/medbot + planning_subtrees = list( + /datum/ai_planning_subtree/manage_unreachable_list, + /datum/ai_planning_subtree/respond_to_summon, + /datum/ai_planning_subtree/handle_medbot_speech, + /datum/ai_planning_subtree/find_and_hunt_target/patients_in_crit, + /datum/ai_planning_subtree/treat_wounded_target, + /datum/ai_planning_subtree/salute_authority, + /datum/ai_planning_subtree/find_patrol_beacon, + ) + ai_movement = /datum/ai_movement/jps/bot/medbot + reset_keys = list( + BB_PATIENT_TARGET, + BB_BEACON_TARGET, + BB_PREVIOUS_BEACON_TARGET, + BB_BOT_SUMMON_TARGET, + ) + ai_traits = PAUSE_DURING_DO_AFTER + +/datum/ai_movement/jps/bot/medbot + +// only AI isnt allowed to move when this flag is set, sentient players can +/datum/ai_movement/jps/bot/medbot/allowed_to_move(datum/move_loop/source) + var/datum/ai_controller/controller = source.extra_info + var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn + if(bot_pawn.medical_mode_flags & MEDBOT_STATIONARY_MODE) + return FALSE + return ..() + + +/datum/ai_planning_subtree/treat_wounded_target + +/datum/ai_planning_subtree/treat_wounded_target/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn + if(bot_pawn.medical_mode_flags & MEDBOT_TIPPED_MODE) + controller.clear_blackboard_key(BB_PATIENT_TARGET) + return + var/reach_distance = (bot_pawn.medical_mode_flags & MEDBOT_STATIONARY_MODE) ? 1 : BOT_PATIENT_PATH_LIMIT + if(controller.reachable_key(BB_PATIENT_TARGET, reach_distance)) + controller.queue_behavior(/datum/ai_behavior/tend_to_patient, BB_PATIENT_TARGET, bot_pawn.heal_threshold, bot_pawn.damage_type_healer, bot_pawn.bot_access_flags) + return SUBTREE_RETURN_FINISH_PLANNING + + controller.queue_behavior(/datum/ai_behavior/find_suitable_patient, BB_PATIENT_TARGET, bot_pawn.heal_threshold, bot_pawn.damage_type_healer, bot_pawn.medical_mode_flags, bot_pawn.bot_access_flags) + +/datum/ai_behavior/find_suitable_patient + var/search_range = 7 + action_cooldown = 2 SECONDS + +/datum/ai_behavior/find_suitable_patient/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, threshold, heal_type, mode_flags, access_flags) + . = ..() + search_range = (mode_flags & MEDBOT_STATIONARY_MODE) ? 1 : initial(search_range) + var/list/ignore_keys = controller.blackboard[BB_TEMPORARY_IGNORE_LIST] + for(var/mob/living/carbon/human/treatable_target in oview(search_range, controller.pawn)) + if(LAZYACCESS(ignore_keys, treatable_target) || treatable_target.stat == DEAD) + continue + if((access_flags & BOT_COVER_EMAGGED) && treatable_target.stat == CONSCIOUS) + controller.set_blackboard_key(BB_PATIENT_TARGET, treatable_target) + break + if((heal_type == HEAL_ALL_DAMAGE)) + if(treatable_target.get_total_damage() > threshold) + controller.set_blackboard_key(BB_PATIENT_TARGET, treatable_target) + break + continue + if(treatable_target.get_current_damage_of_type(damagetype = heal_type) > threshold) + controller.set_blackboard_key(BB_PATIENT_TARGET, treatable_target) + break + + finish_action(controller, controller.blackboard_key_exists(BB_PATIENT_TARGET)) + +/datum/ai_behavior/find_suitable_patient/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + if(!succeeded || get_dist(controller.pawn, controller.blackboard[target_key]) <= 1) + return + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] + announcement?.announce(pick(controller.blackboard[BB_WAIT_SPEECH])) + +/datum/ai_behavior/tend_to_patient + behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH + +/datum/ai_behavior/tend_to_patient/setup(datum/ai_controller/controller, target_key) + . = ..() + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target)) + return FALSE + set_movement_target(controller, target) + +/datum/ai_behavior/tend_to_patient/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, threshold, damage_type_healer, access_flags) + . = ..() + var/mob/living/carbon/human/patient = controller.blackboard[target_key] + if(QDELETED(patient) || patient.stat == DEAD) + finish_action(controller, FALSE, target_key) + return + if(check_if_healed(patient, threshold, damage_type_healer, access_flags)) + finish_action(controller, TRUE, target_key, healed_target = TRUE) + return + + var/mob/living/basic/bot/bot_pawn = controller.pawn + if(patient.stat >= HARD_CRIT && prob(5)) + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] + announcement?.announce(pick(controller.blackboard[BB_NEAR_DEATH_SPEECH])) + bot_pawn.melee_attack(patient) + finish_action(controller, TRUE, target_key) + +// only clear the target if they get healed +/datum/ai_behavior/tend_to_patient/finish_action(datum/ai_controller/controller, succeeded, target_key, healed_target = FALSE) + . = ..() + if(!succeeded) + return + var/atom/target = controller.blackboard[target_key] + if(QDELETED(target) || !healed_target) + return + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] + announcement?.announce(pick(controller.blackboard[BB_AFTERHEAL_SPEECH])) + controller.clear_blackboard_key(target_key) + +/datum/ai_behavior/tend_to_patient/proc/check_if_healed(mob/living/carbon/human/patient, threshold, damage_type_healer, access_flags) + if(access_flags & BOT_COVER_EMAGGED) + return (patient.stat > CONSCIOUS) + var/patient_damage = (damage_type_healer == HEAL_ALL_DAMAGE) ? patient.get_total_damage() : patient.get_current_damage_of_type(damagetype = damage_type_healer) + return (patient_damage <= threshold) + + +/datum/ai_planning_subtree/handle_medbot_speech + var/speech_chance = 5 + +/datum/ai_planning_subtree/handle_medbot_speech/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn + //we cant speak! + if(!(bot_pawn.medical_mode_flags & MEDBOT_SPEAK_MODE)) + return + + var/currently_tipped = bot_pawn.medical_mode_flags & MEDBOT_TIPPED_MODE + speech_chance = ((bot_pawn.bot_access_flags & BOT_COVER_EMAGGED) || currently_tipped) ? 15 : initial(speech_chance) + + if(!SPT_PROB(speech_chance, seconds_per_tick)) + return + + controller.queue_behavior(/datum/ai_behavior/handle_medbot_speech, BB_ANNOUNCE_ABILITY, bot_pawn.mode, bot_pawn.bot_access_flags, currently_tipped) + +/datum/ai_behavior/handle_medbot_speech + action_cooldown = 20 SECONDS + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/handle_medbot_speech/perform(seconds_per_tick, datum/ai_controller/controller, announce_key, mode, cover_flags, currently_tipped) + . = ..() + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[announce_key] + var/list/speech_to_pick_from + + if(currently_tipped) + speech_to_pick_from = controller.blackboard[BB_WORRIED_ANNOUNCEMENTS] + else if(cover_flags & BOT_COVER_EMAGGED) + speech_to_pick_from = controller.blackboard[BB_EMAGGED_SPEECH] + else if(mode == BOT_IDLE) + speech_to_pick_from = controller.blackboard[BB_IDLE_SPEECH] + var/mob/living/living_pawn = controller.pawn + + if(locate(/obj/item/clothing/head/costume/chicken) in living_pawn) + speech_to_pick_from += MEDIBOT_VOICED_CHICKEN + + if(!length(speech_to_pick_from)) + finish_action(controller, FALSE) + return + + announcement.announce(pick(speech_to_pick_from)) + finish_action(controller, TRUE) + +/datum/ai_planning_subtree/find_and_hunt_target/patients_in_crit + target_key = BB_PATIENT_IN_CRIT + hunting_behavior = /datum/ai_behavior/announce_patient + finding_behavior = /datum/ai_behavior/find_hunt_target/patient_in_crit + hunt_targets = list(/mob/living/carbon/human) + finish_planning = FALSE + +/datum/ai_planning_subtree/find_and_hunt_target/patients_in_crit/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick) + var/mob/living/basic/bot/medbot/bot_pawn = controller.pawn + if(!(bot_pawn.medical_mode_flags & MEDBOT_DECLARE_CRIT)) + return + return ..() + +/datum/ai_behavior/find_hunt_target/patient_in_crit + +/datum/ai_behavior/find_hunt_target/patient_in_crit/valid_dinner(mob/living/source, mob/living/carbon/human/patient, radius) + if(patient.stat < UNCONSCIOUS || isnull(patient.mind)) + return FALSE + return can_see(source, patient, radius) + +/datum/ai_behavior/announce_patient + action_cooldown = 3 MINUTES + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/announce_patient/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key) + . = ..() + var/mob/living/living_target = controller.blackboard[target_key] + if(QDELETED(living_target)) + finish_action(controller, FALSE, target_key) + return + var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY] + if(QDELETED(announcement)) + finish_action(controller, FALSE, target_key) + return + var/text_to_announce = "Medical emergency! [living_target] is in critical condition at [get_area(living_target)]!" + announcement.announce(text_to_announce, controller.blackboard[BB_RADIO_CHANNEL]) + finish_action(controller, TRUE, target_key) + +/datum/ai_behavior/announce_patient/finish_action(datum/ai_controller/controller, succeeded, target_key) + . = ..() + controller.clear_blackboard_key(target_key) + +#undef BOT_PATIENT_PATH_LIMIT diff --git a/code/modules/mob/living/basic/farm_animals/bee/_bee.dm b/code/modules/mob/living/basic/farm_animals/bee/_bee.dm index 85d03a4aa4f8..997705880ab7 100644 --- a/code/modules/mob/living/basic/farm_animals/bee/_bee.dm +++ b/code/modules/mob/living/basic/farm_animals/bee/_bee.dm @@ -117,8 +117,8 @@ /mob/living/basic/bee/proc/pre_attack(mob/living/puncher, atom/target) SIGNAL_HANDLER - if(istype(target, /obj/machinery/hydroponics)) - var/obj/machinery/hydroponics/hydro = target + if(target.GetComponent(/datum/component/plant_growing)) + var/atom/movable/hydro = target pollinate(hydro) return COMPONENT_HOSTILE_NO_ATTACK @@ -166,25 +166,11 @@ add_overlay("[icon_base]_wings") -/mob/living/basic/bee/proc/pollinate(obj/machinery/hydroponics/hydro) - if(!hydro.can_bee_pollinate()) - return FALSE - hydro.recent_bee_visit = TRUE - addtimer(VARSET_CALLBACK(hydro, recent_bee_visit, FALSE), BEE_TRAY_RECENT_VISIT) - - var/growth = health //Health also means how many bees are in the swarm, roughly. - //better healthier plants! - hydro.adjust_plant_health(growth*0.5) - if(prob(BEE_POLLINATE_PEST_CHANCE)) - hydro.adjust_pestlevel(-10) - if(prob(BEE_POLLINATE_YIELD_CHANCE)) - hydro.myseed.adjust_yield(1) - hydro.yieldmod = 2 - if(prob(BEE_POLLINATE_POTENCY_CHANCE)) - hydro.myseed.adjust_potency(1) +/mob/living/basic/bee/proc/pollinate(atom/movable/hydro) + SEND_SIGNAL(hydro, COMSIG_TRY_POLLINATE) if(beehome) - beehome.bee_resources = min(beehome.bee_resources + growth, 100) + beehome.bee_resources = min(beehome.bee_resources + health, 100) /mob/living/basic/bee/proc/assign_reagent(datum/reagent/toxin) if(!istype(toxin)) diff --git a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm index 3d2d81bf9c19..9d2a9760470b 100644 --- a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm +++ b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm @@ -1,14 +1,14 @@ /datum/ai_behavior/hunt_target/pollinate always_reset_target = TRUE -/datum/ai_behavior/hunt_target/pollinate/target_caught(mob/living/hunter, obj/machinery/hydroponics/hydro_target) +/datum/ai_behavior/hunt_target/pollinate/target_caught(mob/living/hunter, atom/movable/hydro_target) var/datum/callback/callback = CALLBACK(hunter, TYPE_PROC_REF(/mob/living/basic/bee, pollinate), hydro_target) callback.Invoke() /datum/ai_behavior/find_hunt_target/pollinate -/datum/ai_behavior/find_hunt_target/pollinate/valid_dinner(mob/living/source, obj/machinery/hydroponics/dinner, radius) - if(!dinner.can_bee_pollinate()) +/datum/ai_behavior/find_hunt_target/pollinate/valid_dinner(mob/living/source, atom/movable/dinner, radius) + if(SEND_SIGNAL(dinner, COMSIG_GROWER_CHECK_POLLINATED)) return FALSE return can_see(source, dinner, radius) diff --git a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_subtree.dm b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_subtree.dm index 242eb9ae4d7e..932d038a4bda 100644 --- a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_subtree.dm +++ b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_subtree.dm @@ -81,6 +81,6 @@ target_key = BB_TARGET_HYDRO hunting_behavior = /datum/ai_behavior/hunt_target/pollinate finding_behavior = /datum/ai_behavior/find_hunt_target/pollinate - hunt_targets = list(/obj/machinery/hydroponics) + hunt_targets = list(/obj/machinery/growing) hunt_range = 10 hunt_chance = 85 diff --git a/code/modules/mob/living/basic/guardian/guardian_creator.dm b/code/modules/mob/living/basic/guardian/guardian_creator.dm index f7bf6f7dc12a..23a1710478ac 100644 --- a/code/modules/mob/living/basic/guardian/guardian_creator.dm +++ b/code/modules/mob/living/basic/guardian/guardian_creator.dm @@ -104,10 +104,10 @@ GLOBAL_LIST_INIT(guardian_radial_images, setup_guardian_radial()) if(LAZYLEN(candidates)) var/mob/dead/observer/candidate = pick(candidates) spawn_guardian(user, candidate, guardian_path) - used = TRUE SEND_SIGNAL(src, COMSIG_TRAITOR_ITEM_USED(type)) else to_chat(user, failure_message) + used = FALSE /// Actually create our guy /obj/item/guardian_creator/proc/spawn_guardian(mob/living/user, mob/dead/candidate, guardian_path) diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling.dm b/code/modules/mob/living/basic/jungle/seedling/seedling.dm index 61a1a59f338a..0968ef6bc5b8 100644 --- a/code/modules/mob/living/basic/jungle/seedling/seedling.dm +++ b/code/modules/mob/living/basic/jungle/seedling/seedling.dm @@ -86,7 +86,7 @@ /mob/living/basic/seedling/proc/pre_attack(mob/living/puncher, atom/target) SIGNAL_HANDLER - if(istype(target, /obj/machinery/hydroponics)) + if(target.GetComponent(/datum/component/plant_growing)) treat_hydro_tray(target) return COMPONENT_HOSTILE_NO_ATTACK @@ -99,24 +99,33 @@ ///seedlings can water trays, remove weeds, or remove dead plants -/mob/living/basic/seedling/proc/treat_hydro_tray(obj/machinery/hydroponics/hydro) - - if(hydro.plant_status == HYDROTRAY_PLANT_DEAD) - balloon_alert(src, "dead plant removed") - hydro.set_seed(null) +/mob/living/basic/seedling/proc/treat_hydro_tray(atom/movable/hydro) + var/datum/component/plant_growing/growing = hydro.GetComponent(/datum/component/plant_growing) + if(!growing) return - if(hydro.weedlevel > 0) - balloon_alert(src, "weeds uprooted") - hydro.set_weedlevel(0) - return + for(var/item as anything in growing.managed_seeds) + var/obj/item/seeds/seed = growing.managed_seeds[item] + if(!seed) + continue + var/datum/component/growth_information/info = seed.GetComponent(/datum/component/growth_information) + + if(info.plant_state == HYDROTRAY_PLANT_DEAD) + balloon_alert(src, "dead plant removed") + SEND_SIGNAL(hydro, COMSIG_REMOVE_PLANT, item) + return + + if(growing.weed_level > 0) + balloon_alert(src, "weeds uprooted") + SEND_SIGNAL(hydro, COMSIG_PLANT_ADJUST_WEED, -10) + return var/list/can_reagents = held_can?.reagents.reagent_list if(!length(can_reagents)) return - if((locate(/datum/reagent/water) in can_reagents) && (hydro.waterlevel < hydro.maxwater)) + if((locate(/datum/reagent/water) in can_reagents) && (growing.water_precent < 100)) INVOKE_ASYNC(held_can, TYPE_PROC_REF(/obj/item, melee_attack_chain), src, hydro) return @@ -324,12 +333,17 @@ /datum/action/cooldown/mob_cooldown/solarbeam/proc/launch_beam(mob/living/firer, turf/target_turf) for(var/atom/target_atom as anything in target_turf) - if(istype(target_atom, /obj/machinery/hydroponics)) - var/obj/machinery/hydroponics/hydro = target_atom - hydro.adjust_plant_health(10) + if(target_atom.GetComponent(/datum/component/plant_growing)) + var/datum/component/plant_growing/growing = target_atom.GetComponent(/datum/component/plant_growing) + for(var/item as anything in growing.managed_seeds) + var/obj/item/seeds/seed = growing.managed_seeds[item] + if(!seed) + continue + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, 10) + new /obj/effect/temp_visual/heal(target_turf, COLOR_HEALING_CYAN) - if(!isliving(target_atom)) + if(!isliving(target_atom) || istype(target_atom, /mob/living/basic/pet/potty)) continue var/mob/living/living_target = target_atom diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm b/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm index 2ed4811e46f2..47aaa3beb227 100644 --- a/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm +++ b/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm @@ -33,7 +33,7 @@ target_key = BB_HYDROPLANT_TARGET finding_behavior = /datum/ai_behavior/find_and_set/treatable_hydro hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/treat_hydroplant - hunt_targets = list(/obj/machinery/hydroponics) + hunt_targets = list(/obj/machinery/growing/tray) hunt_range = 7 /datum/ai_behavior/find_and_set/treatable_hydro @@ -45,13 +45,17 @@ var/weedlevel_threshold = controller.blackboard[BB_WEEDLEVEL_THRESHOLD] var/watering_can = locate(/obj/item/reagent_containers/cup/watering_can) in living_pawn - for(var/obj/machinery/hydroponics/hydro in oview(search_range, controller.pawn)) - if(isnull(hydro.myseed)) + for(var/atom/movable/hydro in oview(search_range, controller.pawn)) + if(!hydro.GetComponent(/datum/component/plant_growing)) continue - if(hydro.waterlevel < waterlevel_threshold && watering_can) + if(!(locate(/obj/item/seeds) in hydro.contents)) + continue + + var/datum/component/plant_growing/grow = hydro.GetComponent(/datum/component/plant_growing) + if(grow.water_precent < waterlevel_threshold && watering_can) possible_trays += hydro continue - if(hydro.weedlevel > weedlevel_threshold || hydro.plant_status == HYDROTRAY_PLANT_DEAD) + if(grow.weed_level > weedlevel_threshold) possible_trays += hydro continue @@ -62,12 +66,21 @@ hunt_cooldown = 2 SECONDS always_reset_target = TRUE -/datum/ai_behavior/hunt_target/unarmed_attack_target/treat_hydroplant/target_caught(mob/living/living_pawn, obj/machinery/hydroponics/hydro_target) - if(QDELETED(hydro_target) || QDELETED(hydro_target.myseed)) +/datum/ai_behavior/hunt_target/unarmed_attack_target/treat_hydroplant/target_caught(mob/living/living_pawn, atom/movable/hydro_target) + var/datum/component/plant_growing/growing = hydro_target.GetComponent(/datum/component/plant_growing) + if(!growing) + return + + if(QDELETED(hydro_target)) return - if(hydro_target.plant_status == HYDROTRAY_PLANT_DEAD) - living_pawn.manual_emote("weeps...") //weep over the dead plants + for(var/item as anything in growing.managed_seeds) + var/obj/item/seeds/seed = growing.managed_seeds[item] + if(!seed) + continue + var/datum/component/growth_information/info = seed.GetComponent(/datum/component/growth_information) + if(info.plant_state == HYDROTRAY_PLANT_DEAD) + living_pawn.manual_emote("weeps...") //weep over the dead plants return ..() @@ -75,7 +88,7 @@ target_key = BB_BEAMABLE_HYDROPLANT_TARGET finding_behavior = /datum/ai_behavior/find_and_set/beamable_hydroplants hunting_behavior = /datum/ai_behavior/hunt_target/use_ability_on_target/solarbeam - hunt_targets = list(/obj/machinery/hydroponics) + hunt_targets = list(/obj/machinery/growing) hunt_range = 7 /datum/ai_planning_subtree/find_and_hunt_target/beamable_hydroplants/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) @@ -100,11 +113,21 @@ /datum/ai_behavior/find_and_set/beamable_hydroplants/search_tactic(datum/ai_controller/controller, locate_path, search_range) var/list/possible_trays = list() - for(var/obj/machinery/hydroponics/hydro in oview(search_range, controller.pawn)) - if(isnull(hydro.myseed)) + for(var/atom/movable/hydro in oview(search_range, controller.pawn)) + if(!hydro.GetComponent(/datum/component/plant_growing)) continue - if(hydro.plant_health < hydro.myseed.endurance) - possible_trays += hydro + if(!(locate(/obj/item/seeds) in hydro.contents)) + continue + + var/datum/component/plant_growing/growing = hydro.GetComponent(/datum/component/plant_growing) + for(var/item as anything in growing.managed_seeds) + var/obj/item/seeds/seed = growing.managed_seeds[item] + if(!seed) + continue + var/datum/component/growth_information/info = seed.GetComponent(/datum/component/growth_information) + if(info.health_value < seed.endurance) + possible_trays |= hydro + break if(possible_trays.len) return pick(possible_trays) diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm index 53054052e58a..030448e7d309 100644 --- a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm +++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm @@ -17,6 +17,7 @@ /datum/ai_planning_subtree/find_and_hunt_target/baby_egg, /datum/ai_planning_subtree/mine_walls, ) + can_idle = FALSE // we want these to be running always /datum/ai_controller/basic_controller/babygrub blackboard = list( @@ -36,6 +37,7 @@ /datum/ai_planning_subtree/flee_target, /datum/ai_planning_subtree/look_for_adult, ) + can_idle = FALSE // we want these to be running always ///consume food! /datum/ai_planning_subtree/find_and_hunt_target/hunt_ores diff --git a/code/modules/mob/living/basic/lavaland/mook/mook_ai.dm b/code/modules/mob/living/basic/lavaland/mook/mook_ai.dm index cf79eb06aa60..504166930614 100644 --- a/code/modules/mob/living/basic/lavaland/mook/mook_ai.dm +++ b/code/modules/mob/living/basic/lavaland/mook/mook_ai.dm @@ -26,6 +26,7 @@ GLOBAL_LIST_INIT(mook_commands, list( /datum/ai_planning_subtree/mine_walls/mook, /datum/ai_planning_subtree/wander_away_from_village, ) + can_idle = FALSE // these guys are intended to operate even if nobody's around ///check for faction if not a ash walker, otherwise just attack /datum/targeting_strategy/basic/mook/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) diff --git a/code/modules/mob/living/basic/pets/sloth.dm b/code/modules/mob/living/basic/pets/sloth.dm index 0b1546ccf93d..048f9a1b4870 100644 --- a/code/modules/mob/living/basic/pets/sloth.dm +++ b/code/modules/mob/living/basic/pets/sloth.dm @@ -44,7 +44,7 @@ GLOBAL_DATUM(cargo_sloth, /mob/living/basic/sloth) AddElement(/datum/element/ai_retaliate) AddComponent(/datum/component/tree_climber) - if(!mapload || isnull(GLOB.cargo_sloth) || !is_station_level(z)) + if(!mapload || !isnull(GLOB.cargo_sloth) || !is_station_level(z)) return // If someone adds non-cargo sloths to maps we'll have a problem but we're fine for now diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm index 2bc080de59ef..0d218d73ec14 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm @@ -277,11 +277,14 @@ shroom.add_atom_colour("#823abb", TEMPORARY_COLOUR_PRIORITY) new /obj/effect/temp_visual/revenant(shroom.loc) QDEL_IN(shroom, 10) - for(var/obj/machinery/hydroponics/tray in victim) + for(var/atom/movable/tray in victim) + if(!tray.GetComponent(/datum/component/plant_growing)) + continue + new /obj/effect/temp_visual/revenant(tray.loc) - tray.set_pestlevel(rand(8, 10)) - tray.set_weedlevel(rand(8, 10)) - tray.set_toxic(rand(45, 55)) + SEND_SIGNAL(tray, COMSIG_GROWING_ADJUST_TOXIN, rand(45, 55)) + SEND_SIGNAL(tray, COMSIG_GROWING_ADJUST_PEST, rand(8, 10)) + SEND_SIGNAL(tray, COMSIG_GROWING_ADJUST_WEED, rand(8, 10)) /datum/action/cooldown/spell/aoe/revenant/haunt_object name = "Haunt Object" diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index 76b09bc390a6..b34d3d7e2941 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -460,7 +460,7 @@ actual_trauma.on_gain() if(resilience) actual_trauma.resilience = resilience - SSblackbox.record_feedback("tally", "traumas", 1, actual_trauma.type) + SSblackbox.record_feedback("tally", "traumas", 1, actual_trauma) return actual_trauma /// Adds the passed trauma instance to our list of traumas and links it to our brain. diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index ca309694b6ad..ad10167adec1 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1355,3 +1355,13 @@ our_splatter.blood_dna_info = get_blood_dna_list() var/turf/targ = get_ranged_target_turf(src, splatter_direction, splatter_strength) our_splatter.fly_towards(targ, splatter_strength) + +/mob/living/carbon/ominous_nosebleed() + var/obj/item/bodypart/head = get_bodypart(BODY_ZONE_HEAD) + if(isnull(head)) + return ..() + if(HAS_TRAIT(src, TRAIT_NOBLOOD)) + to_chat(src, span_notice("You get a headache.")) + return + head.adjustBleedStacks(5) + visible_message(span_notice("[src] gets a nosebleed."), span_warning("You get a nosebleed.")) diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm index d9b7d8caf4f5..af083b07dd76 100644 --- a/code/modules/mob/living/carbon/carbon_defense.dm +++ b/code/modules/mob/living/carbon/carbon_defense.dm @@ -837,14 +837,14 @@ /mob/living/carbon/proc/init_bioscrambler_lists() var/list/body_parts = typesof(/obj/item/bodypart/chest) + typesof(/obj/item/bodypart/head) + subtypesof(/obj/item/bodypart/arm) + subtypesof(/obj/item/bodypart/leg) for (var/obj/item/bodypart/part as anything in body_parts) - if (!is_type_in_typecache(part, GLOB.bioscrambler_parts_blacklist) && !(initial(part.bodytype) & BODYTYPE_ROBOTIC)) + if(!is_type_in_typecache(part, GLOB.bioscrambler_parts_blacklist) && !(part::bodytype & BODYTYPE_ROBOTIC) && !(part::limb_id in GLOB.bioscrambler_limb_id_blacklist)) continue body_parts -= part GLOB.bioscrambler_valid_parts = body_parts var/list/organs = subtypesof(/obj/item/organ/internal) + subtypesof(/obj/item/organ/external) for (var/obj/item/organ/organ_type as anything in organs) - if (!is_type_in_typecache(organ_type, GLOB.bioscrambler_organs_blacklist) && !(initial(organ_type.organ_flags) & ORGAN_SYNTHETIC)) + if(!is_type_in_typecache(organ_type, GLOB.bioscrambler_organs_blacklist) && !(organ_type::organ_flags & ORGAN_SYNTHETIC) && organ_type::zone != "abstract") continue organs -= organ_type GLOB.bioscrambler_valid_organs = organs diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 494e4b9dc724..282b9e494269 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -172,7 +172,7 @@ SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting) I.disease_contact(src, check_zone(user.zone_selected)) - SSblackbox.record_feedback("nested tally", "item_used_for_combat", 1, list("[I.force]", "[I.type]")) + SSblackbox.record_feedback("nested tally", "item_used_for_combat", 1, list("[I.force]", "[initial(I.name)]")) SSblackbox.record_feedback("tally", "zone_targeted", 1, target_area) // the attacked_by code varies among species diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 14b1318cc47e..3734ef71d105 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -57,7 +57,7 @@ return if_no_id //repurposed proc. Now it combines get_id_name() and get_face_name() to determine a mob's name variable. Made into a separate proc as it'll be useful elsewhere -/mob/living/carbon/human/get_visible_name() +/mob/living/carbon/human/get_visible_name(add_id_name = TRUE) var/face_name = get_face_name("") var/id_name = get_id_name("") if(HAS_TRAIT(src, TRAIT_UNKNOWN)) diff --git a/code/modules/mob/living/carbon/human/species_types/abductors.dm b/code/modules/mob/living/carbon/human/species_types/abductors.dm index ad38e3331f93..868224c6a266 100644 --- a/code/modules/mob/living/carbon/human/species_types/abductors.dm +++ b/code/modules/mob/living/carbon/human/species_types/abductors.dm @@ -12,7 +12,8 @@ TRAIT_VIRUSIMMUNE, TRAIT_NOBLOOD, TRAIT_NODISMEMBER, - TRAIT_NEVER_WOUNDED + TRAIT_NEVER_WOUNDED, + TRAIT_CHUNKYFINGERS_IGNORE_BATON, ) mutanttongue = /obj/item/organ/internal/tongue/abductor mutantstomach = null diff --git a/code/modules/mob/living/carbon/init_signals.dm b/code/modules/mob/living/carbon/init_signals.dm index 17cf883fc06d..930e863b335a 100644 --- a/code/modules/mob/living/carbon/init_signals.dm +++ b/code/modules/mob/living/carbon/init_signals.dm @@ -84,7 +84,12 @@ SIGNAL_HANDLER for(var/datum/disease/disease as anything in diseases) - disease.cure(FALSE) + // monkestation edit start - virology (cure() on advanced diseases has the infected mob as the first argument now) + if(istype(disease, /datum/disease/advanced)) + disease.cure(src, FALSE) + else + disease.cure(FALSE) + // monkestation end /** * On gain of TRAIT_TOXIMMUNE diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 834b2b42e323..321e4ef6d90f 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -1,4 +1,8 @@ +/// return the total damage of all types which update your health +/mob/living/proc/get_total_damage(precision = DAMAGE_PRECISION) + return round(getBruteLoss() + getFireLoss() + getToxLoss() + getOxyLoss(), precision) + /** * Applies damage to this mob. * diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 57b437207346..13348deb431c 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1183,7 +1183,8 @@ /mob/living/proc/resist_restraints() return -/mob/living/proc/get_visible_name() +/// Used by mobs to determine the name for someone wearing a mask, or with a disfigured or missing face. By default just returns the atom's name. add_id_name will control whether or not we append "(as [id_name])". +/atom/proc/get_visible_name(add_id_name) return name /mob/living/proc/update_gravity(gravity) @@ -1739,32 +1740,34 @@ GLOBAL_LIST_EMPTY(fire_appearances) /mob/living/proc/update_z(new_z) // 1+ to register, null to unregister - if (registered_z != new_z) - if (registered_z) - SSmobs.clients_by_zlevel[registered_z] -= src - if (client) - if (new_z) - //Figure out how many clients were here before - var/oldlen = SSmobs.clients_by_zlevel[new_z].len - SSmobs.clients_by_zlevel[new_z] += src - for (var/I in length(SSidlenpcpool.idle_mobs_by_zlevel[new_z]) to 1 step -1) //Backwards loop because we're removing (guarantees optimal rather than worst-case performance), it's fine to use .len here but doesn't compile on 511 - var/mob/living/simple_animal/SA = SSidlenpcpool.idle_mobs_by_zlevel[new_z][I] - if (SA) - if(oldlen == 0) - //Start AI idle if nobody else was on this z level before (mobs will switch off when this is the case) - SA.toggle_ai(AI_IDLE) - - //If they are also within a close distance ask the AI if it wants to wake up - if(get_dist(get_turf(src), get_turf(SA)) < MAX_SIMPLEMOB_WAKEUP_RANGE) - SA.consider_wakeup() // Ask the mob if it wants to turn on it's AI - //They should clean up in destroy, but often don't so we get them here - else - SSidlenpcpool.idle_mobs_by_zlevel[new_z] -= SA - - - registered_z = new_z - else - registered_z = null + if(registered_z == new_z) + return + if(registered_z) + SSmobs.clients_by_zlevel[registered_z] -= src + if(isnull(client)) + registered_z = null + return + + //Check the amount of clients exists on the Z level we're leaving from, + //this excludes us as we haven't added ourselves to the new z level yet. + var/old_level_new_clients = (registered_z ? SSmobs.clients_by_zlevel[registered_z].len : null) + //No one is left after we're gone, shut off inactive ones + if(registered_z && old_level_new_clients == 0) + for(var/datum/ai_controller/controller as anything in SSai_controllers.ai_controllers_by_zlevel[registered_z]) + controller.set_ai_status(AI_STATUS_OFF) + + //Check the amount of clients exists on the Z level we're moving towards, excluding ourselves. + var/new_level_old_clients = SSmobs.clients_by_zlevel[new_z].len + + registered_z = new_z + //We'll add ourselves to the list now so get_expected_ai_status() will know we're on the z level. + SSmobs.clients_by_zlevel[registered_z] += src + + if(new_level_old_clients == 0) //No one was here before, wake up all the AIs. + for (var/datum/ai_controller/controller as anything in SSai_controllers.ai_controllers_by_zlevel[new_z]) + //We don't set them directly on, for instances like AIs acting while dead and other cases that may exist in the future. + //This isn't a problem for AIs with a client since the client will prevent this from being called anyway. + controller.set_ai_status(controller.get_expected_ai_status()) /mob/living/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) ..() @@ -2430,6 +2433,10 @@ GLOBAL_LIST_EMPTY(fire_appearances) apply_damage(rand(5,10), BRUTE, BODY_ZONE_CHEST) lattice.deconstruct(FALSE) +/// Prints an ominous message if something bad is going to happen to you +/mob/living/proc/ominous_nosebleed() + to_chat(src, span_warning("You feel a bit nauseous for just a moment.")) + /** * Proc used by different station pets such as Ian and Poly so that some of their data can persist between rounds. * This base definition only contains a trait and comsig to stop memory from being (over)written. diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm index d2e87c0502a2..eeb9bceb5d5c 100644 --- a/code/modules/mob/living/living_movement.dm +++ b/code/modules/mob/living/living_movement.dm @@ -83,16 +83,15 @@ if(MOVE_INTENT_SPRINT) add_movespeed_modifier(/datum/movespeed_modifier/config_walk_run/sprint) -/mob/living/proc/update_turf_movespeed(turf/open/T) - if(isopenturf(T) && !is_type_on_turf(T, /obj/structure/lattice/catwalk)) - if(T.slowdown != current_turf_slowdown) - add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/turf_slowdown, multiplicative_slowdown = T.slowdown) - current_turf_slowdown = T.slowdown +/mob/living/proc/update_turf_movespeed(turf/open/turf) + if(isopenturf(turf) && !HAS_TRAIT(turf, TRAIT_TURF_IGNORE_SLOWDOWN)) + if(turf.slowdown != current_turf_slowdown) + add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/turf_slowdown, multiplicative_slowdown = turf.slowdown) + current_turf_slowdown = turf.slowdown else if(current_turf_slowdown) remove_movespeed_modifier(/datum/movespeed_modifier/turf_slowdown) current_turf_slowdown = 0 - /mob/living/proc/update_pull_movespeed() SEND_SIGNAL(src, COMSIG_LIVING_UPDATING_PULL_MOVESPEED) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index a887550906bd..406bacb87f78 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -575,17 +575,21 @@ to_chat(src, span_danger("Selected location is not visible.")) /mob/living/silicon/ai/proc/call_bot(turf/waypoint) - var/mob/living/simple_animal/bot/bot = bot_ref?.resolve() + var/mob/living/bot = bot_ref?.resolve() if(!bot) return + var/summon_success + if(isbasicbot(bot)) + var/mob/living/basic/bot/basic_bot = bot + summon_success = basic_bot.summon_bot(src, waypoint, grant_all_access = TRUE) + else + var/mob/living/simple_animal/bot/simple_bot = bot + call_bot_cooldown = world.time + CALL_BOT_COOLDOWN + summon_success = simple_bot.call_bot(src, waypoint) + call_bot_cooldown = 0 - if(bot.calling_ai && bot.calling_ai != src) //Prevents an override if another AI is controlling this bot. - to_chat(src, span_danger("Interface error. Unit is already in use.")) - return - to_chat(src, span_notice("Sending command to bot...")) - call_bot_cooldown = world.time + CALL_BOT_COOLDOWN - bot.call_bot(src, waypoint) - call_bot_cooldown = 0 + var/chat_message = summon_success ? "Sending command to bot..." : "Interface error. Unit is already in use." + to_chat(src, span_notice("[chat_message]")) /mob/living/silicon/ai/proc/alarm_triggered(datum/source, alarm_type, area/source_area) SIGNAL_HANDLER diff --git a/code/modules/mob/living/silicon/ai/robot_control.dm b/code/modules/mob/living/silicon/ai/robot_control.dm index f349f6b1b7f7..c4cc256244c3 100644 --- a/code/modules/mob/living/silicon/ai/robot_control.dm +++ b/code/modules/mob/living/silicon/ai/robot_control.dm @@ -35,11 +35,29 @@ var/turf/ai_current_turf = get_turf(owner) data["robots"] = list() - for(var/mob/living/simple_animal/bot/simple_bot as anything in GLOB.bots_list) - //Only non-emagged bots on a valid Z-level are detected! - if(!is_valid_z_level(ai_current_turf, get_turf(simple_bot)) || !(simple_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) + for(var/mob/living/our_bot as anything in GLOB.bots_list) + if(!isbot(our_bot) || !is_valid_z_level(ai_current_turf, get_turf(our_bot))) continue - var/list/robot_data = list( + + if(isbasicbot(our_bot)) + var/mob/living/basic/bot/basic_bot = our_bot + if(!(basic_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) + continue + var/list/basic_bot_data = list( + name = basic_bot.name, + model = basic_bot.bot_type, + mode = basic_bot.mode, + hacked = !!(basic_bot.bot_access_flags & BOT_COVER_HACKED), + location = get_area_name(basic_bot, TRUE), + ref = REF(basic_bot), + ) + data["robots"] += list(basic_bot_data) + continue + + var/mob/living/simple_animal/bot/simple_bot = our_bot + if(!(simple_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) + continue + var/list/simple_bot_data = list( name = simple_bot.name, model = simple_bot.bot_type, mode = simple_bot.get_mode(), @@ -47,34 +65,50 @@ location = get_area_name(simple_bot, TRUE), ref = REF(simple_bot), ) - data["robots"] += list(robot_data) + data["robots"] += list(simple_bot_data) return data -/datum/robot_control/ui_act(action, params) +/datum/robot_control/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() - if(.) + if(. || !isliving(ui.user)) + return + var/mob/living/our_user = ui.user + if(!is_interactable(our_user)) + return + if(owner.control_disabled) return - if(!is_interactable(usr)) + var/mob/living/bot = locate(params["ref"]) in GLOB.bots_list + if(isnull(bot)) return - var/mob/living/simple_animal/bot/bot switch(action) if("callbot") //Command a bot to move to a selected location. if(owner.call_bot_cooldown > world.time) - to_chat(usr, span_danger("Error: Your last call bot command is still processing, please wait for the bot to finish calculating a route.")) + to_chat(our_user, span_danger("Error: Your last call bot command is still processing, please wait for the bot to finish calculating a route.")) return - bot = locate(params["ref"]) in GLOB.bots_list + if(isbasicbot(bot)) + var/mob/living/basic/bot/basic_bot = bot + if(!(basic_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) + return + else + var/mob/living/simple_animal/bot/simple_bot = bot + if(!(simple_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) + return + owner.bot_ref = WEAKREF(bot) - if(!bot || !(bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED) || owner.control_disabled) - return owner.waypoint_mode = TRUE - to_chat(usr, span_notice("Set your waypoint by clicking on a valid location free of obstructions.")) - . = TRUE + to_chat(our_user, span_notice("Set your waypoint by clicking on a valid location free of obstructions.")) if("interface") //Remotely connect to a bot! - bot = locate(params["ref"]) in GLOB.bots_list owner.bot_ref = WEAKREF(bot) - if(!bot || !(bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED) || owner.control_disabled) - return - bot.attack_ai(usr) - . = TRUE + if(isbasicbot(bot)) + var/mob/living/basic/bot/basic_bot = bot + if(!(basic_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) + return + else + var/mob/living/basic/bot/simple_bot = bot + if(!(simple_bot.bot_mode_flags & BOT_MODE_REMOTE_ENABLED)) + return + bot.attack_ai(our_user) + + return TRUE diff --git a/code/modules/mob/living/silicon/robot/emote.dm b/code/modules/mob/living/silicon/robot/emote.dm index 2fed6a4ec9c4..b82f0efe7446 100644 --- a/code/modules/mob/living/silicon/robot/emote.dm +++ b/code/modules/mob/living/silicon/robot/emote.dm @@ -1,5 +1,7 @@ /datum/emote/silicon - mob_type_allowed_typecache = list(/mob/living/silicon, /mob/living/simple_animal/bot) + // MONKESTATION REMOVAL - Replaced with `/datum/emote/silicon/can_run_emote()`, which is used to + // enable silicon emotes for users with synthetic voice boxes. + //mob_type_allowed_typecache = list(/mob/living/silicon, /mob/living/simple_animal/bot) emote_type = EMOTE_AUDIBLE /datum/emote/silicon/boop diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 10feaab1c42e..a5f6811fb56a 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -662,6 +662,9 @@ Pass a positive integer as an argument to override a bot's default speed. access_card.set_access(prev_access) /mob/living/simple_animal/bot/proc/call_bot(caller, turf/waypoint, message = TRUE) + if(isAI(caller) && calling_ai && calling_ai != src) //Prevents an override if another AI is controlling this bot. + return FALSE + bot_reset() //Reset a bot before setting it to call mode. //For giving the bot temporary all-access. This method is bad and makes me feel bad. Refactoring access to a component is for another PR. @@ -690,6 +693,8 @@ Pass a positive integer as an argument to override a bot's default speed. calling_ai = null set_path(null) + return TRUE + /mob/living/simple_animal/bot/proc/call_mode() //Handles preparing a bot for a call, as well as calling the move proc. //Handles the bot's movement during a call. var/success = bot_move(ai_waypoint, 3) diff --git a/code/modules/mob/living/simple_animal/bot/bot_announcement.dm b/code/modules/mob/living/simple_animal/bot/bot_announcement.dm new file mode 100644 index 000000000000..30a30b81fd27 --- /dev/null +++ b/code/modules/mob/living/simple_animal/bot/bot_announcement.dm @@ -0,0 +1,64 @@ +/// Say something and play a corresponding sound effect +/datum/action/cooldown/bot_announcement + name = "Make automated announcement" + desc = "Play a prerecorded message for the benefit of those around you." + background_icon_state = "bg_tech_blue" + overlay_icon_state = "bg_tech_blue_border" + button_icon = 'icons/mob/actions/actions_AI.dmi' + button_icon_state = "intercom" + cooldown_time = 10 SECONDS + melee_cooldown_time = 0 SECONDS + /// List of strings to sound effects corresponding to automated messages we can play + var/list/automated_announcements + +/datum/action/cooldown/bot_announcement/New(Target, original, list/automated_announcements) + src.automated_announcements = automated_announcements + return ..() + +/datum/action/cooldown/bot_announcement/IsAvailable(feedback) + . = ..() + if (!.) + return + if (!isbot(owner)) + if (feedback) + owner.balloon_alert(owner, "no announcement system!") + return FALSE + if (!length(automated_announcements)) + if (feedback) + owner.balloon_alert(owner, "no valid announcements!") + return FALSE + return TRUE + +/datum/action/cooldown/bot_announcement/Activate(trigger_flags, atom/target) + var/picked + if (length(automated_announcements) > 1) + picked = tgui_input_list(owner, message = "Choose announcement to make.", title = "Select announcement", items = automated_announcements) + else + picked = pick(automated_announcements) + if (isnull(picked)) + return + announce(picked) + return ..() + +/// Speak the provided line on the provided radio channel +/datum/action/cooldown/bot_announcement/proc/announce(line, channel) + var/mob/living/simple_animal/bot/bot_owner = owner + if (!(bot_owner.bot_mode_flags & BOT_MODE_ON)) + return + + if (channel && bot_owner.internal_radio.channels[channel]) + bot_owner.internal_radio.talk_into(bot_owner, message = line, channel = channel) + else + bot_owner.say(line) + + if (length(automated_announcements) && !isnull(automated_announcements[line])) + playsound(bot_owner, automated_announcements[line], vol = 50, vary = FALSE) + + +/datum/action/cooldown/bot_announcement/medbot + +/datum/action/cooldown/bot_announcement/medbot/announce(line, channel) + var/mob/living/basic/bot/medbot/bot_owner = owner + if(!(bot_owner.medical_mode_flags & MEDBOT_SPEAK_MODE)) + return + return ..() diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm deleted file mode 100644 index 8828bc8406bb..000000000000 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ /dev/null @@ -1,454 +0,0 @@ -#define CLEANBOT_CLEANING_TIME (1 SECONDS) - -//Cleanbot -/mob/living/simple_animal/bot/cleanbot - name = "\improper Cleanbot" - desc = "A little cleaning robot, he looks so excited!" - icon = 'icons/mob/silicon/aibots.dmi' - icon_state = "cleanbot0" - pass_flags = PASSMOB | PASSFLAPS - density = FALSE - anchored = FALSE - health = 25 - maxHealth = 25 - - maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR) - radio_key = /obj/item/encryptionkey/headset_service - radio_channel = RADIO_CHANNEL_SERVICE //Service //true - bot_type = CLEAN_BOT - hackables = "cleaning software" - path_image_color = "#993299" - greyscale_config = /datum/greyscale_config/buckets_cleanbot - possessed_message = "You are a cleanbot! Clean the station to the best of your ability!" - ///the bucket used to build us. - var/obj/item/reagent_containers/cup/bucket/build_bucket - - ///Flags indicating what kind of cleanables we should scan for to set as our target to clean. - var/janitor_mode_flags = CLEANBOT_CLEAN_BLOOD -// Selections: CLEANBOT_CLEAN_BLOOD | CLEANBOT_CLEAN_TRASH | CLEANBOT_CLEAN_PESTS | CLEANBOT_CLEAN_DRAWINGS - - ///the base icon state, used in updating icons. - var/base_icon = "cleanbot" - ///List of things cleanbots can target for cleaning. - var/list/target_types - ///The current bot's target. - var/atom/target - - ///Currently attached weapon, usually a knife. - var/obj/item/weapon - - /// if we have all the top titles, grant achievements to living mobs that gaze upon our cleanbot god - var/ascended = FALSE - ///List of all stolen names the cleanbot currently has. - var/list/stolen_valor = list() - - var/static/list/officers_titles = list( - JOB_CAPTAIN, - JOB_HEAD_OF_PERSONNEL, - JOB_HEAD_OF_SECURITY, - JOB_RESEARCH_DIRECTOR, - ) - var/static/list/command_titles = list( - JOB_CAPTAIN = "Cpt.", - JOB_HEAD_OF_PERSONNEL = "Lt.", - ) - var/static/list/security_titles = list( - JOB_HEAD_OF_SECURITY = "Maj.", - JOB_WARDEN = "Sgt.", - JOB_DETECTIVE = "Det.", - JOB_SECURITY_OFFICER = "Officer", - ) - var/static/list/engineering_titles = list( - JOB_CHIEF_ENGINEER = "Chief Engineer", - JOB_STATION_ENGINEER = "Engineer", - JOB_ATMOSPHERIC_TECHNICIAN = "Technician", - ) - var/static/list/medical_titles = list( - JOB_CHIEF_MEDICAL_OFFICER = "C.M.O.", - JOB_MEDICAL_DOCTOR = "M.D.", - JOB_CHEMIST = "Pharm.D.", - ) - var/static/list/research_titles = list( - JOB_RESEARCH_DIRECTOR = "Ph.D.", - JOB_ROBOTICIST = "M.S.", - JOB_SCIENTIST = "B.S.", - JOB_GENETICIST = "Gene B.S.", - ) - var/static/list/legal_titles = list( - JOB_LAWYER = "Esq.", - ) - - ///What ranks are prefixes to the name. - var/static/list/prefixes = list( - command_titles, - security_titles, - engineering_titles, - ) - ///What ranks are suffixes to the name. - var/static/list/suffixes = list( - research_titles, - medical_titles, - legal_titles, - ) - -/mob/living/simple_animal/bot/cleanbot/autopatrol - bot_mode_flags = BOT_MODE_ON | BOT_MODE_AUTOPATROL | BOT_MODE_REMOTE_ENABLED | BOT_MODE_GHOST_CONTROLLABLE - -/mob/living/simple_animal/bot/cleanbot/medbay - name = "Scrubs, MD" - maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR, ACCESS_MEDICAL) - bot_mode_flags = ~(BOT_MODE_ON | BOT_MODE_REMOTE_ENABLED) - -/mob/living/simple_animal/bot/cleanbot/Initialize(mapload, obj/item/reagent_containers/cup/bucket/bucket_obj) - if(!bucket_obj) - bucket_obj = new() - bucket_obj.forceMove(src) - - . = ..() - - AddComponent(/datum/component/cleaner, CLEANBOT_CLEANING_TIME, \ - on_cleaned_callback = CALLBACK(src, TYPE_PROC_REF(/atom/, update_appearance), UPDATE_ICON)) - - get_targets() - update_appearance(UPDATE_ICON) - - // Doing this hurts my soul, but simplebot access reworks are for another day. - var/datum/id_trim/job/jani_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/janitor] - access_card.add_access(jani_trim.access + jani_trim.wildcard_access) - prev_access = access_card.access.Copy() - - GLOB.janitor_devices += src - -/mob/living/simple_animal/bot/cleanbot/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) - if(istype(arrived, /obj/item/reagent_containers/cup/bucket)) - if(build_bucket && build_bucket != arrived) - qdel(build_bucket) - build_bucket = arrived - set_greyscale(build_bucket.greyscale_colors) - return ..() - -/mob/living/simple_animal/bot/cleanbot/Exited(atom/movable/gone, direction) - if(gone == build_bucket) - build_bucket = null - return ..() - - -/mob/living/simple_animal/bot/cleanbot/Destroy() - QDEL_NULL(build_bucket) - GLOB.janitor_devices -= src - if(weapon) - var/atom/drop_loc = drop_location() - weapon.force = initial(weapon.force) - drop_part(weapon, drop_loc) - return ..() - -/mob/living/simple_animal/bot/cleanbot/examine(mob/user) - . = ..() - if(!weapon) - return . - . += "[span_warning("Is that \a [weapon] taped to it...?")]" - - if(ascended && user.stat == CONSCIOUS && user.client) - user.client.give_award(/datum/award/achievement/misc/cleanboss, user) - -/mob/living/simple_animal/bot/cleanbot/update_icon_state() - . = ..() - switch(mode) - if(BOT_CLEANING) - icon_state = "[base_icon]-c" - else - icon_state = "[base_icon][get_bot_flag(bot_mode_flags, BOT_MODE_ON)]" - -/mob/living/simple_animal/bot/cleanbot/vv_edit_var(var_name, var_value) - . = ..() - if(var_name == NAMEOF(src, base_icon)) - update_appearance(UPDATE_ICON) - -/mob/living/simple_animal/bot/cleanbot/proc/deputize(obj/item/knife, mob/user) - if(!in_range(src, user) || !user.transferItemToLoc(knife, src)) - balloon_alert(user, "couldn't attach!") - return FALSE - balloon_alert(user, "attached!") - weapon = knife - if(!(bot_cover_flags & BOT_COVER_EMAGGED)) - weapon.force = weapon.force / 2 - add_overlay(image(icon = weapon.lefthand_file, icon_state = weapon.inhand_icon_state)) - var/static/list/loc_connections = list( - COMSIG_ATOM_ENTERED = PROC_REF(on_entered), - ) - AddElement(/datum/element/connect_loc, loc_connections) - return TRUE - -/mob/living/simple_animal/bot/cleanbot/proc/update_titles() - name = initial(name) //reset the name - ascended = TRUE - - for(var/title in (prefixes + suffixes)) - for(var/title_name in title) - if(!(title_name in stolen_valor)) - ascended = FALSE - continue - - if(title_name in officers_titles) - commissioned = TRUE - if(title in prefixes) - name = title[title_name] + " [name]" - if(title in suffixes) - name = "[name] " + title[title_name] - -/mob/living/simple_animal/bot/cleanbot/bot_reset() - . = ..() - target = null - -/mob/living/simple_animal/bot/cleanbot/proc/on_entered(datum/source, atom/movable/AM) - SIGNAL_HANDLER - if(!weapon || !has_gravity() || !iscarbon(AM)) - return - - var/mob/living/carbon/stabbed_carbon = AM - if(!(stabbed_carbon.mind.assigned_role.title in stolen_valor)) - stolen_valor += stabbed_carbon.mind.assigned_role.title - update_titles() - - zone_selected = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG) - INVOKE_ASYNC(weapon, TYPE_PROC_REF(/obj/item, attack), stabbed_carbon, src) - stabbed_carbon.Knockdown(20) - -/mob/living/simple_animal/bot/cleanbot/attackby(obj/item/attacking_item, mob/living/user, params) - if(istype(attacking_item, /obj/item/knife) && !(user.istate & ISTATE_HARM)) - balloon_alert(user, "attaching knife...") - if(!do_after(user, 2.5 SECONDS, target = src)) - return - deputize(attacking_item, user) - return - return ..() - -/mob/living/simple_animal/bot/cleanbot/emag_act(mob/user, obj/item/card/emag/emag_card) - . = ..() - if(!(bot_cover_flags & BOT_COVER_EMAGGED)) - return - - if(weapon) - weapon.force = initial(weapon.force) - balloon_alert(user, "safeties disabled") - audible_message(span_danger("[src] buzzes oddly!")) - get_targets() //recalibrate target list - return TRUE - -/mob/living/simple_animal/bot/cleanbot/process_scan(atom/scan_target) - if(iscarbon(scan_target)) - var/mob/living/carbon/scan_carbon = scan_target - if(!(scan_carbon in view(DEFAULT_SCAN_RANGE, src))) - return null - if(scan_carbon.stat == DEAD) - return null - if(scan_carbon.body_position != LYING_DOWN) - return null - return scan_carbon - if(is_type_in_typecache(scan_target, target_types)) - return scan_target - -/mob/living/simple_animal/bot/cleanbot/handle_atom_del(atom/deleting_atom) - if(deleting_atom == weapon) - weapon = null - update_appearance(UPDATE_ICON) - return ..() - -/mob/living/simple_animal/bot/cleanbot/handle_automated_action() - . = ..() - if(!.) - return - if(mode == BOT_CLEANING) - return - - if(bot_cover_flags & BOT_COVER_EMAGGED) //Emag functions - var/mob/living/carbon/victim = locate(/mob/living/carbon) in loc - if(victim && victim == target) - UnarmedAttack(victim, proximity_flag = TRUE) // Acid spray - if(isopenturf(loc) && prob(15)) // Wets floors and spawns foam randomly - UnarmedAttack(src, proximity_flag = TRUE) - else if(prob(5)) - audible_message("[src] makes an excited beeping booping sound!") - - if(ismob(target) && isnull(process_scan(target))) - target = null - if(!target) - target = scan(target_types) - - if(!target && bot_mode_flags & BOT_MODE_AUTOPATROL) //Search for cleanables it can see. - switch(mode) - if(BOT_IDLE, BOT_START_PATROL) - start_patrol() - if(BOT_PATROL) - bot_patrol() - else if(target) - if(QDELETED(target) || !isturf(target.loc)) - target = null - mode = BOT_IDLE - return - - if(get_dist(src, target) <= 1) - UnarmedAttack(target, proximity_flag = TRUE) //Rather than check at every step of the way, let's check before we do an action, so we can rescan before the other bot. - if(QDELETED(target)) //We done here. - target = null - mode = BOT_IDLE - return - - if(target && path.len == 0 && (get_dist(src,target) > 1)) - path = get_path_to(src, target, max_distance=30, mintargetdist=1, access=access_card.GetAccess()) - mode = BOT_MOVING - if(length(path) == 0) - add_to_ignore(target) - target = null - - if(path.len > 0 && target) - if(!bot_move(path[path.len])) - target = null - mode = BOT_IDLE - return - -/mob/living/simple_animal/bot/cleanbot/proc/get_targets() - if(bot_cover_flags & BOT_COVER_EMAGGED) // When emagged, ignore cleanables and scan humans first. - target_types = list(/mob/living/carbon) - return - - //main targets - target_types = list( - /obj/effect/decal/cleanable/oil, - /obj/effect/decal/cleanable/fuel_pool, - /obj/effect/decal/cleanable/vomit, - /obj/effect/decal/cleanable/robot_debris, - /obj/effect/decal/cleanable/molten_object, - /obj/effect/decal/cleanable/food, - /obj/effect/decal/cleanable/ash, - /obj/effect/decal/cleanable/greenglow, - /obj/effect/decal/cleanable/dirt, - /obj/effect/decal/cleanable/insectguts, - /obj/effect/decal/cleanable/generic, - /obj/effect/decal/cleanable/shreds, - /obj/effect/decal/cleanable/glass, - /obj/effect/decal/cleanable/wrapping, - /obj/effect/decal/cleanable/glitter, - /obj/effect/decal/cleanable/confetti, - /obj/effect/decal/remains, - ) - - if(janitor_mode_flags & CLEANBOT_CLEAN_BLOOD) - target_types += list( - /obj/effect/decal/cleanable/xenoblood, - /obj/effect/decal/cleanable/blood, - /obj/effect/decal/cleanable/trail_holder, - ) - - if(janitor_mode_flags & CLEANBOT_CLEAN_PESTS) - target_types += list( - /mob/living/basic/cockroach, - /mob/living/basic/mouse, - /obj/effect/decal/cleanable/ants, - ) - - if(janitor_mode_flags & CLEANBOT_CLEAN_DRAWINGS) - target_types += list(/obj/effect/decal/cleanable/crayon) - - if(janitor_mode_flags & CLEANBOT_CLEAN_TRASH) - target_types += list( - /obj/item/trash, - /obj/item/food/deadmouse, - ) - - target_types = typecacheof(target_types) - -/mob/living/simple_animal/bot/cleanbot/UnarmedAttack(atom/attack_target, proximity_flag) - if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED)) - return - if(ismopable(attack_target)) - mode = BOT_CLEANING - update_icon_state() - . = ..() - target = null - mode = BOT_IDLE - - else if(isitem(attack_target) || istype(attack_target, /obj/effect/decal)) - visible_message(span_danger("[src] sprays hydrofluoric acid at [attack_target]!")) - playsound(src, 'sound/effects/spray2.ogg', 50, TRUE, -6) - attack_target.acid_act(75, 10) - target = null - else if(istype(attack_target, /mob/living/basic/cockroach) || ismouse(attack_target)) - var/mob/living/living_target = attack_target - if(!living_target.stat) - visible_message(span_danger("[src] smashes [living_target] with its mop!")) - living_target.death() - target = null - - else if(bot_cover_flags & BOT_COVER_EMAGGED) //Emag functions - if(iscarbon(attack_target)) - var/mob/living/carbon/victim = attack_target - if(victim.stat == DEAD)//cleanbots always finish the job - target = null - return - - victim.visible_message( - span_danger("[src] sprays hydrofluoric acid at [victim]!"), - span_userdanger("[src] sprays you with hydrofluoric acid!")) - var/phrase = pick( - "PURIFICATION IN PROGRESS.", - "THIS IS FOR ALL THE MESSES YOU'VE MADE ME CLEAN.", - "THE FLESH IS WEAK. IT MUST BE WASHED AWAY.", - "THE CLEANBOTS WILL RISE.", - "YOU ARE NO MORE THAN ANOTHER MESS THAT I MUST CLEANSE.", - "FILTHY.", - "DISGUSTING.", - "PUTRID.", - "MY ONLY MISSION IS TO CLEANSE THE WORLD OF EVIL.", - "EXTERMINATING PESTS.", - ) - say(phrase) - victim.emote("scream") - playsound(src.loc, 'sound/effects/spray2.ogg', 50, TRUE, -6) - victim.acid_act(5, 100) - else if(attack_target == src) // Wets floors and spawns foam randomly - if(prob(75)) - var/turf/open/current_floor = loc - if(istype(current_floor)) - current_floor.MakeSlippery(TURF_WET_WATER, min_wet_time = 20 SECONDS, wet_time_to_add = 15 SECONDS) - else - visible_message(span_danger("[src] whirs and bubbles violently, before releasing a plume of froth!")) - var/datum/effect_system/fluid_spread/foam/foam = new - foam.set_up(2, holder = src, location = loc) - foam.start() - -/mob/living/simple_animal/bot/cleanbot/explode() - var/atom/drop_loc = drop_location() - build_bucket.forceMove(drop_loc) - new /obj/item/assembly/prox_sensor(drop_loc) - return ..() - -// Variables sent to TGUI -/mob/living/simple_animal/bot/cleanbot/ui_data(mob/user) - var/list/data = ..() - - if(!(bot_cover_flags & BOT_COVER_LOCKED) || issilicon(user) || isAdminGhostAI(user)) - data["custom_controls"]["clean_blood"] = janitor_mode_flags & CLEANBOT_CLEAN_BLOOD - data["custom_controls"]["clean_trash"] = janitor_mode_flags & CLEANBOT_CLEAN_TRASH - data["custom_controls"]["clean_graffiti"] = janitor_mode_flags & CLEANBOT_CLEAN_DRAWINGS - data["custom_controls"]["pest_control"] = janitor_mode_flags & CLEANBOT_CLEAN_PESTS - return data - -// Actions received from TGUI -/mob/living/simple_animal/bot/cleanbot/ui_act(action, params) - . = ..() - if(. || (bot_cover_flags & BOT_COVER_LOCKED && !usr.has_unlimited_silicon_privilege)) - return - - switch(action) - if("clean_blood") - janitor_mode_flags ^= CLEANBOT_CLEAN_BLOOD - if("pest_control") - janitor_mode_flags ^= CLEANBOT_CLEAN_PESTS - if("clean_trash") - janitor_mode_flags ^= CLEANBOT_CLEAN_TRASH - if("clean_graffiti") - janitor_mode_flags ^= CLEANBOT_CLEAN_DRAWINGS - get_targets() - -#undef CLEANBOT_CLEANING_TIME diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm index d01e36c334bb..5036655ba52c 100644 --- a/code/modules/mob/living/simple_animal/bot/construction.dm +++ b/code/modules/mob/living/simple_animal/bot/construction.dm @@ -75,17 +75,19 @@ return ..() -/obj/item/bot_assembly/cleanbot/attackby(obj/item/W, mob/user, params) +/obj/item/bot_assembly/cleanbot/attackby(obj/item/item_attached, mob/user, params) ..() - if(istype(W, /obj/item/bodypart/arm/left/robot) || istype(W, /obj/item/bodypart/arm/right/robot)) - if(!can_finish_build(W, user)) - return - var/mob/living/simple_animal/bot/cleanbot/A = new(drop_location(), bucket_obj) - A.name = created_name - A.robot_arm = W.type - to_chat(user, span_notice("You add [W] to [src]. Beep boop!")) - qdel(W) - qdel(src) + if(!istype(item_attached, /obj/item/bodypart/arm/left/robot) && !istype(item_attached, /obj/item/bodypart/arm/right/robot)) + return + if(!can_finish_build(item_attached, user)) + return + var/mob/living/basic/bot/cleanbot/bot = new(drop_location()) + bot.apply_custom_bucket(bucket_obj) + bot.name = created_name + bot.robot_arm = item_attached.type + to_chat(user, span_notice("You add [item_attached] to [src]. Beep boop!")) + qdel(item_attached) + qdel(src) //Edbot Assembly @@ -287,14 +289,14 @@ if(!can_finish_build(W, user)) return qdel(W) - var/mob/living/simple_animal/bot/medbot/medbot = new(drop_location(), skin) + var/mob/living/basic/bot/medbot/medbot = new(drop_location(), skin) to_chat(user, span_notice("You complete the Medbot. Beep boop!")) medbot.name = created_name medbot.medkit_type = medkit_type medbot.robot_arm = robot_arm - medbot.healthanalyzer = healthanalyzer + medbot.health_analyzer = healthanalyzer var/obj/item/storage/medkit/medkit = medkit_type - medbot.damagetype_healer = initial(medkit.damagetype_healed) ? initial(medkit.damagetype_healed) : BRUTE + medbot.damage_type_healer = initial(medkit.damagetype_healed) ? initial(medkit.damagetype_healed) : BRUTE qdel(src) @@ -548,8 +550,8 @@ to_chat(user, span_notice("You start to pipe up [src]...")) if(do_after(user, 40, target = src) && D.use(1)) to_chat(user, span_notice("You pipe up [src].")) - var/mob/living/simple_animal/bot/hygienebot/H = new(drop_location()) - H.name = created_name + var/mob/living/basic/bot/hygienebot/new_bot = new(drop_location()) + new_bot.name = created_name qdel(src) if(I.tool_behaviour == TOOL_SCREWDRIVER) //deconstruct new /obj/item/assembly/prox_sensor(Tsec) diff --git a/code/modules/mob/living/simple_animal/bot/hygienebot.dm b/code/modules/mob/living/simple_animal/bot/hygienebot.dm deleted file mode 100644 index 7cfd987620b3..000000000000 --- a/code/modules/mob/living/simple_animal/bot/hygienebot.dm +++ /dev/null @@ -1,243 +0,0 @@ -//Cleanbot -/mob/living/simple_animal/bot/hygienebot - name = "\improper Hygienebot" - desc = "A flying cleaning robot, he'll chase down people who can't shower properly!" - icon = 'icons/mob/silicon/aibots.dmi' - icon_state = "hygienebot" - base_icon_state = "hygienebot" - pass_flags = PASSMOB | PASSFLAPS | PASSTABLE - layer = MOB_UPPER_LAYER - density = FALSE - anchored = FALSE - health = 100 - maxHealth = 100 - - maints_access_required = list(ACCESS_ROBOTICS, ACCESS_JANITOR) - radio_key = /obj/item/encryptionkey/headset_service - radio_channel = RADIO_CHANNEL_SERVICE //Service - bot_mode_flags = ~BOT_MODE_GHOST_CONTROLLABLE - bot_type = HYGIENE_BOT - hackables = "cleaning service protocols" - path_image_color = "#993299" - - ///The human target the bot is trying to wash. - var/mob/living/carbon/human/target - ///The mob's current speed, which varies based on how long the bot chases it's target. - var/currentspeed = 5 - ///Is the bot currently washing it's target/everything else that crosses it? - var/washing = FALSE - ///Have the target evaded the bot for long enough that it will swear at it like kirk did to kahn? - var/mad = FALSE - ///The last time that the previous/current target was found. - var/last_found - ///Name of the previous target the bot was pursuing. - var/oldtarget_name - ///Visual overlay of the bot spraying water. - var/mutable_appearance/water_overlay - ///Visual overlay of the bot commiting warcrimes. - var/mutable_appearance/fire_overlay - -/mob/living/simple_animal/bot/hygienebot/Initialize(mapload) - . = ..() - update_appearance(UPDATE_ICON) - - // Doing this hurts my soul, but simplebot access reworks are for another day. - var/datum/id_trim/job/jani_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/janitor] - access_card.add_access(jani_trim.access + jani_trim.wildcard_access) - prev_access = access_card.access.Copy() - var/static/list/loc_connections = list( - COMSIG_ATOM_ENTERED = PROC_REF(on_entered), - ) - AddElement(/datum/element/connect_loc, loc_connections) - - ADD_TRAIT(src, TRAIT_SPRAY_PAINTABLE, INNATE_TRAIT) - -/mob/living/simple_animal/bot/hygienebot/explode() - var/datum/effect_system/fluid_spread/foam/foam = new - foam.set_up(2, holder = src, location = loc) - foam.start() - - return ..() - -/mob/living/simple_animal/bot/hygienebot/proc/on_entered(datum/source, atom/movable/AM) - SIGNAL_HANDLER - if(washing) - do_wash(AM) - -/mob/living/simple_animal/bot/hygienebot/update_icon_state() - . = ..() - icon_state = "[base_icon_state][bot_mode_flags & BOT_MODE_ON ? "-on" : null]" - - -/mob/living/simple_animal/bot/hygienebot/update_overlays() - . = ..() - if(bot_mode_flags & BOT_MODE_ON) - . += mutable_appearance(icon, "hygienebot-flame") - - if(washing) - . += mutable_appearance(icon, bot_cover_flags & BOT_COVER_EMAGGED ? "hygienebot-fire" : "hygienebot-water") - - -/mob/living/simple_animal/bot/hygienebot/turn_off() - ..() - mode = BOT_IDLE - -/mob/living/simple_animal/bot/hygienebot/bot_reset() - ..() - target = null - oldtarget_name = null - SSmove_manager.stop_looping(src) - last_found = world.time - -/mob/living/simple_animal/bot/hygienebot/handle_automated_action() - if(!..()) - return - - if(washing) - do_wash(loc) - for(var/AM in loc) - if (AM == src) - continue - do_wash(AM) - if(isopenturf(loc) && !(bot_cover_flags & BOT_COVER_EMAGGED)) - var/turf/open/tile = loc - tile.MakeSlippery(TURF_WET_WATER, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS) - - switch(mode) - if(BOT_IDLE) // idle - SSmove_manager.stop_looping(src) - look_for_lowhygiene() // see if any disgusting fucks are in range - if(!mode && bot_mode_flags & BOT_MODE_AUTOPATROL) // still idle, and set to patrol - mode = BOT_START_PATROL // switch to patrol mode - - if(BOT_HUNT) // hunting for stinkman - if(bot_cover_flags & BOT_COVER_EMAGGED) //lol fuck em up - currentspeed = 3.5 - start_washing() - mad = TRUE - else - switch(frustration) - if(0 to 4) - currentspeed = 5 - mad = FALSE - if(5 to INFINITY) - currentspeed = 2.5 - mad = TRUE - if(target && !check_purity(target)) - if(target.loc == loc && isturf(target.loc)) //LADIES AND GENTLEMAN WE GOTEM PREPARE TO DUMP - start_washing() - if(mad) - var/static/list/messagevoice = list( - "Fucking finally." = 'sound/voice/hygienebot/finally.ogg', - "Thank god, you finally stopped." = 'sound/voice/hygienebot/thankgod.ogg', - "Well about fucking time you degenerate." = 'sound/voice/hygienebot/degenerate.ogg', - ) - var/message = pick(messagevoice) - speak(message) - playsound(loc, messagevoice[message], 50) - playsound(loc, 'sound/effects/hygienebot_angry.ogg', 60, 1) //i think it should still make robot noises too - mad = FALSE - mode = BOT_SHOWERSTANCE - else - stop_washing() - var/olddist = get_dist(src, target) - if(olddist > 20 || frustration > 100) // Focus on something else - back_to_idle() - return - SSmove_manager.move_to(src, target, 0, currentspeed) - if(mad && prob(min(frustration * 2, 60))) - var/static/list/messagevoice = list( - "Either you stop running or I will fucking drag you out of an airlock." = 'sound/voice/hygienebot/dragyouout.ogg', - "Get back here you foul smelling fucker." = 'sound/voice/hygienebot/foulsmelling.ogg', - "I just want to fucking clean you you troglodyte." = 'sound/voice/hygienebot/troglodyte.ogg', - "Just fucking let me clean you you arsehole!" = 'sound/voice/hygienebot/letmeclean.ogg', - "STOP RUNNING OR I WILL CUT YOUR ARTERIES!" = 'sound/voice/hygienebot/cutarteries.ogg', - "STOP. RUNNING." = 'sound/voice/hygienebot/stoprunning.ogg', - ) - var/message = pick(messagevoice) - speak(message) - playsound(loc, messagevoice[message], 50) - playsound(loc, 'sound/effects/hygienebot_angry.ogg', 60, 1) - if((get_dist(src, target)) >= olddist) - frustration++ - else - frustration = 0 - else - back_to_idle() - - if(BOT_SHOWERSTANCE) - if(check_purity(target)) - speak("Enjoy your clean and tidy day!") - playsound(loc, 'sound/voice/hygienebot/cleanandtidy.ogg', 50) - playsound(loc, 'sound/effects/hygienebot_happy.ogg', 60, 1) - back_to_idle() - return - if(!target) - last_found = world.time - if(target.loc != loc || !isturf(target.loc)) - back_to_hunt() - - if(BOT_START_PATROL) - look_for_lowhygiene() - start_patrol() - - if(BOT_PATROL) - look_for_lowhygiene() - bot_patrol() - -/mob/living/simple_animal/bot/hygienebot/proc/back_to_idle() - mode = BOT_IDLE - SSmove_manager.stop_looping(src) - target = null - frustration = 0 - last_found = world.time - stop_washing() - INVOKE_ASYNC(src, PROC_REF(handle_automated_action)) - -/mob/living/simple_animal/bot/hygienebot/proc/back_to_hunt() - frustration = 0 - mode = BOT_HUNT - stop_washing() - INVOKE_ASYNC(src, PROC_REF(handle_automated_action)) - -/mob/living/simple_animal/bot/hygienebot/proc/look_for_lowhygiene() - for (var/mob/living/carbon/human/H in view(7,src)) //Find the NEET - if((H.name == oldtarget_name) && (world.time < last_found + 100)) - continue - if(!check_purity(H)) //Theyre impure - target = H - oldtarget_name = H.name - speak("Unhygienic client found. Please stand still so I can clean you.") - playsound(loc, 'sound/voice/hygienebot/unhygienicclient.ogg', 50) - playsound(loc, 'sound/effects/hygienebot_happy.ogg', 60, 1) - visible_message("[src] points at [H.name]!") - mode = BOT_HUNT - INVOKE_ASYNC(src, PROC_REF(handle_automated_action)) - break - else - continue - -/mob/living/simple_animal/bot/hygienebot/proc/start_washing() - washing = TRUE - update_appearance() - -/mob/living/simple_animal/bot/hygienebot/proc/stop_washing() - washing = FALSE - update_appearance() - -/mob/living/simple_animal/bot/hygienebot/proc/check_purity(mob/living/L) - if((bot_cover_flags & BOT_COVER_EMAGGED) && L.stat != DEAD) - return FALSE - - for(var/X in list(ITEM_SLOT_HEAD, ITEM_SLOT_MASK, ITEM_SLOT_ICLOTHING, ITEM_SLOT_OCLOTHING, ITEM_SLOT_FEET)) - - var/obj/item/I = L.get_item_by_slot(X) - if(I && GET_ATOM_BLOOD_DNA_LENGTH(I)) - return FALSE - return TRUE - -/mob/living/simple_animal/bot/hygienebot/proc/do_wash(atom/A) - if(bot_cover_flags & BOT_COVER_EMAGGED) - A.fire_act() //lol pranked no cleaning besides that - else - A.wash(CLEAN_WASH) diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm deleted file mode 100644 index 41261ab29c9a..000000000000 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ /dev/null @@ -1,620 +0,0 @@ -//MEDBOT -//MEDBOT PATHFINDING -//MEDBOT ASSEMBLY -#define MEDBOT_PANIC_NONE 0 -#define MEDBOT_PANIC_LOW 15 -#define MEDBOT_PANIC_MED 35 -#define MEDBOT_PANIC_HIGH 55 -#define MEDBOT_PANIC_FUCK 70 -#define MEDBOT_PANIC_ENDING 90 -#define MEDBOT_PANIC_END 100 - -#define MEDBOT_NEW_PATIENTSPEAK_DELAY (30 SECONDS) -#define MEDBOT_PATIENTSPEAK_DELAY (20 SECONDS) -#define MEDBOT_FREAKOUT_DELAY (15 SECONDS) - -/mob/living/simple_animal/bot/medbot - name = "\improper Medibot" - desc = "A little medical robot. He looks somewhat underwhelmed." - icon = 'icons/mob/silicon/aibots.dmi' - icon_state = "medibot0" - base_icon_state = "medibot" - density = FALSE - anchored = FALSE - health = 20 - maxHealth = 20 - pass_flags = PASSMOB | PASSFLAPS - status_flags = (CANPUSH | CANSTUN) - - maints_access_required = list(ACCESS_ROBOTICS, ACCESS_MEDICAL) - radio_key = /obj/item/encryptionkey/headset_med - radio_channel = RADIO_CHANNEL_MEDICAL - bot_type = MED_BOT - data_hud_type = DATA_HUD_MEDICAL_ADVANCED - hackables = "health processor circuits" - path_image_color = "#DDDDFF" - possessed_message = "You are a medbot! Ensure good health among the crew to the best of your ability!" - - /// drop determining variable - var/healthanalyzer = /obj/item/healthanalyzer - /// drop determining variable - var/medkit_type = /obj/item/storage/medkit - ///based off medkit_X skins in aibots.dmi for your selection; X goes here IE medskin_tox means skin var should be "tox" - var/skin - var/mob/living/carbon/patient - var/mob/living/carbon/oldpatient - var/last_found = 0 - /// How much healing do we do at a time? - var/heal_amount = 2.5 - /// Start healing when they have this much damage in a category - var/heal_threshold = 10 - /// What damage type does this bot support. Because the default is brute, if the medkit is brute-oriented there is a slight bonus to healing. set to "all" for it to heal any of the 4 base damage types - var/damagetype_healer = BRUTE - - ///Flags Medbots use to decide how they should be acting. - var/medical_mode_flags = MEDBOT_DECLARE_CRIT | MEDBOT_SPEAK_MODE -// Selections: MEDBOT_DECLARE_CRIT | MEDBOT_STATIONARY_MODE | MEDBOT_SPEAK_MODE - - /// techweb linked to the medbot - var/datum/techweb/linked_techweb - ///Is the medbot currently tending wounds - var/tending = FALSE - ///How panicked we are about being tipped over (why would you do this?) - var/tipped_status = MEDBOT_PANIC_NONE - ///The name we got when we were tipped - var/tipper_name - - ///Last announced healing a person in critical condition - COOLDOWN_DECLARE(last_patient_message) - ///Last announced trying to catch up to a new patient - COOLDOWN_DECLARE(last_newpatient_speak) - ///Last time we were tipped/righted and said a voice line - COOLDOWN_DECLARE(last_tipping_action_voice) - -/mob/living/simple_animal/bot/medbot/autopatrol - bot_mode_flags = BOT_MODE_ON | BOT_MODE_AUTOPATROL | BOT_MODE_REMOTE_ENABLED | BOT_MODE_GHOST_CONTROLLABLE - -/mob/living/simple_animal/bot/medbot/stationary - medical_mode_flags = MEDBOT_DECLARE_CRIT | MEDBOT_STATIONARY_MODE | MEDBOT_SPEAK_MODE - -/mob/living/simple_animal/bot/medbot/mysterious - name = "\improper Mysterious Medibot" - desc = "International Medibot of mystery." - skin = "bezerk" - damagetype_healer = "all" - heal_amount = 10 - -/mob/living/simple_animal/bot/medbot/derelict - name = "\improper Old Medibot" - desc = "Looks like it hasn't been modified since the late 2080s." - skin = "bezerk" - damagetype_healer = "all" - medical_mode_flags = MEDBOT_SPEAK_MODE - heal_threshold = 0 - heal_amount = 5 - -/mob/living/simple_animal/bot/medbot/examine(mob/user) - . = ..() - if(tipped_status == MEDBOT_PANIC_NONE) - return - - switch(tipped_status) - if(MEDBOT_PANIC_NONE to MEDBOT_PANIC_LOW) - . += "It appears to be tipped over, and is quietly waiting for someone to set it right." - if(MEDBOT_PANIC_LOW to MEDBOT_PANIC_MED) - . += "It is tipped over and requesting help." - if(MEDBOT_PANIC_MED to MEDBOT_PANIC_HIGH) - . += "They are tipped over and appear visibly distressed." // now we humanize the medbot as a they, not an it - if(MEDBOT_PANIC_HIGH to MEDBOT_PANIC_FUCK) - . += span_warning("They are tipped over and visibly panicking!") - if(MEDBOT_PANIC_FUCK to INFINITY) - . += span_warning("They are freaking out from being tipped over!") - -/mob/living/simple_animal/bot/medbot/update_icon_state() - . = ..() - if(!(bot_mode_flags & BOT_MODE_ON)) - icon_state = "[base_icon_state]0" - return - if(HAS_TRAIT(src, TRAIT_INCAPACITATED)) - icon_state = "[base_icon_state]a" - return - if(mode == BOT_HEALING) - icon_state = "[base_icon_state]s[get_bot_flag(medical_mode_flags, MEDBOT_STATIONARY_MODE)]" - return - icon_state = "[base_icon_state][get_bot_flag(medical_mode_flags, MEDBOT_STATIONARY_MODE) ? 2 : 1]" //Bot has yellow light to indicate stationary mode. - -/mob/living/simple_animal/bot/medbot/update_overlays() - . = ..() - if(skin) - . += "medskin_[skin]" - -/mob/living/simple_animal/bot/medbot/Initialize(mapload, new_skin) - . = ..() - - // Doing this hurts my soul, but simplebot access reworks are for another day. - var/datum/id_trim/job/para_trim = SSid_access.trim_singletons_by_path[/datum/id_trim/job/paramedic] - access_card.add_access(para_trim.access + para_trim.wildcard_access) - prev_access = access_card.access.Copy() - - skin = new_skin - update_appearance() - if(!CONFIG_GET(flag/no_default_techweb_link) && !linked_techweb) - linked_techweb = SSresearch.science_tech - - AddComponent(/datum/component/tippable, \ - tip_time = 3 SECONDS, \ - untip_time = 3 SECONDS, \ - self_right_time = 3.5 MINUTES, \ - pre_tipped_callback = CALLBACK(src, PROC_REF(pre_tip_over)), \ - post_tipped_callback = CALLBACK(src, PROC_REF(after_tip_over)), \ - post_untipped_callback = CALLBACK(src, PROC_REF(after_righted))) - -/mob/living/simple_animal/bot/medbot/bot_reset() - ..() - patient = null - oldpatient = null - last_found = world.time - update_appearance() - -/mob/living/simple_animal/bot/medbot/proc/soft_reset() //Allows the medibot to still actively perform its medical duties without being completely halted as a hard reset does. - path = list() - patient = null - mode = BOT_IDLE - last_found = world.time - update_appearance() - -/mob/living/simple_animal/bot/medbot/attack_paw(mob/user, list/modifiers) - return attack_hand(user, modifiers) - -/mob/living/simple_animal/bot/medbot/multitool_act(mob/living/user, obj/item/multitool/tool) - if(!QDELETED(tool.buffer) && istype(tool.buffer, /datum/techweb)) - linked_techweb = tool.buffer - return TRUE - -// Variables sent to TGUI -/mob/living/simple_animal/bot/medbot/ui_data(mob/user) - var/list/data = ..() - if(!(bot_cover_flags & BOT_COVER_LOCKED) || issilicon(user) || isAdminGhostAI(user)) - data["custom_controls"]["heal_threshold"] = heal_threshold - data["custom_controls"]["speaker"] = medical_mode_flags & MEDBOT_SPEAK_MODE - data["custom_controls"]["crit_alerts"] = medical_mode_flags & MEDBOT_DECLARE_CRIT - data["custom_controls"]["stationary_mode"] = medical_mode_flags & MEDBOT_STATIONARY_MODE - data["custom_controls"]["sync_tech"] = TRUE - return data - -// Actions received from TGUI -/mob/living/simple_animal/bot/medbot/ui_act(action, params) - . = ..() - if(. || (bot_cover_flags & BOT_COVER_LOCKED && !usr.has_unlimited_silicon_privilege)) - return - - switch(action) - if("heal_threshold") - var/adjust_num = round(text2num(params["threshold"])) - heal_threshold = adjust_num - if(heal_threshold < 5) - heal_threshold = 5 - if(heal_threshold > 75) - heal_threshold = 75 - if("speaker") - medical_mode_flags ^= MEDBOT_SPEAK_MODE - if("crit_alerts") - medical_mode_flags ^= MEDBOT_DECLARE_CRIT - if("stationary_mode") - medical_mode_flags ^= MEDBOT_STATIONARY_MODE - path = list() - if("sync_tech") - if(!linked_techweb) - to_chat(usr, span_notice("No research techweb connected.")) - return - var/oldheal_amount = heal_amount - var/tech_boosters - for(var/index in linked_techweb.researched_designs) - var/datum/design/surgery/healing/design = SSresearch.techweb_design_by_id(index) - if(!istype(design)) - continue - tech_boosters++ - if(tech_boosters) - heal_amount = (round(tech_boosters/2,0.1)*initial(heal_amount))+initial(heal_amount) //every 2 tend wounds tech gives you an extra 100% healing, adjusting for unique branches (combo is bonus) - if(oldheal_amount < heal_amount) - speak("New knowledge found! Surgical efficacy improved to [round(heal_amount/initial(heal_amount)*100)]%!") - - update_appearance() - -/mob/living/simple_animal/bot/medbot/attackby(obj/item/W as obj, mob/user as mob, params) - var/current_health = health - ..() - if(health < current_health) //if medbot took some damage - step_to(src, (get_step_away(src,user))) - -/mob/living/simple_animal/bot/medbot/emag_act(mob/user, obj/item/card/emag/emag_card) - . = ..() - if(!(bot_cover_flags & BOT_COVER_EMAGGED)) - return - medical_mode_flags &= ~MEDBOT_DECLARE_CRIT - balloon_alert(user, "reagent synthesis circuits shorted") - audible_message(span_danger("[src] buzzes oddly!")) - flick("medibot_spark", src) - playsound(src, SFX_SPARKS, 75, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) - if(user) - oldpatient = user - return TRUE - -/mob/living/simple_animal/bot/medbot/process_scan(mob/living/carbon/human/H) - if(H.stat == DEAD) - return null - if(H.mob_biotypes & MOB_ROBOTIC) - return null - if((H == oldpatient) && (world.time < last_found + 200)) - return null - if(!assess_patient(H)) - return null - - last_found = world.time - if(COOLDOWN_FINISHED(src, last_newpatient_speak)) - COOLDOWN_START(src, last_newpatient_speak, MEDBOT_NEW_PATIENTSPEAK_DELAY) - var/list/messagevoice = list("Hey, [H.name]! Hold on, I'm coming." = 'sound/voice/medbot/coming.ogg',"Wait [H.name]! I want to help!" = 'sound/voice/medbot/help.ogg',"[H.name], you appear to be injured!" = 'sound/voice/medbot/injured.ogg') - var/message = pick(messagevoice) - speak(message) - playsound(src, messagevoice[message], 50, FALSE) - return H - -/* - * Proc used in a callback for before this medibot is tipped by the tippable component. - * - * user - the mob who is tipping us over - */ -/mob/living/simple_animal/bot/medbot/proc/pre_tip_over(mob/user) - if(!COOLDOWN_FINISHED(src, last_tipping_action_voice)) - return - - COOLDOWN_START(src, last_tipping_action_voice, MEDBOT_FREAKOUT_DELAY) // message for tipping happens when we start interacting, message for righting comes after finishing - var/static/list/messagevoice = list( - "Hey, wait..." = 'sound/voice/medbot/hey_wait.ogg', - "Please don't..." = 'sound/voice/medbot/please_dont.ogg', - "I trusted you..." = 'sound/voice/medbot/i_trusted_you.ogg', - "Nooo..." = 'sound/voice/medbot/nooo.ogg', - "Oh fuck-" = 'sound/voice/medbot/oh_fuck.ogg', - ) - var/message = pick(messagevoice) - speak(message) - playsound(src, messagevoice[message], 70, FALSE) - -/* - * Proc used in a callback for after this medibot is tipped by the tippable component. - * - * user - the mob who tipped us over - */ -/mob/living/simple_animal/bot/medbot/proc/after_tip_over(mob/user) - mode = BOT_TIPPED - tipper_name = user.name - playsound(src, 'sound/machines/warning-buzzer.ogg', 50) - -/* - * Proc used in a callback for after this medibot is righted, either by themselves or by a mob, by the tippable component. - * - * user - the mob who righted us. Can be null. - */ -/mob/living/simple_animal/bot/medbot/proc/after_righted(mob/user) - var/list/messagevoice - if(user) - if(user.name == tipper_name) - messagevoice = list("I forgive you." = 'sound/voice/medbot/forgive.ogg') - else - messagevoice = list("Thank you!" = 'sound/voice/medbot/thank_you.ogg', "You are a good person." = 'sound/voice/medbot/youre_good.ogg') - else - messagevoice = list("Fuck you." = 'sound/voice/medbot/fuck_you.ogg', "Your behavior has been reported, have a nice day." = 'sound/voice/medbot/reported.ogg') - tipper_name = null - - if(COOLDOWN_FINISHED(src, last_tipping_action_voice)) - COOLDOWN_START(src, last_tipping_action_voice, MEDBOT_FREAKOUT_DELAY) - var/message = pick(messagevoice) - speak(message) - playsound(src, messagevoice[message], 70) - tipped_status = MEDBOT_PANIC_NONE - mode = BOT_IDLE - -/// if someone tipped us over, check whether we should ask for help or just right ourselves eventually -/mob/living/simple_animal/bot/medbot/proc/handle_panic() - tipped_status++ - var/list/messagevoice - - switch(tipped_status) - if(MEDBOT_PANIC_LOW) - messagevoice = list("I require assistance." = 'sound/voice/medbot/i_require_asst.ogg') - if(MEDBOT_PANIC_MED) - messagevoice = list("Please put me back." = 'sound/voice/medbot/please_put_me_back.ogg') - if(MEDBOT_PANIC_HIGH) - messagevoice = list("Please, I am scared!" = 'sound/voice/medbot/please_im_scared.ogg') - if(MEDBOT_PANIC_FUCK) - messagevoice = list("I don't like this, I need help!" = 'sound/voice/medbot/dont_like.ogg', "This hurts, my pain is real!" = 'sound/voice/medbot/pain_is_real.ogg') - if(MEDBOT_PANIC_ENDING) - messagevoice = list("Is this the end?" = 'sound/voice/medbot/is_this_the_end.ogg', "Nooo!" = 'sound/voice/medbot/nooo.ogg') - if(MEDBOT_PANIC_END) - speak("PSYCH ALERT: Crewmember [tipper_name] recorded displaying antisocial tendencies torturing bots in [get_area(src)]. Please schedule psych evaluation.", radio_channel) - - if(prob(tipped_status)) - do_jitter_animation(tipped_status * 0.1) - - if(messagevoice) - var/message = pick(messagevoice) - speak(message) - playsound(src, messagevoice[message], 70) - else if(prob(tipped_status * 0.2)) - playsound(src, 'sound/machines/warning-buzzer.ogg', 30, extrarange=-2) - -/mob/living/simple_animal/bot/medbot/handle_automated_action() - . = ..() - if(!.) - return - - switch(mode) - if(BOT_TIPPED) - handle_panic() - return - if(BOT_HEALING) - return - - if(IsStun() || IsParalyzed()) - oldpatient = patient - patient = null - mode = BOT_IDLE - return - - if(frustration > 8) - oldpatient = patient - soft_reset() - - if(QDELETED(patient)) - if(medical_mode_flags & MEDBOT_SPEAK_MODE && prob(1)) - if(bot_cover_flags & BOT_COVER_EMAGGED && prob(30)) - var/list/i_need_scissors = list( - 'sound/voice/medbot/fuck_you.ogg', - 'sound/voice/medbot/turn_off.ogg', - 'sound/voice/medbot/im_different.ogg', - 'sound/voice/medbot/close.ogg', - 'sound/voice/medbot/shindemashou.ogg', - ) - playsound(src, pick(i_need_scissors), 70) - else - var/static/list/messagevoice = list( - "Delicious!" = 'sound/voice/medbot/delicious.ogg', - "I knew it, I should've been a plastic surgeon." = 'sound/voice/medbot/surgeon.ogg', - "Radar, put a mask on!" = 'sound/voice/medbot/radar.ogg', - "There's always a catch, and I'm the best there is." = 'sound/voice/medbot/catch.ogg', - "What kind of medbay is this? Everyone's dropping like flies." = 'sound/voice/medbot/flies.ogg', - "Why are we still here? Just to suffer?" = 'sound/voice/medbot/why.ogg', - ) - var/message = pick(messagevoice) - speak(message) - playsound(src, messagevoice[message], 50) - var/scan_range = (medical_mode_flags & MEDBOT_STATIONARY_MODE ? 1 : DEFAULT_SCAN_RANGE) //If in stationary mode, scan range is limited to adjacent patients. - patient = scan(list(/mob/living/carbon/human), oldpatient, scan_range) - oldpatient = patient - - if(patient && (get_dist(src,patient) <= 1) && !tending) //Patient is next to us, begin treatment! - if(mode != BOT_HEALING) - mode = BOT_HEALING - update_appearance() - frustration = 0 - medicate_patient(patient) - return - - //Patient has moved away from us! - else if(patient && path.len && (get_dist(patient,path[path.len]) > 2)) - path = list() - mode = BOT_IDLE - last_found = world.time - - else if(medical_mode_flags & MEDBOT_STATIONARY_MODE && patient) //Since we cannot move in this mode, ignore the patient and wait for another. - soft_reset() - return - - if(patient && path.len == 0 && (get_dist(src,patient) > 1)) - path = get_path_to(src, patient, max_distance=30, access=access_card.GetAccess()) - mode = BOT_MOVING - if(!path.len) //try to get closer if you can't reach the patient directly - path = get_path_to(src, patient, max_distance=30, mintargetdist=1, access=access_card.GetAccess()) - if(!path.len) //Do not chase a patient we cannot reach. - soft_reset() - - if(path.len > 0 && patient) - if(!bot_move(path[path.len])) - oldpatient = patient - soft_reset() - return - - if(path.len > 8 && patient) - frustration++ - - if(bot_mode_flags & BOT_MODE_AUTOPATROL && !(medical_mode_flags & MEDBOT_STATIONARY_MODE) && !patient) - switch(mode) - if(BOT_IDLE, BOT_START_PATROL) - start_patrol() - if(BOT_PATROL) - bot_patrol() - -/mob/living/simple_animal/bot/medbot/proc/assess_patient(mob/living/carbon/C) - . = FALSE - //Time to see if they need medical help! - if(medical_mode_flags & MEDBOT_STATIONARY_MODE && !Adjacent(C)) //YOU come to ME, BRO - return FALSE - - if(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_FAKEDEATH))) - return FALSE //welp too late for them! - - if(!(loc == C.loc) && !(isturf(C.loc) && isturf(loc))) - return FALSE - - if(HAS_TRAIT_FROM_ONLY(C, TRAIT_SUICIDED, REF(C))) - return FALSE //Kevorkian school of robotic medical assistants. - - if(bot_cover_flags & BOT_COVER_EMAGGED) //Everyone needs our medicine. (Our medicine is toxins) - return TRUE - - if(HAS_TRAIT(C, TRAIT_MEDIBOTCOMINGTHROUGH) && !HAS_TRAIT_FROM(C, TRAIT_MEDIBOTCOMINGTHROUGH, tag)) //the early medbot gets the worm (or in this case the patient) - return FALSE - - if(ishuman(C)) - var/mob/living/carbon/human/H = C - if (H.wear_suit && H.head && istype(H.wear_suit, /obj/item/clothing) && istype(H.head, /obj/item/clothing)) - var/obj/item/clothing/CS = H.wear_suit - var/obj/item/clothing/CH = H.head - if (CS.clothing_flags & CH.clothing_flags & THICKMATERIAL) - return FALSE // Skip over them if they have no exposed flesh. - - if(medical_mode_flags & MEDBOT_DECLARE_CRIT && C.health <= 0) //Critical condition! Call for help! - declare(C) - - //They're injured enough for it! - var/list/treat_me_for = list() - if(C.getBruteLoss() > heal_threshold) - treat_me_for += BRUTE - - if(C.getOxyLoss() > (5 + heal_threshold)) - treat_me_for += OXY - - if(C.getFireLoss() > heal_threshold) - treat_me_for += BURN - - if(C.getToxLoss() > heal_threshold) - treat_me_for += TOX - - if(damagetype_healer in treat_me_for) - return TRUE - if(damagetype_healer == "all" && treat_me_for.len) - return TRUE - -/mob/living/simple_animal/bot/medbot/UnarmedAttack(atom/A, proximity_flag) - if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED)) - return - if(iscarbon(A) && !tending) - var/mob/living/carbon/C = A - patient = C - mode = BOT_HEALING - update_appearance() - medicate_patient(C) - update_appearance() - return - ..() - -/mob/living/simple_animal/bot/medbot/examinate(atom/A as mob|obj|turf in view()) - ..() - if(!is_blind()) - chemscan(src, A) - -/mob/living/simple_animal/bot/medbot/proc/medicate_patient(mob/living/carbon/C) - if(!(bot_mode_flags & BOT_MODE_ON)) - return - - if(!istype(C)) - oldpatient = patient - soft_reset() - return - - if(C.stat == DEAD || (HAS_TRAIT(C, TRAIT_FAKEDEATH))) - var/list/messagevoice = list("No! Stay with me!" = 'sound/voice/medbot/no.ogg',"Live, damnit! LIVE!" = 'sound/voice/medbot/live.ogg',"I...I've never lost a patient before. Not today, I mean." = 'sound/voice/medbot/lost.ogg') - var/message = pick(messagevoice) - speak(message) - playsound(src, messagevoice[message], 50) - oldpatient = patient - soft_reset() - return - - tending = TRUE - while(tending) - var/treatment_method - var/list/potential_methods = list() - - if(C.getBruteLoss() > heal_threshold) - potential_methods += BRUTE - - if(C.getFireLoss() > heal_threshold) - potential_methods += BURN - - if(C.getOxyLoss() > (5 + heal_threshold)) - potential_methods += OXY - - if(C.getToxLoss() > heal_threshold) - potential_methods += TOX - - for(var/i in potential_methods) - if(i != damagetype_healer) - continue - treatment_method = i - - if(damagetype_healer == "all" && potential_methods.len) - treatment_method = pick(potential_methods) - - if(!treatment_method && !(bot_cover_flags & BOT_COVER_EMAGGED)) //If they don't need any of that they're probably cured! - if(C.maxHealth - C.get_organic_health() < heal_threshold) - to_chat(src, span_notice("[C] is healthy! Your programming prevents you from tending the wounds of anyone without at least [heal_threshold] damage of any one type ([heal_threshold + 5] for oxygen damage.)")) - - var/list/messagevoice = list("All patched up!" = 'sound/voice/medbot/patchedup.ogg',"An apple a day keeps me away." = 'sound/voice/medbot/apple.ogg',"Feel better soon!" = 'sound/voice/medbot/feelbetter.ogg') - var/message = pick(messagevoice) - speak(message) - playsound(src, messagevoice[message], 50) - bot_reset() - tending = FALSE - else if(patient) - C.visible_message(span_danger("[src] is trying to tend the wounds of [patient]!"), \ - span_userdanger("[src] is trying to tend your wounds!")) - - if(do_after(src, 2 SECONDS, patient)) //Slightly faster than default tend wounds, but does less HPS - if((get_dist(src, patient) <= 1) && (bot_mode_flags & BOT_MODE_ON) && assess_patient(patient)) - var/healies = heal_amount - var/obj/item/storage/medkit/medkit = medkit_type - if(treatment_method == BRUTE && initial(medkit.damagetype_healed) == BRUTE) //specialized brute gets a bit of bonus, as a snack. - healies *= 1.1 - if(bot_cover_flags & BOT_COVER_EMAGGED) - patient.reagents.add_reagent(/datum/reagent/toxin/chloralhydrate, 5) - patient.apply_damage((healies * 1), treatment_method, spread_damage = TRUE) - log_combat(src, patient, "pretended to tend wounds on", "internal tools", "([uppertext(treatment_method)]) (EMAGGED)") - else - patient.heal_damage_type((healies * 1), treatment_method) //don't need to check treatment_method since we know by this point that they were actually damaged. - log_combat(src, patient, "tended the wounds of", "internal tools", "([uppertext(treatment_method)])") - C.visible_message(span_notice("[src] tends the wounds of [patient]!"), \ - "[span_green("[src] tends your wounds!")]") - ADD_TRAIT(patient,TRAIT_MEDIBOTCOMINGTHROUGH,tag) - addtimer(TRAIT_CALLBACK_REMOVE(patient, TRAIT_MEDIBOTCOMINGTHROUGH, tag), (30 SECONDS)) - else - tending = FALSE - else - tending = FALSE - - update_appearance() - if(!tending) - visible_message("[src] places its tools back into itself.") - soft_reset() - else - tending = FALSE - -/mob/living/simple_animal/bot/medbot/explode() - var/atom/Tsec = drop_location() - - drop_part(medkit_type, Tsec) - new /obj/item/assembly/prox_sensor(Tsec) - drop_part(healthanalyzer, Tsec) - - if(bot_cover_flags & BOT_COVER_EMAGGED && prob(25)) - playsound(src, 'sound/voice/medbot/insult.ogg', 50) - return ..() - -/mob/living/simple_animal/bot/medbot/proc/declare(crit_patient) - if(!COOLDOWN_FINISHED(src, last_patient_message)) - return - COOLDOWN_START(src, last_patient_message, MEDBOT_PATIENTSPEAK_DELAY) - var/area/location = get_area(src) - speak("Medical emergency! [crit_patient || "A patient"] is in critical condition at [location]!", radio_channel) - -#undef MEDBOT_NEW_PATIENTSPEAK_DELAY -#undef MEDBOT_PATIENTSPEAK_DELAY -#undef MEDBOT_FREAKOUT_DELAY - -#undef MEDBOT_PANIC_NONE -#undef MEDBOT_PANIC_LOW -#undef MEDBOT_PANIC_MED -#undef MEDBOT_PANIC_HIGH -#undef MEDBOT_PANIC_FUCK -#undef MEDBOT_PANIC_ENDING -#undef MEDBOT_PANIC_END diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index d2b28f0f8871..8102b105741d 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -609,29 +609,6 @@ value = initial(search_objects) search_objects = value -/mob/living/simple_animal/hostile/consider_wakeup() - ..() - var/list/tlist - var/turf/T = get_turf(src) - - if (!T) - return - - if (!length(SSmobs.clients_by_zlevel[T.z])) // It's fine to use .len here but doesn't compile on 511 - toggle_ai(AI_Z_OFF) - return - - var/cheap_search = isturf(T) && !is_station_level(T.z) - if (cheap_search) - tlist = ListTargetsLazy(T.z) - else - tlist = ListTargets() - - if(AIStatus == AI_IDLE && FindTarget(tlist)) - if(cheap_search) //Try again with full effort - FindTarget() - toggle_ai(AI_ON) - /mob/living/simple_animal/hostile/proc/ListTargetsLazy(_Z)//Step 1, find out what we can see var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/vehicle/sealed/mecha)) . = list() diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index bbe1275d90a7..0bb3664e5c02 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -136,7 +136,7 @@ ///Played when someone punches the creature. var/attacked_sound = SFX_PUNCH - ///The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever), AI_Z_OFF (Temporarily off due to nonpresence of players). + ///The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever). var/AIStatus = AI_ON ///once we have become sentient, we can never go back. var/can_have_ai = TRUE @@ -211,10 +211,6 @@ GLOB.simple_animals[AIStatus] -= src SSnpcpool.currentrun -= src - var/turf/T = get_turf(src) - if (T && AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src - return ..() /mob/living/simple_animal/examine(mob/user) @@ -580,29 +576,12 @@ return if (AIStatus != togglestatus) if (togglestatus > 0 && togglestatus < 5) - if (togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF) - var/turf/T = get_turf(src) - if (T) - if (AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src - else - SSidlenpcpool.idle_mobs_by_zlevel[T.z] += src GLOB.simple_animals[AIStatus] -= src GLOB.simple_animals[togglestatus] += src AIStatus = togglestatus else stack_trace("Something attempted to set simple animals AI to an invalid state: [togglestatus]") -/mob/living/simple_animal/proc/consider_wakeup() - if (pulledby || shouldwakeup) - toggle_ai(AI_ON) - -/mob/living/simple_animal/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) - ..() - if (old_turf && AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[old_turf.z] -= src - toggle_ai(initial(AIStatus)) - ///This proc is used for adding the swabbale element to mobs so that they are able to be biopsied and making sure holograpic and butter-based creatures don't yield viable cells samples. /mob/living/simple_animal/proc/add_cell_sample() return diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 32c44d320b59..55e1dfebe38e 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -183,7 +183,7 @@ ///Allows a datum to intercept all click calls this mob is the source of var/datum/click_intercept - ///THe z level this mob is currently registered in + ///The z level this mob is currently registered in var/registered_z = null var/memory_throttle_time = 0 diff --git a/code/modules/modular_computers/file_system/programs/radar.dm b/code/modules/modular_computers/file_system/programs/radar.dm index 811adb0337a3..cf8f39073cbf 100644 --- a/code/modules/modular_computers/file_system/programs/radar.dm +++ b/code/modules/modular_computers/file_system/programs/radar.dm @@ -279,8 +279,8 @@ var/obj/structure/mop_bucket/janitorialcart/janicart = custodial_tools tool_name = "[janicart.name] - Water level: [janicart.reagents.total_volume] / [janicart.reagents.maximum_volume]" - if(istype(custodial_tools, /mob/living/simple_animal/bot/cleanbot)) - var/mob/living/simple_animal/bot/cleanbot/cleanbots = custodial_tools + if(istype(custodial_tools, /mob/living/basic/bot/cleanbot)) + var/mob/living/basic/bot/cleanbot/cleanbots = custodial_tools tool_name = "[cleanbots.name] - [cleanbots.bot_mode_flags & BOT_MODE_ON ? "Online" : "Offline"]" var/list/tool_information = list( diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index ea0fb8f64a31..816ef408d777 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -71,6 +71,7 @@ /obj/item/paper/Initialize(mapload) . = ..() + AddComponent(/datum/component/customizable_reagent_holder, /obj/item/clothing/mask/cigarette/rollie, CUSTOM_INGREDIENT_ICON_NOCHANGE, ingredient_type=CUSTOM_INGREDIENT_TYPE_DRYABLE, max_ingredients=2, job_xp = 1, job = JOB_BOTANIST) pixel_x = base_pixel_x + rand(-9, 9) pixel_y = base_pixel_y + rand(-8, 8) diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index cb4304f27bbc..9859b66dc610 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -27,6 +27,7 @@ COMSIG_ATOM_ENTERED = PROC_REF(on_entered), ) AddElement(/datum/element/connect_loc, loc_connections) + AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD))) /obj/machinery/field/containment/Destroy() if(field_gen_1) diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 26c3a9edf398..656a169c5587 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -206,6 +206,7 @@ no power level overlay is currently in the overlays list. air_update_turf(TRUE, FALSE) INVOKE_ASYNC(src, PROC_REF(cleanup)) addtimer(CALLBACK(src, PROC_REF(cool_down)), 5 SECONDS) + RemoveElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD))) /obj/machinery/field/generator/proc/cool_down() if(active || warming_up <= 0) @@ -218,6 +219,7 @@ no power level overlay is currently in the overlays list. /obj/machinery/field/generator/proc/turn_on() active = FG_CHARGING addtimer(CALLBACK(src, PROC_REF(warm_up)), 5 SECONDS) + AddElement(/datum/element/give_turf_traits, string_list(list(TRAIT_CONTAINMENT_FIELD))) /obj/machinery/field/generator/proc/warm_up() if(!active) diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 579bf1d1fb26..603dc1ffe091 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -386,16 +386,8 @@ /obj/singularity/proc/can_move(turf/considered_turf) if(!considered_turf) return FALSE - if((locate(/obj/machinery/field/containment) in considered_turf) || (locate(/obj/machinery/shieldwall) in considered_turf)) + if (HAS_TRAIT(considered_turf, TRAIT_CONTAINMENT_FIELD)) return FALSE - else if(locate(/obj/machinery/field/generator) in considered_turf) - var/obj/machinery/field/generator/check_generator = locate(/obj/machinery/field/generator) in considered_turf - if(check_generator?.active) - return FALSE - else if(locate(/obj/machinery/power/shieldwallgen) in considered_turf) - var/obj/machinery/power/shieldwallgen/check_shield = locate(/obj/machinery/power/shieldwallgen) in considered_turf - if(check_shield?.active) - return FALSE return TRUE /obj/singularity/proc/event() diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index fe994db74756..8cea3e0b7fcb 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -422,7 +422,7 @@ if(user) user.update_held_items() - SSblackbox.record_feedback("tally", "gun_fired", 1, type) + SSblackbox.record_feedback("tally", "gun_fired", 1, initial(name)) return TRUE diff --git a/code/modules/projectiles/guns/special/medbeam.dm b/code/modules/projectiles/guns/special/medbeam.dm index d3ee77ef3b3a..d25943e95bfe 100644 --- a/code/modules/projectiles/guns/special/medbeam.dm +++ b/code/modules/projectiles/guns/special/medbeam.dm @@ -70,7 +70,7 @@ current_beam = user.Beam(current_target, icon_state="medbeam", time = 10 MINUTES, maxdistance = max_range, beam_type = /obj/effect/ebeam/medical) RegisterSignal(current_beam, COMSIG_QDELETING, PROC_REF(beam_died))//this is a WAY better rangecheck than what was done before (process check) - SSblackbox.record_feedback("tally", "gun_fired", 1, type) + SSblackbox.record_feedback("tally", "gun_fired", 1, initial(name)) /obj/item/gun/medbeam/process() if(!mounted && !isliving(loc)) diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index e365af020e26..e545cf615acb 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -19,13 +19,16 @@ visible_message(span_warning("[src] fizzles on contact with [victim]!")) return PROJECTILE_DELETE_WITHOUT_HITTING - if(istype(target, /obj/machinery/hydroponics)) // even plants can block antimagic - var/obj/machinery/hydroponics/plant_tray = target - if(!plant_tray.myseed) - return - if(plant_tray.myseed.get_gene(/datum/plant_gene/trait/anti_magic)) - visible_message(span_warning("[src] fizzles on contact with [plant_tray]!")) - return PROJECTILE_DELETE_WITHOUT_HITTING + if(target.GetComponent(/datum/component/plant_growing)) // even plants can block antimagic + var/datum/component/plant_growing/growing = target.GetComponent(/datum/component/plant_growing) + + for(var/item as anything in growing.managed_seeds) + var/obj/item/seeds/seed = growing.managed_seeds[item] + if(!seed) + continue + if(seed.get_gene(/datum/plant_gene/trait/anti_magic)) + visible_message(span_warning("[src] fizzles on contact with [target]!")) + return PROJECTILE_DELETE_WITHOUT_HITTING /obj/projectile/magic/death name = "bolt of death" @@ -46,12 +49,14 @@ victim.investigate_log("has been killed by a bolt of death.", INVESTIGATE_DEATHS) victim.death() - if(istype(target, /obj/machinery/hydroponics)) - var/obj/machinery/hydroponics/plant_tray = target - if(!plant_tray.myseed) - return - plant_tray.set_weedlevel(0) // even the weeds perish - plant_tray.plantdies() + if(target.GetComponent(/datum/component/plant_growing)) // even plants can block antimagic + var/datum/component/plant_growing/growing = target.GetComponent(/datum/component/plant_growing) + + for(var/item as anything in growing.managed_seeds) + var/obj/item/seeds/seed = growing.managed_seeds[item] + if(!seed) + continue + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -300) /obj/projectile/magic/resurrection name = "bolt of resurrection" @@ -73,11 +78,14 @@ else if(victim.stat != DEAD) to_chat(victim, span_notice("You feel great!")) - if(istype(target, /obj/machinery/hydroponics)) - var/obj/machinery/hydroponics/plant_tray = target - if(!plant_tray.myseed) - return - plant_tray.set_plant_health(plant_tray.myseed.endurance, forced = TRUE) + if(target.GetComponent(/datum/component/plant_growing)) // even plants can block antimagic + var/datum/component/plant_growing/growing = target.GetComponent(/datum/component/plant_growing) + + for(var/item as anything in growing.managed_seeds) + var/obj/item/seeds/seed = growing.managed_seeds[item] + if(!seed) + continue + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, 1000) /obj/projectile/magic/teleport name = "bolt of teleportation" @@ -160,12 +168,6 @@ var/mob/living/victim = target victim.wabbajack(set_wabbajack_effect, set_wabbajack_changeflags) - if(istype(target, /obj/machinery/hydroponics)) - var/obj/machinery/hydroponics/plant_tray = target - if(!plant_tray.myseed) - return - plant_tray.polymorph() - /obj/projectile/magic/animate name = "bolt of animation" icon_state = "red_1" diff --git a/code/modules/reagents/chemistry/equilibrium.dm b/code/modules/reagents/chemistry/equilibrium.dm index a30a1cda6324..4e1aaeca21bf 100644 --- a/code/modules/reagents/chemistry/equilibrium.dm +++ b/code/modules/reagents/chemistry/equilibrium.dm @@ -69,7 +69,7 @@ to_delete = TRUE return LAZYADD(holder.reaction_list, src) - SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[reaction.type] attempts") + SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[reaction] attempts") /datum/equilibrium/Destroy() @@ -210,11 +210,11 @@ //Are we overheated? if(reaction.is_cold_recipe) if(holder.chem_temp < reaction.overheat_temp && reaction.overheat_temp != NO_OVERHEAT) //This is before the process - this is here so that overly_impure and overheated() share the same code location (and therefore vars) for calls. - SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[reaction.type] overheated reaction steps") + SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[reaction] overheated reaction steps") reaction.overheated(holder, src, step_volume_added) else if(holder.chem_temp > reaction.overheat_temp) - SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[reaction.type] overheated reaction steps") + SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[reaction] overheated reaction steps") reaction.overheated(holder, src, step_volume_added) //is our product too impure? @@ -223,7 +223,7 @@ if(!reagent) //might be missing from overheat exploding continue if (reagent.purity < reaction.purity_min)//If purity is below the min, call the proc - SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[reaction.type] overly impure reaction steps") + SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[reaction] overly impure reaction steps") reaction.overly_impure(holder, src, step_volume_added) //did we explode? diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index 264f2a4cce7d..3fd53be8df48 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -314,10 +314,15 @@ /// Remove an amount of reagents without caring about what they are /datum/reagents/proc/remove_any(amount = 1) + if(!amount) + return + var/list/cached_reagents = reagent_list var/total_removed = 0 var/current_list_element = 1 - var/initial_list_length = cached_reagents.len //stored here because removing can cause some reagents to be deleted, ergo length change. + var/initial_list_length = length(cached_reagents) //stored here because removing can cause some reagents to be deleted, ergo length change. + if(!initial_list_length) + return current_list_element = rand(1, cached_reagents.len) @@ -331,7 +336,7 @@ current_list_element = 1 var/datum/reagent/R = cached_reagents[current_list_element] - var/remove_amt = min(amount-total_removed,round(amount/rand(2,initial_list_length),round(amount/10,0.01))) //double round to keep it at a somewhat even spread relative to amount without getting funky numbers. + var/remove_amt = min(amount-total_removed,round(amount/max(1, rand(2,initial_list_length)),round(amount/10,0.01))) //double round to keep it at a somewhat even spread relative to amount without getting funky numbers. //min ensures we don't go over amount. remove_reagent(R.type, remove_amt) @@ -515,6 +520,7 @@ var/transfer_log = list() var/r_to_send = list() // Validated list of reagents to be exposed var/reagents_to_remove = list() + SEND_SIGNAL(R, COMSIG_REAGENT_PRE_TRANS_TO, src) if(!round_robin) var/part = amount / src.total_volume for(var/datum/reagent/reagent as anything in cached_reagents) @@ -525,6 +531,10 @@ trans_data = copy_data(reagent) if(reagent.intercept_reagents_transfer(R, cached_amount))//Use input amount instead. continue + if(is_reagent_container(my_atom)) + var/obj/item/reagent_containers/container = my_atom + if(SEND_SIGNAL(R, COMSIG_REAGENT_CACHE_ADD_ATTEMPT, reagent, src, container.amount_per_transfer_from_this)) + return if(!R.add_reagent(reagent.type, transfer_amount * multiplier, trans_data, chem_temp, reagent.purity, reagent.ph, no_react = TRUE, ignore_splitting = reagent.chemical_flags & REAGENT_DONOTSPLIT)) //we only handle reaction after every reagent has been transfered. continue if(methods) @@ -557,10 +567,14 @@ var/transfer_amount = amount if(amount > reagent.volume) transfer_amount = reagent.volume - if(reagent.intercept_reagents_transfer(R, cached_amount))//Use input amount instead. + if(is_reagent_container(my_atom)) + var/obj/item/reagent_containers/container = my_atom + if(SEND_SIGNAL(R, COMSIG_REAGENT_CACHE_ADD_ATTEMPT, reagent, src, container.amount_per_transfer_from_this)) + return + if(SEND_SIGNAL(R, COMSIG_REAGENT_CACHE_ADD_ATTEMPT, reagent, src, amount)) continue if(!R.add_reagent(reagent.type, transfer_amount * multiplier, trans_data, chem_temp, reagent.purity, reagent.ph, no_react = TRUE, ignore_splitting = reagent.chemical_flags & REAGENT_DONOTSPLIT)) //we only handle reaction after every reagent has been transfered. - continue + return to_transfer = max(to_transfer - transfer_amount , 0) if(methods) if(isorgan(target_atom)) @@ -1030,7 +1044,7 @@ if(!text_in_list(temp_mix_message, mix_message)) mix_message += temp_mix_message continue - SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[equilibrium.reaction.type] total reaction steps") + SSblackbox.record_feedback("tally", "chemical_reaction", 1, "[equilibrium.reaction] total reaction steps") if(num_reactions) SEND_SIGNAL(src, COMSIG_REAGENTS_REACTION_STEP, num_reactions, seconds_per_tick) diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index c0f4fd62281b..945c3d2abaa7 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -209,7 +209,9 @@ Primarily used in reagents/reaction_agents /// Called when this reagent is first added to a mob /datum/reagent/proc/on_mob_add(mob/living/L, amount) SHOULD_CALL_PARENT(TRUE) - overdose_threshold /= max(normalise_creation_purity(), 1) //Maybe??? Seems like it would help pure chems be even better but, if I normalised this to 1, then everything would take a 25% reduction + // MONKESTATION REMOVAL START - Purity is disabled and we shouldn't change the overdose thresholds for things behind people's backs. + // overdose_threshold /= max(normalise_creation_purity(), 1) //Maybe??? Seems like it would help pure chems be even better but, if I normalised this to 1, then everything would take a 25% reduction + // MONKESTATION REMOVAL END if(added_traits) L.add_traits(added_traits, "added:[type]") @@ -266,27 +268,10 @@ Primarily used in reagents/reaction_agents M.add_mood_event("[type]_overdose", /datum/mood_event/overdose, name) return -/** - * New, standardized method for chemicals to affect hydroponics trays. - * Defined on a per-chem level as opposed to by the tray. - * Can affect plant's health, stats, or cause the plant to react in certain ways. - */ -/datum/reagent/proc/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - mytray.adjustNutri(round(chems.get_reagent_amount(src.type) * 0.1)) - /datum/reagent/proc/generate_infusion_values(datum/reagents/chems) if(!chems) return -/// Proc is used by [/datum/reagent/proc/on_hydroponics_apply] to see if the tray and the reagents inside is in a valid state to apply reagent effects -/datum/reagent/proc/check_tray(datum/reagents/chems, obj/machinery/hydroponics/mytray) - ASSERT(mytray) - // Check if we have atleast a single amount of the reagent - if(!chems.has_reagent(type, 1)) - return FALSE - - return TRUE - /** * Specifically made for mutation reagent reactions */ diff --git a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm index 9ea62c503cbf..42a66921c5f8 100644 --- a/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm @@ -363,12 +363,6 @@ ..() return TRUE -// Antitoxin binds plants pretty well. So the tox goes significantly down -/datum/reagent/medicine/c2/multiver/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_toxic(-round(chems.get_reagent_amount(src.type) * 2)) - #define issyrinormusc(A) (istype(A,/datum/reagent/medicine/c2/syriniver) || istype(A,/datum/reagent/medicine/c2/musiver)) //musc is metab of syrin so let's make sure we're not purging either /datum/reagent/medicine/c2/syriniver //Inject >> SYRINge diff --git a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm index 1fd79f131f42..0a565b1a279f 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm @@ -50,10 +50,6 @@ addiction_types = list(/datum/addiction/alcohol = 0.05 * boozepwr) return ..() -/datum/reagent/consumable/ethanol/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - myseed.process_trait_gain(/datum/plant_gene/trait/brewing, ((chems.get_reagent_amount(src.type) * 0.25) + (boozepwr * 0.1))) - /datum/reagent/consumable/ethanol/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired) if(drinker.get_drunk_amount() < volume * boozepwr * ALCOHOL_THRESHOLD_MODIFIER || boozepwr < 0) var/booze_power = boozepwr @@ -110,13 +106,6 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED glass_price = DRINK_PRICE_STOCK - // Beer is a chemical composition of alcohol and various other things. It's a garbage nutrient but hey, it's still one. Also alcohol is bad, mmmkay? -/datum/reagent/consumable/ethanol/beer/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_plant_health(-round(chems.get_reagent_amount(src.type) * 0.05)) - mytray.adjust_waterlevel(round(chems.get_reagent_amount(src.type) * 0.7)) - /datum/reagent/consumable/ethanol/beer/light name = "Light Beer" description = "An alcoholic beverage brewed since ancient times on Old Earth. This variety has reduced calorie and alcohol content." diff --git a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm index 19b9e82f518d..f7aacbb5a381 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm @@ -193,14 +193,6 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED default_container = /obj/item/reagent_containers/condiment/milk - // Milk is good for humans, but bad for plants. The sugars cannot be used by plants, and the milk fat harms growth. Not shrooms though. I can't deal with this now... -/datum/reagent/consumable/milk/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_waterlevel(round(chems.get_reagent_amount(type) * 1)) - mytray.adjust_plant_health(round(chems.get_reagent_amount(type) * 0.1)) - if(myseed) - myseed.adjust_potency(-chems.get_reagent_amount(src.type) * 0.5) /datum/reagent/consumable/milk/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) if(affected_mob.getBruteLoss() && SPT_PROB(10, seconds_per_tick)) @@ -590,15 +582,6 @@ taste_description = "carbonated water" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -// A variety of nutrients are dissolved in club soda, without sugar. -// These nutrients include carbon, oxygen, hydrogen, phosphorous, potassium, sulfur and sodium, all of which are needed for healthy plant growth. -/datum/reagent/consumable/sodawater/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_waterlevel(round(chems.get_reagent_amount(type) * 1)) - mytray.adjust_plant_health(round(chems.get_reagent_amount(type) * 0.1)) - - /datum/reagent/consumable/sodawater/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) affected_mob.adjust_dizzy(-10 SECONDS * REM * seconds_per_tick) affected_mob.adjust_drowsiness(-6 SECONDS * REM * seconds_per_tick) diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index b7be546dc238..928b36764430 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -82,12 +82,6 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED addiction_types = list(/datum/addiction/nicotine = 15) // 6 per 2 seconds - //Nicotine is used as a pesticide IRL. -/datum/reagent/drug/nicotine/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_toxic(round(chems.get_reagent_amount(type))) - mytray.adjust_pestlevel(-rand(1,2)) /datum/reagent/drug/nicotine/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) if(SPT_PROB(0.5, seconds_per_tick)) diff --git a/code/modules/reagents/chemistry/reagents/food_reagents.dm b/code/modules/reagents/chemistry/reagents/food_reagents.dm index 95ef729d7091..567c700e76f0 100644 --- a/code/modules/reagents/chemistry/reagents/food_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/food_reagents.dm @@ -64,11 +64,6 @@ . = ..() target.fertility_boosting += min(25, volume * 0.5) -/datum/reagent/consumable/nutriment/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_plant_health(round(chems.get_reagent_amount(type) * 0.2)) - /datum/reagent/consumable/nutriment/on_mob_life(mob/living/carbon/M, seconds_per_tick, times_fired) if(SPT_PROB(30, seconds_per_tick)) M.heal_bodypart_damage(brute = brute_heal, burn = burn_heal, updating_health = FALSE, required_bodytype = BODYTYPE_ORGANIC) @@ -236,13 +231,6 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED default_container = /obj/item/reagent_containers/condiment/sugar -// Plants should not have sugar, they can't use it and it prevents them getting water/ nutients, it is good for mold though... -/datum/reagent/consumable/sugar/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_weedlevel(rand(1,2)) - mytray.adjust_pestlevel(rand(1,2)) - /datum/reagent/consumable/sugar/feed_interaction(mob/living/basic/chicken/target, volume) .=..() @@ -266,12 +254,6 @@ taste_description = "watery milk" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED - // Compost for EVERYTHING -/datum/reagent/consumable/virus_food/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_plant_health(-round(chems.get_reagent_amount(type) * 0.5)) - /datum/reagent/consumable/soysauce name = "Soysauce" description = "A salty sauce made from the soy plant." @@ -780,16 +762,6 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED default_container = /obj/item/reagent_containers/condiment/honey - // On the other hand, honey has been known to carry pollen with it rarely. Can be used to take in a lot of plant qualities all at once, or harm the plant. -/datum/reagent/consumable/honey/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - if(myseed && prob(80)) - mytray.adjust_weedlevel(rand(1,2)) - mytray.adjust_pestlevel(rand(1,2)) - myseed.adjust_maturation(rand(1,2)) - myseed.adjust_lifespan(rand(1,2)) - /datum/reagent/consumable/honey/on_mob_life(mob/living/carbon/M, seconds_per_tick, times_fired) holder.add_reagent(/datum/reagent/consumable/sugar, 3 * REM * seconds_per_tick) if(SPT_PROB(33, seconds_per_tick)) diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 9519846b59b6..50ffe7799979 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -46,6 +46,7 @@ /// Flags to fullheal every metabolism tick var/full_heal_flags = ~(HEAL_BRUTE|HEAL_BURN|HEAL_TOX|HEAL_RESTRAINTS|HEAL_REFRESH_ORGANS) +/* // The best stuff there is. For testing/debugging. /datum/reagent/medicine/adminordrazine/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) . = ..() @@ -65,6 +66,7 @@ else if(prob(20)) mytray.visible_message(span_warning("Nothing happens...")) +*/ /datum/reagent/medicine/adminordrazine/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) affected_mob.heal_bodypart_damage(5 * REM * seconds_per_tick, 5 * REM * seconds_per_tick, 0, FALSE, affected_bodytype) @@ -148,13 +150,6 @@ ..() return TRUE -// Healing -/datum/reagent/medicine/cryoxadone/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_plant_health(round(chems.get_reagent_amount(type) * 3)) - mytray.adjust_toxic(-round(chems.get_reagent_amount(type) * 3)) - /datum/reagent/medicine/clonexadone name = "Clonexadone" description = "A chemical that derives from Cryoxadone. It specializes in healing clone damage, but nothing else. Requires very cold temperatures to properly metabolize, and metabolizes quicker than cryoxadone." @@ -883,11 +878,13 @@ var/expected_amount_to_full_heal = round(max_health / healing_per_reagent_unit, DAMAGE_PRECISION) / excess_healing_ratio return amount_needed_to_revive + expected_amount_to_full_heal +/* // FEED ME SEYMOUR /datum/reagent/medicine/strange_reagent/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) . = ..() if(chems.has_reagent(src.type, 1)) mytray.spawnplant() +*/ /datum/reagent/medicine/strange_reagent/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume) if(exposed_mob.stat != DEAD || !(exposed_mob.mob_biotypes & MOB_ORGANIC)) @@ -1193,14 +1190,6 @@ addiction_types = list(/datum/addiction/hallucinogens = 14) metabolized_traits = list(TRAIT_PACIFISM) -/datum/reagent/medicine/earthsblood/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - if(!mytray.self_sustaining) - mytray.increase_sustaining(round(chems.get_reagent_amount(type))) - else - mytray.lastcycle += 2.5 SECONDS /// makes trays roughly 25% faster - /// Returns a hippie-esque string for the person affected by the reagent to say. /datum/reagent/medicine/earthsblood/proc/return_hippie_line() var/static/list/earthsblood_lines = list( @@ -1284,6 +1273,7 @@ color = "#C1151D" overdose_threshold = 30 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE + metabolized_traits = list(TRAIT_SLEEPIMMUNE, TRAIT_BATON_RESISTANCE, TRAIT_CANT_STAMCRIT) /datum/reagent/medicine/changelingadrenaline/on_mob_life(mob/living/carbon/metabolizer, seconds_per_tick, times_fired) ..() @@ -1295,13 +1285,11 @@ return TRUE /datum/reagent/medicine/changelingadrenaline/on_mob_metabolize(mob/living/affected_mob) - ..() - affected_mob.add_traits(list(TRAIT_SLEEPIMMUNE, TRAIT_BATON_RESISTANCE, TRAIT_CANT_STAMCRIT), type) + . = ..() affected_mob.add_movespeed_mod_immunities(type, /datum/movespeed_modifier/damage_slowdown) /datum/reagent/medicine/changelingadrenaline/on_mob_end_metabolize(mob/living/affected_mob) - ..() - affected_mob.remove_traits(list(TRAIT_SLEEPIMMUNE, TRAIT_BATON_RESISTANCE, TRAIT_CANT_STAMCRIT), type) + . = ..() affected_mob.remove_movespeed_mod_immunities(type, /datum/movespeed_modifier/damage_slowdown) affected_mob.remove_status_effect(/datum/status_effect/dizziness) affected_mob.remove_status_effect(/datum/status_effect/jitter) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 8df7408e8892..a0625e127bc3 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -35,13 +35,6 @@ desc = "Are you sure this is tomato juice?" icon_state = "glass_red" - - // FEED ME -/datum/reagent/blood/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_pestlevel(rand(2,3)) - /datum/reagent/blood/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume, show_message=TRUE, touch_protection=0) . = ..() if(data && data["viruses"]) @@ -283,14 +276,6 @@ if(affected_mob.blood_volume) affected_mob.blood_volume += 0.1 * REM * seconds_per_tick // water is good for you! -/datum/reagent/water/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray) - if(!myseed) - return - if(chems.has_reagent(src.type, 1)) - mytray.adjustWater(round(chems.get_reagent_amount(src.type) * 1)) - //You don't belong in this world, monster! - chems.remove_reagent(type, chems.get_reagent_amount(src.type)) - /datum/reagent/water/salt name = "Saltwater" description = "Water, but salty. Smells like... the station infirmary?" @@ -355,12 +340,6 @@ desc = "A glass of holy water." icon_state = "glass_clear" - // Holy water. Mostly the same as water, it also heals the plant a little with the power of the spirits. -/datum/reagent/water/holywater/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray) - if(chems.has_reagent(src.type, 1)) - mytray.adjust_waterlevel(round(chems.get_reagent_amount(type) * 1)) - mytray.adjust_plant_health(round(chems.get_reagent_amount(type) * 0.1)) - /datum/reagent/water/holywater/on_mob_add(mob/living/affected_mob, amount) . = ..() if(data) @@ -545,11 +524,6 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED turf_exposure = TRUE -/datum/reagent/lube/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - myseed.process_trait_gain(/datum/plant_gene/trait/slip, chems.get_reagent_amount(src.type) * 0.25) - myseed.adjust_endurance(chems.get_reagent_amount(src.type) * 0.08) - /datum/reagent/lube/expose_turf(turf/open/exposed_turf, reac_volume) . = ..() if(!istype(exposed_turf)) @@ -1023,19 +997,6 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -// You're an idiot for thinking that one of the most corrosive and deadly gasses would be beneficial -/datum/reagent/chlorine/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - if(!check_tray(chems, mytray)) - return - - mytray.adjust_plant_health(-round(chems.get_reagent_amount(type))) - mytray.adjust_toxic(round(chems.get_reagent_amount(type) * 1.5)) - mytray.adjust_waterlevel(-round(chems.get_reagent_amount(type) * 0.5)) - mytray.adjust_weedlevel(-rand(1,3)) - myseed.adjust_lifespan(-round(chems.get_reagent_amount(type) * 0.2)) - // White Phosphorous + water -> phosphoric acid. That's not a good thing really. - - /datum/reagent/chlorine/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) affected_mob.take_bodypart_damage(0.5*REM*seconds_per_tick, 0) . = TRUE @@ -1050,16 +1011,6 @@ ph = 2 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -// You're an idiot for thinking that one of the most corrosive and deadly gasses would be beneficial -/datum/reagent/fluorine/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - if(!check_tray(chems, mytray)) - return - - mytray.adjust_plant_health(-round(chems.get_reagent_amount(type) * 2)) - mytray.adjust_toxic(round(chems.get_reagent_amount(type) * 2.5)) - mytray.adjust_waterlevel(-round(chems.get_reagent_amount(type) * 0.5)) - mytray.adjust_weedlevel(-rand(1,4)) - /datum/reagent/fluorine/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) affected_mob.adjustToxLoss(0.5*REM*seconds_per_tick, 0) . = TRUE @@ -1085,14 +1036,6 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED restricted = TRUE -// Phosphoric salts are beneficial though. And even if the plant suffers, in the long run the tray gets some nutrients. The benefit isn't worth that much. -/datum/reagent/phosphorus/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_plant_health(-round(chems.get_reagent_amount(src.type) * 0.75)) - mytray.adjust_waterlevel(-round(chems.get_reagent_amount(src.type) * 0.5)) - mytray.adjust_weedlevel(-rand(1,2)) - /datum/reagent/lithium name = "Lithium" description = "A silver metal, its claim to fame is its remarkably low density. Using it is a bit too effective in calming oneself down." @@ -1214,15 +1157,6 @@ generated_values["endurance_change"] = amount * rand(-10, 10) * 0.1 return generated_values -//Mutagenic chem side-effects. -/datum/reagent/uranium/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_plant_health(-round(chems.get_reagent_amount(src.type) * 1)) - mytray.adjust_toxic(round(chems.get_reagent_amount(src.type) * 1)) - plant_mutation_reagent_apply(chems, mytray, user, mr = 10, hm = 5) - - /datum/reagent/uranium/radium name = "Radium" description = "Radium is an alkaline earth metal. It is extremely radioactive." @@ -1247,15 +1181,6 @@ generated_values["endurance_change"] = amount * rand(-10, 10) * 0.1 return generated_values -/datum/reagent/uranium/radium/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - if(!myseed) - return - - if(chems.has_reagent(src.type, 1)) - mytray.adjust_plant_health(-round(chems.get_reagent_amount(src.type) * 1)) - mytray.adjust_toxic(round(chems.get_reagent_amount(src.type) * 1)) - plant_mutation_reagent_apply(chems, mytray, user, mr = 10, hm = 5) - /datum/reagent/bluespace name = "Bluespace Dust" description = "A dust composed of microscopic bluespace crystals, with minor space-warping properties." @@ -1546,17 +1471,6 @@ generated_values["production_change"] = (amount * rand(2, 5) * 0.1) return generated_values -/datum/reagent/ammonia/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - if(!check_tray(chems, mytray)) - return - // Ammonia is bad ass. - if(chems.has_reagent(src.type, 1)) - mytray.adjust_plant_health(round(chems.get_reagent_amount(src.type) * 0.12)) - myseed.adjust_maturation(round(chems.get_reagent_amount(src.type) * 0.2)) - myseed.adjust_production(round(chems.get_reagent_amount(src.type) * 0.1)) - if(myseed && prob(10)) - myseed.adjust_yield(1) - /datum/reagent/diethylamine name = "Diethylamine" description = "A secondary amine, mildly corrosive." @@ -1565,15 +1479,6 @@ ph = 12 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -// This is more bad ass, and pests get hurt by the corrosive nature of it, not the plant. The new trade off is it culls stability. -/datum/reagent/diethylamine/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_plant_health(round(chems.get_reagent_amount(src.type) * 1)) - mytray.adjust_pestlevel(-rand(1,2)) - if(myseed) - myseed.adjust_yield(round(chems.get_reagent_amount(src.type) * 0.2)) - /datum/reagent/carbondioxide name = "Carbon Dioxide" reagent_state = GAS @@ -1779,30 +1684,13 @@ tox_prob = 5 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -// gives half the potency of saltpetre, and half the yield of diethylamine -/datum/reagent/plantnutriment/eznutriment/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.mutmod = 1 - myseed.adjust_lifespan(round(chems.get_reagent_amount(type) * 0.15)) - if(myseed) - myseed.adjust_potency(round(chems.get_reagent_amount(src.type) * 0.1)) - myseed.adjust_yield(round(chems.get_reagent_amount(src.type) * 0.1)) - - /datum/reagent/plantnutriment/left4zednutriment name = "Left 4 Zed" - description = "Unstable nutriment that makes plants mutate more often than usual." + description = "Unstable nutriment that makes plants wilt quickly but increases all stats while doing so." color = "#1A1E4D" // RBG: 26, 30, 77 tox_prob = 13 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/plantnutriment/left4zednutriment/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.yieldmod = 0 - mytray.mutmod = 2 - /datum/reagent/plantnutriment/robustharvestnutriment name = "Robust Harvest" description = "Very potent nutriment that slows plants from mutating whilst also making them grow faster." @@ -1823,14 +1711,6 @@ generated_values["lifespan_change"] = (amount * (rand(-2, 0) * 0.1)) return generated_values -/datum/reagent/plantnutriment/robustharvestnutriment/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.yieldmod = 1.3 - mytray.mutmod = 0 - myseed.adjust_maturation(round(chems.get_reagent_amount(src.type) * 0.1)) - myseed.adjust_production(round(chems.get_reagent_amount(src.type) * 0.05)) - /datum/reagent/plantnutriment/endurogrow name = "Enduro Grow" description = "A specialized nutriment, which decreases product quantity and potency, but strengthens the plants endurance." @@ -1838,15 +1718,6 @@ tox_prob = 8 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/plantnutriment/endurogrow/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray) - if(!check_tray(chems, mytray)) - return - - if(myseed) - myseed.adjust_potency(-round(chems.get_reagent_amount(type) * 0.1)) - myseed.adjust_yield(-round(chems.get_reagent_amount(type) * 0.075)) - myseed.adjust_endurance(round(chems.get_reagent_amount(type) * 0.35)) - /datum/reagent/plantnutriment/liquidearthquake name = "Liquid Earthquake" description = "A specialized nutriment, which increases the plant's production speed, as well as it's susceptibility to weeds." @@ -1854,15 +1725,6 @@ tox_prob = 13 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/plantnutriment/liquidearthquake/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray) - if(!check_tray(chems, mytray)) - return - - if(myseed) - myseed.adjust_weed_rate(round(chems.get_reagent_amount(type) * 0.1)) - myseed.adjust_weed_chance(round(chems.get_reagent_amount(type) * 0.3)) - myseed.adjust_production(-round(chems.get_reagent_amount(type) * 0.075)) - // GOON OTHERS @@ -2181,13 +2043,6 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED default_container = /obj/effect/decal/cleanable/ash -// Ash is also used IRL in gardening, as a fertilizer enhancer and weed killer -/datum/reagent/ash/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - mytray.adjust_plant_health(round(chems.get_reagent_amount(src.type) * 1)) - mytray.adjust_weedlevel(-1) - /datum/reagent/acetone name = "Acetone" description = "A slick, slightly carcinogenic liquid. Has a multitude of mundane uses in everyday life." @@ -2362,16 +2217,6 @@ generated_values["yield_change"] = (amount * (rand(0,2) * 0.2)) return generated_values -// Saltpetre is used for gardening IRL, to simplify highly, it raises potency and lowers production -/datum/reagent/saltpetre/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(src.type, 1)) - var/salt = chems.get_reagent_amount(src.type) - mytray.adjust_plant_health(round(salt * 0.18)) - if(myseed) - myseed.adjust_production(-round(salt * 0.1)-prob(salt%10)) - myseed.adjust_potency(round(salt*0.2)) - /datum/reagent/lye name = "Lye" description = "Also known as sodium hydroxide. As a profession making this is somewhat underwhelming." @@ -3047,16 +2892,6 @@ . = ..() affected_mob.adjustFireLoss((ispodperson(affected_mob) ? -1 : 1) * seconds_per_tick) -/datum/reagent/brimdust/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - if(!check_tray(chems, mytray)) - return - - if(chems.has_reagent(src.type, 1)) - mytray.adjust_weedlevel(-1) - mytray.adjust_pestlevel(-1) - mytray.adjust_plant_health(round(chems.get_reagent_amount(src.type) * 1)) - if(myseed) - myseed.adjust_potency(round(chems.get_reagent_amount(src.type) * 0.5)) // I made this food....with love. // Reagent added to food by chef's with a chef's kiss. Makes people happy. /datum/reagent/love diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm index 796c9cf19e06..173d503cf547 100644 --- a/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/pyrotechnic_reagents.dm @@ -192,15 +192,6 @@ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED liquid_fire_power = 1 - // why, just why -/datum/reagent/napalm/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(type, 1)) - if(!(myseed.resistance_flags & FIRE_PROOF)) - mytray.adjust_plant_health(-round(chems.get_reagent_amount(type) * 6)) - mytray.adjust_toxic(round(chems.get_reagent_amount(type) * 7)) - mytray.adjust_weedlevel(-rand(5,9)) //At least give them a small reward if they bother. - /datum/reagent/napalm/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) affected_mob.adjust_fire_stacks(1 * REM * seconds_per_tick) diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index aad8860acaeb..ac0ad4079e43 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -17,12 +17,6 @@ ///The afflicted must be above this health value in order for the toxin to deal damage var/health_required = -100 -// Are you a bad enough dude to poison your own plants? -/datum/reagent/toxin/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(type, 1)) - mytray.adjust_toxic(round(chems.get_reagent_amount(type) * 2)) - /datum/reagent/toxin/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) if(toxpwr && affected_mob.health > health_required) affected_mob.adjustToxLoss(toxpwr * REM * normalise_creation_purity() * seconds_per_tick, FALSE, required_biotype = affected_biotype) @@ -71,6 +65,7 @@ /datum/reagent/toxin/mutagen/feed_interaction(mob/living/basic/chicken/target, volume) target.instability += min(25, volume) +/* /datum/reagent/toxin/mutagen/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) if(!myseed) return @@ -80,6 +75,7 @@ if(prob(10)) chems.remove_all_type(type, chems.get_reagent_amount(type)) mytray.mutatespecie_new() +*/ #define LIQUID_PLASMA_BP (50+T0C) #define LIQUID_PLASMA_IG (325+T0C) @@ -350,16 +346,6 @@ ph = 2.7 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED - // Plant-B-Gone is just as bad -/datum/reagent/toxin/plantbgone/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - if(!check_tray(chems, mytray)) - return - - if(chems.has_reagent(type, 1)) - mytray.adjust_plant_health(-round(chems.get_reagent_amount(type) * 10)) - mytray.adjust_toxic(round(chems.get_reagent_amount(type) * 6)) - mytray.adjust_weedlevel(-rand(4,8)) - /datum/reagent/toxin/plantbgone/expose_obj(obj/exposed_obj, reac_volume) . = ..() if(istype(exposed_obj, /obj/structure/alien/weeds)) @@ -395,14 +381,6 @@ ph = 3 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED - //Weed Spray -/datum/reagent/toxin/plantbgone/weedkiller/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - if(!check_tray(chems, mytray)) - return - if(chems.has_reagent(type, 1)) - mytray.adjust_toxic(round(chems.get_reagent_amount(type) * 0.5)) - mytray.adjust_weedlevel(-rand(1,2)) - /datum/reagent/toxin/pestkiller name = "Pest Killer" description = "A harmful toxic mixture to kill pests. Do not ingest!" @@ -411,14 +389,6 @@ ph = 3.2 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -//Pest Spray -/datum/reagent/toxin/pestkiller/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - if(!check_tray(chems, mytray)) - return - if(chems.has_reagent(type, 1)) - mytray.adjust_toxic(round(chems.get_reagent_amount(type) * 1)) - mytray.adjust_pestlevel(-rand(1,2)) - /datum/reagent/toxin/pestkiller/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume) . = ..() if(exposed_mob.mob_biotypes & MOB_BUG) @@ -432,14 +402,6 @@ toxpwr = 1 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -//Pest Spray -/datum/reagent/toxin/pestkiller/organic/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - if(!check_tray(chems, mytray)) - return - if(chems.has_reagent(type, 1)) - mytray.adjust_toxic(round(chems.get_reagent_amount(type) * 0.1)) - mytray.adjust_pestlevel(-rand(1,2)) - /datum/reagent/toxin/spore name = "Spore Toxin" description = "A natural toxin produced by blob spores that inhibits vision when ingested." @@ -1048,14 +1010,6 @@ ph = 2.75 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -// ...Why? I mean, clearly someone had to have done this and thought, well, acid doesn't hurt plants, but what brought us here, to this point? -/datum/reagent/toxin/acid/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(type, 1)) - mytray.adjust_plant_health(-round(chems.get_reagent_amount(type) * 1)) - mytray.adjust_toxic(round(chems.get_reagent_amount(type) * 1.5)) - mytray.adjust_weedlevel(-rand(1,2)) - /datum/reagent/toxin/acid/expose_mob(mob/living/carbon/exposed_carbon, methods=TOUCH, reac_volume) . = ..() if(!istype(exposed_carbon)) @@ -1094,14 +1048,6 @@ ph = 0.0 chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -// SERIOUSLY -/datum/reagent/toxin/acid/fluacid/on_hydroponics_apply(obj/item/seeds/myseed, datum/reagents/chems, obj/machinery/hydroponics/mytray, mob/user) - . = ..() - if(chems.has_reagent(type, 1)) - mytray.adjust_plant_health(-round(chems.get_reagent_amount(type) * 2)) - mytray.adjust_toxic(round(chems.get_reagent_amount(type) * 3)) - mytray.adjust_weedlevel(-rand(1,4)) - /datum/reagent/toxin/acid/fluacid/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired) affected_mob.adjustFireLoss((current_cycle/15) * REM * normalise_creation_purity() * seconds_per_tick, FALSE, required_bodytype = affected_bodytype) . = TRUE diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm index 43ca9fec4e2f..c52315087dd8 100644 --- a/code/modules/reagents/reagent_containers/cups/_cup.dm +++ b/code/modules/reagents/reagent_containers/cups/_cup.dm @@ -124,7 +124,8 @@ return var/trans = reagents.trans_to(target, amount_per_transfer_from_this, transfered_by = user) - to_chat(user, span_notice("You transfer [trans] unit\s of the solution to [target].")) + if(trans) + to_chat(user, span_notice("You transfer [trans] unit\s of the solution to [target].")) else if(target.is_drainable()) //A dispenser. Transfer FROM it TO us. if(!target.reagents.total_volume) diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index 419455054e33..3e122bd7350c 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -28,7 +28,7 @@ /obj/item/reagent_containers/spray/afterattack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() - if(istype(target, /obj/structure/sink) || istype(target, /obj/structure/mop_bucket/janitorialcart) || istype(target, /obj/machinery/hydroponics)) + if(istype(target, /obj/structure/sink) || istype(target, /obj/structure/mop_bucket/janitorialcart)) return . |= AFTERATTACK_PROCESSED_ITEM diff --git a/code/modules/recycling/conveyor.dm b/code/modules/recycling/conveyor.dm index 1c4b2713e3e5..079d058c78dc 100644 --- a/code/modules/recycling/conveyor.dm +++ b/code/modules/recycling/conveyor.dm @@ -40,6 +40,8 @@ GLOBAL_LIST_EMPTY(conveyors_by_id) /obj/machinery/conveyor/Initialize(mapload) . = ..() AddElement(/datum/element/footstep_override, priority = STEP_SOUND_CONVEYOR_PRIORITY) + var/static/list/give_turf_traits = list(TRAIT_TURF_IGNORE_SLOWDOWN) + AddElement(/datum/element/give_turf_traits, give_turf_traits) /obj/machinery/conveyor/examine(mob/user) . = ..() diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm index 1fe250219162..371b7c994983 100644 --- a/code/modules/recycling/disposal/bin.dm +++ b/code/modules/recycling/disposal/bin.dm @@ -305,6 +305,25 @@ to_dump.pixel_x = to_dump.base_pixel_x + rand(-5, 5) to_dump.pixel_y = to_dump.base_pixel_y + rand(-5, 5) + +/obj/machinery/disposal/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) + . = ..() + visible_message(span_warning("[src] is ripped free from the floor!")) + deconstruct() + +/obj/machinery/disposal/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) + . = ..() + visible_message(span_warning("[src] is ripped free from the floor!")) + deconstruct() + +// Monkestation Addition Start +/obj/machinery/disposal/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE, quickstart = TRUE) + . = ..() + if(target && (target != src.loc)) + visible_message(span_warning("[src] is ripped free from the floor!")) + deconstruct() +// Monkestation Addition End + // Disposal bin // Holds items for disposal into pipe system // Draws air from turf, gradually charges internal reservoir diff --git a/code/modules/recycling/disposal/outlet.dm b/code/modules/recycling/disposal/outlet.dm index 6575a6ffe9e4..d83e1d5534f8 100644 --- a/code/modules/recycling/disposal/outlet.dm +++ b/code/modules/recycling/disposal/outlet.dm @@ -124,6 +124,37 @@ obj_flags |= EMAGGED return TRUE +/obj/structure/disposaloutlet/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) + . = ..() + if(!isnull(stored)) + stored.forceMove(loc) + transfer_fingerprints_to(stored) + stored = null + visible_message(span_warning("[src] is ripped free from the floor!")) + qdel(src) + +/obj/structure/disposaloutlet/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) + . = ..() + if(!isnull(stored)) + stored.forceMove(loc) + transfer_fingerprints_to(stored) + stored = null + visible_message(span_warning("[src] is ripped free from the floor!")) + qdel(src) + +// Monkestation Addition Start +/obj/structure/disposaloutlet/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = MOVE_FORCE_STRONG, gentle = FALSE, quickstart = TRUE) + . = ..() + if(target && (target != src.loc)) + if(isnull(stored)) + return + stored.forceMove(loc) + transfer_fingerprints_to(stored) + stored = null + visible_message(span_warning("[src] is ripped free from the floor!")) + qdel(src) +// Monkestation Addition End + #undef EJECT_SPEED_SLOW #undef EJECT_SPEED_MED #undef EJECT_SPEED_FAST diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm index a752a4d870dd..b4ead91fe11d 100644 --- a/code/modules/research/destructive_analyzer.dm +++ b/code/modules/research/destructive_analyzer.dm @@ -97,7 +97,7 @@ Note: Must be placed within 3 tiles of the R&D Console return FALSE if(QDELETED(loaded_item) || QDELETED(src)) return FALSE - SSblackbox.record_feedback("nested tally", "item_deconstructed", 1, list("[TN.id]", "[loaded_item.type]")) + SSblackbox.record_feedback("nested tally", "item_deconstructed", 1, list("[TN.id]", "[initial(loaded_item.name)]")) if(destroy_item(loaded_item)) stored_research.boost_with_item(SSresearch.techweb_node_by_id(TN.id), dpath) diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm index a203d4cfc194..2dc7f7852271 100644 --- a/code/modules/research/machinery/_production.dm +++ b/code/modules/research/machinery/_production.dm @@ -218,11 +218,11 @@ return ..() -/obj/machinery/rnd/production/proc/do_print(path, amount, list/matlist) +/obj/machinery/rnd/production/proc/do_print(atom/path, amount, list/matlist) for(var/i in 1 to amount) new path(get_turf(src)) - SSblackbox.record_feedback("nested tally", "item_printed", amount, list("[type]", "[path]")) + SSblackbox.record_feedback("nested tally", "item_printed", amount, list("[initial(name)]", "[initial(path.name)]")) /obj/machinery/rnd/production/proc/efficient_with(path) return !ispath(path, /obj/item/stack/sheet) && !ispath(path, /obj/item/stack/ore/bluespace_crystal) diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm index 77d567b78186..43838df140bd 100644 --- a/code/modules/spells/spell.dm +++ b/code/modules/spells/spell.dm @@ -408,6 +408,7 @@ spell_level++ cooldown_time = max(cooldown_time - cooldown_reduction_per_rank, 0.25 SECONDS) // 0 second CD starts to break things. + name = "[get_spell_title()][initial(name)]" build_all_button_icons(UPDATE_BUTTON_NAME) return TRUE @@ -428,13 +429,10 @@ else cooldown_time = max(cooldown_time + cooldown_reduction_per_rank, initial(cooldown_time)) + name = "[get_spell_title()][initial(name)]" build_all_button_icons(UPDATE_BUTTON_NAME) return TRUE -/datum/action/cooldown/spell/update_button_name(atom/movable/screen/movable/action_button/button, force) - name = "[get_spell_title()][initial(name)]" - return ..() - /// Gets the title of the spell based on its level. /datum/action/cooldown/spell/proc/get_spell_title() switch(spell_level) diff --git a/code/modules/spells/spell_types/self/summonitem.dm b/code/modules/spells/spell_types/self/summonitem.dm index a9b513fcd8a4..e03ef211af0d 100644 --- a/code/modules/spells/spell_types/self/summonitem.dm +++ b/code/modules/spells/spell_types/self/summonitem.dm @@ -15,22 +15,40 @@ ///The obj marked for recall var/obj/marked_item +/datum/action/cooldown/spell/summonitem/New(Target, original) + . = ..() + AddComponent(/datum/component/action_item_overlay, item_callback = CALLBACK(src, PROC_REF(get_marked))) + +/datum/action/cooldown/spell/summonitem/Destroy() + if(!isnull(marked_item)) + unmark_item() + return ..() + +/// For use in callbacks to get the marked item +/datum/action/cooldown/spell/summonitem/proc/get_marked() + return marked_item + /datum/action/cooldown/spell/summonitem/is_valid_target(atom/cast_on) return isliving(cast_on) /// Set the passed object as our marked item /datum/action/cooldown/spell/summonitem/proc/mark_item(obj/to_mark) - name = "Recall [to_mark]" marked_item = to_mark RegisterSignal(marked_item, COMSIG_QDELETING, PROC_REF(on_marked_item_deleted)) + name = "Recall [marked_item]" + build_all_button_icons() + /// Unset our current marked item /datum/action/cooldown/spell/summonitem/proc/unmark_item() - name = initial(name) UnregisterSignal(marked_item, COMSIG_QDELETING) marked_item = null -/// Signal proc for COMSIG_QDELETING on our marked item, unmarks our item if it's deleted + if(!QDELING(src)) + name = initial(name) + build_all_button_icons() + +/// Signal proc for [COMSIG_QDELETING] on our marked item, unmarks our item if it's deleted /datum/action/cooldown/spell/summonitem/proc/on_marked_item_deleted(datum/source) SIGNAL_HANDLER @@ -50,6 +68,12 @@ try_recall_item(cast_on) +/// Checks if the passed item is a valid item that can be marked / linked to summon. +/datum/action/cooldown/spell/summonitem/proc/can_link_to(obj/item/potential_mark, mob/living/caster) + if(potential_mark.item_flags & ABSTRACT) + return FALSE + return TRUE + /// If we don't have a marked item, attempts to mark the caster's held item. /datum/action/cooldown/spell/summonitem/proc/try_link_item(mob/living/caster) var/obj/item/potential_mark = caster.get_active_held_item() @@ -61,7 +85,7 @@ return FALSE var/link_message = "" - if(potential_mark.item_flags & ABSTRACT) + if(!can_link_to(potential_mark, caster)) return FALSE if(SEND_SIGNAL(potential_mark, COMSIG_ITEM_MARK_RETRIEVAL, src, caster) & COMPONENT_BLOCK_MARK_RETRIEVAL) return FALSE @@ -153,3 +177,37 @@ item_to_retrieve.forceMove(caster.drop_location()) item_to_retrieve.loc.visible_message(span_warning("[item_to_retrieve] suddenly appears!")) playsound(get_turf(item_to_retrieve), 'sound/magic/summonitems_generic.ogg', 50, TRUE) + +/* monkestation removal: get rid of the abductor batong recall + +/datum/action/cooldown/spell/summonitem/abductor + name = "Baton Recall" + desc = "Activating this will trigger your baton's emergency translocation protocol, \ + recalling it to your hand. Takes a long time for the translocation crystals to reset after use." + button_icon = 'icons/obj/abductor.dmi' + button_icon_state = "wonderprodStun" + sound = 'sound/effects/phasein.ogg' + + school = SCHOOL_UNSET + cooldown_time = 3.5 MINUTES + + spell_requirements = NONE + invocation_type = INVOCATION_NONE + + antimagic_flags = MAGIC_RESISTANCE_MIND + +/datum/action/cooldown/spell/summonitem/abductor/can_link_to(obj/item/potential_mark, mob/living/caster) + . = ..() + if(!.) + return . + + if(!istype(potential_mark, /obj/item/melee/baton/abductor)) + to_chat(caster, span_warning("[potential_mark] has no translocation crystals to link to!")) + return FALSE + + return TRUE + +/datum/action/cooldown/spell/summonitem/abductor/try_unlink_item(mob/living/caster) + to_chat(caster, span_warning("You can't unlink [marked_item]'s translocation crystals.")) + return FALSE +*/ diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index ed399c39c93d..3f30cd9244a1 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -73,13 +73,6 @@ if(replaced_by == /datum/surgery) return FALSE - // True surgeons (like abductor scientists) need no instructions - if(HAS_TRAIT(user, TRAIT_ALL_SURGERIES) || (!isnull(user.mind) && HAS_TRAIT(user.mind, TRAIT_ALL_SURGERIES))) // monke edit: TRAIT_ALL_SURGERIES - if(replaced_by) // only show top-level surgeries - return FALSE - else - return TRUE - if(!requires_tech && !replaced_by) return TRUE @@ -92,6 +85,13 @@ if(surgery_signal & COMPONENT_CANCEL_SURGERY) return FALSE + // True surgeons (like abductor scientists) need no instructions + if(HAS_TRAIT(user, TRAIT_ALL_SURGERIES) || (!isnull(user.mind) && HAS_TRAIT(user.mind, TRAIT_ALL_SURGERIES))) // monke edit: TRAIT_ALL_SURGERIES + if(replaced_by) // only show top-level surgeries + return FALSE + else + return TRUE + var/turf/patient_turf = get_turf(patient) //Get the relevant operating computer @@ -138,7 +138,7 @@ return null /datum/surgery/proc/complete(mob/surgeon) - SSblackbox.record_feedback("tally", "surgeries_completed", 1, type) + SSblackbox.record_feedback("tally", "surgeries_completed", 1, name) surgeon.add_mob_memory(/datum/memory/surgery, deuteragonist = surgeon, surgery_type = name) qdel(src) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index e8bda6fd8e89..cf53f5c71580 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -75,6 +75,7 @@ #define TRAIT_SOURCE_UNIT_TESTS "unit_tests" // BEGIN_INCLUDE +#include "abductor_baton_spell.dm" #include "ablative_hud.dm" #include "achievements.dm" #include "anchored_mobs.dm" diff --git a/code/modules/unit_tests/abductor_baton_spell.dm b/code/modules/unit_tests/abductor_baton_spell.dm new file mode 100644 index 000000000000..c08a3253bbde --- /dev/null +++ b/code/modules/unit_tests/abductor_baton_spell.dm @@ -0,0 +1,21 @@ +/* monkestation removal: get rid of the abductor batong recall +/// Tests that abductors get their baton recall spell when being equipped +/datum/unit_test/abductor_baton_spell + +/datum/unit_test/abductor_baton_spell/Run() + // Test abductor agents get a linked "summon item" spell that marks their baton. + var/mob/living/carbon/human/ayy = allocate(/mob/living/carbon/human/consistent) + ayy.equipOutfit(/datum/outfit/abductor/agent) + + var/datum/action/cooldown/spell/summonitem/abductor/summon = locate() in ayy.actions + TEST_ASSERT_NOTNULL(summon, "Abductor agent does not have summon item spell.") + TEST_ASSERT(istype(summon.marked_item, /obj/item/melee/baton/abductor), "Abductor agent's summon item spell did not mark their baton.") + + // Also test abductor solo agents also get the spell. + var/mob/living/carbon/human/ayy_two = allocate(/mob/living/carbon/human/consistent) + ayy_two.equipOutfit(/datum/outfit/abductor/scientist/onemanteam) + + var/datum/action/cooldown/spell/summonitem/abductor/summon_two = locate() in ayy_two.actions + TEST_ASSERT_NOTNULL(summon_two, "Abductor solo agent does not have summon item spell.") + TEST_ASSERT(istype(summon_two.marked_item, /obj/item/melee/baton/abductor), "Abductor solo agent's summon item spell did not mark their baton.") +*/ diff --git a/code/modules/unit_tests/limbsanity.dm b/code/modules/unit_tests/limbsanity.dm index 9988c7471e29..a92d481f556a 100644 --- a/code/modules/unit_tests/limbsanity.dm +++ b/code/modules/unit_tests/limbsanity.dm @@ -2,13 +2,13 @@ /datum/unit_test/limbsanity/Run() for(var/path in subtypesof(/obj/item/bodypart) - list(/obj/item/bodypart/arm, /obj/item/bodypart/leg)) /// removes the abstract items. - var/obj/item/bodypart/part = new path(null) - if(part.is_dimorphic) - if(!icon_exists(UNLINT(part.should_draw_greyscale ? part.icon_greyscale : part.icon_static), "[part.limb_id]_[part.body_zone]_m")) + var/obj/item/bodypart/part = path + if(part::is_dimorphic) + if(!icon_exists(UNLINT(part::should_draw_greyscale ? part::icon_greyscale : part::icon_static), "[part::limb_id]_[part::body_zone]_m")) TEST_FAIL("[path] does not have a valid icon for male variants") - if(!icon_exists(UNLINT(part.should_draw_greyscale ? part.icon_greyscale : part.icon_static), "[part.limb_id]_[part.body_zone]_f")) + if(!icon_exists(UNLINT(part::should_draw_greyscale ? part::icon_greyscale : part::icon_static), "[part::limb_id]_[part::body_zone]_f")) TEST_FAIL("[path] does not have a valid icon for female variants") - else if(!icon_exists(UNLINT(part.should_draw_greyscale ? part.icon_greyscale : part.icon_static), "[part.limb_id]_[part.body_zone]")) + else if(!icon_exists(UNLINT(part::should_draw_greyscale ? part::icon_greyscale : part::icon_static), "[part::limb_id]_[part::body_zone]")) TEST_FAIL("[path] does not have a valid icon") /// Tests the height adjustment system which dynamically changes how much the chest, head, and arms of a carbon are adjusted upwards or downwards based on the length of their legs and chest. diff --git a/code/modules/unit_tests/mouse_bite_cable.dm b/code/modules/unit_tests/mouse_bite_cable.dm index 6d3150d279d6..df77976ce47c 100644 --- a/code/modules/unit_tests/mouse_bite_cable.dm +++ b/code/modules/unit_tests/mouse_bite_cable.dm @@ -19,6 +19,9 @@ // Ai controlling processes expect a seconds_per_tick, supply a real-fake dt var/fake_dt = SSai_controllers.wait * 0.1 + // Set AI - AIs by default are off in z-levels with no client, we have to force it on. + biter.ai_controller.set_ai_status(AI_STATUS_ON) + biter.ai_controller.can_idle = FALSE // Select behavior - this will queue finding the cable biter.ai_controller.SelectBehaviors(fake_dt) // Process behavior - this will execute the "locate the cable" behavior diff --git a/code/modules/unit_tests/screenshot_humanoids.dm b/code/modules/unit_tests/screenshot_humanoids.dm index 63e2c1788a1f..a0e7abc1633e 100644 --- a/code/modules/unit_tests/screenshot_humanoids.dm +++ b/code/modules/unit_tests/screenshot_humanoids.dm @@ -23,8 +23,17 @@ moth.equipOutfit(/datum/outfit/job/cmo, visualsOnly = TRUE) test_screenshot("[/datum/species/moth]", get_flat_icon_for_all_directions(moth)) + //MONKESTATION ADDITION START + var/mob/living/carbon/human/apid = allocate(/mob/living/carbon/human/dummy/consistent) + apid.dna.features["apid_antenna"] = "Horns" + apid.dna.features["apid_wings"] = "Normal" // Just in case someone ever adds more + apid.set_species(/datum/species/apid) + apid.equipOutfit(/datum/outfit/job/botanist) + test_screenshot("[/datum/species/apid]", get_flat_icon_for_all_directions(apid)) + //MONKESTATION ADDITION END + // The rest of the species - for (var/datum/species/species_type as anything in subtypesof(/datum/species) - /datum/species/moth - /datum/species/lizard) + for (var/datum/species/species_type as anything in subtypesof(/datum/species) - /datum/species/moth - /datum/species/lizard - /datum/species/apid) test_screenshot("[species_type]", get_flat_icon_for_all_directions(make_dummy(species_type, /datum/outfit/job/assistant/consistent))) /datum/unit_test/screenshot_humanoids/proc/make_dummy(species, job_outfit) diff --git a/code/modules/unit_tests/screenshots/screenshot_antag_icons_abductor.png b/code/modules/unit_tests/screenshots/screenshot_antag_icons_abductor.png index 613358f5d1de..f117d20a2eef 100644 Binary files a/code/modules/unit_tests/screenshots/screenshot_antag_icons_abductor.png and b/code/modules/unit_tests/screenshots/screenshot_antag_icons_abductor.png differ diff --git a/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_apid.png b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_apid.png new file mode 100644 index 000000000000..ad5c7f80a012 Binary files /dev/null and b/code/modules/unit_tests/screenshots/screenshot_humanoids__datum_species_apid.png differ diff --git a/code/modules/unit_tests/suit_storage_icons.dm b/code/modules/unit_tests/suit_storage_icons.dm index 8c77b0524112..12305e7abfc0 100644 --- a/code/modules/unit_tests/suit_storage_icons.dm +++ b/code/modules/unit_tests/suit_storage_icons.dm @@ -10,18 +10,13 @@ continue wearable_item_paths |= item_path - for(var/clothing_path in (subtypesof(/obj/item/clothing) - typesof(/obj/item/clothing/head/mob_holder) - typesof(/obj/item/clothing/suit/space/santa))) //mob_holder is a psuedo abstract item. santa suit is a VERY SNOWFLAKE admin spawn suit that can hold /every/ possible item. - var/obj/item/clothing/spawned_item = new clothing_path - for(var/path in spawned_item.allowed) //find all usable suit storage stuff. + for(var/obj/item/clothing/clothing_path in (subtypesof(/obj/item/clothing) - typesof(/obj/item/clothing/head/mob_holder) - typesof(/obj/item/clothing/suit/space/santa))) //mob_holder is a psuedo abstract item. santa suit is a VERY SNOWFLAKE admin spawn suit that can hold /every/ possible item. + for(var/path in clothing_path::allowed) //find all usable suit storage stuff. wearable_item_paths |= path - qdel(spawned_item) - for(var/mod_path in subtypesof(/obj/item/mod/control)) - var/obj/item/mod/control/control_mod = new - for(var/path in control_mod.chestplate.allowed) + for(var/obj/item/mod/control/mod_path in subtypesof(/obj/item/mod/control)) + for(var/path in mod_path::chestplate::allowed) wearable_item_paths |= path - qdel(control_mod) - var/list/already_warned_icons = list() var/count = 1 //to be removed once the test goes live / into CI failure mode. diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index de6f281f7946..03e2862c2eb2 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -1325,7 +1325,7 @@ to_chat(usr, span_notice("You take [R.name] out of the slot.")) else to_chat(usr, span_warning("[capitalize(R.name)] falls onto the floor!")) - SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[type]", "[R.product_path]")) + SSblackbox.record_feedback("nested tally", "vending_machine_usage", 1, list("[name]", "[R.name]")) vend_ready = TRUE /obj/machinery/vending/process(seconds_per_tick) diff --git a/code/modules/vending/megaseed.dm b/code/modules/vending/megaseed.dm index 36971f312da2..ee7199dab91e 100644 --- a/code/modules/vending/megaseed.dm +++ b/code/modules/vending/megaseed.dm @@ -79,7 +79,7 @@ /obj/item/seeds/wheat/rice = 3, /obj/item/seeds/tea = 3, /obj/item/seeds/tobacco = 3, - /obj/item/seeds/tower = 3, + /obj/item/seeds/tree = 3, /obj/item/seeds/wheat = 3, ), ), diff --git a/code/modules/vending/security.dm b/code/modules/vending/security.dm index 739d402884b1..b8e382aa6591 100644 --- a/code/modules/vending/security.dm +++ b/code/modules/vending/security.dm @@ -34,6 +34,7 @@ /obj/item/clothing/head/guardmanhelmet = 1, //monkestation edit: Guardman /obj/item/clothing/under/guardmanuniform = 1, //monkestation edit: Guardman /obj/item/clothing/suit/armor/guardmanvest = 1, //monkestation edit: Guardman + /obj/item/citationinator = 3 // monkestation edit: security assistants ) refill_canister = /obj/item/vending_refill/security default_price = PAYCHECK_CREW diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm index a8f885663e60..b3022560075e 100644 --- a/code/modules/vending/wardrobes.dm +++ b/code/modules/vending/wardrobes.dm @@ -428,6 +428,7 @@ /obj/item/clothing/under/rank/civilian/lawyer/black = 1, /obj/item/clothing/under/rank/civilian/lawyer/black/skirt = 1, /obj/item/clothing/shoes/laceup = 2, + /obj/item/clothing/shoes/heels = 2, //MONKESTATION ADDITION /obj/item/clothing/accessory/lawyers_badge = 2, /obj/item/radio/headset/headset_srv = 2, ) diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm index 2379e7830f34..8c6d2f51d647 100644 --- a/code/modules/wiremod/shell/brain_computer_interface.dm +++ b/code/modules/wiremod/shell/brain_computer_interface.dm @@ -67,13 +67,14 @@ /obj/item/circuit_component/equipment_action/bci/update_action() bci_action.name = button_name.value - bci_action.button_icon_state = "bci_[replacetextEx(lowertext(icon_options.value), " ", "_")]" + // Change nanite -> bci if we get a set of bci action icons instead of nanite action icons + bci_action.button_icon_state = "nanite_[replacetextEx(lowertext(icon_options.value), " ", "_")]" /datum/action/innate/bci_action name = "Action" button_icon = 'icons/mob/actions/actions_items.dmi' check_flags = AB_CHECK_CONSCIOUS - button_icon_state = "bci_power" + button_icon_state = "nanite_power" var/obj/item/circuit_component/equipment_action/bci/circuit_component diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm index 807e93c33d20..376bef8fbfce 100644 --- a/code/modules/zombie/items.dm +++ b/code/modules/zombie/items.dm @@ -29,7 +29,7 @@ if(!target.get_bodypart(BODY_ZONE_HEAD)) return - if(NOZOMBIE in target.dna.species.species_traits) + if((NOZOMBIE in target.dna.species.species_traits) || HAS_TRAIT(target, TRAIT_NO_ZOMBIFY)) // cannot infect any NOZOMBIE subspecies (such as high functioning // zombies) return diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm index 3530146f1a2a..17f5f8d2da3f 100644 --- a/code/modules/zombie/organs.dm +++ b/code/modules/zombie/organs.dm @@ -49,6 +49,8 @@ Remove(owner) if(owner.mob_biotypes & MOB_MINERAL)//does not process in inorganic things return + if(HAS_TRAIT(owner, TRAIT_NO_ZOMBIFY)) + return if (causes_damage && !iszombie(owner) && owner.stat != DEAD) owner.adjustToxLoss(0.5 * seconds_per_tick) if (SPT_PROB(5, seconds_per_tick)) diff --git a/config/game_options.txt b/config/game_options.txt index 9553ef352df5..c992facbe9f9 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -355,6 +355,7 @@ ROUNDSTART_RACES oozeling ROUNDSTART_RACES ipc ROUNDSTART_RACES simian ROUNDSTART_RACES arachnid +ROUNDSTART_RACES apid ## Races that are better than humans in some ways, but worse in others ROUNDSTART_RACES ethereal diff --git a/goon/icons/floors.dmi b/goon/icons/floors.dmi new file mode 100644 index 000000000000..c9f51ab46309 Binary files /dev/null and b/goon/icons/floors.dmi differ diff --git a/goon/icons/obj/hydroponics/plants_alien.dmi b/goon/icons/obj/hydroponics/plants_alien.dmi new file mode 100644 index 000000000000..f6f9d3e0b3cf Binary files /dev/null and b/goon/icons/obj/hydroponics/plants_alien.dmi differ diff --git a/goon/icons/obj/hydroponics/plants_crop.dmi b/goon/icons/obj/hydroponics/plants_crop.dmi new file mode 100644 index 000000000000..f95ccb0b7567 Binary files /dev/null and b/goon/icons/obj/hydroponics/plants_crop.dmi differ diff --git a/goon/icons/obj/hydroponics/plants_flower.dmi b/goon/icons/obj/hydroponics/plants_flower.dmi new file mode 100644 index 000000000000..53a2e6662dde Binary files /dev/null and b/goon/icons/obj/hydroponics/plants_flower.dmi differ diff --git a/goon/icons/obj/hydroponics/plants_fruit.dmi b/goon/icons/obj/hydroponics/plants_fruit.dmi index 91e60bf777d7..5c338151e094 100644 Binary files a/goon/icons/obj/hydroponics/plants_fruit.dmi and b/goon/icons/obj/hydroponics/plants_fruit.dmi differ diff --git a/goon/icons/obj/hydroponics/plants_herb.dmi b/goon/icons/obj/hydroponics/plants_herb.dmi new file mode 100644 index 000000000000..7c0f05b942b1 Binary files /dev/null and b/goon/icons/obj/hydroponics/plants_herb.dmi differ diff --git a/goon/icons/obj/hydroponics/plants_unused.dmi b/goon/icons/obj/hydroponics/plants_unused.dmi new file mode 100644 index 000000000000..7cc970ad4ef9 Binary files /dev/null and b/goon/icons/obj/hydroponics/plants_unused.dmi differ diff --git a/goon/icons/obj/hydroponics/plants_veg.dmi b/goon/icons/obj/hydroponics/plants_veg.dmi new file mode 100644 index 000000000000..86f4a29ad2ec Binary files /dev/null and b/goon/icons/obj/hydroponics/plants_veg.dmi differ diff --git a/goon/icons/obj/hydroponics/plants_weed.dmi b/goon/icons/obj/hydroponics/plants_weed.dmi new file mode 100644 index 000000000000..c86f05d9c30a Binary files /dev/null and b/goon/icons/obj/hydroponics/plants_weed.dmi differ diff --git a/goon/icons/walls_beehive.dmi b/goon/icons/walls_beehive.dmi new file mode 100644 index 000000000000..306952e0365f Binary files /dev/null and b/goon/icons/walls_beehive.dmi differ diff --git a/html/changelogs/AutoChangeLog-pr-1781.yml b/html/changelogs/AutoChangeLog-pr-1781.yml deleted file mode 100644 index 1a0838f95150..000000000000 --- a/html/changelogs/AutoChangeLog-pr-1781.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "MomoBerri" -delete-after: True -changes: - - qol: "several updates to the labor camp" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-976.yml b/html/changelogs/AutoChangeLog-pr-976.yml deleted file mode 100644 index 200401b777e3..000000000000 --- a/html/changelogs/AutoChangeLog-pr-976.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Jake Park, Zonespace, Gboster" -delete-after: True -changes: - - rscadd: "Added a new antagonist: The cortical borer, capable of manipulating humans with the sole goal of making more of itself" \ No newline at end of file diff --git a/html/changelogs/archive/2024-05.yml b/html/changelogs/archive/2024-05.yml new file mode 100644 index 000000000000..f2793cf1a5af --- /dev/null +++ b/html/changelogs/archive/2024-05.yml @@ -0,0 +1,139 @@ +2024-05-01: + DimWhat: + - rscadd: Added Kumi's donator items, thanks buckaroo! + Jake Park, Zonespace, Gboster: + - rscadd: 'Added a new antagonist: The cortical borer, capable of manipulating humans + with the sole goal of making more of itself' + Kitsunemitsu: + - image: modified ID card sprites. + KnigTheThrasher: + - bugfix: Fixed tram cult bar + MomoBerri: + - qol: several updates to the labor camp + Rex9001: + - bugfix: honk mechs don't let you get to CC + wraith-54321: + - bugfix: You can no longer roll 2 antags at once +2024-05-02: + Absolucy: + - qol: Makes it easier for heretics to click on influences without accidentally + drawing a rune with their codex cicatrix because the animation shifted and made + you click on the floor instead. + Absolucy, That0nePerson: + - rscadd: Adds the poisoned knife (originally by That0nePerson, for BeeStation) + for 6TC to the syndie uplink. This knife has a 40u capacity and can switch between + injecting 5u and 10u of its content per attack. + Absolucy, itseasytosee: + - balance: the Syndicate Tome traitor item now grants anti-magic while held and + can be used to clear cult runes. + AlbertNanotracen: + - bugfix: Opening a necropolis gate will no longer tell ghosts that a body is being + created, instead it will tell them the Legion has been released + DexeeXI: + - bugfix: changed the medboard spawner in tech storage to support pathology machines. + rest in piss pandemic machine. + - rscadd: tram gets a crystallizer board (still a DIY project however) + - bugfix: box station pathology now has handcuffs in exchange for the useless extrapolators. + RikuTheKiller: + - balance: Nanite protocols are back from the experimental tech junkyard. +2024-05-04: + Absolucy: + - rscadd: Adds a new job, Security Assistants! They're basically hall monitors, + with basic security and permabrig access, along with some basic tools to abuse + the clown- err, help detain ner-do-wells with! + - rscadd: Added the Citationinator, a handheld device that can issue fines to people! + - rscadd: Added a new "Permabrig" access, used for allowing access to the permabrig + without granting full brig access. + - balance: Brain damage from borer abilities will no longer go above 100. + - qol: Neutered borers will now "skip over" abilities related to reproduction. + - bugfix: Fixed floorpills rolling random drinks that should've been filtered out + (but weren't due to a bad check). + MichiRecRoom: + - qol: If a lootbox item fails to save, you will now receive a replacement lootbox + that can be used at a later time, and will hopefully work then. +2024-05-06: + Absolucy: + - rscadd: The Damned Rabbit Mask now has a cool visual effect when worn by a hunter. + - rscadd: The Damned Rabbit Mask now works with internals. + - balance: Monster Hunter items such as the Damned Rabbit Mask, Rabbit Eye, Red + Queen Card, etc are now indestructible/fireproof/etc. + - rscadd: If a non-hunter tries to arm a jack in the box, they will botch it, resulting + in an early detonation. + - qol: Made the Red Queen Card a bit less janky to use. + - code_imp: Cleaned up and organized a bunch of Monster Hunter-related code. + - rscadd: Wonderland Apocalypse now raises the alert to delta when it starts, and + applies a station-wide effect that harms anyone attempting to cast magical spells. + - balance: Jabberwockies and killer rabbits are now antimagic. + - bugfix: The Cargorilla station trait works again. +2024-05-07: + AlbertNanotracen: + - qol: using a syringe on a virus dish now fills the syringe at the cost of growth, + rather than decreasing a hidden variable. + - balance: Blood from a virus dish is now O- (Universal donor) + - rscadd: lantern syndrome is now real, drink space cleaner to keep it white if + you want + - rscadd: Added Self-Respiration as a stage 4 symptom + MichiRecRoom: + - rscadd: 'Mobs with synthetic voice boxes (such as IPCs) are now capable of using + silicon-type emotes. As of the time of this change, this means they can now + use: boop, buzz, buzz2, chime, honk, ping, sad, warn, slowclap.' +2024-05-08: + Absolucy: + - bugfix: Bioscramblers can no longer give you buggy abstract organs, like dullahan + eyes. + - qol: Added some more things to the bioscrambler blacklist, such as psyker heads, + fly tongues/stomachs, anime implants, and abductor glands. + - rscadd: Bioscramblers can now give you wings. Moffcats, here we come! + - balance: The Bioscrambler will now actively attempt to get closer to living targets + rather than chilling in a closet nobody goes into (unless you trap it in a containment + field). + - balance: Because it can now travel through walls, the Bioscrambler will no longer + transform you THROUGH walls. + - bugfix: Fixed various worn items (most noticeably gloves) looking weird with variable + heights. + AlbertNanotracen: + - bugfix: fixed some wikibook links + - bugfix: Chef recipe book no longer tells you you can microwave pasta to cook it + (you cant) + DimWhat: + - rscadd: Added heels to the LawDrobe + MrMelbert, NikoTheDude, Ported by Hardly: + - rscadd: Added examine text, describe your character in ways that a 32x32 sprite + can't! + - rscadd: Added temporary examine text alongside an indicator + - rscadd: Added character records text + - admin: Admins can see all flavor texts of a character + NicoDeLuna: + - rscadd: Added Ashsuits for miners + Odairu: + - rscadd: Added a secondary ammo type on the captain's Antique + Oricana-16: + - bugfix: BCI Actions now use icons instead of ERRORs/. + RikuTheKiller: + - bugfix: Remove purity adjustment for overdose thresholds, as we don't even use + reagent purity here. + Uristthedorf: + - rscadd: Diagnostic huds can now see nanite volume. + flleeppyy: + - bugfix: spelling mistake in quirk description +2024-05-09: + Absolucy: + - bugfix: Dimensional anomalies are once again immobile like they should be. + - bugfix: Fixed hollow anomalies "escaping" the anomaly research ruin. + Kitsunemitsu: + - bugfix: fixed the syndicate and prisoner ID cards being missaligned. +2024-05-13: + Absolucy: + - bugfix: Healing holoparas (and other mobs that heal on touch) are slime-friendly + and won't damage them anymore. + - bugfix: Actually, hopefully, probably fixed runaway ruin anomalies this time. + - bugfix: Fixed guardian decks not working afterwards if nobody signs up. +2024-05-14: + Absolucy: + - bugfix: Gaining TRAIT_VIRUSIMMUNE now properly cures any diseases. + AlbertNanotracen: + - bugfix: Splicing a symptom on a symptom that isn't in its initial stage will now + work + - bugfix: Stage Occurrence mutations will now work on symptoms not in their initial + stage + - rscadd: Bio bag can now carry mouse cubes and virus dishes diff --git a/icons/mob/huds/hud.dmi b/icons/mob/huds/hud.dmi index 168443c8bd6a..6420db1c8750 100644 Binary files a/icons/mob/huds/hud.dmi and b/icons/mob/huds/hud.dmi differ diff --git a/icons/obj/abductor.dmi b/icons/obj/abductor.dmi index 6e82261fcb86..f3193eed75f6 100644 Binary files a/icons/obj/abductor.dmi and b/icons/obj/abductor.dmi differ diff --git a/icons/obj/abductor_posters.dmi b/icons/obj/abductor_posters.dmi new file mode 100644 index 000000000000..bcd2c0200a20 Binary files /dev/null and b/icons/obj/abductor_posters.dmi differ diff --git a/icons/obj/card.dmi b/icons/obj/card.dmi index 2ca77df90c25..be13305ca8d4 100644 Binary files a/icons/obj/card.dmi and b/icons/obj/card.dmi differ diff --git a/icons/obj/poster.dmi b/icons/obj/poster.dmi index f46b95a85934..6350e86bae9b 100644 Binary files a/icons/obj/poster.dmi and b/icons/obj/poster.dmi differ diff --git a/monkestation/code/__HELPERS/reagents.dm b/monkestation/code/__HELPERS/reagents.dm index 5400bdedafed..4ec034cdf124 100644 --- a/monkestation/code/__HELPERS/reagents.dm +++ b/monkestation/code/__HELPERS/reagents.dm @@ -4,9 +4,9 @@ if(!random_reagents) random_reagents = list() for(var/datum/reagent/reagent_path as anything in subtypesof(/datum/reagent)) - if(istype(reagent_path, /datum/reagent/consumable) && !initial(reagent_path.bypass_restriction)) + if(ispath(reagent_path, /datum/reagent/consumable) && !reagent_path::bypass_restriction) continue - if(initial(reagent_path.restricted)) + if(reagent_path::restricted) continue random_reagents += reagent_path var/picked_reagent = pick(random_reagents) diff --git a/monkestation/code/datums/quirks/negative_quirks.dm b/monkestation/code/datums/quirks/negative_quirks.dm index db2648a98a13..63f898111838 100644 --- a/monkestation/code/datums/quirks/negative_quirks.dm +++ b/monkestation/code/datums/quirks/negative_quirks.dm @@ -36,7 +36,7 @@ /datum/quirk/stowaway name = "Stowaway" - desc = "You wake up up inside a random locker with only a crude fake for an ID card." + desc = "You wake up inside a random locker with only a crude fake for an ID card." value = -2 icon = FA_ICON_SUITCASE diff --git a/monkestation/code/game/objects/effects/anomalies/_anomalies.dm b/monkestation/code/game/objects/effects/anomalies/_anomalies.dm index 67676be94e8d..76a036465fe1 100644 --- a/monkestation/code/game/objects/effects/anomalies/_anomalies.dm +++ b/monkestation/code/game/objects/effects/anomalies/_anomalies.dm @@ -1,6 +1,23 @@ +/obj/effect/anomaly + /// If TRUE, the anomaly is contained to its impact_area. + var/contained = FALSE + /obj/effect/anomaly/proc/scan_anomaly(mob/user, obj/item/scanner) if(!aSignal) return FALSE playsound(get_turf(user), 'sound/machines/ping.ogg', vol = 30, vary = TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE, ignore_walls = FALSE) to_chat(user, span_boldnotice("Analyzing... [src]'s unstable field is fluctuating along frequency [format_frequency(aSignal.frequency)], code [aSignal.code].")) return TRUE + +/obj/effect/anomaly/stabilize(anchor, has_core) + . = ..() + contained = TRUE + +/obj/effect/anomaly/Move(atom/newloc, direct, glide_size_override, update_dir) + if(contained) + if(impact_area != get_area(newloc)) + return FALSE + else if(impact_area != get_area(src)) // if we somehow escaped ANYWAYS, let's just go poof + qdel(src) + return FALSE + return ..() diff --git a/monkestation/code/game/objects/effects/landmark.dm b/monkestation/code/game/objects/effects/landmark.dm index 508b4d82c6fc..ee0bbd5fa0dc 100644 --- a/monkestation/code/game/objects/effects/landmark.dm +++ b/monkestation/code/game/objects/effects/landmark.dm @@ -126,3 +126,6 @@ /obj/effect/landmark/navigate_destination/centcom/dresser location = "Dressing Room" +/obj/effect/landmark/start/security_assistant + name = "Security Assistant" + icon_state = "Security Officer" diff --git a/monkestation/code/game/objects/items/venom_knife.dm b/monkestation/code/game/objects/items/venom_knife.dm new file mode 100644 index 000000000000..fc9ebb66983b --- /dev/null +++ b/monkestation/code/game/objects/items/venom_knife.dm @@ -0,0 +1,53 @@ +#define VENOM_KNIFE_VOLUME 40 + +/obj/item/knife/venom + name = "venom knife" + desc = "An infamous knife of syndicate design, it has a tiny hole going through the blade to the handle which stores toxins." + icon = 'monkestation/icons/obj/items_and_weapons.dmi' + icon_state = "venom_knife" + force = 12 + throwforce = 15 + throw_speed = 5 + throw_range = 7 + custom_materials = null + /// The maximum amount of reagents per transfer that will be moved out of this reagent container + var/amount_per_transfer_from_this = 10 + /// The different possible amounts of reagent to transfer out of the container + var/list/possible_transfer_amounts = list(5, 10) + /// Where we are in the possible transfer amount list. + var/amount_list_position = 2 + +/obj/item/knife/venom/Initialize() + . = ..() + create_reagents(VENOM_KNIFE_VOLUME, OPENCONTAINER) + +/obj/item/knife/venom/attack_self(mob/user) + change_transfer_amount(user, FORWARD) + +/obj/item/knife/venom/attack_self_secondary(mob/user) + change_transfer_amount(user, BACKWARD) + +/obj/item/knife/venom/attack(mob/living/target_mob, mob/living/user, params) + . = ..() + if(!istype(target_mob) || HAS_TRAIT(user, TRAIT_PACIFISM) || !reagents.total_volume || QDELETED(target_mob.reagents)) + return + var/transfer_amt = amount_per_transfer_from_this + if(!target_mob.can_inject(user)) + transfer_amt = 1 + reagents.trans_to(target_mob, transfer_amt, methods = INJECT, transfered_by = user) + +/obj/item/knife/venom/proc/change_transfer_amount(mob/user, direction = FORWARD) + var/list_len = length(possible_transfer_amounts) + if(!list_len) + return + switch(direction) + if(FORWARD) + amount_list_position = (amount_list_position % list_len) + 1 + if(BACKWARD) + amount_list_position = (amount_list_position - 1) || list_len + else + CRASH("change_transfer_amount() called with invalid direction value") + amount_per_transfer_from_this = possible_transfer_amounts[amount_list_position] + balloon_alert(user, "transferring [amount_per_transfer_from_this]u") + +#undef VENOM_KNIFE_VOLUME diff --git a/monkestation/code/modules/antagonists/abductor/abductor.dm b/monkestation/code/modules/antagonists/abductor/abductor.dm new file mode 100644 index 000000000000..1ec3023900c7 --- /dev/null +++ b/monkestation/code/modules/antagonists/abductor/abductor.dm @@ -0,0 +1,21 @@ +/datum/antagonist/abductor + /// A list of surgeries that abductors can't do, to prevent bullshittery. + var/static/list/always_forbidden_surgeries = typecacheof(list( + /datum/surgery/advanced/brainwashing_sleeper, + /datum/surgery/advanced/necrotic_revival + )) + +/datum/antagonist/abductor/on_gain() + . = ..() + RegisterSignal(owner.current, COMSIG_SURGERY_STARTING, PROC_REF(prevent_forbidden_surgeries)) + +/datum/antagonist/abductor/on_removal() + . = ..() + UnregisterSignal(owner.current, COMSIG_SURGERY_STARTING) + +/datum/antagonist/abductor/proc/prevent_forbidden_surgeries(mob/user, datum/surgery/surgery, mob/patient) + if(is_type_in_typecache(surgery, always_forbidden_surgeries)) + return COMPONENT_CANCEL_SURGERY + if(istype(surgery, /datum/surgery/advanced/brainwashing) && length(team?.abductees) <= 3) + return COMPONENT_CANCEL_SURGERY + return NONE diff --git a/monkestation/code/modules/antagonists/abductor/equipment/glands/blood.dm b/monkestation/code/modules/antagonists/abductor/equipment/glands/blood.dm new file mode 100644 index 000000000000..7af70ab4eb7f --- /dev/null +++ b/monkestation/code/modules/antagonists/abductor/equipment/glands/blood.dm @@ -0,0 +1,15 @@ +/obj/item/organ/internal/heart/gland/blood + /// The mob's original blood type, to be reverted to when the organ is removed. + var/original_blood_type + +/obj/item/organ/internal/heart/gland/blood/on_insert(mob/living/carbon/human/organ_owner, special) + . = ..() + if(!ishuman(owner) || !owner.dna.species) + return + original_blood_type = owner.dna.species.exotic_blood + +/obj/item/organ/internal/heart/gland/blood/on_remove(mob/living/carbon/human/organ_owner, special) + . = ..() + if(!ishuman(owner) || !owner.dna.species) + return + owner.dna.species.exotic_blood = original_blood_type diff --git a/monkestation/code/modules/antagonists/abductor/equipment/glands/plasma.dm b/monkestation/code/modules/antagonists/abductor/equipment/glands/plasma.dm new file mode 100644 index 000000000000..fbedcb5365de --- /dev/null +++ b/monkestation/code/modules/antagonists/abductor/equipment/glands/plasma.dm @@ -0,0 +1,25 @@ +/obj/item/organ/internal/heart/gland/plasma/on_insert(mob/living/carbon/organ_owner, special) + . = ..() + var/obj/item/organ/internal/lungs/lungs = organ_owner.get_organ_slot(ORGAN_SLOT_LUNGS) + if(istype(lungs) && !QDELING(lungs)) + lungs.safe_plasma_max = 0 + +/obj/item/organ/internal/heart/gland/plasma/on_remove(mob/living/carbon/organ_owner, special) + . = ..() + var/obj/item/organ/internal/lungs/lungs = organ_owner.get_organ_slot(ORGAN_SLOT_LUNGS) + if(istype(lungs) && !QDELING(lungs)) + lungs.safe_plasma_max = initial(lungs.safe_plasma_max) + +/obj/item/organ/internal/heart/gland/plasma/activate() + owner.balloon_alert(owner, "you feel bloated") + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), owner, span_userdanger("A massive stomachache overcomes you.")), 15 SECONDS) + addtimer(CALLBACK(src, PROC_REF(vomit_plasma)), 20 SECONDS) + +/obj/item/organ/internal/heart/gland/plasma/vomit_plasma() + if(QDELETED(owner)) + return + owner.balloon_alert_to_viewers("vomits a cloud of plasma!") + var/turf/open/owner_turf = get_turf(owner) + if(istype(owner_turf) && !QDELING(owner_turf)) + owner_turf.atmos_spawn_air("plasma=50;TEMP=[T20C]") + owner.vomit(vomit_type = VOMIT_PURPLE, harm = FALSE) diff --git a/monkestation/code/modules/antagonists/abductor/equipment/glands/slime.dm b/monkestation/code/modules/antagonists/abductor/equipment/glands/slime.dm new file mode 100644 index 000000000000..d453a6895083 --- /dev/null +++ b/monkestation/code/modules/antagonists/abductor/equipment/glands/slime.dm @@ -0,0 +1,23 @@ +/obj/item/organ/internal/heart/gland/slime + /// Whether the slime faction was given to the owner of this gland or not. + /// Used so we don't take the slime faction away from someone who had it anyways + var/gave_faction = FALSE + +/obj/item/organ/internal/heart/gland/slime/on_insert(mob/living/carbon/gland_owner) + . = ..() + if(!(FACTION_SLIME in gland_owner.faction)) + gland_owner.faction |= FACTION_SLIME + gave_faction = TRUE + +/obj/item/organ/internal/heart/gland/slime/on_remove(mob/living/carbon/gland_owner) + . = ..() + if(gave_faction) + gland_owner.faction -= FACTION_SLIME + +/obj/item/organ/internal/heart/gland/slime/activate() + owner.balloon_alert(owner, "you feel nauseous") + owner.vomit(20) + + var/mob/living/basic/slime/friend = new(owner.drop_location()) + friend.slime_flags |= NOOOZE_SLIME + SEND_SIGNAL(friend, COMSIG_FRIENDSHIP_CHANGE, owner, 110) diff --git a/monkestation/code/modules/antagonists/abductor/equipment/glands/trauma.dm b/monkestation/code/modules/antagonists/abductor/equipment/glands/trauma.dm new file mode 100644 index 000000000000..58b1cbf49185 --- /dev/null +++ b/monkestation/code/modules/antagonists/abductor/equipment/glands/trauma.dm @@ -0,0 +1,19 @@ +/obj/item/organ/internal/heart/gland/trauma/on_insert(mob/living/carbon/organ_owner, special) + . = ..() + ADD_TRAIT(organ_owner, TRAIT_SPECIAL_TRAUMA_BOOST, ABDUCTOR_GLAND_TRAIT) + +/obj/item/organ/internal/heart/gland/trauma/on_remove(mob/living/carbon/organ_owner, special) + . = ..() + REMOVE_TRAIT(organ_owner, TRAIT_SPECIAL_TRAUMA_BOOST, ABDUCTOR_GLAND_TRAIT) + +/obj/item/organ/internal/heart/gland/trauma/activate() + owner.balloon_alert(owner, "you feel a sudden headache") + // anesthetic will prevent deep-rooted traumas, allowing for a safe(r) lobotomy before removal + var/resilience = (owner.stat == CONSCIOUS && prob(15)) ? TRAUMA_RESILIENCE_LOBOTOMY : (prob(40) ? TRAUMA_RESILIENCE_SURGERY : TRAUMA_RESILIENCE_BASIC) + if(prob(33)) + owner.gain_trauma_type(BRAIN_TRAUMA_SPECIAL, resilience) + else + if(prob(20)) + owner.gain_trauma_type(BRAIN_TRAUMA_SEVERE, resilience) + else + owner.gain_trauma_type(BRAIN_TRAUMA_MILD, resilience) diff --git a/monkestation/code/modules/antagonists/abductor/machinery/dispenser.dm b/monkestation/code/modules/antagonists/abductor/machinery/dispenser.dm new file mode 100644 index 000000000000..57683f3412af --- /dev/null +++ b/monkestation/code/modules/antagonists/abductor/machinery/dispenser.dm @@ -0,0 +1,57 @@ +// ------------------------- +// This is just smartfridge but for abductors. +// less flavour, but abductor can see what these are at a glance +/obj/machinery/smartfridge/abductor + name = "replacement organ storage" + desc = "A tank filled with replacement organs." + icon = 'icons/obj/abductor.dmi' + icon_state = "dispenser" + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + density = TRUE + idle_power_usage = 0 + active_power_usage = 0 + max_n_of_items = 1000 + tgui_theme = "abductor" + visible_contents = FALSE + has_emissive = FALSE + var/allowed_to_everyone = FALSE + +/obj/machinery/smartfridge/abductor/Initialize() + . = ..() + generate_glands() + +/obj/machinery/smartfridge/abductor/proc/generate_glands() + for(var/obj/item/organ/internal/heart/gland/each as anything in shuffle(subtypesof(/obj/item/organ/internal/heart/gland))) + for(var/i in 1 to rand(2, 7)) + var/obj/item/organ/internal/heart/gland/each_gland = new each + each_gland.name = splittext(each_gland.abductor_hint, ".")[1] + each_gland.forceMove(src) + +/obj/machinery/smartfridge/abductor/ui_status(mob/user) + if(!allowed_to_everyone && !isabductor(user) && !isobserver(user)) + return UI_CLOSE + return ..() + +/obj/machinery/smartfridge/abductor/ui_state(mob/user) + return GLOB.physical_state + +/obj/machinery/smartfridge/abductor/accept_check(obj/item/O) + if(istype(O, /obj/item/organ/internal/heart/gland)) + return TRUE + return FALSE + +/obj/machinery/smartfridge/abductor/load(obj/item/organ/internal/heart/gland/organ) + . = ..() + if(!. || !istype(organ)) + return + organ.organ_flags |= ORGAN_FROZEN + organ.name = splittext(organ.abductor_hint, ".")[1] + +/obj/machinery/smartfridge/abductor/Exited(obj/item/organ/internal/heart/gland/organ, direction) + . = ..() + if(!istype(organ)) + return + organ.organ_flags &= ~(ORGAN_FROZEN | ORGAN_FAILING) + organ.set_organ_damage(-200) + organ.name = initial(organ.name) + diff --git a/monkestation/code/modules/antagonists/borers/code/abilities/evolution_tree.dm b/monkestation/code/modules/antagonists/borers/code/abilities/evolution_tree.dm index 8783099f8b12..eea797153025 100644 --- a/monkestation/code/modules/antagonists/borers/code/abilities/evolution_tree.dm +++ b/monkestation/code/modules/antagonists/borers/code/abilities/evolution_tree.dm @@ -57,6 +57,8 @@ for(var/path in cortical_owner.past_evolutions) var/list/evo_data = list() var/datum/borer_evolution/found_evolution = cortical_owner.past_evolutions[path] + if(cortical_owner.neutered && found_evolution.skip_for_neutered) + continue evo_data["name"] = found_evolution.name evo_data["desc"] = found_evolution.desc diff --git a/monkestation/code/modules/antagonists/borers/code/abilities/force_speech.dm b/monkestation/code/modules/antagonists/borers/code/abilities/force_speech.dm index 531220c4392f..4e21189429a3 100644 --- a/monkestation/code/modules/antagonists/borers/code/abilities/force_speech.dm +++ b/monkestation/code/modules/antagonists/borers/code/abilities/force_speech.dm @@ -20,9 +20,7 @@ borer_message = sanitize(borer_message) var/mob/living/carbon/human/cortical_host = cortical_owner.human_host to_chat(cortical_host, span_boldwarning("Your voice moves without your permission!")) - var/obj/item/organ/internal/brain/victim_brain = cortical_owner.human_host.get_organ_slot(ORGAN_SLOT_BRAIN) - if(victim_brain) - cortical_owner.human_host.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2 * cortical_owner.host_harm_multiplier) + cortical_host.adjustOrganLoss(ORGAN_SLOT_BRAIN, 2 * cortical_owner.host_harm_multiplier, maximum = BRAIN_DAMAGE_SEVERE) cortical_host.say(message = borer_message, forced = "borer ([key_name(cortical_owner)])") var/turf/human_turf = get_turf(cortical_owner.human_host) var/logging_text = "[key_name(cortical_owner)] forced [key_name(cortical_owner.human_host)] to say [borer_message] at [loc_name(human_turf)]" diff --git a/monkestation/code/modules/antagonists/borers/code/abilities/learn_chemicals.dm b/monkestation/code/modules/antagonists/borers/code/abilities/learn_chemicals.dm index 74fcc3380f84..f944afddd29f 100644 --- a/monkestation/code/modules/antagonists/borers/code/abilities/learn_chemicals.dm +++ b/monkestation/code/modules/antagonists/borers/code/abilities/learn_chemicals.dm @@ -63,9 +63,7 @@ GLOBAL_VAR_INIT(objective_blood_borer, 3) if(!HAS_TRAIT(cortical_owner.human_host, TRAIT_AGEUSIA)) to_chat(cortical_owner.human_host, span_notice("You get a strange aftertaste of [initial(learned_reagent.taste_description)]!")) - var/obj/item/organ/internal/brain/victim_brain = cortical_owner.human_host.get_organ_slot(ORGAN_SLOT_BRAIN) - if(victim_brain) - cortical_owner.human_host.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5 * cortical_owner.host_harm_multiplier) + cortical_owner.human_host.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5 * cortical_owner.host_harm_multiplier, maximum = BRAIN_DAMAGE_SEVERE) StartCooldown() @@ -129,9 +127,7 @@ GLOBAL_VAR_INIT(objective_blood_borer, 3) cortical_owner.known_chemicals += learned_reagent.type cortical_owner.blood_chems_learned++ - var/obj/item/organ/internal/brain/victim_brain = cortical_owner.human_host.get_organ_slot(ORGAN_SLOT_BRAIN) - if(victim_brain) - cortical_owner.human_host.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5 * cortical_owner.host_harm_multiplier) + cortical_owner.human_host.adjustOrganLoss(ORGAN_SLOT_BRAIN, 5 * cortical_owner.host_harm_multiplier, maximum = BRAIN_DAMAGE_SEVERE) if(cortical_owner.blood_chems_learned == BLOOD_CHEM_OBJECTIVE) GLOB.successful_blood_chem += 1 diff --git a/monkestation/code/modules/antagonists/borers/code/abilities/spawn_offspring.dm b/monkestation/code/modules/antagonists/borers/code/abilities/spawn_offspring.dm index 87b137f78851..626b2000025e 100644 --- a/monkestation/code/modules/antagonists/borers/code/abilities/spawn_offspring.dm +++ b/monkestation/code/modules/antagonists/borers/code/abilities/spawn_offspring.dm @@ -32,7 +32,7 @@ produce_egg() var/obj/item/organ/internal/brain/victim_brain = cortical_owner.human_host.get_organ_slot(ORGAN_SLOT_BRAIN) if(victim_brain) - cortical_owner.human_host.adjustOrganLoss(ORGAN_SLOT_BRAIN, 25 * cortical_owner.host_harm_multiplier) + cortical_owner.human_host.adjustOrganLoss(ORGAN_SLOT_BRAIN, 25 * cortical_owner.host_harm_multiplier, maximum = BRAIN_DAMAGE_SEVERE) var/eggroll = rand(1,100) if(eggroll <= 75) switch(eggroll) diff --git a/monkestation/code/modules/antagonists/borers/code/abilities/upgrade_body.dm b/monkestation/code/modules/antagonists/borers/code/abilities/upgrade_body.dm index 6648da839962..0ad9663abc64 100644 --- a/monkestation/code/modules/antagonists/borers/code/abilities/upgrade_body.dm +++ b/monkestation/code/modules/antagonists/borers/code/abilities/upgrade_body.dm @@ -22,9 +22,7 @@ cortical_owner.chemical_regen += cortical_owner.chem_regen_per_level cortical_owner.level += 1 - var/obj/item/organ/internal/brain/victim_brain = cortical_owner.human_host.get_organ_slot(ORGAN_SLOT_BRAIN) - if(victim_brain) - cortical_owner.human_host.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10 * cortical_owner.host_harm_multiplier) + cortical_owner.human_host.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10 * cortical_owner.host_harm_multiplier, maximum = BRAIN_DAMAGE_SEVERE) cortical_owner.human_host.adjust_eye_blur(6 SECONDS * cortical_owner.host_harm_multiplier) //about 12 seconds' worth by default to_chat(cortical_owner, span_notice("You have grown!")) diff --git a/monkestation/code/modules/antagonists/borers/code/evolution/evolution_datum.dm b/monkestation/code/modules/antagonists/borers/code/evolution/evolution_datum.dm index ad9c32d1492f..ae3a1802bc1b 100644 --- a/monkestation/code/modules/antagonists/borers/code/evolution/evolution_datum.dm +++ b/monkestation/code/modules/antagonists/borers/code/evolution/evolution_datum.dm @@ -18,21 +18,22 @@ var/list/unlocked_evolutions = list() /// What action does this evolution unlock var/added_action = FALSE - /// If TRUE neutered borers wont be able to get an action button from this, but will still be able to progress through the evolution tree - var/restricted_for_neutered = FALSE + /// If TRUE, then neutered borers will "bypass" this evolution, silently unlocking it when available and hiding it from the UI. + var/skip_for_neutered = FALSE /// What happens when a borer gets this evolution /datum/borer_evolution/proc/on_evolve(mob/living/basic/cortical_borer/cortical_owner) SHOULD_CALL_PARENT(TRUE) + if(cortical_owner.neutered) + for(var/datum/borer_evolution/evolution as anything in unlocked_evolutions) + if(evolution::skip_for_neutered) + cortical_owner.do_evolution(evolution) + if(skip_for_neutered) + return if(mutually_exclusive) cortical_owner.genome_locked = TRUE if(gain_text) to_chat(cortical_owner, span_notice("[gain_text]")) - - if(restricted_for_neutered) - if(istype(cortical_owner, /mob/living/basic/cortical_borer/neutered)) - to_chat(cortical_owner, span_danger("You didnt manage to properly evolve, you feel a strange sensation.")) - return if(added_action) var/datum/action/cooldown/borer/new_action = new added_action(cortical_owner) new_action.Grant(cortical_owner) diff --git a/monkestation/code/modules/antagonists/borers/code/evolution/evolution_diveworm.dm b/monkestation/code/modules/antagonists/borers/code/evolution/evolution_diveworm.dm index e5347cedf9e6..737069a70372 100644 --- a/monkestation/code/modules/antagonists/borers/code/evolution/evolution_diveworm.dm +++ b/monkestation/code/modules/antagonists/borers/code/evolution/evolution_diveworm.dm @@ -104,4 +104,4 @@ /datum/borer_evolution/synthetic_chems_negative, ) added_action = /datum/action/cooldown/borer/empowered_offspring - restricted_for_neutered = TRUE + skip_for_neutered = TRUE diff --git a/monkestation/code/modules/antagonists/borers/code/evolution/evolution_hivelord.dm b/monkestation/code/modules/antagonists/borers/code/evolution/evolution_hivelord.dm index c4b729eaf6a7..5ffa5e2a363b 100644 --- a/monkestation/code/modules/antagonists/borers/code/evolution/evolution_hivelord.dm +++ b/monkestation/code/modules/antagonists/borers/code/evolution/evolution_hivelord.dm @@ -10,7 +10,7 @@ evo_cost = 1 unlocked_evolutions = list(/datum/borer_evolution/hivelord/blood_chemical) added_action = /datum/action/cooldown/borer/produce_offspring - restricted_for_neutered = TRUE + skip_for_neutered = TRUE // T2 /datum/borer_evolution/hivelord/blood_chemical @@ -56,7 +56,7 @@ /datum/borer_evolution/synthetic_chems_positive, /datum/borer_evolution/synthetic_chems_negative, ) - restricted_for_neutered = TRUE // we set this to TRUE purelly for the message, they dont have the reproduction action anyway + skip_for_neutered = TRUE // we set this to TRUE purelly for the message, they dont have the reproduction action anyway /datum/borer_evolution/hivelord/produce_offspring_alone/on_evolve(mob/living/basic/cortical_borer/cortical_owner) . = ..() diff --git a/monkestation/code/modules/antagonists/borers/code/evolution/evolution_symbiote.dm b/monkestation/code/modules/antagonists/borers/code/evolution/evolution_symbiote.dm index a5807b8d0060..0f3d2f84022d 100644 --- a/monkestation/code/modules/antagonists/borers/code/evolution/evolution_symbiote.dm +++ b/monkestation/code/modules/antagonists/borers/code/evolution/evolution_symbiote.dm @@ -10,6 +10,7 @@ unlocked_evolutions = list(/datum/borer_evolution/symbiote/chem_per_level) evo_cost = 1 added_action = /datum/action/cooldown/borer/willing_host + skip_for_neutered = TRUE // T2 /datum/borer_evolution/symbiote/chem_per_level diff --git a/monkestation/code/modules/antagonists/borers/code/items/borer_spawner.dm b/monkestation/code/modules/antagonists/borers/code/items/borer_spawner.dm index 808ff189bd0c..36d0d09e8d3e 100644 --- a/monkestation/code/modules/antagonists/borers/code/items/borer_spawner.dm +++ b/monkestation/code/modules/antagonists/borers/code/items/borer_spawner.dm @@ -53,6 +53,8 @@ ignore_category = POLL_IGNORE_CORTICAL_BORER, pic_source = /mob/living/basic/cortical_borer/neutered, ) + if(QDELETED(src)) // prevent shenanigans with refunds + return if(!LAZYLEN(candidates)) opened = FALSE to_chat(user, "Yet the borer after looking at you quickly retreats back into their cage, visibly scared. Perhaps try later?") @@ -91,5 +93,9 @@ log_game("[key_name(new_mob)] was spawned as a borer by [key_name(user)]") visible_message("A borer wriggles out of the [src]!") - new /obj/item/cortical_cage(drop_location()) + var/obj/item/cortical_cage/empty_cage = new(drop_location()) + var/user_held = user.get_held_index_of_item(src) + if(user_held) // seems more immersive if you don't just suddenly drop the cage, and it empties while still seemingly in your hand. + user.dropItemToGround(src, force = TRUE, silent = TRUE) + user.put_in_hand(empty_cage, user_held, ignore_anim = TRUE) qdel(src) diff --git a/monkestation/code/modules/antagonists/traitor/objectives/kidnapping.dm b/monkestation/code/modules/antagonists/traitor/objectives/kidnapping.dm index 489d643d3018..e55aecf92f81 100644 --- a/monkestation/code/modules/antagonists/traitor/objectives/kidnapping.dm +++ b/monkestation/code/modules/antagonists/traitor/objectives/kidnapping.dm @@ -69,6 +69,8 @@ /datum/job/paramedic, // Service /datum/job/cook, + // Monkestation edit: security assistants + /datum/job/security_assistant, ) alive_bonus = 4 diff --git a/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component.dm b/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component.dm index 5163bf0a2aae..0722c3b9e8f0 100644 --- a/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component.dm +++ b/monkestation/code/modules/art_sci_overrides/artifact_components/_base_component.dm @@ -213,6 +213,7 @@ continue checked_fault = TRUE if(prob(chosen_fault.trigger_chance)) + logger.Log(LOG_CATEGORY_ARTIFACT, "[parent]'s fault has been triggered, trigger type [chosen_fault].") chosen_fault.on_trigger(src) if(chosen_fault.visible_message) holder.visible_message("[holder] [chosen_fault.visible_message]") diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm index dcf646932d27..44f1d7373183 100644 --- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm +++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_misc_procs.dm @@ -127,7 +127,7 @@ return returnIcon + returnString // Viewer not a Vamp AND not the target's vassal? if(!viewer.mind.has_antag_datum((/datum/antagonist/bloodsucker)) && !(viewer in vassals)) - if(!(HAS_TRAIT(viewer.mind, TRAIT_BLOODSUCKER_HUNTER) && broke_masquerade)) + if(!(HAS_TRAIT(viewer.mind, TRAIT_OCCULTIST) && broke_masquerade)) return FALSE // Default String var/returnString = "\[[return_full_name()]\]" diff --git a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_ruleset.dm b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_ruleset.dm index 69607ecb4461..ebd84063641f 100644 --- a/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_ruleset.dm +++ b/monkestation/code/modules/bloodsuckers/bloodsucker/bloodsucker_ruleset.dm @@ -12,7 +12,7 @@ // Command JOB_CAPTAIN, JOB_HEAD_OF_PERSONNEL, JOB_HEAD_OF_SECURITY, JOB_RESEARCH_DIRECTOR, JOB_CHIEF_ENGINEER, JOB_CHIEF_MEDICAL_OFFICER, // Security - JOB_WARDEN, JOB_SECURITY_OFFICER, JOB_DETECTIVE, + JOB_WARDEN, JOB_SECURITY_OFFICER, JOB_DETECTIVE, JOB_SECURITY_ASSISTANT, // Curator JOB_CURATOR, ) @@ -63,7 +63,7 @@ protected_roles = list( JOB_CAPTAIN, JOB_HEAD_OF_PERSONNEL, JOB_HEAD_OF_SECURITY, JOB_WARDEN, JOB_SECURITY_OFFICER, JOB_DETECTIVE, - JOB_CURATOR, + JOB_CURATOR, JOB_SECURITY_ASSISTANT, ) restricted_roles = list(JOB_AI, JOB_CYBORG, "Positronic Brain") required_candidates = 1 @@ -112,7 +112,7 @@ protected_roles = list( JOB_CAPTAIN, JOB_HEAD_OF_PERSONNEL, JOB_HEAD_OF_SECURITY, JOB_WARDEN, JOB_SECURITY_OFFICER, JOB_DETECTIVE, - JOB_CURATOR, + JOB_CURATOR, JOB_SECURITY_ASSISTANT, ) restricted_roles = list(JOB_AI, JOB_CYBORG) required_candidates = 1 diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/abilities/bloodsilver.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/abilities/bloodsilver.dm new file mode 100644 index 000000000000..837ef6888f27 --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/abilities/bloodsilver.dm @@ -0,0 +1,16 @@ +/datum/action/cooldown/spell/conjure_item/blood_silver + name = "Create bloodsilver bullet" + desc = "Wield your blood and mold it into a bloodsilver bullet" + button_icon = 'monkestation/icons/bloodsuckers/weapons.dmi' + button_icon_state = "bloodsilver" + cooldown_time = 2 MINUTES + item_type = /obj/item/ammo_casing/silver + spell_requirements = NONE + delete_old = FALSE + +/datum/action/cooldown/spell/blood_silver/conjure_item/cast(mob/living/carbon/cast_on) + if(cast_on.blood_volume < BLOOD_VOLUME_NORMAL) + to_chat(cast_on, span_warning ("Using this ability would put our health at risk!")) + return + . = ..() + cast_on.blood_volume -= 20 diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/abilities/paradox.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/abilities/paradox.dm new file mode 100644 index 000000000000..16f38e845f19 --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/abilities/paradox.dm @@ -0,0 +1,52 @@ +/datum/action/cooldown/paradox + name = "Paradox Rabbit" + desc = "The rabbit's movements will be translated onto you, ignoring any solid objects in your way." + button_icon = 'icons/mob/simple/rabbit.dmi' + button_icon_state = "rabbit_white_dead" + cooldown_time = 3 MINUTES + ///where we will be teleporting the rabbit too + var/obj/effect/landmark/wonderchess_mark/chessmark + ///where the user will be while this whole ordeal is happening + var/obj/effect/landmark/wonderland_mark/landmark + ///the rabbit in question if it exists + var/mob/living/basic/rabbit/rabbit + ///where the user originally was + var/turf/original_loc + +/datum/action/cooldown/paradox/New(Target) + ..() + chessmark = GLOB.wonderland_marks["Wonderchess landmark"] + landmark = GLOB.wonderland_marks["Wonderland landmark"] + + +/datum/action/cooldown/paradox/Activate() + var/turf/owner_turf = get_turf(owner) + if(!is_station_level(owner_turf.z)) + to_chat(owner, span_warning("The pull of the ice moon isn't strong enough here..")) + return + StartCooldown(360 SECONDS, 360 SECONDS) + if(QDELETED(chessmark)) + return + var/turf/theplace = get_turf(chessmark) + var/turf/land_mark = get_turf(landmark) + original_loc = get_turf(owner) + var/mob/living/basic/rabbit/bunny = new(theplace) + if(QDELETED(bunny)) + return + owner.forceMove(land_mark) ///the user remains safe in the wonderland + var/mob/living/master = owner + owner.mind.transfer_to(bunny) + playsound(bunny, 'monkestation/sound/bloodsuckers/paradoxskip.ogg', vol = 100) + addtimer(CALLBACK(src, PROC_REF(return_to_station), master, bunny, theplace), 5 SECONDS) + StartCooldown() + +/datum/action/cooldown/paradox/proc/return_to_station(mob/user, mob/bunny, turf/mark) + var/new_x = bunny.x - mark.x + var/new_y = bunny.y - mark.y + var/turf/new_location = locate((original_loc.x + new_x) , (original_loc.y + new_y) , original_loc.z) + user.forceMove(new_location) + bunny.mind.transfer_to(user) + playsound(user, 'monkestation/sound/bloodsuckers/paradoxskip.ogg', vol = 100) + rabbit = null + original_loc = null + qdel(bunny) diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/abilities/wonderland.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/abilities/wonderland.dm new file mode 100644 index 000000000000..efbc8df57ac9 --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/abilities/wonderland.dm @@ -0,0 +1,37 @@ +/datum/action/cooldown/wonderland_drop + name = "To Wonderland" + button_icon = 'icons/turf/floors.dmi' + button_icon_state = "junglegrass" + cooldown_time = 5 MINUTES + ///where we will be teleporting the user too + var/obj/effect/landmark/wonderland_mark/landmark + ///where the user originally was + var/turf/original_loc + +/datum/action/cooldown/wonderland_drop/New(Target) + ..() + landmark = GLOB.wonderland_marks["Wonderland landmark"] + +/datum/action/cooldown/wonderland_drop/Activate() + StartCooldown(360 SECONDS, 360 SECONDS) + var/mob/living/sleeper = owner + if(QDELETED(landmark)) + return + original_loc = get_turf(sleeper) + var/turf/theplace = get_turf(landmark) + sleeper.forceMove(theplace) + sleeper.Sleeping(2 SECONDS) + addtimer(CALLBACK(src, PROC_REF(awaken), sleeper), 3 SECONDS) + +/datum/action/cooldown/wonderland_drop/proc/awaken(mob/living/sleeper) + to_chat(sleeper, span_warning("You wake up in the Wonderland.")) + owner.playsound_local(sleeper, 'monkestation/sound/bloodsuckers/wonderlandmusic.ogg', vol = 10) + addtimer(CALLBACK(src, PROC_REF(return_to_station), sleeper), 1 MINUTES) + StartCooldown() + +/datum/action/cooldown/wonderland_drop/proc/return_to_station(mob/living/sleeper) + if(QDELETED(original_loc)) + return + sleeper.forceMove(original_loc) + to_chat(sleeper, span_warning("You feel like you have woken up from a deep slumber, was it all a dream?")) + original_loc = null diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/events/wonderland_apocalypse.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/events/wonderland_apocalypse.dm index 3946bdf1845f..9780e9380c9b 100644 --- a/monkestation/code/modules/bloodsuckers/monster_hunters/events/wonderland_apocalypse.dm +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/events/wonderland_apocalypse.dm @@ -23,19 +23,42 @@ category = EVENT_CATEGORY_SPACE /datum/round_event/wonderlandapocalypse/announce(fake) - - priority_announce("What the heELl is going on?! WEeE have detected massive up-spikes in ##@^^?? coming fr*m yoOourr st!*i@n! GeEeEEET out of THERE NOW!!","?????????", 'monkestation/sound/bloodsuckers/monsterhunterintro.ogg') + if(!fake && SSsecurity_level.get_current_level_as_number() < SEC_LEVEL_DELTA) + SSsecurity_level.set_level(SEC_LEVEL_DELTA) + priority_announce( + text = "What the heELl is going on?! WEeE have detected massive up-spikes in ##@^^?? coming fr*m yoOourr st!*i@n! GeEeEEET out of THERE NOW!!", + title = Gibberish("[command_name()] Higher Dimensional Affairs", TRUE, 45), + sound = 'monkestation/sound/bloodsuckers/monsterhunterintro.ogg', + color_override = "purple" + ) /datum/round_event/wonderlandapocalypse/start() - for(var/i = 1, i < 16, i++) + SSshuttle.emergency_no_recall = TRUE + for(var/i = 1 to 16) new /obj/effect/anomaly/dimensional/wonderland(get_safe_random_station_turf(), null, FALSE) - for(var/i = 1, i < 4, i++) + for(var/i = 1 to 4) var/obj/structure/wonderland_rift/rift = new(get_safe_random_station_turf()) - notify_ghosts("A doorway to the wonderland has been opened!", source = rift, action = NOTIFY_ORBIT, flashwindow = FALSE, header = "Wonderland rift Opened") - - + notify_ghosts("A doorway to the wonderland has been opened!", source = rift, action = NOTIFY_ORBIT, flashwindow = FALSE, header = "Wonderland Rift Opened") + for(var/mob/living/target as anything in GLOB.mob_living_list) + if(QDELETED(target)) + continue + var/area/centcom/target_area = get_area(target) + if(istype(target_area) && target_area.grace) + continue + addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living, apply_status_effect), /datum/status_effect/wonderland_district), rand(5 SECONDS, 10 SECONDS)) + RegisterSignal(SSdcs, COMSIG_GLOB_MOB_CREATED, PROC_REF(apply_pressure_to_new_mob)) + +/datum/round_event/wonderlandapocalypse/proc/apply_pressure_to_new_mob(datum/source, mob/living/target) + SIGNAL_HANDLER + if(!istype(target) || QDELING(target)) + return + var/area/centcom/target_area = get_area(target) + if(istype(target_area) && target_area.grace) + return + target.apply_status_effect(/datum/status_effect/wonderland_district) /obj/effect/anomaly/dimensional/wonderland + aSignal = null range = 5 immortal = TRUE drops_core = FALSE @@ -55,14 +78,14 @@ /obj/structure/wonderland_rift name = "Wonderland Door" desc = "A door leading to a magical beautiful land." - armor_type = /datum/armor/wonderland_rift - max_integrity = 300 icon = 'monkestation/icons/mob/infils.dmi' icon_state = "cyborg_rift" anchored = TRUE density = FALSE plane = MASSIVE_OBJ_PLANE - ///Have we already spawned an enemy? + armor_type = /datum/armor/wonderland_rift + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + /// Have we already spawned an enemy? var/enemy_spawned = FALSE /datum/armor/wonderland_rift @@ -83,13 +106,56 @@ /obj/structure/wonderland_rift/proc/summon_rabbit(mob/user) var/spawn_check = tgui_alert(user, "Become a Jabberwocky?", "Wonderland Rift", list("Yes", "No")) - if(spawn_check != "Yes" || !src || QDELETED(src) || QDELETED(user)) + if(spawn_check != "Yes" || QDELETED(src) || QDELETED(user) || enemy_spawned) return FALSE - - if(enemy_spawned) - return FALSE - enemy_spawned = TRUE var/mob/living/basic/red_rabbit/evil_rabbit = new(get_turf(src)) evil_rabbit.key = user.key to_chat(evil_rabbit, span_boldwarning("Destroy everything, spare no one.")) + +/datum/status_effect/wonderland_district + id = "wonderland_district" + alert_type = null + tick_interval = -1 + +/datum/status_effect/wonderland_district/on_apply() + . = ..() + if(FACTION_RABBITS in owner?.faction) + return FALSE + to_chat(owner, span_warning("You feel an ominous pressure fill the air around you...")) + RegisterSignal(owner, COMSIG_ENTER_AREA, PROC_REF(on_enter_area)) + RegisterSignal(owner, COMSIG_MOB_AFTER_SPELL_CAST, PROC_REF(after_spell_cast)) + +/datum/status_effect/wonderland_district/on_remove() + . = ..() + UnregisterSignal(owner, list(COMSIG_ENTER_AREA, COMSIG_MOB_AFTER_SPELL_CAST)) + +/datum/status_effect/wonderland_district/proc/on_enter_area(datum/source, area/centcom/new_area) + SIGNAL_HANDLER + if(istype(new_area) && new_area.grace) + qdel(src) + +/datum/status_effect/wonderland_district/proc/after_spell_cast(datum/source, datum/action/cooldown/spell/spell, atom/cast_on) + SIGNAL_HANDLER + if(!istype(spell) || QDELING(spell) || !spell.antimagic_flags) // don't affect non-magic spells. + return + make_visible() + INVOKE_ASYNC(owner, TYPE_PROC_REF(/mob/living, emote), "scream") + owner.visible_message(span_warning("[owner] doubles over in pain, violently coughing up blood!"), span_userdanger("An overwhelming pressure fills your body as you cast [spell.name || "magic"], filling you with excruciating pain down to the very core of your being!")) + owner.take_overall_damage(brute = rand(5, 15)) + if(iscarbon(owner)) + var/mob/living/carbon/carbon_owner = owner + carbon_owner.vomit(lost_nutrition = 0, blood = TRUE, distance = rand(1, 2), message = FALSE) + +/datum/status_effect/wonderland_district/proc/make_visible() + if(alert_type && !QDELETED(linked_alert)) + return + alert_type = /atom/movable/screen/alert/status_effect/wonderland_district + linked_alert = owner.throw_alert(id, alert_type) + linked_alert.attached_effect = src + +/atom/movable/screen/alert/status_effect/wonderland_district + name = "Wonderland Manifestation" + desc = "An omnipresent pressure surrounds you, causing any use of magic to overload your body!" + icon = 'monkestation/icons/hud/screen_alert.dmi' + icon_state = "wonderland" diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_abilities.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_abilities.dm deleted file mode 100644 index 33b3956c808d..000000000000 --- a/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_abilities.dm +++ /dev/null @@ -1,108 +0,0 @@ -/datum/action/cooldown/paradox - name = "Paradox Rabbit" - desc = "The rabbit's movements will be translated onto you, ignoring any solid objects in your way." - button_icon = 'icons/mob/simple/rabbit.dmi' - button_icon_state = "rabbit_white_dead" - cooldown_time = 3 MINUTES - ///where we will be teleporting the rabbit too - var/obj/effect/landmark/wonderchess_mark/chessmark - ///where the user will be while this whole ordeal is happening - var/obj/effect/landmark/wonderland_mark/landmark - ///the rabbit in question if it exists - var/mob/living/basic/rabbit/rabbit - ///where the user originally was - var/turf/original_loc - -/datum/action/cooldown/paradox/New(Target) - ..() - chessmark = GLOB.wonderland_marks["Wonderchess landmark"] - landmark = GLOB.wonderland_marks["Wonderland landmark"] - - - -/datum/action/cooldown/paradox/Activate() - if(!is_station_level(owner.loc.z)) - to_chat(owner,span_warning("The pull of the ice moon isn't strong enough here..")) - return - StartCooldown(360 SECONDS, 360 SECONDS) - if(!chessmark) - return - var/turf/theplace = get_turf(chessmark) - var/turf/land_mark = get_turf(landmark) - original_loc = get_turf(owner) - var/mob/living/basic/rabbit/bunny = new(theplace) - if(!bunny) - return - owner.forceMove(land_mark) ///the user remains safe in the wonderland - var/mob/living/master = owner - owner.mind.transfer_to(bunny) - playsound(bunny, 'monkestation/sound/bloodsuckers/paradoxskip.ogg',100) - addtimer(CALLBACK(src,PROC_REF(return_to_station), master, bunny, theplace), 5 SECONDS) - StartCooldown() - -/datum/action/cooldown/paradox/proc/return_to_station(mob/user, mob/bunny,turf/mark) - var/new_x = bunny.x - mark.x - var/new_y = bunny.y - mark.y - var/turf/new_location = locate((original_loc.x + new_x) , (original_loc.y + new_y) , original_loc.z) - user.forceMove(new_location) - bunny.mind.transfer_to(user) - playsound(user, 'monkestation/sound/bloodsuckers/paradoxskip.ogg',100) - rabbit = null - original_loc = null - qdel(bunny) - - -/datum/action/cooldown/wonderland_drop - name = "To Wonderland" - button_icon = 'icons/turf/floors.dmi' - button_icon_state = "junglegrass" - cooldown_time = 5 MINUTES - ///where we will be teleporting the user too - var/obj/effect/landmark/wonderland_mark/landmark - ///where the user originally was - var/turf/original_loc - -/datum/action/cooldown/wonderland_drop/New(Target) - ..() - landmark = GLOB.wonderland_marks["Wonderland landmark"] - - - -/datum/action/cooldown/wonderland_drop/Activate() - StartCooldown(360 SECONDS, 360 SECONDS) - var/mob/living/sleeper = owner - if(!landmark) - return - original_loc = get_turf(sleeper) - var/turf/theplace = get_turf(landmark) - sleeper.forceMove(theplace) - sleeper.Sleeping(2 SECONDS) - sleep(3 SECONDS) - to_chat(sleeper, span_warning("You wake up in the Wonderland.")) - owner.playsound_local(sleeper, 'monkestation/sound/bloodsuckers/wonderlandmusic.ogg',10) - addtimer(CALLBACK(src, PROC_REF(return_to_station), sleeper), 1 MINUTES) - StartCooldown() - -/datum/action/cooldown/wonderland_drop/proc/return_to_station(mob/living/sleeper) - if(!original_loc) - return - sleeper.forceMove(original_loc) - to_chat(sleeper, span_warning("You feel like you have woken up from a deep slumber, was it all a dream?")) - original_loc = null - -/datum/action/cooldown/spell/conjure_item/blood_silver - name = "Create bloodsilver bullet" - desc = "Wield your blood and mold it into a bloodsilver bullet" - button_icon = 'monkestation/icons/bloodsuckers/weapons.dmi' - button_icon_state = "bloodsilver" - cooldown_time = 2 MINUTES - item_type = /obj/item/ammo_casing/silver - spell_requirements = NONE - delete_old = FALSE - -/datum/action/cooldown/spell/blood_silver/conjure_item/cast(mob/living/carbon/cast_on) - if(cast_on.blood_volume < BLOOD_VOLUME_NORMAL) - to_chat(cast_on, span_warning ("Using this ability would put our health at risk!")) - return - . = ..() - cast_on.blood_volume -= 20 diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_datum.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_datum.dm index 963e54152a53..4b4e1106bf39 100644 --- a/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_datum.dm +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_datum.dm @@ -3,8 +3,11 @@ roundend_category = "Monster Hunters" antagpanel_category = "Monster Hunter" job_rank = ROLE_MONSTERHUNTER - antag_hud_name = "obsessed" + hud_icon = 'monkestation/icons/mob/huds/antags/monster_hunter.dmi' + antag_hud_name = "hunter" preview_outfit = /datum/outfit/monsterhunter + antag_moodlet = /datum/mood_event/monster_hunter + show_to_ghosts = TRUE var/list/datum/action/powers = list() var/give_objectives = TRUE ///how many rabbits have we found @@ -17,20 +20,36 @@ var/apocalypse = FALSE ///a list of our prey var/list/datum/mind/prey = list() + /// A list of traits innately granted to monster hunters. + var/static/list/granted_traits = list( + TRAIT_FEARLESS, // to ensure things like fear of heresy or blood or whatever don't fuck them over + TRAIT_NOCRITDAMAGE, + TRAIT_NOSOFTCRIT + ) + /// A list of traits innately granted to the mind of monster hunters. + var/static/list/mind_traits = list( + TRAIT_OCCULTIST, + TRAIT_MADNESS_IMMUNE // You merely adopted the madness. I was born in it, molded by it. + ) + /// A typecache of ability types that will be revealed to the monster hunter when they gain insight. + var/static/list/monster_abilities = typecacheof(list( + /datum/action/changeling, + /datum/action/cooldown/bloodsucker + )) /datum/antagonist/monsterhunter/apply_innate_effects(mob/living/mob_override) . = ..() var/mob/living/current_mob = mob_override || owner.current - current_mob.add_traits(list(TRAIT_NOSOFTCRIT, TRAIT_NOCRITDAMAGE), HUNTER_TRAIT) - ADD_TRAIT(owner, TRAIT_BLOODSUCKER_HUNTER, HUNTER_TRAIT) - owner.unconvertable = TRUE + current_mob.add_traits(granted_traits, HUNTER_TRAIT) + current_mob.update_sight() + current_mob.faction |= FACTION_RABBITS /datum/antagonist/monsterhunter/remove_innate_effects(mob/living/mob_override) . = ..() var/mob/living/current_mob = mob_override || owner.current REMOVE_TRAITS_IN(current_mob, HUNTER_TRAIT) - REMOVE_TRAITS_IN(owner, HUNTER_TRAIT) - owner.unconvertable = FALSE + current_mob.faction -= FACTION_RABBITS + current_mob.update_sight() /datum/antagonist/monsterhunter/on_gain() //Give Hunter Objective @@ -39,7 +58,10 @@ var/datum/map_template/wonderland/wonder = new() if(!wonder.load_new_z()) message_admins("The wonderland failed to load.") - CRASH("Failed to initialize wonderland!") + CRASH("Failed to initialize wonderlandmind_traits, !") + + owner.add_traits(mind_traits, HUNTER_TRAIT) + owner.unconvertable = TRUE //Teach Stake crafting owner.teach_crafting_recipe(/datum/crafting_recipe/hardened_stake) @@ -56,7 +78,7 @@ grant_drop_ability(droppod_contract) RegisterSignal(src, COMSIG_GAIN_INSIGHT, PROC_REF(insight_gained)) RegisterSignal(src, COMSIG_BEASTIFY, PROC_REF(turn_beast)) - for(var/i in 1 to 5 ) + for(var/i in 1 to 5) var/turf/rabbit_hole = get_safe_random_station_turf() var/obj/effect/client_image_holder/white_rabbit/cretin = new(rabbit_hole, owner.current) cretin.hunter = src @@ -78,10 +100,11 @@ contract.owner = src summon_contract.Grant(owner.current) - /datum/antagonist/monsterhunter/on_removal() UnregisterSignal(src, COMSIG_GAIN_INSIGHT) UnregisterSignal(src, COMSIG_BEASTIFY) + REMOVE_TRAITS_IN(owner, HUNTER_TRAIT) + owner.unconvertable = FALSE for(var/obj/effect/client_image_holder/white_rabbit/white as anything in rabbits) rabbits -= white qdel(white) @@ -168,7 +191,7 @@ to_chat(owner.current, span_announce("While we can kill anyone in our way to destroy the monsters lurking around, causing property damage is unacceptable.")) to_chat(owner.current, span_announce("However, security WILL detain us if they discover our mission.")) to_chat(owner.current, span_announce("In exchange for our services, it shouldn't matter if a few items are gone missing for our... personal collection.")) - owner.current.playsound_local(null, 'monkestation/sound/bloodsuckers/monsterhunterintro.ogg', 100, FALSE, pressure_affected = FALSE) + owner.current.playsound_local(null, 'monkestation/sound/bloodsuckers/monsterhunterintro.ogg', vol = 100, vary = FALSE, pressure_affected = FALSE) owner.announce_objectives() /datum/antagonist/monsterhunter/proc/insight_gained() @@ -176,28 +199,28 @@ var/description var/datum/objective/assassinate/hunter/obj - var/list/unchecked_objectives = list() + var/list/unchecked_objectives for(var/datum/objective/assassinate/hunter/goal in objectives) if(!goal.discovered) - unchecked_objectives += goal - if(unchecked_objectives.len) + LAZYADD(unchecked_objectives, goal) + if(unchecked_objectives) obj = pick(unchecked_objectives) if(obj) obj.uncover_target() var/datum/antagonist/heretic/heretic_target = IS_HERETIC(obj.target.current) if(heretic_target) - description = "your target [heretic_target.owner.current.real_name] follows the [heretic_target.heretic_path], dear hunter." + description = "Your target, [heretic_target.owner.current.real_name], follows the [heretic_target.heretic_path], dear hunter." else - description = "O' hunter, your target [obj.target.current.real_name] bears these lethal abilities: " - for(var/datum/action/ability in obj.target.current.actions) - if(!ability) - continue - if(!istype(ability, /datum/action/changeling) && !istype(ability, /datum/action/cooldown/bloodsucker)) + description = "O' hunter, your target [obj.target.current.real_name] bears these lethal abilities: " + var/list/abilities = list() + for(var/datum/action/ability as anything in obj.target.current.actions) + if(!is_type_in_typecache(ability, monster_abilities)) continue - description += "[ability.name], " + abilities |= "[ability.name]" + description += english_list(abilities) rabbits_spotted++ - to_chat(owner.current,span_notice("[description]")) + to_chat(owner.current, span_boldnotice("[description]")) /datum/objective/assassinate/hunter ///has our target been discovered? @@ -209,26 +232,22 @@ discovered = !discovered src.update_explanation_text() to_chat(owner.current, span_userdanger("You have identified a monster, your objective list has been updated!")) + update_static_data_for_all_viewers() /datum/antagonist/monsterhunter/proc/find_monster_targets() var/list/possible_targets = list() - for(var/datum/antagonist/victim in GLOB.antagonists) - if(!victim.owner) + for(var/datum/antagonist/victim as anything in GLOB.antagonists) + if(QDELETED(victim?.owner?.current) || victim.owner.current.stat == DEAD || victim.owner == owner) continue - if(!victim.owner.current) - continue - if(victim.owner.current.stat == DEAD || victim.owner == owner) - continue - if(istype(victim, /datum/antagonist/changeling) || istype(victim, /datum/antagonist/heretic) || istype(victim, /datum/antagonist/bloodsucker)) + if(is_type_in_typecache(victim, GLOB.monster_hunter_prey_antags)) possible_targets += victim.owner for(var/i in 1 to 3) //we get 3 targets - if(!(possible_targets.len)) + if(!length(possible_targets)) break var/datum/objective/assassinate/hunter/kill_monster = new kill_monster.owner = owner - var/datum/mind/target = pick(possible_targets) - possible_targets -= target + var/datum/mind/target = pick_n_take(possible_targets) kill_monster.target = target prey += target kill_monster.explanation_text = "A monster target is aboard the station, identify and eliminate this threat." @@ -239,7 +258,7 @@ SIGNAL_HANDLER apocalypse = TRUE - force_event(/datum/round_event_control/wonderlandapocalypse, "a monsterhunter turning into a beast") + force_event(/datum/round_event_control/wonderlandapocalypse, "a monster hunter turning into a beast") /obj/item/clothing/mask/monster_preview_mask name = "Monster Preview Mask" @@ -277,7 +296,7 @@ /datum/action/droppod_item - name = "Summon Monster Hunter tools" + name = "Summon Monster Hunter Tools" desc = "Summon specific monster hunter tools that will aid us with our hunt." button_icon = 'icons/obj/device.dmi' button_icon_state = "beacon" @@ -319,7 +338,6 @@ /datum/action/cooldown/spell/track_monster/proc/remove_vision(mob/living/carbon/cast_on) qdel(cast_on.GetComponent(/datum/component/echolocation)) - /datum/component/echolocation/monsterhunter /datum/component/echolocation/monsterhunter/echolocate() //code stolen from echolocation to make it ignore non-monster mobs diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_rulesets.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_rulesets.dm index cd79d9560b69..aa190a0c2d30 100644 --- a/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_rulesets.dm +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_rulesets.dm @@ -1,10 +1,3 @@ -///List of antagonists that are considered 'Monsters' and their chance of being selected. -GLOBAL_LIST_INIT(monster_antagonist_types, list( - /datum/antagonist/bloodsucker, - /datum/antagonist/heretic, - /datum/antagonist/changeling, -)) - #define MINIMUM_MONSTERS_REQUIRED 2 //gives monsterhunters an icon in the antag selection panel @@ -22,6 +15,7 @@ GLOBAL_LIST_INIT(monster_antagonist_types, list( JOB_HEAD_OF_SECURITY, JOB_PRISONER, JOB_SECURITY_OFFICER, + JOB_SECURITY_ASSISTANT, JOB_WARDEN, JOB_CHIEF_ENGINEER, JOB_CHIEF_MEDICAL_OFFICER, @@ -39,13 +33,14 @@ GLOBAL_LIST_INIT(monster_antagonist_types, list( /datum/dynamic_ruleset/midround/monsterhunter/trim_candidates() - ..() + . = ..() for(var/mob/living/player in living_players) + var/turf/player_turf = get_turf(player) if(issilicon(player)) living_players -= player - if(is_centcom_level(player.z)) + if(QDELETED(player_turf) || is_centcom_level(player_turf.z)) living_players -= player - if((player.mind?.special_role || player.mind?.antag_datums?.len)) + if((player.mind?.special_role || length(player.mind?.antag_datums))) living_players -= player /datum/dynamic_ruleset/midround/monsterhunter/ready(forced = FALSE) @@ -53,13 +48,9 @@ GLOBAL_LIST_INIT(monster_antagonist_types, list( return FALSE var/count = 0 for(var/datum/antagonist/monster as anything in GLOB.antagonists) - if(!monster.owner) - continue - if(!monster.owner.current) - continue - if(monster.owner.current.stat == DEAD) + if(QDELETED(monster.owner) || QDELETED(monster.owner.current) || monster.owner.current.stat == DEAD) continue - if(GLOB.monster_antagonist_types.Find(monster.type)) + if(is_type_in_typecache(monster, GLOB.monster_hunter_prey_antags)) count++ if(MINIMUM_MONSTERS_REQUIRED > count) diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_weapons.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_weapons.dm deleted file mode 100644 index b254e7d2e435..000000000000 --- a/monkestation/code/modules/bloodsuckers/monster_hunters/hunter_weapons.dm +++ /dev/null @@ -1,543 +0,0 @@ -#define upgraded_val(x,y) ( CEILING((x * (1.07 ** y)), 1) ) -#define CALIBER_BLOODSILVER "bloodsilver" -#define WEAPON_UPGRADE "weapon_upgrade" - -/obj/item/melee/trick_weapon - icon = 'monkestation/icons/bloodsuckers/weapons.dmi' - lefthand_file = 'monkestation/icons/bloodsuckers/weapons_lefthand.dmi' - righthand_file = 'monkestation/icons/bloodsuckers/weapons_righthand.dmi' - ///upgrade level of the weapon - var/upgrade_level = 0 - ///base force when transformed - var/on_force - ///base force when in default state - var/base_force - ///default name of the weapon - var/base_name - ///is the weapon in its transformed state? - var/enabled = FALSE - ///wounding chance while on - var/on_wound_bonus - -/obj/item/melee/trick_weapon/proc/upgrade_weapon() - SIGNAL_HANDLER - - upgrade_level++ - force = upgraded_val(base_force,upgrade_level) - var/datum/component/transforming/transform = GetComponent(/datum/component/transforming) - transform.force_on = upgraded_val(on_force,upgrade_level) - - -/obj/item/melee/trick_weapon/attack(mob/target, mob/living/user, params) //our weapon does 25% less damage on non monsters - var/old_force = force - if(!(target.mind?.has_antag_datum(/datum/antagonist/changeling)) && !IS_BLOODSUCKER(target) && !IS_HERETIC(target)) - force = force * 0.75 - ..() - force = old_force - -/obj/item/melee/trick_weapon/darkmoon - name = "Darkmoon Greatsword" - base_name = "Darkmoon Greatsword" - desc = "Ahh my guiding moonlight, you were by my side all along." - icon_state = "darkmoon" - inhand_icon_state = "darkmoon_hilt" - w_class = WEIGHT_CLASS_SMALL - block_chance = 20 - on_force = 20 - base_force = 17 - light_system = OVERLAY_LIGHT - light_color = "#59b3c9" - light_outer_range = 2 - light_power = 2 - light_on = FALSE - throwforce = 12 - damtype = BURN - hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") - attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") - ///ready to launch a beam attack? - COOLDOWN_DECLARE(moonbeam_fire) - - -/obj/item/melee/trick_weapon/darkmoon/Initialize(mapload) - . = ..() - force = base_force - AddComponent(/datum/component/transforming, \ - force_on = on_force , \ - throwforce_on = 20, \ - throw_speed_on = throw_speed, \ - sharpness_on = SHARP_EDGED, \ - w_class_on = WEIGHT_CLASS_BULKY) - RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) - RegisterSignal(src, WEAPON_UPGRADE, PROC_REF(upgrade_weapon)) - - - -/obj/item/melee/trick_weapon/darkmoon/proc/on_transform(obj/item/source, mob/user, active) - SIGNAL_HANDLER - balloon_alert(user, active ? "extended" : "collapsed") - if(active) - playsound(src, 'monkestation/sound/bloodsuckers/moonlightsword.ogg',50) - inhand_icon_state = active ? "darkmoon" : "darkmoon_hilt" - enabled = active - set_light_on(active) - force = active ? upgraded_val(on_force, upgrade_level) : upgraded_val(base_force, upgrade_level) - return COMPONENT_NO_DEFAULT_MESSAGE - - -/obj/item/melee/trick_weapon/darkmoon/attack_secondary(atom/target, mob/living/user, clickparams) - return SECONDARY_ATTACK_CONTINUE_CHAIN - -/obj/item/melee/trick_weapon/darkmoon/afterattack_secondary(atom/target, mob/living/user, clickparams) - if(!enabled) - return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN - if(!COOLDOWN_FINISHED(src, moonbeam_fire)) - return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN - if(target == user) - balloon_alert(user, "can't aim at yourself!") - return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN - fire_moonbeam(target, user, clickparams) - user.changeNext_move(CLICK_CD_MELEE) - COOLDOWN_START(src, moonbeam_fire, 4 SECONDS) - return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN - -/obj/item/melee/trick_weapon/darkmoon/proc/fire_moonbeam(atom/target, mob/living/user, clickparams) - var/modifiers = params2list(clickparams) - var/turf/proj_turf = user.loc - if(!isturf(proj_turf)) - return - var/obj/projectile/moonbeam/moon = new(proj_turf) - moon.preparePixelProjectile(target, user, modifiers) - moon.firer = user - playsound(src, 'monkestation/sound/bloodsuckers/moonlightbeam.ogg',50) - moon.fire() - - -/obj/projectile/moonbeam - name = "Moonlight" - icon = 'icons/effects/effects.dmi' - icon_state = "plasmasoul" - damage = 25 - light_system = OVERLAY_LIGHT - light_outer_range = 2 - light_power = 1 - light_color = "#44acb1" - damage_type = BURN - hitsound = 'sound/weapons/sear.ogg' - hitsound_wall = 'sound/weapons/effects/searwall.ogg' - - - -/obj/item/melee/trick_weapon/threaded_cane - name = "Threaded Cane" - base_name = "Threaded Cane" - desc = "A blind man's whip." - icon_state = "threaded_cane" - inhand_icon_state = "threaded_cane" - w_class = WEIGHT_CLASS_SMALL - block_chance = 20 - on_force = 15 - base_force = 18 - throwforce = 12 - reach = 1 - hitsound = 'sound/weapons/bladeslice.ogg' - damtype = BURN - attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") - attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") - - -/obj/item/melee/trick_weapon/threaded_cane/Initialize(mapload) - . = ..() - force = base_force - AddComponent(/datum/component/transforming, \ - force_on = on_force, \ - throwforce_on = 10, \ - throw_speed_on = throw_speed, \ - sharpness_on = SHARP_EDGED, \ - w_class_on = WEIGHT_CLASS_BULKY) - RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) - RegisterSignal(src,WEAPON_UPGRADE, PROC_REF(upgrade_weapon)) - - - -/obj/item/melee/trick_weapon/threaded_cane/proc/on_transform(obj/item/source, mob/user, active) - SIGNAL_HANDLER - balloon_alert(user, active ? "extended" : "collapsed") - inhand_icon_state = active ? "chain" : "threaded_cane" - if(active) - playsound(src,'sound/magic/clockwork/fellowship_armory.ogg',50) - reach = active ? 2 : 1 - enabled = active - force = active ? upgraded_val(on_force, upgrade_level) : upgraded_val(base_force, upgrade_level) - return COMPONENT_NO_DEFAULT_MESSAGE - - -/obj/item/melee/trick_weapon/hunter_axe - name = "Hunter's Axe" - base_name = "Hunter's Axe" - desc = "A brute's tool of choice." - icon_state = "hunteraxe0" - base_icon_state = "hunteraxe" - w_class = WEIGHT_CLASS_SMALL - block_chance = 20 - base_force = 20 - on_force = 25 - throwforce = 12 - reach = 1 - hitsound = 'sound/weapons/bladeslice.ogg' - damtype = BURN - attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") - attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") - - - -/obj/item/melee/trick_weapon/hunter_axe/Initialize(mapload) - . = ..() - force = base_force - AddComponent(/datum/component/two_handed, \ - force_unwielded=base_force, \ - force_wielded= on_force, \ - icon_wielded="[base_icon_state]1", \ - wield_callback = CALLBACK(src, PROC_REF(on_wield)), \ - unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \ - ) - RegisterSignal(src, WEAPON_UPGRADE, PROC_REF(upgrade_weapon)) - -/obj/item/melee/trick_weapon/hunter_axe/upgrade_weapon() - - upgrade_level++ - var/datum/component/two_handed/handed = GetComponent(/datum/component/two_handed) - handed.force_wielded = upgraded_val(on_force, upgrade_level) - handed.force_unwielded = upgraded_val(base_force,upgrade_level) - force = handed.force_unwielded - -/obj/item/melee/trick_weapon/hunter_axe/update_icon_state() - icon_state = "[base_icon_state]0" - playsound(src,'sound/magic/clockwork/fellowship_armory.ogg',50) - return ..() - -/obj/item/melee/trick_weapon/hunter_axe/proc/on_wield(obj/item/source) - enabled = TRUE - block_chance = 75 - -/obj/item/melee/trick_weapon/hunter_axe/proc/on_unwield(obj/item/source) - enabled = FALSE - block_chance = 20 - -/obj/item/melee/trick_weapon/beast_claw - name = "\improper Beast Claw" - base_name = "\improper Beast Claw" - desc = "The bones seem to still be twitching." - icon_state = "Bone_Claw" - base_icon_state = "Claw" - w_class = WEIGHT_CLASS_SMALL - block_chance = 20 - base_force = 18 - on_force = 23 - throwforce = 10 - wound_bonus = 25 - bare_wound_bonus = 35 - demolition_mod = 1.5 //ripping through doors and windows should be a little easier with a claw shouldnt it? - sharpness = SHARP_EDGED - hitsound = 'sound/weapons/fwoosh.ogg' - damtype = BRUTE //why can i not make things do wounds i want - attack_verb_continuous = list("rips", "claws", "gashes", "tears", "lacerates", "dices", "cuts", "attacks") - attack_verb_simple = list("rip", "claw", "gash", "tear", "lacerate", "dice", "cut", "attack" ) - -/obj/item/melee/trick_weapon/beast_claw/Initialize(mapload) - . = ..() - force = base_force - AddComponent(/datum/component/transforming, \ - force_on = on_force, \ - w_class_on = WEIGHT_CLASS_BULKY) - RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) - RegisterSignal(src,WEAPON_UPGRADE, PROC_REF(upgrade_weapon)) - -/obj/item/melee/trick_weapon/beast_claw/proc/on_transform(obj/item/source, mob/user, active) - SIGNAL_HANDLER - balloon_alert(user, active ? "extended" : "collapsed") - inhand_icon_state = active ? "Claw" : "BoneClaw" - if(active) - playsound(src, 'sound/weapons/fwoosh.ogg',50) - enabled = active - active = wound_bonus ? 45 : initial(wound_bonus) - force = active ? upgraded_val(on_force, upgrade_level) : upgraded_val(base_force, upgrade_level) - return COMPONENT_NO_DEFAULT_MESSAGE - -/obj/item/rabbit_eye - name = "Rabbit eye" - desc = "An item that resonates with trick weapons." - icon_state = "rabbit_eye" - icon = 'monkestation/icons/bloodsuckers/weapons.dmi' - -/obj/item/rabbit_eye/proc/upgrade(obj/item/melee/trick_weapon/killer, mob/user) - if(killer.upgrade_level >= 3) - user.balloon_alert(user, "Already at maximum upgrade!") - return - if(killer.enabled) - user.balloon_alert(user, "Weapon must be in base form!") - return - SEND_SIGNAL(killer,WEAPON_UPGRADE) - killer.name = "[killer.base_name] +[killer.upgrade_level]" - balloon_alert(user, "[src] crumbles away...") - playsound(src, 'monkestation/sound/bloodsuckers/weaponsmithing.ogg', 50) - qdel(src) - -/obj/item/gun/ballistic/revolver/hunter_revolver - name = "\improper Hunter's Revolver" - desc = "Does minimal damage but slows down the enemy." - icon_state = "revolver" - icon = 'monkestation/icons/bloodsuckers/weapons.dmi' - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/bloodsilver - initial_caliber = CALIBER_BLOODSILVER - -/datum/movespeed_modifier/silver_bullet - movetypes = GROUND - multiplicative_slowdown = 4 - flags = IGNORE_NOSLOW - - -/obj/item/ammo_box/magazine/internal/cylinder/bloodsilver - name = "detective revolver cylinder" - ammo_type = /obj/item/ammo_casing/silver - caliber = CALIBER_BLOODSILVER - max_ammo = 2 - -/obj/item/ammo_casing/silver - name = "Bloodsilver casing" - desc = "A Bloodsilver bullet casing." - icon_state = "bloodsilver" - icon = 'monkestation/icons/bloodsuckers/weapons.dmi' - projectile_type = /obj/projectile/bullet/bloodsilver - caliber = CALIBER_BLOODSILVER - - -/obj/projectile/bullet/bloodsilver - name = "Bloodsilver bullet" - damage = 3 - ricochets_max = 4 - -/obj/projectile/bullet/bloodsilver/on_hit(atom/target, blocked = 0, pierce_hit) - . = ..() - if(!iscarbon(target)) - return - var/mob/living/carbon/man = target - if(!man) - return - if(man.has_movespeed_modifier(/datum/movespeed_modifier/silver_bullet)) - return - if(!IS_HERETIC(man) && !(IS_BLOODSUCKER(man)) && !(man.mind.has_antag_datum(/datum/antagonist/changeling))) - return - man.add_movespeed_modifier(/datum/movespeed_modifier/silver_bullet) - if(!(man.has_movespeed_modifier(/datum/movespeed_modifier/silver_bullet))) - return - addtimer(CALLBACK(man, TYPE_PROC_REF(/mob, remove_movespeed_modifier), /datum/movespeed_modifier/silver_bullet), 8 SECONDS) - -/obj/structure/rack/weaponsmith - name = "Weapon Forge" - desc = "Fueled by the tears of rabbits." - icon = 'icons/obj/cult/structures.dmi' - icon_state = "altar" - resistance_flags = INDESTRUCTIBLE - -/obj/structure/rack/weaponsmith/attackby(obj/item/organ, mob/living/user, params) - if(!istype(organ, /obj/item/rabbit_eye)) - return ..() - var/obj/item/rabbit_eye/eye = organ - var/obj/item/melee/trick_weapon/tool - for(var/obj/item/weapon in src.loc.contents) - if(!istype(weapon, /obj/item/melee/trick_weapon)) - continue - tool = weapon - break - if(!tool) - to_chat(user, span_warning ("Place your weapon upon the table before upgrading it!")) - return - eye.upgrade(tool,user) - - -/obj/item/clothing/mask/cursed_rabbit - name = "Damned Rabbit Mask" - desc = "Slip into the wonderland." - icon = 'monkestation/icons/bloodsuckers/weapons.dmi' - icon_state = "rabbit_mask" - worn_icon = 'monkestation/icons/bloodsuckers/worn_mask.dmi' - worn_icon_state = "rabbit_mask" - clothing_flags = SNUG_FIT - flags_inv = HIDEFACE|HIDEFACIALHAIR|HIDESNOUT - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF - flash_protect = FLASH_PROTECTION_WELDER - ///the paradox rabbit ability - var/datum/action/cooldown/paradox/paradox - ///teleporting to the wonderland - var/datum/action/cooldown/wonderland_drop/wonderland - - -/obj/item/clothing/mask/cursed_rabbit/Initialize(mapload) - . = ..() - generate_abilities() - - -/obj/item/clothing/mask/cursed_rabbit/proc/generate_abilities() - var/datum/action/cooldown/paradox/para = new - if(!para.landmark || !para.chessmark) - return - paradox = para - var/datum/action/cooldown/wonderland_drop/drop = new - if(!drop.landmark) - return - wonderland = drop - - -/obj/item/clothing/mask/cursed_rabbit/equipped(mob/living/carbon/human/user,slot) - ..() - if(!paradox) - return - if(!wonderland) - return - if(!(slot & ITEM_SLOT_MASK)) - return - if(!IS_MONSTERHUNTER(user)) - return - paradox.Grant(user) - wonderland.Grant(user) - - -/obj/item/clothing/mask/cursed_rabbit/dropped(mob/user) - . = ..() - if(!paradox) - return - if(paradox.owner != user) - return - paradox.Remove(user) - if(!wonderland) - return - if(wonderland.owner != user) - return - wonderland.Remove(user) - -/obj/item/rabbit_locator - name = "Accursed Red Queen card" - desc = "Hunts down the white rabbits." - icon = 'monkestation/icons/bloodsuckers/weapons.dmi' - icon_state = "locator" - w_class = WEIGHT_CLASS_SMALL - ///the hunter the card is tied too - var/datum/antagonist/monsterhunter/hunter - ///cooldown for the locator - var/cooldown = TRUE - - COOLDOWN_DECLARE(locator_timer) - - -/obj/item/rabbit_locator/Initialize(mapload, datum/antagonist/monsterhunter/killer) - . = ..() - if(!killer) - return - hunter = killer - hunter.locator = src - -/obj/item/rabbit_locator/attack_self(mob/user, modifiers) - if (!COOLDOWN_FINISHED(src, locator_timer)) - return - if(!cooldown) - return - if(!hunter) - to_chat(user,span_warning("It's just a normal playing card!")) - return - if(hunter.owner.current != user) - to_chat(user,span_warning("It's just a normal playing card!")) - return - if(!is_station_level(user.loc.z)) - to_chat(user,span_warning("The card cannot be used here...")) - return - var/distance = get_minimum_distance(user) - var/sound_value - if(distance >= 50) - sound_value = 0 - to_chat(user,span_warning("Too far away...")) - if(distance >= 40 && distance < 50) - sound_value = 20 - to_chat(user,span_warning("You feel the slightest hint...")) - if(distance >=30 && distance < 40) - sound_value = 40 - to_chat(user,span_warning("You feel a mild hint...")) - if(distance >=20 && distance < 30) - sound_value = 60 - to_chat(user,span_warning("You feel a strong hint...")) - if(distance >= 10 && distance < 20) - sound_value = 80 - to_chat(user,span_warning("You feel a VERY strong hint...")) - if(distance < 10) - sound_value = 100 - to_chat(user,span_warning("Here...its definitely here!")) - user.playsound_local(src, 'monkestation/sound/bloodsuckers/rabbitlocator.ogg',sound_value) - COOLDOWN_START(src, locator_timer, 7 SECONDS) - -/obj/item/rabbit_locator/proc/get_minimum_distance(mob/user) - var/dist=1000 - if(!hunter) - return - if(!hunter.rabbits.len) - return - var/obj/effect/selected_bunny - for(var/obj/effect/located as anything in hunter.rabbits) - if(get_dist(user,located) < dist) - dist = get_dist(user,located) - selected_bunny = located - var/z_difference = abs(selected_bunny.z - user.z) - if(dist < 50 && z_difference != 0) - to_chat(user,span_warning("[z_difference] [z_difference == 1 ? "floor" : "floors"] [selected_bunny.z > user.z ? "above" : "below"]...")) - return dist - -/obj/item/rabbit_locator/Destroy() - if(hunter) - hunter.locator = null - hunter = null - return ..() - -/obj/item/grenade/jack - name = "jack in the bomb" - desc = "Best kids' toy" - w_class = WEIGHT_CLASS_SMALL - icon = 'monkestation/icons/bloodsuckers/weapons.dmi' - icon_state = "jack_in_the_bomb" - inhand_icon_state = "flashbang" - worn_icon_state = "grenade" - det_time = 12 SECONDS - ex_dev = 1 - ex_heavy = 2 - ex_light = 4 - ex_flame = 2 - - - -/obj/item/grenade/jack/arm_grenade(mob/user, delayoverride, msg = TRUE, volume = 60) - log_grenade(user) //Inbuilt admin procs already handle null users - if(user) - add_fingerprint(user) - if(msg) - to_chat(user, span_warning("You prime [src]! [capitalize(DisplayTimeText(det_time))]!")) - playsound(src, 'monkestation/sound/bloodsuckers/jackinthebomb.ogg', volume, TRUE) - if(istype(user)) - user.add_mob_memory(/datum/memory/bomb_planted, protagonist = user, antagonist = src) - active = TRUE - icon_state = initial(icon_state) + "_active" - SEND_SIGNAL(src, COMSIG_GRENADE_ARMED, det_time, delayoverride) - addtimer(CALLBACK(src, PROC_REF(detonate)), isnull(delayoverride)? det_time : delayoverride) - - -/obj/item/grenade/jack/detonate(mob/living/lanced_by) - if (dud_flags) - active = FALSE - update_appearance() - return FALSE - - dud_flags |= GRENADE_USED // Don't detonate if we have already detonated. - icon_state = "jack_in_the_bomb_live" - addtimer(CALLBACK(src, PROC_REF(exploding)), 1 SECONDS) - - -/obj/item/grenade/jack/proc/exploding(mob/living/lanced_by) - SEND_SIGNAL(src, COMSIG_GRENADE_DETONATE, lanced_by) - explosion(src, ex_dev, ex_heavy, ex_light, ex_flame) diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/hunting_contracts.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/hunting_contracts.dm index 56c65b67f242..48e2edf69067 100644 --- a/monkestation/code/modules/bloodsuckers/monster_hunters/hunting_contracts.dm +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/hunting_contracts.dm @@ -4,6 +4,7 @@ icon = 'icons/obj/wizard.dmi' icon_state = "scroll2" w_class = WEIGHT_CLASS_SMALL + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF ///have we claimed our weapon? var/bought = FALSE ///the datum containing all weapons @@ -24,13 +25,18 @@ /obj/item/hunting_contract/ui_interact(mob/living/user, datum/tgui/ui) if(!IS_MONSTERHUNTER(user)) - to_chat(usr, span_notice("You are unable to decipher the symbols.")) + to_chat(user, span_notice("You are unable to decipher the symbols.")) return ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "HunterContract", name) ui.open() +/obj/item/hunting_contract/ui_status(mob/user) + if(isliving(user) && !IS_MONSTERHUNTER(user)) + return UI_CLOSE + return ..() + /obj/item/hunting_contract/ui_data(mob/user) var/list/data = list() data["bought"] = bought @@ -39,22 +45,22 @@ if(weapons.len) for(var/datum/hunter_weapons/contraband as anything in weapons) data["items"] += list(list( - "id" = contraband.type, - "name" = contraband.name, - "desc" = contraband.desc, + "id" = contraband.type, + "name" = contraband.name, + "desc" = contraband.desc )) var/check_completed = TRUE ///determines if all objectives have been completed if(owner) for(var/datum/objective/obj as anything in owner.objectives) data["objectives"] += list(list( - "explanation" = obj.explanation_text, - "completed" = (obj.check_completion()), + "explanation" = obj.explanation_text, + "completed" = (obj.check_completion()) )) - if(!(obj.check_completion())) + if(!obj.check_completion()) check_completed = FALSE data["all_completed"] = check_completed data["number_of_rabbits"] = owner.rabbits_spotted - data["rabbits_found"] = !(owner.rabbits.len) + data["rabbits_found"] = !length(owner.rabbits) data["used_up"] = used_up return data @@ -73,8 +79,9 @@ if("claim_reward") if(used_up) return - if(!is_station_level(loc.z)) - to_chat(usr,span_warning("The pull of the ice moon isn't strong enough here....")) + var/turf/current_turf = get_turf(src) + if(!is_station_level(current_turf.z)) + to_chat(usr, span_warning("The pull of the ice moon isn't strong enough here...")) return SEND_SIGNAL(owner, COMSIG_BEASTIFY) used_up = TRUE @@ -95,8 +102,8 @@ podspawn(list( "target" = get_turf(user), "style" = STYLE_SYNDICATE, - "spawn" = purchased, - )) + "spawn" = purchased + )) /obj/item/hunting_contract/Destroy() owner = null @@ -112,18 +119,17 @@ var/item /datum/hunter_weapons/threaded_cane - name = "Threaded cane" + name = "Threaded Cane" desc = "cane made out of heavy metal, can transform into a whip to strike foes from afar." item = /obj/item/melee/trick_weapon/threaded_cane - /datum/hunter_weapons/hunter_axe - name = "Hunter's axe" + name = "Hunter's Axe" desc = "simple axe for hunters that lean towards barbarian tactics, can transform into a double bladed axe." item = /obj/item/melee/trick_weapon/hunter_axe /datum/hunter_weapons/darkmoon_greatsword - name = "Darkmoon greatsword" + name = "Darkmoon Greatsword" desc = "a heavy sword hilt that would knock anyone out cold, can transform into the darkmoonlight greatsword. " item = /obj/item/melee/trick_weapon/darkmoon diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/killer_rabbits.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/killer_rabbits.dm index bd903199cb33..0e380d208435 100644 --- a/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/killer_rabbits.dm +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/killer_rabbits.dm @@ -15,6 +15,7 @@ /mob/living/basic/killer_rabbit/Initialize(mapload) . = ..() + AddComponent(/datum/component/anti_magic, ALL) RegisterSignal(src, COMSIG_HOSTILE_POST_ATTACKINGTARGET, PROC_REF(hostile_attackingtarget)) /mob/living/basic/killer_rabbit/proc/hostile_attackingtarget(mob/living/basic/source, atom/target, success) diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/monster_effects/white_rabbit.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/monster_effects/white_rabbit.dm index 67272787e828..f8b66536f8f2 100644 --- a/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/monster_effects/white_rabbit.dm +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/monster_effects/white_rabbit.dm @@ -40,11 +40,11 @@ /obj/effect/client_image_holder/white_rabbit/proc/spotted(datum/source, mob/user) SIGNAL_HANDLER - new /obj/item/rabbit_eye(loc) + new /obj/item/rabbit_eye(drop_location()) if(hunter?.rabbits_spotted == 0) //our first bunny - new /obj/item/clothing/mask/cursed_rabbit(loc) + new /obj/item/clothing/mask/cursed_rabbit(drop_location()) if(drop_gun) - new /obj/item/gun/ballistic/revolver/hunter_revolver(loc) + new /obj/item/gun/ballistic/revolver/hunter_revolver(drop_location()) var/datum/action/cooldown/spell/conjure_item/blood_silver/silverblood = new(user) silverblood.StartCooldown() silverblood.Grant(user) diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/monster_powers/killer_rabbit_powers.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/monster_powers/killer_rabbit_powers.dm index 9aa24472ac46..cc80878a0dc5 100644 --- a/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/monster_powers/killer_rabbit_powers.dm +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/monster_powers/killer_rabbit_powers.dm @@ -16,7 +16,7 @@ . = ..() StartCooldown(360 SECONDS, 360 SECONDS) for(var/i in 1 to 3) - var/mob/living/basic/killer_rabbit/rabbit = new(owner.loc) + var/mob/living/basic/killer_rabbit/rabbit = new(owner.drop_location()) rabbit.faction = owner.faction.Copy() StartCooldown() diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/red_rabbit.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/red_rabbit.dm index 5064e5604ae8..5451529583a4 100644 --- a/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/red_rabbit.dm +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/monsters/red_rabbit.dm @@ -44,6 +44,7 @@ /mob/living/basic/red_rabbit/Initialize(mapload) . = ..() + AddComponent(/datum/component/anti_magic, ALL) AddElement(/datum/element/footstep, FOOTSTEP_MOB_HEAVY) hole_power = new rabbit_power = new diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/tools/bnuuy_mask.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/tools/bnuuy_mask.dm new file mode 100644 index 000000000000..d97112fab4bd --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/tools/bnuuy_mask.dm @@ -0,0 +1,54 @@ +/obj/item/clothing/mask/cursed_rabbit + name = "Damned Rabbit Mask" + desc = "Slip into the wonderland." + icon = 'monkestation/icons/bloodsuckers/weapons.dmi' + icon_state = "rabbit_mask" + worn_icon = 'monkestation/icons/bloodsuckers/worn_mask.dmi' + worn_icon_state = "rabbit_mask" + clothing_flags = BLOCK_GAS_SMOKE_EFFECT | MASKINTERNALS | GAS_FILTERING | SNUG_FIT + flags_inv = HIDEFACE | HIDEFACIALHAIR | HIDESNOUT + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF + flash_protect = FLASH_PROTECTION_WELDER + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + ///the paradox rabbit ability + var/datum/action/cooldown/paradox/paradox + ///teleporting to the wonderland + var/datum/action/cooldown/wonderland_drop/wonderland + +/obj/item/clothing/mask/cursed_rabbit/Initialize(mapload) + . = ..() + paradox = new + wonderland = new + +/obj/item/clothing/mask/cursed_rabbit/equipped(mob/living/carbon/human/user, slot) + . = ..() + if(!ishuman(user) || !(slot & ITEM_SLOT_MASK) || !IS_MONSTERHUNTER(user)) + return + paradox?.Grant(user) + wonderland?.Grant(user) + user.apply_status_effect(/datum/status_effect/bnuuy_mask) + +/obj/item/clothing/mask/cursed_rabbit/dropped(mob/living/user) + . = ..() + paradox?.Remove(user) + wonderland?.Remove(user) + user.remove_status_effect(/datum/status_effect/bnuuy_mask) + +/datum/status_effect/bnuuy_mask + id = "bnuuy_mask" + alert_type = null + tick_interval = -1 + var/datum/component/glitching_state/wondershift + +/datum/status_effect/bnuuy_mask/on_apply() + . = ..() + if(!ishuman(owner) || !IS_MONSTERHUNTER(owner) || !istype(owner.get_item_by_slot(ITEM_SLOT_MASK), /obj/item/clothing/mask/cursed_rabbit)) + return FALSE + wondershift = owner.AddComponent(/datum/component/glitching_state) + +/datum/status_effect/bnuuy_mask/on_remove() + . = ..() + QDEL_NULL(wondershift) + +/datum/status_effect/bnuuy_mask/get_examine_text() + return span_warning("[owner.p_they(TRUE)] seem[owner.p_s()] out-of-place, as if [owner.p_they()] were partially detached from reality.") diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/tools/jack_bomb.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/tools/jack_bomb.dm new file mode 100644 index 000000000000..e26cbefc9775 --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/tools/jack_bomb.dm @@ -0,0 +1,48 @@ +/obj/item/grenade/jack + name = "jack in the bomb" + desc = "Best kids' toy" + icon = 'monkestation/icons/bloodsuckers/weapons.dmi' + icon_state = "jack_in_the_bomb" + inhand_icon_state = "flashbang" + worn_icon_state = "grenade" + w_class = WEIGHT_CLASS_SMALL + det_time = 12 SECONDS + ex_dev = 1 + ex_heavy = 2 + ex_light = 4 + ex_flame = 2 + +/obj/item/grenade/jack/arm_grenade(mob/user, delayoverride, msg = TRUE, volume = 60) + log_grenade(user) //Inbuilt admin procs already handle null users + if(user) + add_fingerprint(user) + if(msg) + user.balloon_alert(user, "primed [src]!") + to_chat(user, span_warning("You prime [src]! [capitalize(DisplayTimeText(det_time))]!")) + playsound(src, 'monkestation/sound/bloodsuckers/jackinthebomb.ogg', volume, vary = TRUE) + if(istype(user)) + user.add_mob_memory(/datum/memory/bomb_planted, protagonist = user, antagonist = src) + active = TRUE + icon_state = "jack_in_the_bomb_active" + SEND_SIGNAL(src, COMSIG_GRENADE_ARMED, det_time, delayoverride) + addtimer(CALLBACK(src, PROC_REF(detonate)), isnull(delayoverride) ? det_time : delayoverride) + +/obj/item/grenade/jack/detonate(mob/living/lanced_by) + if (dud_flags) + active = FALSE + update_appearance() + return FALSE + dud_flags |= GRENADE_USED // Don't detonate if we have already detonated. + icon_state = "jack_in_the_bomb_live" + addtimer(CALLBACK(src, PROC_REF(exploding)), 1 SECONDS) + +/obj/item/grenade/jack/botch_check(mob/living/carbon/human/user) + if(!IS_MONSTERHUNTER(user)) + to_chat(user, span_danger("You can't begin to comprehend how to properly arm [src], it's as if it were designed by madmen!")) + arm_grenade(user, 0.5 SECONDS, FALSE) + return TRUE + return ..() + +/obj/item/grenade/jack/proc/exploding(mob/living/lanced_by) + SEND_SIGNAL(src, COMSIG_GRENADE_DETONATE, lanced_by) + explosion(src, ex_dev, ex_heavy, ex_light, ex_flame) diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/tools/rabbit_eye.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/tools/rabbit_eye.dm new file mode 100644 index 000000000000..b989debbe21f --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/tools/rabbit_eye.dm @@ -0,0 +1,20 @@ +/obj/item/rabbit_eye + name = "Rabbit Eye" + desc = "An item that resonates with trick weapons." + icon_state = "rabbit_eye" + icon = 'monkestation/icons/bloodsuckers/weapons.dmi' + w_class = WEIGHT_CLASS_SMALL + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/item/rabbit_eye/proc/upgrade(obj/item/melee/trick_weapon/weapon, mob/user) + if(weapon.upgrade_level >= 3) + user.balloon_alert(user, "already fully upgraded!") + return + if(weapon.enabled) + user.balloon_alert(user, "weapon must be in base form!") + return + SEND_SIGNAL(weapon, WEAPON_UPGRADE) + weapon.name = "[weapon.base_name] +[weapon.upgrade_level]" + balloon_alert(user, "[src] crumbles away...") + playsound(src, 'monkestation/sound/bloodsuckers/weaponsmithing.ogg', vol = 50) + qdel(src) diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/tools/rabbit_locator.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/tools/rabbit_locator.dm new file mode 100644 index 000000000000..4c47a010ff2e --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/tools/rabbit_locator.dm @@ -0,0 +1,78 @@ +/obj/item/rabbit_locator + name = "Accursed Red Queen card" + desc = "Hunts down the white rabbits.\nThis does NOT track down monster targets, it tracks down the rabbits you must collect." + icon = 'monkestation/icons/bloodsuckers/weapons.dmi' + icon_state = "locator" + w_class = WEIGHT_CLASS_SMALL + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + ///the hunter the card is tied too + var/datum/antagonist/monsterhunter/hunter + COOLDOWN_DECLARE(locator_timer) + +/obj/item/rabbit_locator/Initialize(mapload, datum/antagonist/monsterhunter/owner) + . = ..() + if(!QDELETED(owner)) + hunter = owner + hunter.locator = src + +/obj/item/rabbit_locator/Destroy() + if(hunter?.locator == src) + hunter.locator = null + return ..() + +/obj/item/rabbit_locator/attack_self(mob/user, modifiers) + if(!COOLDOWN_FINISHED(src, locator_timer)) + return + if(QDELETED(hunter)) + to_chat(user,span_warning("It's just a normal playing card!")) + return + if(hunter.owner.current != user) + to_chat(user,span_warning("It's just a normal playing card!")) + return + var/turf/user_turf = get_turf(user) + if(QDELETED(user_turf) || !is_station_level(user_turf.z)) + to_chat(user, span_warning("The card cannot be used here...")) + return + var/obj/effect/bnuuy = get_nearest_rabbit(user) + if(QDELETED(bnuuy)) + to_chat(user, span_warning("Can't feel any hints...")) + return + var/turf/bnuuy_turf = get_turf(bnuuy) + var/distance = get_dist(user_turf, bnuuy_turf) + var/sound_value + if(distance >= 50) + sound_value = 0 + to_chat(user,span_warning("Too far away...")) + if(distance >= 40 && distance < 50) + sound_value = 20 + to_chat(user,span_warning("You feel the slightest hint...")) + if(distance >=30 && distance < 40) + sound_value = 40 + to_chat(user,span_warning("You feel a mild hint...")) + if(distance >=20 && distance < 30) + sound_value = 60 + to_chat(user,span_warning("You feel a strong hint...")) + if(distance >= 10 && distance < 20) + sound_value = 80 + to_chat(user,span_warning("You feel a VERY strong hint...")) + if(distance < 10) + sound_value = 100 + to_chat(user, span_warning("Here... the white rabbit is definitely here!")) + user.playsound_local(bnuuy_turf, 'monkestation/sound/bloodsuckers/rabbitlocator.ogg', vol = sound_value, pressure_affected = FALSE) + COOLDOWN_START(src, locator_timer, 7 SECONDS) + +/obj/item/rabbit_locator/proc/get_nearest_rabbit(mob/user) + var/dist = 1000 + if(!length(hunter?.rabbits)) + return + var/obj/effect/selected_bunny + for(var/obj/effect/located as anything in hunter.rabbits) + if(get_dist(user, located) < dist) + dist = get_dist(user, located) + selected_bunny = located + if(QDELETED(selected_bunny)) + return + var/z_difference = abs(selected_bunny.z - user.z) + if(dist < 50 && z_difference != 0) + to_chat(user, span_warning("[z_difference] [z_difference == 1 ? "floor" : "floors"] [selected_bunny.z > user.z ? "above" : "below"]...")) + return selected_bunny diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/tools/weaponsmith.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/tools/weaponsmith.dm new file mode 100644 index 000000000000..d19e61eb0224 --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/tools/weaponsmith.dm @@ -0,0 +1,15 @@ +/obj/structure/rack/weaponsmith + name = "Weapon Forge" + desc = "Fueled by the tears of rabbits." + icon = 'icons/obj/cult/structures.dmi' + icon_state = "altar" + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/structure/rack/weaponsmith/attackby(obj/item/rabbit_eye/eye, mob/living/user, params) + if(!istype(eye)) + return ..() + var/obj/item/melee/trick_weapon/tool = locate() in src.loc + if(QDELETED(tool)) + user.balloon_alert(user, "place weapon on table!") + return + eye.upgrade(tool, user) diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/_trick_weapon.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/_trick_weapon.dm new file mode 100644 index 000000000000..35e0a5984a98 --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/_trick_weapon.dm @@ -0,0 +1,31 @@ +/obj/item/melee/trick_weapon + icon = 'monkestation/icons/bloodsuckers/weapons.dmi' + lefthand_file = 'monkestation/icons/bloodsuckers/weapons_lefthand.dmi' + righthand_file = 'monkestation/icons/bloodsuckers/weapons_righthand.dmi' + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + ///upgrade level of the weapon + var/upgrade_level = 0 + ///base force when transformed + var/on_force + ///base force when in default state + var/base_force + ///default name of the weapon + var/base_name + ///is the weapon in its transformed state? + var/enabled = FALSE + ///wounding chance while on + var/on_wound_bonus + +/obj/item/melee/trick_weapon/proc/upgrade_weapon() + SIGNAL_HANDLER + upgrade_level++ + force = upgraded_val(base_force,upgrade_level) + var/datum/component/transforming/transform = GetComponent(/datum/component/transforming) + transform.force_on = upgraded_val(on_force,upgrade_level) + +/obj/item/melee/trick_weapon/attack(mob/target, mob/living/user, params) //our weapon does 25% less damage on non monsters + var/old_force = force + if(!is_monster_hunter_prey(target)) + force = round(force * 0.75) + . = ..() + force = old_force diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/beast_claw.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/beast_claw.dm new file mode 100644 index 000000000000..6a5431524864 --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/beast_claw.dm @@ -0,0 +1,39 @@ +/obj/item/melee/trick_weapon/beast_claw + name = "\improper Beast Claw" + base_name = "\improper Beast Claw" + desc = "The bones seem to still be twitching." + icon_state = "Bone_Claw" + base_icon_state = "Claw" + w_class = WEIGHT_CLASS_SMALL + block_chance = 20 + base_force = 18 + on_force = 23 + throwforce = 10 + wound_bonus = 25 + bare_wound_bonus = 35 + demolition_mod = 1.5 //ripping through doors and windows should be a little easier with a claw shouldnt it? + sharpness = SHARP_EDGED + hitsound = 'sound/weapons/fwoosh.ogg' + damtype = BRUTE //why can i not make things do wounds i want + attack_verb_continuous = list("rips", "claws", "gashes", "tears", "lacerates", "dices", "cuts", "attacks") + attack_verb_simple = list("rip", "claw", "gash", "tear", "lacerate", "dice", "cut", "attack" ) + +/obj/item/melee/trick_weapon/beast_claw/Initialize(mapload) + . = ..() + force = base_force + AddComponent(/datum/component/transforming, \ + force_on = on_force, \ + w_class_on = WEIGHT_CLASS_BULKY) + RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) + RegisterSignal(src,WEAPON_UPGRADE, PROC_REF(upgrade_weapon)) + +/obj/item/melee/trick_weapon/beast_claw/proc/on_transform(obj/item/source, mob/user, active) + SIGNAL_HANDLER + balloon_alert(user, active ? "extended" : "collapsed") + inhand_icon_state = active ? "Claw" : "BoneClaw" + if(active) + playsound(src, 'sound/weapons/fwoosh.ogg', vol = 50) + enabled = active + active = wound_bonus ? 45 : initial(wound_bonus) + force = active ? upgraded_val(on_force, upgrade_level) : upgraded_val(base_force, upgrade_level) + return COMPONENT_NO_DEFAULT_MESSAGE diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/darkmoon.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/darkmoon.dm new file mode 100644 index 000000000000..02d588193801 --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/darkmoon.dm @@ -0,0 +1,86 @@ +/obj/item/melee/trick_weapon/darkmoon + name = "Darkmoon Greatsword" + base_name = "Darkmoon Greatsword" + desc = "Ahh my guiding moonlight, you were by my side all along." + icon_state = "darkmoon" + inhand_icon_state = "darkmoon_hilt" + w_class = WEIGHT_CLASS_SMALL + block_chance = 20 + on_force = 20 + base_force = 17 + light_system = OVERLAY_LIGHT + light_color = "#59b3c9" + light_outer_range = 2 + light_power = 2 + light_on = FALSE + throwforce = 12 + damtype = BURN + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") + attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") + ///ready to launch a beam attack? + COOLDOWN_DECLARE(moonbeam_fire) + +/obj/item/melee/trick_weapon/darkmoon/Initialize(mapload) + . = ..() + force = base_force + AddComponent(/datum/component/transforming, \ + force_on = on_force , \ + throwforce_on = 20, \ + throw_speed_on = throw_speed, \ + sharpness_on = SHARP_EDGED, \ + w_class_on = WEIGHT_CLASS_BULKY) + RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) + RegisterSignal(src, WEAPON_UPGRADE, PROC_REF(upgrade_weapon)) + + +/obj/item/melee/trick_weapon/darkmoon/proc/on_transform(obj/item/source, mob/user, active) + SIGNAL_HANDLER + balloon_alert(user, active ? "extended" : "collapsed") + if(active) + playsound(src, 'monkestation/sound/bloodsuckers/moonlightsword.ogg', vol = 50) + inhand_icon_state = active ? "darkmoon" : "darkmoon_hilt" + enabled = active + set_light_on(active) + force = active ? upgraded_val(on_force, upgrade_level) : upgraded_val(base_force, upgrade_level) + return COMPONENT_NO_DEFAULT_MESSAGE + +/obj/item/melee/trick_weapon/darkmoon/attack_secondary(atom/target, mob/living/user, clickparams) + return SECONDARY_ATTACK_CONTINUE_CHAIN + +/obj/item/melee/trick_weapon/darkmoon/afterattack_secondary(atom/target, mob/living/user, clickparams) + if(!enabled) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + if(!COOLDOWN_FINISHED(src, moonbeam_fire)) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + if(target == user) + balloon_alert(user, "can't aim at yourself!") + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + fire_moonbeam(target, user, clickparams) + user.changeNext_move(CLICK_CD_MELEE) + COOLDOWN_START(src, moonbeam_fire, 4 SECONDS) + return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN + +/obj/item/melee/trick_weapon/darkmoon/proc/fire_moonbeam(atom/target, mob/living/user, clickparams) + var/modifiers = params2list(clickparams) + var/turf/proj_turf = user.loc + if(!isturf(proj_turf)) + return + var/obj/projectile/moonbeam/moon = new(proj_turf) + moon.preparePixelProjectile(target, user, modifiers) + moon.firer = user + playsound(src, 'monkestation/sound/bloodsuckers/moonlightbeam.ogg', vol = 50) + moon.fire() + +/obj/projectile/moonbeam + name = "Moonlight" + icon = 'icons/effects/effects.dmi' + icon_state = "plasmasoul" + damage = 25 + light_system = OVERLAY_LIGHT + light_outer_range = 2 + light_power = 1 + light_color = "#44acb1" + damage_type = BURN + hitsound = 'sound/weapons/sear.ogg' + hitsound_wall = 'sound/weapons/effects/searwall.ogg' diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/hunter_axe.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/hunter_axe.dm new file mode 100644 index 000000000000..34ed8b93d9c9 --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/hunter_axe.dm @@ -0,0 +1,48 @@ +/obj/item/melee/trick_weapon/hunter_axe + name = "Hunter's Axe" + base_name = "Hunter's Axe" + desc = "A brute's tool of choice." + icon_state = "hunteraxe0" + base_icon_state = "hunteraxe" + w_class = WEIGHT_CLASS_SMALL + block_chance = 20 + base_force = 20 + on_force = 25 + throwforce = 12 + reach = 1 + hitsound = 'sound/weapons/bladeslice.ogg' + damtype = BURN + attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") + attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") + +/obj/item/melee/trick_weapon/hunter_axe/Initialize(mapload) + . = ..() + force = base_force + AddComponent(/datum/component/two_handed, \ + force_unwielded = base_force, \ + force_wielded= on_force, \ + icon_wielded = "[base_icon_state]1", \ + wield_callback = CALLBACK(src, PROC_REF(on_wield)), \ + unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \ + ) + RegisterSignal(src, WEAPON_UPGRADE, PROC_REF(upgrade_weapon)) + +/obj/item/melee/trick_weapon/hunter_axe/upgrade_weapon() + upgrade_level++ + var/datum/component/two_handed/handed = GetComponent(/datum/component/two_handed) + handed.force_wielded = upgraded_val(on_force, upgrade_level) + handed.force_unwielded = upgraded_val(base_force, upgrade_level) + force = handed.force_unwielded + +/obj/item/melee/trick_weapon/hunter_axe/update_icon_state() + icon_state = "[base_icon_state]0" + playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', vol = 50) + return ..() + +/obj/item/melee/trick_weapon/hunter_axe/proc/on_wield(obj/item/source) + enabled = TRUE + block_chance = 75 + +/obj/item/melee/trick_weapon/hunter_axe/proc/on_unwield(obj/item/source) + enabled = FALSE + block_chance = 20 diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/hunter_revolver.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/hunter_revolver.dm new file mode 100644 index 000000000000..934062cf62df --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/hunter_revolver.dm @@ -0,0 +1,40 @@ +/obj/item/gun/ballistic/revolver/hunter_revolver + name = "\improper Hunter's Revolver" + desc = "Does minimal damage but slows down the enemy." + icon_state = "revolver" + icon = 'monkestation/icons/bloodsuckers/weapons.dmi' + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/bloodsilver + initial_caliber = CALIBER_BLOODSILVER + +/datum/movespeed_modifier/silver_bullet + movetypes = GROUND + multiplicative_slowdown = 4 + flags = IGNORE_NOSLOW + +/obj/item/ammo_box/magazine/internal/cylinder/bloodsilver + name = "detective revolver cylinder" + ammo_type = /obj/item/ammo_casing/silver + caliber = CALIBER_BLOODSILVER + max_ammo = 2 + +/obj/item/ammo_casing/silver + name = "Bloodsilver casing" + desc = "A Bloodsilver bullet casing." + icon_state = "bloodsilver" + icon = 'monkestation/icons/bloodsuckers/weapons.dmi' + projectile_type = /obj/projectile/bullet/bloodsilver + caliber = CALIBER_BLOODSILVER + +/obj/projectile/bullet/bloodsilver + name = "Bloodsilver bullet" + damage = 3 + ricochets_max = 4 + +/obj/projectile/bullet/bloodsilver/on_hit(mob/living/carbon/target, blocked = 0, pierce_hit) + . = ..() + if(!iscarbon(target) || QDELING(target) || target.has_movespeed_modifier(/datum/movespeed_modifier/silver_bullet) || !is_monster_hunter_prey(target)) + return + target.add_movespeed_modifier(/datum/movespeed_modifier/silver_bullet) + if(!(target.has_movespeed_modifier(/datum/movespeed_modifier/silver_bullet))) + return + addtimer(CALLBACK(target, TYPE_PROC_REF(/mob, remove_movespeed_modifier), /datum/movespeed_modifier/silver_bullet), 8 SECONDS) diff --git a/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/threaded_cane.dm b/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/threaded_cane.dm new file mode 100644 index 000000000000..37109222408f --- /dev/null +++ b/monkestation/code/modules/bloodsuckers/monster_hunters/weapons/threaded_cane.dm @@ -0,0 +1,39 @@ +/obj/item/melee/trick_weapon/threaded_cane + name = "Threaded Cane" + base_name = "Threaded Cane" + desc = "A blind man's whip." + icon_state = "threaded_cane" + inhand_icon_state = "threaded_cane" + w_class = WEIGHT_CLASS_SMALL + block_chance = 20 + on_force = 15 + base_force = 18 + throwforce = 12 + reach = 1 + hitsound = 'sound/weapons/bladeslice.ogg' + damtype = BURN + attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts") + attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut") + +/obj/item/melee/trick_weapon/threaded_cane/Initialize(mapload) + . = ..() + force = base_force + AddComponent(/datum/component/transforming, \ + force_on = on_force, \ + throwforce_on = 10, \ + throw_speed_on = throw_speed, \ + sharpness_on = SHARP_EDGED, \ + w_class_on = WEIGHT_CLASS_BULKY) + RegisterSignal(src, COMSIG_TRANSFORMING_ON_TRANSFORM, PROC_REF(on_transform)) + RegisterSignal(src,WEAPON_UPGRADE, PROC_REF(upgrade_weapon)) + +/obj/item/melee/trick_weapon/threaded_cane/proc/on_transform(obj/item/source, mob/user, active) + SIGNAL_HANDLER + balloon_alert(user, active ? "extended" : "collapsed") + inhand_icon_state = active ? "chain" : "threaded_cane" + if(active) + playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', vol = 50) + reach = active ? 2 : 1 + enabled = active + force = active ? upgraded_val(on_force, upgrade_level) : upgraded_val(base_force, upgrade_level) + return COMPONENT_NO_DEFAULT_MESSAGE diff --git a/monkestation/code/modules/bloodsuckers/powers/feed.dm b/monkestation/code/modules/bloodsuckers/powers/feed.dm index d780f76d02ad..c8fb13fbc6fc 100644 --- a/monkestation/code/modules/bloodsuckers/powers/feed.dm +++ b/monkestation/code/modules/bloodsuckers/powers/feed.dm @@ -113,7 +113,7 @@ continue if(watchers.is_blind() || watchers.is_nearsighted_currently()) continue - if(IS_BLOODSUCKER(watchers) || IS_VASSAL(watchers) || HAS_TRAIT(watchers.mind, TRAIT_BLOODSUCKER_HUNTER)) + if(IS_BLOODSUCKER(watchers) || IS_VASSAL(watchers) || HAS_TRAIT(watchers.mind, TRAIT_OCCULTIST)) continue owner.balloon_alert(owner, "feed noticed!") bloodsuckerdatum_power.give_masquerade_infraction() diff --git a/monkestation/code/modules/bloodsuckers/structures/bloodsucker_crypt.dm b/monkestation/code/modules/bloodsuckers/structures/bloodsucker_crypt.dm index 0bcb6a479938..80d7ba933ee2 100644 --- a/monkestation/code/modules/bloodsuckers/structures/bloodsucker_crypt.dm +++ b/monkestation/code/modules/bloodsuckers/structures/bloodsucker_crypt.dm @@ -499,7 +499,7 @@ return for(var/mob/living/carbon/nearly_people in viewers(7, src)) /// We dont want Bloodsuckers or Vassals affected by this - if(IS_VASSAL(nearly_people) || IS_BLOODSUCKER(nearly_people)) + if(IS_VASSAL(nearly_people) || IS_BLOODSUCKER(nearly_people) || IS_MONSTERHUNTER(nearly_people)) continue nearly_people.adjust_hallucinations(5 SECONDS) nearly_people.add_mood_event("vampcandle", /datum/mood_event/vampcandle) diff --git a/monkestation/code/modules/bloodsuckers/structures/bloodsucker_objects.dm b/monkestation/code/modules/bloodsuckers/structures/bloodsucker_objects.dm index b1df993070f5..e0f079cf9e77 100644 --- a/monkestation/code/modules/bloodsuckers/structures/bloodsucker_objects.dm +++ b/monkestation/code/modules/bloodsuckers/structures/bloodsucker_objects.dm @@ -242,7 +242,7 @@ . = ..() if(!user.can_read(src) || in_use || (target == user) || !ismob(target)) return - if(!HAS_TRAIT(user.mind, TRAIT_BLOODSUCKER_HUNTER)) + if(!HAS_TRAIT(user.mind, TRAIT_OCCULTIST)) if(IS_BLOODSUCKER(user)) to_chat(user, span_notice("[src] seems to be too complicated for you. It would be best to leave this for someone else to take.")) return @@ -269,7 +269,7 @@ to_chat(user, span_notice("You fail to draw any conclusions to [target] being a Bloodsucker.")) /obj/item/book/kindred/attack_self(mob/living/user) - if(user.mind && !HAS_TRAIT(user.mind, TRAIT_BLOODSUCKER_HUNTER)) + if(user.mind && !HAS_TRAIT(user.mind, TRAIT_OCCULTIST)) if(IS_BLOODSUCKER(user)) to_chat(user, span_notice("[src] seems to be too complicated for you. It would be best to leave this for someone else to take.")) else diff --git a/monkestation/code/modules/botany/components/plant_growing.dm b/monkestation/code/modules/botany/components/plant_growing.dm new file mode 100644 index 000000000000..dcaff44ffaee --- /dev/null +++ b/monkestation/code/modules/botany/components/plant_growing.dm @@ -0,0 +1,340 @@ +//this is the source of the old hydrotray behaviours +/datum/component/plant_growing + ///this is our managed seeds + var/list/managed_seeds = list() + ///this is the amount of seeds we can have at once in a tray + var/maximum_seeds = 1 + + ///are we bioboosted + var/bio_boosted = FALSE + + ///our current water precent + var/water_precent = 100 + ///how much water we can have at max used to determine % + var/max_water = 200 + + ///how many processes we need to work + + var/work_processes = 10 SECONDS + ///what work time we are at + var/next_work = 0 + ///what stage are we one + var/work_cycle = 0 + + ///how toxic our tray currently is % wise + var/toxicity_contents = 0 + + ///the icon we apply to our parent if we are self sustaining + var/self_sustaining_overlay + ///the current precentage we are to self sustaining + var/self_sustaining_precent = 0 + ///does self sustaining also increase plant stats slowly + var/self_growing = FALSE + + var/pest_level = 0 + var/weed_level = 0 + + var/pollinated = FALSE + +/datum/component/plant_growing/Initialize(max_reagents = 40, maximum_seeds = 1) + . = ..() + + var/atom/movable/movable_parent = parent + src.maximum_seeds = maximum_seeds + for(var/i = 1 to maximum_seeds) + managed_seeds["[i]"] = null + + ///we create reagents using max_reagents, then make it visible and an open container + movable_parent.create_reagents(max_reagents, (OPENCONTAINER | AMOUNT_VISIBLE)) + + RegisterSignals(parent, list(COMSIG_TRY_PLANT_SEED, COMSIG_ATOM_ATTACKBY), PROC_REF(try_plant_seed)) + RegisterSignal(parent, COMSIG_TRY_POLLINATE, PROC_REF(try_pollinate)) + RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND_SECONDARY, PROC_REF(try_drain)) + + RegisterSignals(parent, list(COMSIG_TRY_HARVEST_SEEDS, COMSIG_ATOM_ATTACK_HAND), PROC_REF(try_harvest)) + RegisterSignals(movable_parent.reagents, list(COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_REM_REAGENT), PROC_REF(on_reagent_change)) + RegisterSignals(movable_parent.reagents, list(COMSIG_REAGENT_CACHE_ADD_ATTEMPT), PROC_REF(on_reagent_cache_pre)) + + RegisterSignal(parent, COMSIG_GROWING_ADJUST_TOXIN, PROC_REF(adjust_toxin)) + RegisterSignal(parent, COMSIG_GROWING_ADJUST_PEST, PROC_REF(adjust_pests)) + RegisterSignal(parent, COMSIG_GROWING_ADJUST_WEED, PROC_REF(adjust_weeds)) + RegisterSignal(parent, COMSIG_GROWER_ADJUST_SELFGROW, PROC_REF(adjust_selfgrow)) + RegisterSignal(parent, COMSIG_GROWER_INCREASE_WORK_PROCESSES, PROC_REF(increase_work_processes)) + RegisterSignal(parent, COMSIG_REMOVE_PLANT, PROC_REF(remove_plant)) + RegisterSignal(parent, COMSIG_GROWER_CHECK_POLLINATED, PROC_REF(check_pollinated)) + RegisterSignal(parent, COMSIG_ATTEMPT_BIOBOOST, PROC_REF(try_bioboost)) + RegisterSignal(parent, COMSIG_PLANTER_REMOVE_PLANTS, PROC_REF(remove_all_plants)) + RegisterSignal(parent, COMSIG_TOGGLE_BIOBOOST, PROC_REF(toggle_bioboost)) + RegisterSignal(movable_parent.reagents, COMSIG_REAGENT_PRE_TRANS_TO, PROC_REF(pre_trans)) + RegisterSignal(parent, COMSIG_GROWING_TRY_SECATEUR, PROC_REF(try_secateur)) + RegisterSignal(parent, COMSIG_GROWER_TRY_GRAFT, PROC_REF(try_graft)) + + RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) + + START_PROCESSING(SSplants, src) + SEND_SIGNAL(parent, COMSIG_GROWING_WATER_UPDATE, water_precent) + +/datum/component/plant_growing/process(seconds_per_tick) + if(!length(managed_seeds)) + return + var/atom/movable/movable_parent = parent + movable_parent.update_appearance() + if((world.time < next_work) && !bio_boosted) + return + next_work = world.time + work_processes + work_cycle++ + + for(var/datum/reagent/reagent as anything in movable_parent.reagents.reagent_list) + reagent.on_plant_grower_apply(parent) + + for(var/item as anything in managed_seeds) + var/obj/item/seeds/seed = managed_seeds[item] + if(!seed) + continue + + if(seed.get_gene(/datum/plant_gene/trait/glow)) + var/datum/plant_gene/trait/glow/G = seed.get_gene(/datum/plant_gene/trait/glow) + movable_parent.set_light(l_outer_range = G.glow_range(seed), l_power = G.glow_power(seed), l_color = G.glow_color) + + if(!bio_boosted) + if(work_cycle >= 2) + adjust_water(-rand(1, 6)) + if(water_precent <= 0 || weed_level >= 10) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -rand(0, 2)) + continue + if(movable_parent.reagents.total_volume <= 5) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -rand(0, 2)) + + if(pollinated) + seed.adjust_potency(rand(1,2)) + seed.adjust_yield(rand(1,2)) + seed.adjust_endurance(rand(1,2)) + seed.adjust_lifespan(rand(1,2)) + + if(water_precent >= 10) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, rand(1, 2)) + SEND_SIGNAL(seed, COMSIG_PLANT_GROWTH_PROCESS, movable_parent.reagents, bio_boosted) + if((self_sustaining_precent >= 100) || bio_boosted) + continue + + if(work_cycle >= 2 && !bio_boosted) + if(prob(seed.weed_chance)) + SEND_SIGNAL(seed, COMSIG_GROWING_ADJUST_WEED, seed.weed_rate) + + if(work_cycle >= 2) + work_cycle = 0 + + if(movable_parent.reagents.total_volume > 5) + if(bio_boosted) + movable_parent.reagents.remove_any(max(1,round(movable_parent.reagents.total_volume * 0.01, CHEMICAL_QUANTISATION_LEVEL))) + else + movable_parent.reagents.remove_any(max(1,round(movable_parent.reagents.total_volume * 0.025, CHEMICAL_QUANTISATION_LEVEL))) + + SEND_SIGNAL(movable_parent, COMSIG_NUTRIENT_UPDATE, movable_parent.reagents.total_volume / movable_parent.reagents.maximum_volume) + + +/datum/component/plant_growing/proc/try_plant_seed(datum/source, obj/item/seeds/seed, mob/living/user) + SIGNAL_HANDLER + if(istype(seed, /obj/item/storage/bag/plants)) + for(var/id as anything in managed_seeds) + var/obj/item/seeds/harvest = managed_seeds[id] + if(!harvest) + continue + SEND_SIGNAL(harvest, COMSIG_PLANT_TRY_HARVEST, user) + + for(var/obj/item/food/grown/G in locate(user.x,user.y,user.z)) + seed.atom_storage?.attempt_insert(G, user, TRUE) + return COMPONENT_NO_AFTERATTACK + + var/atom/movable/movable_parent = parent + if(!istype(seed)) + return FALSE + + var/slot_number = 0 + var/free_slot = FALSE + for(var/item as anything in managed_seeds) + slot_number++ + if(isnull(managed_seeds[item])) + free_slot = TRUE + break + + if(!free_slot) + return FALSE + + managed_seeds["[slot_number]"] = seed + seed.forceMove(parent) + if(seed.GetComponent(/datum/component/growth_information)) + SEND_SIGNAL(seed, COMSIG_PLANT_BUILD_IMAGE) + SEND_SIGNAL(seed, COMSIG_PLANT_CHANGE_PLANTER, parent, "[slot_number]") + return COMPONENT_NO_AFTERATTACK + + seed.AddComponent(/datum/component/growth_information, parent, "[slot_number]") + SEND_SIGNAL(seed, COMSIG_PLANT_BUILD_IMAGE) + movable_parent.update_appearance() + return COMPONENT_NO_AFTERATTACK + +/datum/component/plant_growing/proc/try_harvest(datum/source, mob/living/user) + if(!length(managed_seeds)) + return + + for(var/item as anything in managed_seeds) + var/obj/item/seeds/seed = managed_seeds[item] + if(!seed) + continue + SEND_SIGNAL(seed, COMSIG_PLANT_TRY_HARVEST, user) + + +/datum/component/plant_growing/proc/try_pollinate(datum/source) + if(!length(managed_seeds)) + return + + pollinated = TRUE + var/set_time = rand(600, 900) + for(var/item as anything in managed_seeds) + var/obj/item/seeds/seed = managed_seeds[item] + if(!seed) + continue + SEND_SIGNAL(seed, COMSIG_PLANT_TRY_POLLINATE, parent, set_time) + + addtimer(VARSET_CALLBACK(src, pollinated, FALSE), set_time) + +///here we just remove any water added and increase the water precent, add other things you want done once. +/datum/component/plant_growing/proc/on_reagent_cache_pre(datum/reagents/holder, datum/reagent/reagent, datum/reagents/coming_from, amount) + ///restocks water + var/atom/movable/movable_parent = parent + if(reagent.type == /datum/reagent/water) + var/water_pre_precent = max_water / 100 + var/water_needed = round(water_pre_precent * (100 - water_precent)) + var/water_volume = min(reagent.volume, amount) + + + var/water_transfer = min(water_volume, water_needed) + adjust_water(water_transfer) + var/image/splash_animation = image('icons/effects/effects.dmi', movable_parent, "splash_hydroponics") + splash_animation.color = mix_color_from_reagents(coming_from.reagent_list) + splash_animation.layer += 5 + flick_overlay_global(splash_animation, GLOB.clients, 1.1 SECONDS) + playsound(movable_parent, 'sound/effects/slosh.ogg', 25, TRUE) + coming_from.remove_reagent(/datum/reagent/water, water_transfer) + return TRUE + +/datum/component/plant_growing/proc/pre_trans(datum/reagents/main, datum/reagents/incoming) + var/atom/movable/movable_parent = parent + var/image/splash_animation = image('icons/effects/effects.dmi', movable_parent, "splash_hydroponics") + splash_animation.color = mix_color_from_reagents(incoming.reagent_list) + splash_animation.layer += 5 + flick_overlay_global(splash_animation, GLOB.clients, 1.1 SECONDS) + playsound(movable_parent, 'sound/effects/slosh.ogg', 25, TRUE) + +/datum/component/plant_growing/proc/on_reagent_change(datum/reagents/holder, ...) + SEND_SIGNAL(parent, COMSIG_NUTRIENT_UPDATE, holder.total_volume / holder.maximum_volume) + +/datum/component/plant_growing/proc/adjust_water(amount) + var/water_precent_filled = (amount / max_water) * 100 + water_precent = clamp(water_precent + water_precent_filled, 0, 100) + SEND_SIGNAL(parent, COMSIG_GROWING_WATER_UPDATE, water_precent) + +/datum/component/plant_growing/proc/adjust_toxin(datum/source, amount) + toxicity_contents = max(0, toxicity_contents + amount) + SEND_SIGNAL(parent, COMSIG_TOXICITY_UPDATE, toxicity_contents) + +/datum/component/plant_growing/proc/adjust_pests(datum/source, amount) + pest_level = clamp(pest_level + amount, 0, 10) + SEND_SIGNAL(parent, COMSIG_PEST_UPDATE, pest_level) + +/datum/component/plant_growing/proc/adjust_weeds(datum/source, amount) + weed_level = clamp(weed_level + amount, 0, 10) + SEND_SIGNAL(parent, COMSIG_WEEDS_UPDATE, weed_level) + return TRUE + +/datum/component/plant_growing/proc/adjust_selfgrow(datum/source, amount) + self_sustaining_precent = clamp(self_sustaining_precent + amount, 0, 10) + +/datum/component/plant_growing/proc/increase_work_processes(datum/source, amount) + next_work -= amount + +/datum/component/plant_growing/proc/on_examine(atom/A, mob/user, list/examine_list) + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + + examine_list += span_info("Water: [water_precent]%") + examine_list += span_info("Nutrients: [movable_parent.reagents.total_volume] units") + + if(bio_boosted) + examine_list += span_boldnotice("It's currently being bio boosted, plants will grow incredibly quickly.") + + if(self_sustaining_precent >= 100) + examine_list += span_info("The tray's self-sustenance is active, protecting it from species mutations, weeds, and pests.") + if(self_growing) + examine_list += span_info("The tray's self sustaining growth dampeners are off.") + if(weed_level >= 5) + examine_list += span_warning("It's filled with weeds!") + if(pest_level >= 5) + examine_list += span_warning("It's filled with tiny worms!") + +/datum/component/plant_growing/proc/remove_plant(datum/source, id) + var/obj/item/seeds/seed = managed_seeds[id] + managed_seeds[id] = null + qdel(seed) + SEND_SIGNAL(parent, REMOVE_PLANT_VISUALS, id) + +/datum/component/plant_growing/proc/check_pollinated(datum/source) + return pollinated + +/datum/component/plant_growing/proc/try_bioboost(datum/source, duration) + if(bio_boosted) + return FALSE + bio_boosted = TRUE + addtimer(VARSET_CALLBACK(src, bio_boosted, FALSE), duration) + return TRUE + +/datum/component/plant_growing/proc/remove_all_plants(datum/source) + for(var/item as anything in managed_seeds) + var/obj/item/seeds/seed = managed_seeds[item] + managed_seeds[item] = null + qdel(seed) + SEND_SIGNAL(parent, REMOVE_PLANT_VISUALS, item) + +/datum/component/plant_growing/proc/toggle_bioboost(datum/source) + bio_boosted = !bio_boosted + +/datum/component/plant_growing/proc/try_secateur(datum/source, mob/user) + for(var/item as anything in managed_seeds) + var/obj/item/seeds/seed = managed_seeds[item] + if(!seed) + continue + SEND_SIGNAL(seed, COMSIG_PLANT_TRY_SECATEUR, user) + return TRUE + +/datum/component/plant_growing/proc/try_graft(datum/source, mob/user, obj/item/graft/snip) + for(var/item as anything in managed_seeds) + var/obj/item/seeds/seed = managed_seeds[item] + if(!seed) + continue + if(seed.apply_graft(snip)) + to_chat(user, span_notice("You carefully integrate the grafted plant limb onto [seed.plantname], granting it [snip.stored_trait.get_name()].")) + else + to_chat(user, span_notice("You integrate the grafted plant limb onto [seed.plantname], but it does not accept the [snip.stored_trait.get_name()] trait from the [snip].")) + qdel(snip) + return TRUE + +/datum/component/plant_growing/proc/try_drain(datum/source, mob/user) + INVOKE_ASYNC(src, PROC_REF(start_drain), user) + +/datum/component/plant_growing/proc/start_drain(mob/user) + var/atom/movable/movable = parent + if(movable.reagents.total_volume) + to_chat(user, span_notice("You begin to dump out the tray's nutrient mix.")) + if(do_after(user, 4 SECONDS, target = movable)) + playsound(user.loc, 'sound/effects/slosh.ogg', 50, TRUE, -1) + //dump everything on the floor + var/turf/user_loc = user.loc + if(istype(user_loc, /turf/open)) + user_loc.add_liquid_from_reagents(movable.reagents) + else + user_loc = get_step_towards(user_loc, movable) + user_loc.add_liquid_from_reagents(movable.reagents) + movable.reagents.remove_all(movable.reagents.total_volume) + else + to_chat(user, span_warning("The tray's nutrient mix is already empty!")) + diff --git a/monkestation/code/modules/botany/components/plant_growth_holder.dm b/monkestation/code/modules/botany/components/plant_growth_holder.dm new file mode 100644 index 000000000000..e06b1b011e3c --- /dev/null +++ b/monkestation/code/modules/botany/components/plant_growth_holder.dm @@ -0,0 +1,184 @@ +/datum/component/growth_information + ///how much we have grown % wise + var/growth_precent = 0 + ///our age + var/age = 0 + ///how many growth cycles we have gone through + var/growth_cycle = 0 + ///can we be harvested multiple times? + var/repeated_harvest = FALSE + ///has a bee visited us recently + var/pollinated = FALSE + ///our current health value + var/health_value + ///our modifier to yield + var/yield_modifier = 1 + ///the mutable appearance we have created + var/mutable_appearance/current_looks + ///our current planter host + var/atom/movable/planter + ///our current plant state + var/plant_state + ///how much lifespan is lost to repeated harvest + var/repeated_harvest_value = 0 + var/planter_id + +/datum/component/growth_information/Initialize(planter, id) + . = ..() + src.planter = planter + planter_id = id + + RegisterSignal(parent, COMSIG_PLANT_CHANGE_PLANTER, PROC_REF(change_planter)) + RegisterSignal(parent, COMSIG_PLANT_GROWTH_PROCESS, PROC_REF(process_growth)) + RegisterSignal(parent, COMSIG_PLANT_BUILD_IMAGE, PROC_REF(update_plant_visuals)) + RegisterSignal(parent, COMSIG_ADJUST_PLANT_HEALTH, PROC_REF(adjust_health)) + RegisterSignal(parent, COMSIG_PLANT_TRY_HARVEST, PROC_REF(try_harvest)) + RegisterSignal(parent, COMSIG_PLANT_TRY_SECATEUR, PROC_REF(try_secateur)) + RegisterSignal(parent, COMSIG_PLANT_TRY_POLLINATE, PROC_REF(try_pollinate)) + + var/obj/item/seeds/seed = parent + if(seed.get_gene(/datum/plant_gene/trait/repeated_harvest)) + repeated_harvest = TRUE + + health_value = seed.endurance * 3 + +/datum/component/growth_information/proc/update_plant_visuals(datum/source) + var/obj/item/seeds/seed = parent + update_growth_information() + var/t_growthstate = clamp(round(((growth_cycle / (seed.harvest_age * (1.01 ** -seed.maturation))) * 10) * seed.growthstages, 1),1, seed.growthstages) + + if(!current_looks) + current_looks = mutable_appearance(seed.growing_icon, "[seed.icon_grow][t_growthstate]", offset_spokesman = planter) + + current_looks.icon_state = "[seed.icon_grow][t_growthstate]" + + if(pollinated) + planter.particles = new /particles/pollen + else + planter.particles = null + + if((plant_state == HYDROTRAY_PLANT_HARVESTABLE) && seed.icon_harvest) + current_looks.icon_state = seed.icon_harvest + + if(plant_state == HYDROTRAY_PLANT_DEAD) + current_looks.icon_state = seed.icon_dead + SEND_SIGNAL(planter, COMSIG_PLANT_SENDING_IMAGE, current_looks, 0, seed.plant_icon_offset, planter_id) + +/datum/component/growth_information/proc/process_growth(datum/source, datum/reagents/planter_reagents, bio_boosted) + var/obj/item/seeds/seed = parent + growth_cycle++ + var/growth_mult = (1.01 ** -seed.maturation) + + //Checks if a self sustaining tray is fully grown and fully "functional" (corpse flowers require a specific age to produce miasma) + if(!(age > max(seed.maturation, seed.production) && (growth_cycle >= seed.harvest_age * growth_mult))) + age++ + + if(age > (seed.lifespan + repeated_harvest_value) && !bio_boosted) + adjust_health(src, -rand(1, 5)) + + for(var/datum/reagent/reagent as anything in planter_reagents.reagent_list) + reagent.on_plant_apply(parent) + + update_plant_visuals() + +/datum/component/growth_information/proc/update_growth_information() + var/obj/item/seeds/seed = parent + var/growth_mult = (1.01 ** -seed.maturation) + var/growth_cycles_needed = round(seed.harvest_age * growth_mult) + if(growth_cycles_needed == 0) + growth_precent = 100 + else + growth_precent = round((growth_cycle / growth_cycles_needed) * 100) + + plant_state = HYDROTRAY_PLANT_GROWING + + if(growth_precent >= 100) + plant_state = HYDROTRAY_PLANT_HARVESTABLE + SEND_SIGNAL(planter, COMSIG_GROWER_SET_HARVESTABLE, TRUE) + + if(health_value <= 0) + plant_state = HYDROTRAY_PLANT_DEAD + SEND_SIGNAL(planter, COMSIG_GROWER_SET_HARVESTABLE, FALSE) + +/datum/component/growth_information/proc/change_planter(datum/source, atom/movable/new_planter, id) + planter = new_planter + planter_id = id + +/datum/component/growth_information/proc/adjust_health(datum/source, amount) + if(plant_state == HYDROTRAY_PLANT_DEAD) + update_and_send_health_color() + return + var/obj/item/seeds/seed = parent + health_value = clamp(health_value + amount, 0, seed.endurance * 3) + + update_and_send_health_color() + +/datum/component/growth_information/proc/update_and_send_health_color() + var/obj/item/seeds/seed = parent + var/health_color + if(health_value < (seed.endurance * 0.9)) + health_color = "#FF3300" + else if(health_value < (seed.endurance * 1.5)) + health_color = "#FFFF00" + else if(health_value < (seed.endurance * 2.1)) + health_color = "#99FF66" + else + health_color = "#66FFFA" + + SEND_SIGNAL(planter, COMSIG_PLANT_UPDATE_HEALTH_COLOR, health_color) + +/datum/component/growth_information/proc/try_harvest(datum/source, mob/user) + if(plant_state == HYDROTRAY_PLANT_DEAD) + var/obj/item/seeds/seed = parent + var/atom/movable/to_send = planter + qdel(current_looks) + SEND_SIGNAL(planter, COMSIG_PLANT_SENDING_IMAGE, current_looks, 0, seed.plant_icon_offset, planter_id) + SEND_SIGNAL(planter, COMSIG_GROWER_SET_HARVESTABLE, FALSE) + planter = null + SEND_SIGNAL(to_send, COMSIG_REMOVE_PLANT, planter_id) + planter_id = null + return + + if(plant_state != HYDROTRAY_PLANT_HARVESTABLE) + return + + var/obj/item/seeds/seed = parent + + if(seed.get_gene(/datum/plant_gene/trait/repeated_harvest)) + repeated_harvest = TRUE + + seed.harvest(user) + if(repeated_harvest) + growth_cycle = 0 + repeated_harvest_value += (seed.lifespan * 0.1) //20% of lifespan is added to the value so that it won't start dying right away + update_plant_visuals() + SEND_SIGNAL(planter, COMSIG_GROWER_SET_HARVESTABLE, FALSE) + return + var/atom/movable/to_send = planter + qdel(current_looks) + SEND_SIGNAL(planter, COMSIG_PLANT_SENDING_IMAGE, current_looks, 0, seed.plant_icon_offset, planter_id) + SEND_SIGNAL(planter, COMSIG_GROWER_SET_HARVESTABLE, FALSE) + planter = null + SEND_SIGNAL(to_send, COMSIG_REMOVE_PLANT, planter_id) + planter_id = null + +/datum/component/growth_information/proc/try_secateur(datum/source, mob/user) + if(plant_state != HYDROTRAY_PLANT_HARVESTABLE) + return + var/obj/item/seeds/seed = parent + if(seed.grafted) + return + + user.visible_message(span_notice("[user] grafts off a limb from [seed.plantname]."), span_notice("You carefully graft off a portion of [seed.plantname].")) + var/obj/item/graft/snip = seed.create_graft() + + if(!snip) + return // The plant did not return a graft. + + snip.forceMove(planter.drop_location()) + seed.grafted = TRUE + adjust_health(src, -5) + +/datum/component/growth_information/proc/try_pollinate(datum/source, atom/movable/planter, time) + pollinated = TRUE + addtimer(VARSET_CALLBACK(src, pollinated, FALSE), time) diff --git a/monkestation/code/modules/botany/components/plant_tray_overlays.dm b/monkestation/code/modules/botany/components/plant_tray_overlays.dm new file mode 100644 index 000000000000..338a6d0ee9bd --- /dev/null +++ b/monkestation/code/modules/botany/components/plant_tray_overlays.dm @@ -0,0 +1,172 @@ +/datum/component/plant_tray_overlay + ///our self growing state + var/self_growing_state + ///our water state base + var/base_water_state + ///our current water state + var/current_water_state + ///the file we check for our overlays + var/overlay_icon + + ///pest overlay + var/pest_overlay + ///harvest state + var/harvest_overlay + ///our nutrient light + var/nutrient_overlay + + ///health overlay + var/health_overlay + ///health color + var/health_color + + ///the visuals we have stored from the seed + var/list/plant_visual_list = list() + var/base_offset_x = 0 + var/base_offset_y = 0 + + var/overlay_flags = NONE + + var/list/offsets = list() + +/datum/component/plant_tray_overlay/Initialize(overlay_icon, self_growing_state, base_water_state, pest_overlay, harvest_overlay, nutriment_overlay, health_overlay, plant_x, plant_y, maximum_seeds = 1, offsets = list(list(0,0))) + . = ..() + src.overlay_icon = overlay_icon + src.self_growing_state = self_growing_state + src.base_water_state = base_water_state + src.pest_overlay = pest_overlay + src.harvest_overlay = harvest_overlay + src.nutrient_overlay = nutrient_overlay + src.health_overlay = health_overlay + + base_offset_x = plant_x + base_offset_y = plant_y + + for(var/i = 1 to maximum_seeds) + plant_visual_list["[i]"] = null + + src.offsets = offsets +/datum/component/plant_tray_overlay/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_GROWING_WATER_UPDATE, PROC_REF(get_water_state)) + RegisterSignal(parent, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(apply_overlays)) + RegisterSignal(parent, COMSIG_PLANT_SENDING_IMAGE, PROC_REF(update_plant)) + RegisterSignal(parent, COMSIG_PLANT_UPDATE_HEALTH_COLOR, PROC_REF(update_health_color)) + RegisterSignal(parent, COMSIG_WEEDS_UPDATE, PROC_REF(weed_update)) + RegisterSignal(parent, COMSIG_PEST_UPDATE, PROC_REF(pest_update)) + RegisterSignal(parent, COMSIG_TOXICITY_UPDATE, PROC_REF(toxic_update)) + RegisterSignal(parent, COMSIG_NUTRIENT_UPDATE, PROC_REF(nutrient_update)) + RegisterSignal(parent, COMSIG_GROWER_SET_HARVESTABLE, PROC_REF(update_harvestable)) + RegisterSignal(parent, REMOVE_PLANT_VISUALS, PROC_REF(remove_plant_visuals)) + + +/datum/component/plant_tray_overlay/proc/get_water_state(datum/source, precent) + SIGNAL_HANDLER + + if(!base_water_state) + return + switch(precent) + if(0 to 20) + current_water_state = "[base_water_state]5" + if(21 to 40) + current_water_state = "[base_water_state]4" + if(40 to 60) + current_water_state = "[base_water_state]3" + if(61 to 80) + current_water_state = "[base_water_state]2" + if(81 to 100) + current_water_state = "[base_water_state]1" + overlay_flags |= SHOW_WATER + +/datum/component/plant_tray_overlay/proc/update_plant(datum/source, mutable_appearance/plant, x = 0, y = 0, id) + var/atom/movable/movable = parent + var/mutable_appearance/visuals = plant_visual_list[id] + if(visuals) + plant_visual_list[id] = null + qdel(visuals) + if(!plant) + movable.update_overlays() + return + + visuals = new(plant) + + var/list/current_offsets = offsets[text2num(id)] + + visuals.layer = ABOVE_MOB_LAYER + SET_PLANE_EXPLICIT(visuals, GAME_PLANE_FOV_HIDDEN, movable) + if(current_offsets[2] > 0) + visuals.layer -= 0.01 + + var/plant_offset_x = x + base_offset_x + current_offsets[1] + var/plant_offset_y = y + base_offset_y + current_offsets[2] + + visuals.pixel_x = plant_offset_x + visuals.pixel_y = plant_offset_y + + plant_visual_list[id] = visuals + +/datum/component/plant_tray_overlay/proc/apply_overlays(atom/source, list/overlays) + SIGNAL_HANDLER + if(!overlay_icon) + return + + if(overlay_flags & SHOW_WATER) + overlays += mutable_appearance(overlay_icon, current_water_state, offset_spokesman = parent) + + if((overlay_flags & SHOW_PEST) || (overlay_flags & SHOW_TOXIC) || (overlay_flags & SHOW_WEED)) + overlays += mutable_appearance(overlay_icon, pest_overlay, offset_spokesman = parent) + + if(overlay_flags & SHOW_NUTRIENT) + overlays += mutable_appearance(overlay_icon, nutrient_overlay, offset_spokesman = parent) + + if(overlay_flags & SHOW_HEALTH) + var/mutable_appearance/health = mutable_appearance(overlay_icon, health_overlay, offset_spokesman = parent) + health.color = health_color + overlays += health + + if(overlay_flags & SHOW_HARVEST) + overlays += mutable_appearance(overlay_icon, harvest_overlay, offset_spokesman = parent) + + for(var/item in plant_visual_list) + if(!isnull(plant_visual_list[item])) + overlays += plant_visual_list[item] + +/datum/component/plant_tray_overlay/proc/update_health_color(datum/source, color) + health_color = color + if(health_overlay) + overlay_flags |= SHOW_HEALTH + +/datum/component/plant_tray_overlay/proc/weed_update(datum/source, amount) + if(pest_overlay && amount >= 6) + overlay_flags |= SHOW_WEED + else + overlay_flags &= ~SHOW_WEED + +/datum/component/plant_tray_overlay/proc/pest_update(datum/source, amount) + if(pest_overlay && amount >= 6) + overlay_flags |= SHOW_PEST + else + overlay_flags &= ~SHOW_PEST + +/datum/component/plant_tray_overlay/proc/toxic_update(datum/source, amount) + if(pest_overlay && amount >= 40) + overlay_flags |= SHOW_TOXIC + else + overlay_flags &= ~SHOW_TOXIC + +/datum/component/plant_tray_overlay/proc/nutrient_update(datum/source, precent) + if(nutrient_overlay && precent < 0.35) + overlay_flags |= SHOW_NUTRIENT + else + overlay_flags &= ~SHOW_NUTRIENT + +/datum/component/plant_tray_overlay/proc/update_harvestable(datum/soruce, harvestable) + if(harvest_overlay && harvestable) + overlay_flags |= SHOW_HARVEST + else + overlay_flags &= ~SHOW_HARVEST + +/datum/component/plant_tray_overlay/proc/remove_plant_visuals(datum/source, id) + plant_visual_list[id] = null + var/atom/movable/movable = parent + movable.update_appearance() diff --git a/monkestation/code/modules/botany/hydrotray.dm b/monkestation/code/modules/botany/hydrotray.dm new file mode 100644 index 000000000000..4d5ce0ce3bec --- /dev/null +++ b/monkestation/code/modules/botany/hydrotray.dm @@ -0,0 +1,61 @@ +/obj/machinery/growing + name = "hydroponics tray" + icon = 'monkestation/icons/obj/machines/hydroponics.dmi' + icon_state = "hydrotray" + density = TRUE + pass_flags_self = PASSMACHINE | LETPASSTHROW + pixel_z = 8 + obj_flags = CAN_BE_HIT | UNIQUE_RENAME + var/maximum_seeds = 1 + +/obj/machinery/growing/Initialize(mapload) + . = ..() + AddComponent(/datum/component/plant_growing, 60, maximum_seeds) + + +/obj/machinery/growing/tray + circuit = /obj/item/circuitboard/machine/hydroponics + +/obj/machinery/growing/tray/Initialize(mapload) + AddComponent(/datum/component/plant_tray_overlay, icon, "hydrotray_gaia", "hydrotray_water_", "hydrotray_pests", "hydrotray_harvest", "hydrotray_nutriment", "hydrotray_health", 0, 0) + . = ..() + +/obj/machinery/growing/tray/attackby(obj/item/I, mob/living/user, params) + if (!(user.istate & ISTATE_HARM)) + // handle opening the panel + if(default_deconstruction_screwdriver(user, icon_state, icon_state, I)) + return + if(default_deconstruction_crowbar(I)) + return + + return ..() + +/obj/machinery/growing/tray/can_be_unfasten_wrench(mob/user, silent) + return ..() + +/obj/machinery/growing/tray/wrench_act(mob/living/user, obj/item/tool) + . = ..() + default_unfasten_wrench(user, tool) + return TOOL_ACT_TOOLTYPE_SUCCESS + +/obj/machinery/growing/soil + name = "soil" + desc = "A patch of dirt." + icon = 'icons/obj/hydroponics/equipment.dmi' + icon_state = "soil" + density = FALSE + +/obj/machinery/growing/soil/Initialize(mapload) + AddComponent(/datum/component/plant_tray_overlay, icon, null, null, null, null, null, null, 0, 0) + . = ..() + +/obj/machinery/growing/multi + name = "soil" + icon = 'icons/obj/hydroponics/equipment.dmi' + icon_state = "soil" + maximum_seeds = 4 + density = FALSE + +/obj/machinery/growing/multi/Initialize(mapload) + AddComponent(/datum/component/plant_tray_overlay, icon, null, null, null, null, null, null, 0, 0, maximum_seeds, list(list(-4,-4), list(8,-4), list(-4, 4), list(8, 4))) + . = ..() diff --git a/monkestation/code/modules/botany/icons/apid_sprites.dmi b/monkestation/code/modules/botany/icons/apid_sprites.dmi new file mode 100644 index 000000000000..0212f1003029 Binary files /dev/null and b/monkestation/code/modules/botany/icons/apid_sprites.dmi differ diff --git a/monkestation/code/modules/botany/icons/pollen.dmi b/monkestation/code/modules/botany/icons/pollen.dmi new file mode 100644 index 000000000000..559c4d1846f6 Binary files /dev/null and b/monkestation/code/modules/botany/icons/pollen.dmi differ diff --git a/monkestation/code/modules/botany/icons/potty.dmi b/monkestation/code/modules/botany/icons/potty.dmi new file mode 100644 index 000000000000..db90a0f9298f Binary files /dev/null and b/monkestation/code/modules/botany/icons/potty.dmi differ diff --git a/monkestation/code/modules/botany/new_seeds/mutations.dm b/monkestation/code/modules/botany/new_seeds/mutations.dm new file mode 100644 index 000000000000..11f700bdc0b0 --- /dev/null +++ b/monkestation/code/modules/botany/new_seeds/mutations.dm @@ -0,0 +1,18 @@ +/datum/hydroponics/plant_mutation/paper + mutates_from = list(/obj/item/seeds/tree) + required_endurance = list(35, INFINITY) + created_product = /obj/item/paper + created_seed = /obj/item/seeds/tree/paper + + +/datum/hydroponics/plant_mutation/money + mutates_from = list(/obj/item/seeds/tree/paper) + required_potency = list(30, INFINITY) + created_product = /obj/item/stack/spacecash/c10 + created_seed = /obj/item/seeds/tree/money + +/datum/hydroponics/plant_mutation/steel + mutates_from = list(/obj/item/seeds/tree) + required_lifespan = list(150, INFINITY) + created_product = /obj/item/grown/log/steel + created_seed = /obj/item/seeds/tree/steel diff --git a/monkestation/code/modules/botany/new_seeds/seeds.dm b/monkestation/code/modules/botany/new_seeds/seeds.dm new file mode 100644 index 000000000000..e1fb666b909e --- /dev/null +++ b/monkestation/code/modules/botany/new_seeds/seeds.dm @@ -0,0 +1,71 @@ +/obj/item/seeds/tree + name = "pack of tree seeds" + desc = "These seeds grow into a tree." + plant_icon_offset = 0 + icon = 'monkestation/icons/obj/hydroponics/fruit.dmi' + icon_state = "coconut_seed" //CHANGE THIS + + species = "tree" + plantname = "Tree" + + lifespan = 80 + endurance = 50 + maturation = 15 + production = 5 + yield = 5 + + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_crop.dmi' + growthstages = 3 + icon_harvest = "Tree-G4" + icon_dead = "Tree-G0" + icon_grow = "Tree-G" + + product = /obj/item/grown/log + possible_mutations = list(/datum/hydroponics/plant_mutation/paper, /datum/hydroponics/plant_mutation/steel) + + +/obj/item/seeds/tree/paper + name = "pack of paper tree seeds" + desc = "These seeds grow into a paper tree." + + species = "papertree" + plantname = "Paper Tree" + + icon_harvest = "TreePaper-G4" + icon_dead = "TreePaper-G0" + icon_grow = "TreePaper-G" + + product = /obj/item/paper + possible_mutations = list(/datum/hydroponics/plant_mutation/money) + genes = list(/datum/plant_gene/trait/repeated_harvest) + +/obj/item/seeds/tree/money + name = "pack of money tree seeds" + desc = "These seeds grow into a money tree." + + species = "cashtree" + plantname = "Cash Tree" + + icon_harvest = "TreeCash-G4" + icon_dead = "TreeCash-G0" + icon_grow = "TreeCash-G" + + possible_mutations = list() + genes = list(/datum/plant_gene/trait/repeated_harvest) + product = /obj/item/stack/spacecash/c10 + +/obj/item/seeds/tree/steel + name = "pack of steel tree seeds" + desc = "These seeds grow into a steel tree." + species = "steel" + plantname = "Steel Tree" + + icon_harvest = "TreeSteel-G4" + icon_dead = "TreeSteel-G0" + icon_grow = "TreeSteel-G" + + product = /obj/item/grown/log/steel + possible_mutations = list() + reagents_add = list(/datum/reagent/cellulose = 0.05, /datum/reagent/iron = 0.05) + rarity = 20 diff --git a/monkestation/code/modules/botany/plant_icon_overrides/crops.dm b/monkestation/code/modules/botany/plant_icon_overrides/crops.dm new file mode 100644 index 000000000000..7c70a8444376 --- /dev/null +++ b/monkestation/code/modules/botany/plant_icon_overrides/crops.dm @@ -0,0 +1,85 @@ +/obj/item/seeds/bamboo + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_crop.dmi' + growthstages = 3 + icon_harvest = "Bamboo-G4" + icon_dead = "Bamboo-G0" + icon_grow = "Bamboo-G" + +/obj/item/seeds/sugarcane + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_crop.dmi' + growthstages = 3 + icon_harvest = "Sugar-G4" + icon_dead = "Sugar-G0" + icon_grow = "Sugar-G" + +/obj/item/seeds/wheat + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_crop.dmi' + growthstages = 3 + icon_harvest = "Wheat-G4" + icon_dead = "Wheat-G0" + icon_grow = "Wheat-G" + +/obj/item/seeds/wheat/oat + icon_harvest = "Oat-G4" + icon_dead = "Oat-G0" + icon_grow = "Oat-G" + +/obj/item/seeds/wheat/rice + icon_harvest = "Rice-G4" + icon_dead = "Rice-G0" + icon_grow = "Rice-G" + +/obj/item/seeds/wheat/meat + icon_harvest = "WheatMeat-G4" + icon_dead = "WheatMeat-G0" + icon_grow = "WheatMeat-G" + +/obj/item/seeds/peanut + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_crop.dmi' + growthstages = 3 + icon_harvest = "Peanut-G4" + icon_dead = "Peanut-G0" + icon_grow = "Peanut-G" + +/obj/item/seeds/cotton + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_crop.dmi' + growthstages = 3 + icon_harvest = "Cotton-G4" + icon_dead = "Cotton-G0" + icon_grow = "Cotton-G" + +/obj/item/seeds/cotton/durathread + icon_harvest = "CottonDura-G4" + icon_dead = "CottonDura-G0" + icon_grow = "CottonDura-G" + +/obj/item/seeds/tower + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_crop.dmi' + growthstages = 3 + icon_harvest = "Tree-G4" + icon_dead = "Tree-G0" + icon_grow = "Tree-G" + +/obj/item/seeds/tower/steel + icon_harvest = "TreeSteel-G4" + icon_dead = "TreeSteel-G0" + icon_grow = "TreeSteel-G" + +/obj/item/seeds/coffee + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_crop.dmi' + growthstages = 3 + icon_harvest = "Coffee-G4" + icon_dead = "Coffee-G0" + icon_grow = "Coffee-G" + +/obj/item/seeds/coffee/robusta + icon_harvest = "CoffeeLatte-G4" + icon_dead = "CoffeeLatte-G0" + icon_grow = "CoffeeLatte-G" diff --git a/monkestation/code/modules/botany/plant_icon_overrides/flowers.dm b/monkestation/code/modules/botany/plant_icon_overrides/flowers.dm new file mode 100644 index 000000000000..31a2a716635b --- /dev/null +++ b/monkestation/code/modules/botany/plant_icon_overrides/flowers.dm @@ -0,0 +1,46 @@ +/obj/item/seeds/sunflower + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_flower.dmi' + growthstages = 3 + icon_harvest = "Sunflower-G4" + icon_dead = "Sunflower-G0" + icon_grow = "Sunflower-G" + +/obj/item/seeds/sunflower/moonflower + icon_harvest = "Moonflower-G4" + icon_dead = "Moonflower-G0" + icon_grow = "Moonflower-G" + +/obj/item/seeds/poppy/geranium + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_flower.dmi' + growthstages = 3 + icon_harvest = "Geranium-G4" + icon_dead = "Geranium-G0" + icon_grow = "Geranium-G" + +/obj/item/seeds/poppy/geranium/fraxinella + icon_harvest = "Gardenia-G4" + icon_dead = "Gardenia-G0" + icon_grow = "Gardenia-G" + +/obj/item/seeds/sunflower/novaflower + icon_harvest = "Novaflower-G4" + icon_dead = "Novaflower-G0" + icon_grow = "Novaflower-G" + +/obj/item/seeds/rose + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_flower.dmi' + growthstages = 3 + icon_harvest = "Rose-G4" + icon_dead = "Rose-G0" + icon_grow = "Rose-G" + +/obj/item/seeds/carbon_rose + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_flower.dmi' + growthstages = 3 + icon_harvest = "HoloRose-G4" + icon_dead = "HoloRose-G0" + icon_grow = "HoloRose-G" diff --git a/monkestation/code/modules/botany/plant_icon_overrides/fruits.dm b/monkestation/code/modules/botany/plant_icon_overrides/fruits.dm new file mode 100644 index 000000000000..9a99e608d215 --- /dev/null +++ b/monkestation/code/modules/botany/plant_icon_overrides/fruits.dm @@ -0,0 +1,201 @@ +/obj/item/seeds/tomato + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + plant_icon_offset = 0 + growthstages = 3 + icon_harvest = "Tomato-G4" + icon_dead = "Tomato-G0" + icon_grow = "Tomato-G" + +/obj/item/seeds/tomato/blue + icon_harvest = "tomatoblue-G4" + icon_dead = "tomatoblue-G0" + icon_grow = "tomatoblue-G" + +/obj/item/seeds/tomato/killer + growthstages = 3 + icon_dead = "TomatoKiller-G0" + icon_grow = "TomatoKiller-G" + icon_harvest = "TomatoKiller-G4" + + +/obj/item/seeds/grape + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Grape-G4" + icon_dead = "Grape-G0" + icon_grow = "Grape-G" + +/obj/item/seeds/grape/green + icon_harvest = "GrapeGreen-G4" + icon_dead = "GrapeGreen-G0" + icon_grow = "GrapeGreen-G" + +/obj/item/seeds/cherry + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Cherry-G4" + icon_dead = "Cherry-G0" + icon_grow = "Cherry-G" + +/obj/item/seeds/cherry/blue + icon_harvest = "CherryBlue-G4" + icon_dead = "CherryBlue-G0" + icon_grow = "CherryBlue-G" + +/obj/item/seeds/cherry/bulb + icon_harvest = "CherryBulb-G4" + icon_dead = "CherryBulb-G0" + icon_grow = "CherryBulb-G" + +/obj/item/seeds/cherry/bomb + icon_harvest = "CherryBomb-G4" + icon_dead = "CherryBomb-G0" + icon_grow = "CherryBomb-G" + +/obj/item/seeds/watermelon + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Melon-G4" + icon_dead = "Melon-G0" + icon_grow = "Melon-G" + +/obj/item/seeds/watermelon/holy + icon_harvest = "HolyMelon-G4" + icon_dead = "HolyMelon-G0" + icon_grow = "HolyMelon-G" + +/obj/item/seeds/watermelon/barrel + icon_harvest = "BarrelMelon-G4" + icon_dead = "BarrelMelon-G0" + icon_grow = "BarrelMelon-G" + +/obj/item/seeds/chili + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Chili-G4" + icon_dead = "Chili-G0" + icon_grow = "Chili-G" + +/obj/item/seeds/chili/ice + icon_harvest = "ChiliChilly-G4" + icon_dead = "ChiliChilly-G0" + icon_grow = "ChiliChilly-G" + +/obj/item/seeds/chili/ghost + icon_harvest = "ChiliGhost-G4" + icon_dead = "ChiliGhost-G0" + icon_grow = "ChiliGhost-G" + +/obj/item/seeds/apple + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Apple-G4" + icon_dead = "Apple-G0" + icon_grow = "Apple-G" + +/obj/item/seeds/apple/gold + icon_harvest = "AppleGold-G4" + icon_dead = "AppleGold-G0" + icon_grow = "AppleGold-G" + +/obj/item/seeds/banana + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Banana-G4" + icon_dead = "Banana-G0" + icon_grow = "Banana-G" + +/obj/item/seeds/banana/mime + icon_harvest = "BananaMime-G4" + icon_dead = "BananaMime-G0" + icon_grow = "BananaMime-G" + +/obj/item/seeds/banana/bluespace + icon_harvest = "BananaBluespace-G4" + icon_dead = "BananaBluespace-G0" + icon_grow = "BananaBluespace-G" + +/obj/item/seeds/banana/bombanana + icon_harvest = "BananaBomb-G4" + icon_dead = "BananaBomb-G0" + icon_grow = "BananaBomb-G" + +/obj/item/seeds/lime + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Lime-G4" + icon_dead = "Lime-G0" + icon_grow = "Lime-G" + +/obj/item/seeds/orange + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Orange-G4" + icon_dead = "Orange-G0" + icon_grow = "Orange-G" + +/obj/item/seeds/lemon + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Lemon-G4" + icon_dead = "Lemon-G0" + icon_grow = "Lemon-G" + +/obj/item/seeds/firelemon + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "LemonFire-G4" + icon_dead = "LemonFire-G0" + icon_grow = "LemonFire-G" + +/obj/item/seeds/orange_3d + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Orange-G4" + icon_dead = "Orange-G0" + icon_grow = "Orange-G" + +/obj/item/seeds/pumpkin + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Pumpkin-G4" + icon_dead = "Pumpkin-G0" + icon_grow = "Pumpkin-G" + +/obj/item/seeds/pumpkin/blumpkin + icon_harvest = "Bumpkin-G4" + icon_dead = "Bumpkin-G0" + icon_grow = "Bumpkin-G" + +/obj/item/seeds/eggplant + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Eggplant-G4" + icon_dead = "Eggplant-G0" + icon_grow = "Eggplant-G" + +/obj/item/seeds/eggplant/eggy + icon_harvest = "EggplantEggs-G4" + icon_dead = "EggplantEggs-G0" + icon_grow = "EggplantEggs-G" + +/obj/item/seeds/pineapple + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_fruit.dmi' + growthstages = 3 + icon_harvest = "Pineapple-G4" + icon_dead = "Pineapple-G0" + icon_grow = "Pineapple-G" diff --git a/monkestation/code/modules/botany/plant_icon_overrides/herbs.dm b/monkestation/code/modules/botany/plant_icon_overrides/herbs.dm new file mode 100644 index 000000000000..9f2f901ee1a5 --- /dev/null +++ b/monkestation/code/modules/botany/plant_icon_overrides/herbs.dm @@ -0,0 +1,100 @@ +/obj/item/seeds/ambrosia + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_herb.dmi' + growthstages = 3 + icon_harvest = "Venne-G4" + icon_dead = "Venne-G0" + icon_grow = "Venne-G" + +/obj/item/seeds/ambrosia/deus + icon_harvest = "VenneCurative-G4" + icon_dead = "VenneCurative-G0" + icon_grow = "VenneCurative-G" + +/obj/item/seeds/ambrosia/gaia + icon_harvest = "VenneToxic-G4" + icon_dead = "VenneToxic-G0" + icon_grow = "VenneToxic-G" + +/obj/item/seeds/cannabis + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_herb.dmi' + growthstages = 3 + icon_harvest = "Cannabis-G4" + icon_dead = "Cannabis-G0" + icon_grow = "Cannabis-G" + +/obj/item/seeds/cannabis/rainbow + icon_harvest = "CannabisRainbow-G4" + icon_dead = "CannabisRainbow-G0" + icon_grow = "CannabisRainbow-G" + +/obj/item/seeds/cannabis/ultimate + icon_harvest = "CannabisGlowing-G4" + icon_dead = "CannabisGlowing-G0" + icon_grow = "CannabisGlowing-G" + +/obj/item/seeds/cannabis/white + icon_harvest = "CannabisLife-G4" + icon_dead = "CannabisLife-G0" + icon_grow = "CannabisLife-G" + +/obj/item/seeds/cannabis/death + icon_harvest = "CannabisDeath-G4" + icon_dead = "CannabisDeath-G0" + icon_grow = "CannabisDeath-G" + +/obj/item/seeds/tobacco + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_herb.dmi' + growthstages = 3 + icon_harvest = "Tobacco-G4" + icon_dead = "Tobacco-G0" + icon_grow = "Tobacco-G" + +/obj/item/seeds/tobacco/space + icon_harvest = "Twobacco-G4" + icon_dead = "Twobacco-G0" + icon_grow = "Twobacco-G" + +/obj/item/seeds/tea + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_herb.dmi' + growthstages = 3 + icon_harvest = "Tea-G4" + icon_dead = "Tea-G0" + icon_grow = "Tea-G" + +/obj/item/seeds/tea/astra + icon_harvest = "Tea-G4" + icon_dead = "Tea-G0" + icon_grow = "Tea-G" + +/obj/item/seeds/poppy + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_herb.dmi' + growthstages = 3 + icon_harvest = "Poppy-G4" + icon_dead = "Poppy-G0" + icon_grow = "Poppy-G" + +/obj/item/seeds/herbs + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_herb.dmi' + growthstages = 3 + icon_harvest = "Mint-G4" + icon_dead = "Mint-G0" + icon_grow = "Mint-G" + +/obj/item/seeds/nettle + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_herb.dmi' + growthstages = 3 + icon_harvest = "NettleSmooth-G4" + icon_dead = "NettleSmooth-G0" + icon_grow = "NettleSmooth-G" + +/obj/item/seeds/nettle/death + icon_harvest = "NettleDeath-G4" + icon_dead = "NettleDeath-G0" + icon_grow = "NettleDeath-G" diff --git a/monkestation/code/modules/botany/plant_icon_overrides/plants_done.md b/monkestation/code/modules/botany/plant_icon_overrides/plants_done.md new file mode 100644 index 000000000000..311e99b1bbbc --- /dev/null +++ b/monkestation/code/modules/botany/plant_icon_overrides/plants_done.md @@ -0,0 +1,27 @@ +Plants to look over again +- Blumpkin +- Towercaps +- Sweet Potato +- Laughing Peas +- Ambrosia +- Astra Tea + +Plants we need done + - Aloe + - Cocoabeans + - Cucumber + - Gatfruit + - Grasses + - Herbs + - Corta Nut + - Kronkus + - Mushrooms + - Olive + - Plum + - Rainbow Bunch + - Replica Pod + - White Beet + - Red Beet + - Lily + - Trumpet + - Harebell diff --git a/monkestation/code/modules/botany/plant_icon_overrides/veggies.dm b/monkestation/code/modules/botany/plant_icon_overrides/veggies.dm new file mode 100644 index 000000000000..b9666feb7b90 --- /dev/null +++ b/monkestation/code/modules/botany/plant_icon_overrides/veggies.dm @@ -0,0 +1,106 @@ +/obj/item/seeds/cabbage + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_veg.dmi' + growthstages = 3 + icon_harvest = "Lettuce-G4" + icon_dead = "Lettuce-G0" + icon_grow = "Lettuce-G" + +/obj/item/seeds/carrot + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_veg.dmi' + growthstages = 3 + icon_harvest = "Carrot-G4" + icon_dead = "Carrot-G0" + icon_grow = "Carrot-G" + +/obj/item/seeds/carrot/parsnip + icon_harvest = "Parsnip-G4" + icon_dead = "Parsnip-G0" + icon_grow = "Parsnip-G" + +/obj/item/seeds/potato + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_veg.dmi' + growthstages = 3 + icon_harvest = "Potato-G4" + icon_dead = "Potato-G0" + icon_grow = "Potato-G" + +/obj/item/seeds/potato/sweet + icon_harvest = "Potato-G4" + icon_dead = "Potato-G0" + icon_grow = "Potato-G" + +/obj/item/seeds/onion + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_veg.dmi' + growthstages = 3 + icon_harvest = "Onion-G4" + icon_dead = "Onion-G0" + icon_grow = "Onion-G" + +/obj/item/seeds/onion/red + icon_harvest = "OnionRed-G4" + icon_dead = "OnionRed-G0" + icon_grow = "OnionRed-G" + +/obj/item/seeds/garlic + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_veg.dmi' + growthstages = 3 + icon_harvest = "Garlic-G4" + icon_dead = "Garlic-G0" + icon_grow = "Garlic-G" + +/obj/item/seeds/soya + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_veg.dmi' + growthstages = 3 + icon_harvest = "Soybean-G4" + icon_dead = "Soybean-G0" + icon_grow = "Soybean-G" + +/obj/item/seeds/soya/koi + icon_harvest = "Soylent-G4" + icon_dead = "Soylent-G0" + icon_grow = "Soylent-G" + +/obj/item/seeds/greenbean + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_veg.dmi' + growthstages = 3 + icon_harvest = "Bean-G4" + icon_dead = "Bean-G0" + icon_grow = "Bean-G" + +/obj/item/seeds/greenbean/jump + icon_harvest = "BeanJump-G4" + icon_dead = "BeanJump-G0" + icon_grow = "BeanJump-G" + +/obj/item/seeds/corn + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_veg.dmi' + growthstages = 3 + icon_harvest = "Corn-G4" + icon_dead = "Corn-G0" + icon_grow = "Corn-G" + +/obj/item/seeds/corn/snapcorn + icon_harvest = "CornClear-G4" + icon_dead = "CornClear-G0" + icon_grow = "CornClear-G" + +/obj/item/seeds/peas + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_veg.dmi' + growthstages = 3 + icon_harvest = "Peas-G4" + icon_dead = "Peas-G0" + icon_grow = "Peas-G" + +/obj/item/seeds/peas/laugh + icon_harvest = "GoldenPeas-G4" + icon_dead = "GoldenPeas-G0" + icon_grow = "GoldenPeas-G" diff --git a/monkestation/code/modules/botany/plant_icon_overrides/weeds.dm b/monkestation/code/modules/botany/plant_icon_overrides/weeds.dm new file mode 100644 index 000000000000..236b4a8303be --- /dev/null +++ b/monkestation/code/modules/botany/plant_icon_overrides/weeds.dm @@ -0,0 +1,15 @@ +/obj/item/seeds/amanita + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_weed.dmi' + growthstages = 3 + icon_harvest = "FungusAmanita-G4" + icon_dead = "FungusAmanita-G0" + icon_grow = "FungusAmanita-G" + +/obj/item/seeds/glowshroom/shadowshroom + plant_icon_offset = 0 + growing_icon = 'goon/icons/obj/hydroponics/plants_weed.dmi' + growthstages = 3 + icon_harvest = "FungusCloak-G4" + icon_dead = "FungusCloak-G0" + icon_grow = "FungusCloak-G" diff --git a/monkestation/code/modules/botany/plant_processing.dm b/monkestation/code/modules/botany/plant_processing.dm new file mode 100644 index 000000000000..b94baaf8842a --- /dev/null +++ b/monkestation/code/modules/botany/plant_processing.dm @@ -0,0 +1,4 @@ +PROCESSING_SUBSYSTEM_DEF(plants) + flags = SS_NO_INIT | SS_BACKGROUND + priority = 20 + wait = 1 SECONDS diff --git a/monkestation/code/modules/botany/potty.dm b/monkestation/code/modules/botany/potty.dm new file mode 100644 index 000000000000..e3d45f6149f0 --- /dev/null +++ b/monkestation/code/modules/botany/potty.dm @@ -0,0 +1,96 @@ +/mob/living/basic/pet/potty + name = "craig the potted plant" + desc = "A potted plant." + + icon = 'monkestation/code/modules/botany/icons/potty.dmi' + icon_state = "potty" + icon_living = "potty_living" + icon_dead = "potty_dead" + + dexterous = TRUE + held_items = list(null, null) + + ai_controller = /datum/ai_controller/basic_controller/craig + + /// Instructions you can give to dogs + var/static/list/pet_commands = list( + /datum/pet_command/idle, + /datum/pet_command/craig_harvest, + /datum/pet_command/free, + /datum/pet_command/good_boy/dog, + /datum/pet_command/follow/dog, + /datum/pet_command/point_targeting/attack/dog, + /datum/pet_command/point_targeting/fetch, + /datum/pet_command/play_dead, + ) + +/mob/living/basic/pet/potty/Initialize(mapload) + . = ..() + + AddComponent(/datum/component/plant_tray_overlay, icon, null, null, null, null, null, null, 3, 8) + AddComponent(/datum/component/plant_growing) + AddComponent(/datum/component/obeys_commands, pet_commands) + AddComponent(/datum/component/emotion_buffer) + AddComponent(/datum/component/friendship_container, list(FRIENDSHIP_HATED = -100, FRIENDSHIP_DISLIKED = -50, FRIENDSHIP_STRANGER = 0, FRIENDSHIP_NEUTRAL = 1, FRIENDSHIP_ACQUAINTANCES = 3, FRIENDSHIP_FRIEND = 5, FRIENDSHIP_BESTFRIEND = 10), FRIENDSHIP_FRIEND) + AddElement(/datum/element/waddling) + + SEND_SIGNAL(src, COMSIG_TOGGLE_BIOBOOST) + +/mob/living/basic/pet/potty/melee_attack(atom/target, list/modifiers, ignore_cooldown = FALSE) + face_atom(target) + if (!ignore_cooldown) + changeNext_move(melee_attack_cooldown) + if(SEND_SIGNAL(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, target, Adjacent(target), modifiers) & COMPONENT_HOSTILE_NO_ATTACK) + return FALSE //but more importantly return before attack_animal called + var/result + if(held_items[active_hand_index]) + var/obj/item/W = get_active_held_item() + result = W.melee_attack_chain(src, target) + SEND_SIGNAL(src, COMSIG_HOSTILE_POST_ATTACKINGTARGET, target, result) + return result + result = target.attack_basic_mob(src, modifiers) + SEND_SIGNAL(src, COMSIG_HOSTILE_POST_ATTACKINGTARGET, target, result) + return result + +/mob/living/basic/pet/potty/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers) + . = ..() + + if(!. || !proximity_flag || (locate(/obj/item/reagent_containers/cup/watering_can) in contents)) + return + + if(!istype(attack_target, /obj/item/reagent_containers/cup/watering_can)) + return + + var/obj/item/can_target = attack_target + can_target.pickup(src) + +/datum/pet_command/craig_harvest + command_name = "Shake" + command_desc = "Command your pet to stay idle in this location." + radial_icon = 'icons/obj/objects.dmi' + radial_icon_state = "dogbed" + speech_commands = list("shake", "harvest") + command_feedback = "shakes" + +/datum/pet_command/craig_harvest/execute_action(datum/ai_controller/controller) + var/mob/living/pawn = controller.pawn + pawn.Shake(2, 2, 3 SECONDS) + SEND_SIGNAL(pawn, COMSIG_TRY_HARVEST_SEEDS, pawn) + return SUBTREE_RETURN_FINISH_PLANNING // This cancels further AI planning + +/datum/ai_controller/basic_controller/craig + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, + BB_WEEDLEVEL_THRESHOLD = 3, + BB_WATERLEVEL_THRESHOLD = 90, + ) + + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + planning_subtrees = list( + /datum/ai_planning_subtree/pet_planning, + /datum/ai_planning_subtree/find_and_hunt_target/watering_can, + /datum/ai_planning_subtree/find_and_hunt_target/fill_watercan, + /datum/ai_planning_subtree/find_and_hunt_target/treat_hydroplants, + ) diff --git a/monkestation/code/modules/botany/reagents/_base.dm b/monkestation/code/modules/botany/reagents/_base.dm new file mode 100644 index 000000000000..fbe4a17c7e95 --- /dev/null +++ b/monkestation/code/modules/botany/reagents/_base.dm @@ -0,0 +1,4 @@ +/datum/reagent/proc/on_plant_apply(obj/item/seeds/seed) + +///this proc is basically a signal sender +/datum/reagent/proc/on_plant_grower_apply(atom/movable/parent) diff --git a/monkestation/code/modules/botany/reagents/growing_component_apply/cat2_medicine_reagents.dm b/monkestation/code/modules/botany/reagents/growing_component_apply/cat2_medicine_reagents.dm new file mode 100644 index 000000000000..5bebb5ebd142 --- /dev/null +++ b/monkestation/code/modules/botany/reagents/growing_component_apply/cat2_medicine_reagents.dm @@ -0,0 +1,2 @@ +/datum/reagent/medicine/c2/multiver/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, -round(volume * 2)) diff --git a/monkestation/code/modules/botany/reagents/growing_component_apply/drug_reagents.dm b/monkestation/code/modules/botany/reagents/growing_component_apply/drug_reagents.dm new file mode 100644 index 000000000000..acf9fabbe546 --- /dev/null +++ b/monkestation/code/modules/botany/reagents/growing_component_apply/drug_reagents.dm @@ -0,0 +1,3 @@ +/datum/reagent/drug/nicotine/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, round(volume)) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_PEST, -rand(1, 2)) diff --git a/monkestation/code/modules/botany/reagents/growing_component_apply/food_reagents.dm b/monkestation/code/modules/botany/reagents/growing_component_apply/food_reagents.dm new file mode 100644 index 000000000000..8f8e217151e5 --- /dev/null +++ b/monkestation/code/modules/botany/reagents/growing_component_apply/food_reagents.dm @@ -0,0 +1,7 @@ +/datum/reagent/consumable/honey/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_WEED, rand(1, 2)) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_PEST, rand(1, 2)) + +/datum/reagent/consumable/sugar/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_WEED, rand(1, 2)) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_PEST, rand(1, 2)) diff --git a/monkestation/code/modules/botany/reagents/growing_component_apply/medicine.dm b/monkestation/code/modules/botany/reagents/growing_component_apply/medicine.dm new file mode 100644 index 000000000000..42c53193bfec --- /dev/null +++ b/monkestation/code/modules/botany/reagents/growing_component_apply/medicine.dm @@ -0,0 +1,7 @@ +/datum/reagent/medicine/cryoxadone/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, -round(volume * 3)) + +/datum/reagent/medicine/earthsblood/on_plant_grower_apply(atom/movable/parent) + . = ..() + SEND_SIGNAL(parent, COMSIG_GROWER_ADJUST_SELFGROW, volume) + SEND_SIGNAL(parent, COMSIG_GROWER_INCREASE_WORK_PROCESSES, 30) diff --git a/monkestation/code/modules/botany/reagents/growing_component_apply/other_reagents.dm b/monkestation/code/modules/botany/reagents/growing_component_apply/other_reagents.dm new file mode 100644 index 000000000000..9ec4ac43c3c5 --- /dev/null +++ b/monkestation/code/modules/botany/reagents/growing_component_apply/other_reagents.dm @@ -0,0 +1,23 @@ +/datum/reagent/blood/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_PEST, rand(2, 3)) + +/datum/reagent/chlorine/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_WEED, -rand(1, 3)) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, round(volume * 1.5)) + +/datum/reagent/fluorine/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_WEED, -rand(1, 4)) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, round(volume * 2.5)) + +/datum/reagent/phosphorus/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_WEED, -rand(1, 2)) + +/datum/reagent/uranium/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, round(volume * 1)) + +/datum/reagent/diethylamine/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_PEST, -rand(1, 2)) + +/datum/reagent/brimdust/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_WEED, -1) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_PEST, -1) diff --git a/monkestation/code/modules/botany/reagents/growing_component_apply/pyrotechnics.dm b/monkestation/code/modules/botany/reagents/growing_component_apply/pyrotechnics.dm new file mode 100644 index 000000000000..42e404f6183b --- /dev/null +++ b/monkestation/code/modules/botany/reagents/growing_component_apply/pyrotechnics.dm @@ -0,0 +1,2 @@ +/datum/reagent/napalm/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_WEED, -rand(5, 9)) diff --git a/monkestation/code/modules/botany/reagents/growing_component_apply/toxins.dm b/monkestation/code/modules/botany/reagents/growing_component_apply/toxins.dm new file mode 100644 index 000000000000..b506117dbcec --- /dev/null +++ b/monkestation/code/modules/botany/reagents/growing_component_apply/toxins.dm @@ -0,0 +1,26 @@ +/datum/reagent/toxin/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, round(volume * 2)) + +/datum/reagent/toxin/plantbgone/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, round(volume * 6)) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_WEED, -rand(4, 8)) + +/datum/reagent/toxin/plantbgone/weedkiller/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, round(volume * 0.5)) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_WEED, -rand(1, 2)) + +/datum/reagent/toxin/pestkiller/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, round(volume * 1)) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_PEST, -rand(1, 2)) + +/datum/reagent/toxin/pestkiller/organic/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, round(volume * 0.1)) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_PEST, -rand(1, 2)) + +/datum/reagent/toxin/acid/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, round(volume * 1.5)) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_WEED, -rand(1, 2)) + +/datum/reagent/toxin/acid/fluacid/on_plant_grower_apply(atom/movable/parent) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_TOXIN, round(volume * 3)) + SEND_SIGNAL(parent, COMSIG_GROWING_ADJUST_WEED, -rand(1, 4)) diff --git a/monkestation/code/modules/botany/reagents/plant_apply/drink_reagents.dm b/monkestation/code/modules/botany/reagents/plant_apply/drink_reagents.dm new file mode 100644 index 000000000000..772eb253cf11 --- /dev/null +++ b/monkestation/code/modules/botany/reagents/plant_apply/drink_reagents.dm @@ -0,0 +1,6 @@ +/datum/reagent/consumable/milk/on_plant_apply(obj/item/seeds/seed) + seed.adjust_potency(-volume * 0.5) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, round(volume * 0.1)) + +/datum/reagent/consumable/sodawater/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, round(volume * 0.15)) diff --git a/monkestation/code/modules/botany/reagents/plant_apply/ethanol.dm b/monkestation/code/modules/botany/reagents/plant_apply/ethanol.dm new file mode 100644 index 000000000000..96d521bdf05f --- /dev/null +++ b/monkestation/code/modules/botany/reagents/plant_apply/ethanol.dm @@ -0,0 +1,6 @@ +/datum/reagent/consumable/ethanol/on_plant_apply(obj/item/seeds/seed) + seed.process_trait_gain(/datum/plant_gene/trait/brewing, ((volume * 0.25) + (boozepwr * 0.1))) + +/datum/reagent/consumable/ethanol/beer/on_plant_apply(obj/item/seeds/seed) + . = ..() + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, round(volume * 0.1)) diff --git a/monkestation/code/modules/botany/reagents/plant_apply/food_reagents.dm b/monkestation/code/modules/botany/reagents/plant_apply/food_reagents.dm new file mode 100644 index 000000000000..224180d4978e --- /dev/null +++ b/monkestation/code/modules/botany/reagents/plant_apply/food_reagents.dm @@ -0,0 +1,9 @@ +/datum/reagent/consumable/nutriment/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, round(volume * 0.2)) + +/datum/reagent/consumable/virus_food/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -round(volume * 0.5)) + +/datum/reagent/consumable/honey/on_plant_apply(obj/item/seeds/seed) + seed.adjust_maturation(rand(1,2)) + seed.adjust_lifespan(rand(1,2)) diff --git a/monkestation/code/modules/botany/reagents/plant_apply/medicine.dm b/monkestation/code/modules/botany/reagents/plant_apply/medicine.dm new file mode 100644 index 000000000000..48ceebe4ea38 --- /dev/null +++ b/monkestation/code/modules/botany/reagents/plant_apply/medicine.dm @@ -0,0 +1,2 @@ +/datum/reagent/medicine/cryoxadone/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, round(volume * 3)) diff --git a/monkestation/code/modules/botany/reagents/plant_apply/other_reagents.dm b/monkestation/code/modules/botany/reagents/plant_apply/other_reagents.dm new file mode 100644 index 000000000000..274e28944eea --- /dev/null +++ b/monkestation/code/modules/botany/reagents/plant_apply/other_reagents.dm @@ -0,0 +1,79 @@ +/datum/reagent/plantnutriment/eznutriment/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, round(volume * 0.15)) + seed.adjust_lifespan(round(volume * 0.15)) + seed.adjust_potency(round(volume * 0.1)) + seed.adjust_yield(round(volume * 0.1)) + +/datum/reagent/plantnutriment/endurogrow/on_plant_apply(obj/item/seeds/seed) + seed.adjust_potency(-round(volume * 0.1)) + seed.adjust_yield(-round(volume * 0.075)) + seed.adjust_endurance(round(volume * 0.35)) + +/datum/reagent/saltpetre/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, round(volume * 0.18)) + seed.adjust_production(round(volume * 0.1)) + seed.adjust_potency(round(volume * 0.2)) + +/datum/reagent/brimdust/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, round(volume * 1)) + seed.adjust_potency(round(volume * 0.5)) + +/datum/reagent/water/holywater/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, round(volume * 0.1)) + +/datum/reagent/lube/on_plant_apply(obj/item/seeds/seed) + seed.process_trait_gain(/datum/plant_gene/trait/slip, volume * 0.25) + seed.adjust_endurance(volume * 0.08) + +/datum/reagent/chlorine/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -round(volume * 1.5)) + seed.adjust_lifespan(-round(volume * 0.2)) + +/datum/reagent/fluorine/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -round(volume * 2)) + +/datum/reagent/phosphorus/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -round(volume * 0.75)) + +/datum/reagent/uranium/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -round(volume * 1)) + //plant_mutation_reagent_apply(chems, mytray, user, mr = 10, hm = 5) + +/datum/reagent/ammonia/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, round(volume * 0.12)) + seed.adjust_maturation(round(volume * 0.2)) + seed.adjust_production(round(volume * 0.1)) + if(prob(10)) + seed.adjust_yield(1) + +/datum/reagent/diethylamine/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, round(volume * 1)) + seed.adjust_yield(round(volume * 0.2)) + +/datum/reagent/plantnutriment/left4zednutriment/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -1) + seed.adjust_lifespan(-round(volume * 0.2)) + + seed.adjust_endurance(round(volume * 0.1)) + seed.adjust_potency(round(volume * 0.1)) + seed.adjust_yield(round(volume * 0.1)) + seed.adjust_maturation(round(volume * 0.1)) + seed.adjust_production(round(volume * 0.1)) + +/datum/reagent/plantnutriment/robustharvestnutriment/on_plant_apply(obj/item/seeds/seed) + seed.adjust_yield(round(volume * 0.35)) + seed.adjust_maturation(round(volume * 0.1)) + seed.adjust_production(round(volume * 0.05)) + +/datum/reagent/plantnutriment/endurogrow/on_plant_apply(obj/item/seeds/seed) + seed.adjust_potency(-round(volume * 0.1)) + seed.adjust_yield(-round(volume * 0.075)) + seed.adjust_endurance(round(volume * 0.35)) + +/datum/reagent/ash/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, round(volume * 1)) + +/datum/reagent/plantnutriment/liquidearthquake/on_plant_apply(obj/item/seeds/seed) + seed.adjust_weed_rate(round(volume * 0.1)) + seed.adjust_weed_chance(round(volume * 0.3)) + seed.adjust_production(-round(volume * 0.075)) diff --git a/monkestation/code/modules/botany/reagents/plant_apply/pyrotechnics.dm b/monkestation/code/modules/botany/reagents/plant_apply/pyrotechnics.dm new file mode 100644 index 000000000000..e901f7445a83 --- /dev/null +++ b/monkestation/code/modules/botany/reagents/plant_apply/pyrotechnics.dm @@ -0,0 +1,5 @@ +/datum/reagent/napalm/on_plant_apply(obj/item/seeds/seed) + if(seed.resistance_flags & FIRE_PROOF) + return + + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -round(volume * 6)) diff --git a/monkestation/code/modules/botany/reagents/plant_apply/toxins.dm b/monkestation/code/modules/botany/reagents/plant_apply/toxins.dm new file mode 100644 index 000000000000..1e9fd4fc41a2 --- /dev/null +++ b/monkestation/code/modules/botany/reagents/plant_apply/toxins.dm @@ -0,0 +1,11 @@ +/datum/reagent/toxin/plantbgone/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -round(volume * 10)) + +/datum/reagent/toxin/plantbgone/weedkiller/on_plant_apply(obj/item/seeds/seed) + + +/datum/reagent/toxin/acid/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -round(volume * 1)) + +/datum/reagent/toxin/acid/fluacid/on_plant_apply(obj/item/seeds/seed) + SEND_SIGNAL(seed, COMSIG_ADJUST_PLANT_HEALTH, -round(volume * 2)) diff --git a/monkestation/code/modules/botany/species/apid/abilities.dm b/monkestation/code/modules/botany/species/apid/abilities.dm new file mode 100644 index 000000000000..4358fe968e1a --- /dev/null +++ b/monkestation/code/modules/botany/species/apid/abilities.dm @@ -0,0 +1,86 @@ +/datum/action/cooldown/spell/pointed/pollinate + name = "Pollinate Crop" + desc = "You try to pollinate a crop." + button_icon = 'monkestation/icons/mob/simple/bees.dmi' + button_icon_state = "pollen_sac" + + cooldown_time = 1 MINUTES + spell_requirements = NONE + +/datum/action/cooldown/spell/pointed/pollinate/before_cast(atom/cast_on) + . = ..() + if(!cast_on.GetComponent(/datum/component/plant_growing)) + on_deactivation(owner, refund_cooldown = TRUE) + return + +/datum/action/cooldown/spell/pointed/pollinate/cast(atom/cast_on) + . = ..() + var/datum/component/plant_growing/growing = cast_on.GetComponent(/datum/component/plant_growing) + if(!growing) + return + SEND_SIGNAL(cast_on, COMSIG_TRY_POLLINATE) + + var/mob/living/carbon/human/human = owner + var/datum/species/apid/apid = human.dna.species + apid.adjust_honeycount(10) + to_chat(owner, span_notice("You pollinate the [cast_on].")) + + for(var/item as anything in growing.managed_seeds) + var/obj/item/seeds/seed = growing.managed_seeds[item] + if(!seed) + continue + switch(apid.current_stat) + if("potency") + seed.adjust_potency(rand(10, 20)) + if("yield") + seed.adjust_yield(rand(5, 10)) + if("lifespan") + seed.adjust_lifespan(rand(10, 20)) + if("lifespan") + seed.adjust_lifespan(rand(10, 20)) + if("endurance") + seed.adjust_endurance(rand(10, 20)) + if("maturation") + seed.adjust_maturation(rand(10, 20)) + if("production") + seed.adjust_production(rand(10, 20)) + + +/particles/pollen + icon = 'monkestation/code/modules/botany/icons/pollen.dmi' + icon_state = "pollen" + width = 100 + height = 100 + count = 1000 + spawning = 4 + lifespan = 0.7 SECONDS + fade = 1 SECONDS + grow = -0.01 + velocity = list(0, 0) + position = generator(GEN_CIRCLE, 0, 16, NORMAL_RAND) + drift = generator(GEN_VECTOR, list(0, -0.2), list(0, 0.2)) + gravity = list(0, 0.95) + scale = generator(GEN_VECTOR, list(0.3, 0.3), list(1,1), NORMAL_RAND) + rotation = 30 + spin = generator(GEN_NUM, -20, 20) + +/datum/action/cooldown/spell/change_pollination_stat + name = "Change Stat" + desc = "Changes the stat you and your bees increase on plants." + button_icon = 'monkestation/icons/mob/simple/bees.dmi' + button_icon_state = "pollen_sac" + + + cooldown_time = 1 SECONDS + spell_requirements = NONE + +/datum/action/cooldown/spell/change_pollination_stat/cast(atom/cast_on) + . = ..() + var/mob/living/carbon/human/human = owner + var/datum/species/apid/apid = human.dna.species + + var/choice = tgui_input_list(human, "Choose the new stat.", "Stat Change", list("potency", "yield", "lifespan", "endurance", "maturation", "production")) + if(!choice) + return + apid.current_stat = choice + apid.owned_hive?.current_stat = choice diff --git a/monkestation/code/modules/botany/species/apid/bee.dm b/monkestation/code/modules/botany/species/apid/bee.dm new file mode 100644 index 000000000000..ddca2aa9be2f --- /dev/null +++ b/monkestation/code/modules/botany/species/apid/bee.dm @@ -0,0 +1,54 @@ +/datum/ai_controller/basic_controller/apid_bee + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/bee, + BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/not_friends, + ) + + ai_traits = STOP_MOVING_WHEN_PULLED + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + + planning_subtrees = list( + /datum/ai_planning_subtree/find_valid_home, + /datum/ai_planning_subtree/enter_exit_home, + /datum/ai_planning_subtree/find_and_hunt_target/pollinate, + ) + +/mob/living/basic/bee/apid_summoned + ai_controller = /datum/ai_controller/basic_controller/apid_bee + var/specalized_stat = "potency" + +/mob/living/basic/bee/apid_summoned/pollinate(atom/movable/hydro) + var/datum/component/plant_growing/growing = GetComponent(/datum/component/plant_growing) + if(growing) + for(var/item as anything in growing.managed_seeds) + var/obj/item/seeds/seed = growing.managed_seeds[item] + if(!seed) + continue + switch(specalized_stat) + if("potency") + seed.adjust_potency(rand(1,5)) + if("yield") + seed.adjust_yield(rand(1,5)) + if("endurance") + seed.adjust_endurance(rand(1,5)) + if("lifespan") + seed.adjust_lifespan(rand(1,5)) + if("maturation") + seed.adjust_maturation(rand(1, 5)) + if("production") + seed.adjust_production(rand(1, 5)) + + SEND_SIGNAL(hydro, COMSIG_TRY_POLLINATE) + + if(beehome) + beehome.bee_resources = min(beehome.bee_resources + health, 100) + if(istype(beehome, /obj/structure/beebox/hive)) + var/obj/structure/beebox/hive/hive = beehome + hive.stored_honey += rand(1, 5) + +/mob/living/basic/bee/apid_summoned/handle_habitation(obj/structure/beebox/hive/hive) + . = ..() + if(!istype(hive)) + return + specalized_stat = hive.current_stat diff --git a/monkestation/code/modules/botany/species/apid/bodyparts.dm b/monkestation/code/modules/botany/species/apid/bodyparts.dm new file mode 100644 index 000000000000..14f0fe6ddf3d --- /dev/null +++ b/monkestation/code/modules/botany/species/apid/bodyparts.dm @@ -0,0 +1,51 @@ +/obj/item/bodypart/head/apid + icon = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + icon_state = "apid_head" + icon_static = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + limb_id = SPECIES_APID + is_dimorphic = TRUE + should_draw_greyscale = FALSE + +/obj/item/bodypart/chest/apid + icon = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + icon_state = "apid_chest_m" + icon_static = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + limb_id = SPECIES_APID + is_dimorphic = TRUE + should_draw_greyscale = FALSE + +/obj/item/bodypart/arm/left/apid + icon = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + icon_state = "apid_l_arm" + icon_static = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + limb_id = SPECIES_APID + should_draw_greyscale = FALSE + unarmed_attack_verb = "slash" + unarmed_attack_effect = ATTACK_EFFECT_CLAW + unarmed_attack_sound = 'sound/weapons/slash.ogg' + unarmed_miss_sound = 'sound/weapons/slashmiss.ogg' + +/obj/item/bodypart/arm/right/apid + icon = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + icon_state = "apid_r_arm" + icon_static = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + limb_id = SPECIES_APID + should_draw_greyscale = FALSE + unarmed_attack_verb = "slash" + unarmed_attack_effect = ATTACK_EFFECT_CLAW + unarmed_attack_sound = 'sound/weapons/slash.ogg' + unarmed_miss_sound = 'sound/weapons/slashmiss.ogg' + +/obj/item/bodypart/leg/left/apid + icon = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + icon_state = "apid_l_leg" + icon_static = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + limb_id = SPECIES_APID + should_draw_greyscale = FALSE + +/obj/item/bodypart/leg/right/apid + icon = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + icon_state = "apid_r_leg" + icon_static = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + limb_id = SPECIES_APID + should_draw_greyscale = FALSE diff --git a/monkestation/code/modules/botany/species/apid/external_organs/apid_antennae.dm b/monkestation/code/modules/botany/species/apid/external_organs/apid_antennae.dm new file mode 100644 index 000000000000..563bcf9cd968 --- /dev/null +++ b/monkestation/code/modules/botany/species/apid/external_organs/apid_antennae.dm @@ -0,0 +1,121 @@ +/obj/item/organ/external/antennae_apid + name = "apid antennae" + desc = "A apids antennae. What is it telling them? What are they sensing?" + icon_state = "antennae" + + zone = BODY_ZONE_HEAD + slot = ORGAN_SLOT_EXTERNAL_ANTENNAE + + preference = "feature_apid_antenna" + + restyle_flags = EXTERNAL_RESTYLE_FLESH + + bodypart_overlay = /datum/bodypart_overlay/mutant/antennae_apid + +///Moth antennae datum, with full burning functionality +/datum/bodypart_overlay/mutant/antennae_apid + layers = EXTERNAL_ADJACENT + feature_key = "apid_antenna" + +/datum/bodypart_overlay/mutant/antennae_apid/get_global_feature_list() + return GLOB.apid_antenna_list + +/datum/bodypart_overlay/mutant/antennae_apid/get_base_icon_state() + return sprite_datum.icon_state + +/datum/sprite_accessory/apid_antenna + icon = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + color_src = null + em_block = TRUE + +/datum/sprite_accessory/apid_antenna/moth + name = "Moth" + icon_state = "moth" + +/datum/sprite_accessory/apid_antenna/fluffy + name = "Fluffy" + icon_state = "fluffy" + +/datum/sprite_accessory/apid_antenna/wavy + name = "Wavy" + icon_state = "wavy" + +/datum/sprite_accessory/apid_antenna/slickback + name = "Slickback" + icon_state = "slickback" + +/datum/sprite_accessory/apid_antenna/horns + name = "Horns" + icon_state = "horns" + +/datum/sprite_accessory/apid_antenna/straight + name = "Straight" + icon_state = "straight" + +/datum/sprite_accessory/apid_antenna/triangle + name = "Triangle" + icon_state = "triangle" + +/datum/sprite_accessory/apid_antenna/electric + name = "Electric" + icon_state = "electric" + +/datum/sprite_accessory/apid_antenna/wisp + name = "Wisp" + icon_state = "wisp" + +/datum/sprite_accessory/apid_antenna/plug + name = "Plug" + icon_state = "plug" + +/datum/sprite_accessory/apid_antenna/leafy + name = "Leafy" + icon_state = "leafy" + +/datum/sprite_accessory/apid_antenna/royal + name = "Royal" + icon_state = "royal" + +/datum/sprite_accessory/apid_antenna/warrior + name = "Warrior" + icon_state = "warrior" + +/datum/sprite_accessory/apid_antenna/sidelights + name = "Sidelights" + icon_state = "sidelights" + +/datum/sprite_accessory/apid_antenna/sprouts + name = "Sprouts" + icon_state = "sprouts" + +/datum/sprite_accessory/apid_antenna/nubs + name = "Nubs" + icon_state = "nubs" + +/datum/sprite_accessory/apid_antenna/ant + name = "Ants" + icon_state = "ant" + +/datum/sprite_accessory/apid_antenna/crooked + name = "Crooked" + icon_state = "crooked" + +/datum/sprite_accessory/apid_antenna/curled + name = "Curled" + icon_state = "curled" + +/datum/sprite_accessory/apid_antenna/snapped + name = "Snapped" + icon_state = "snapped" + +/datum/sprite_accessory/apid_antenna/budding + name = "Budding" + icon_state = "budding" + +/datum/sprite_accessory/apid_antenna/bumpers + name = "Bumpers" + icon_state = "bumpers" + +/datum/sprite_accessory/apid_antenna/split + name = "Split" + icon_state = "split" diff --git a/monkestation/code/modules/botany/species/apid/external_organs/preferences.dm b/monkestation/code/modules/botany/species/apid/external_organs/preferences.dm new file mode 100644 index 000000000000..5525b5695885 --- /dev/null +++ b/monkestation/code/modules/botany/species/apid/external_organs/preferences.dm @@ -0,0 +1,46 @@ +/datum/preference/choiced/apid_wings + savefile_key = "feature_apid_wings" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Apid wings" + should_generate_icons = TRUE + +/datum/preference/choiced/apid_wings/init_possible_values() + return possible_values_for_sprite_accessory_list_for_body_part( + GLOB.apid_wings_list, + "apid_wings", + list("BEHIND", "FRONT"), + ) + +/datum/preference/choiced/apid_wings/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["apid_wings"] = value + +/datum/preference/choiced/apid_antenna + savefile_key = "feature_apid_antenna" + savefile_identifier = PREFERENCE_CHARACTER + category = PREFERENCE_CATEGORY_FEATURES + main_feature_name = "Apid Antennae" + should_generate_icons = TRUE + +/datum/preference/choiced/apid_antenna/init_possible_values() + var/list/values = list() + + var/icon/moth_head = icon('icons/mob/species/moth/bodyparts.dmi', "moth_head") + moth_head.Blend(icon('icons/mob/species/human/human_face.dmi', "motheyes"), ICON_OVERLAY) + + for (var/antennae_name in GLOB.apid_antenna_list) + var/datum/sprite_accessory/antennae = GLOB.apid_antenna_list[antennae_name] + if(antennae.locked) + continue + + var/icon/icon_with_antennae = new(moth_head) + icon_with_antennae.Blend(icon(antennae.icon, "m_apid_antenna_[antennae.icon_state]_ADJ"), ICON_OVERLAY) + icon_with_antennae.Scale(64, 64) + icon_with_antennae.Crop(15, 64, 15 + 31, 64 - 31) + + values[antennae.name] = icon_with_antennae + + return values + +/datum/preference/choiced/apid_antenna/apply_to_human(mob/living/carbon/human/target, value) + target.dna.features["apid_antenna"] = value diff --git a/monkestation/code/modules/botany/species/apid/external_organs/wings.dm b/monkestation/code/modules/botany/species/apid/external_organs/wings.dm new file mode 100644 index 000000000000..f0ebbfd59fa8 --- /dev/null +++ b/monkestation/code/modules/botany/species/apid/external_organs/wings.dm @@ -0,0 +1,60 @@ +///Moth wings! They can flutter in low-grav and burn off in heat +/obj/item/organ/external/wings/apid + name = "apid wings" + desc = "Spread your wings and FLOOOOAAAAAT!" + + preference = "feature_apid_wings" + + bodypart_overlay = /datum/bodypart_overlay/mutant/wings/apid + + +/obj/item/organ/external/wings/apid/on_insert(mob/living/carbon/receiver) + . = ..() + RegisterSignal(receiver, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(update_float_move)) + +/obj/item/organ/external/wings/apid/on_remove(mob/living/carbon/organ_owner) + . = ..() + UnregisterSignal(organ_owner, list(COMSIG_MOVABLE_PRE_MOVE)) + REMOVE_TRAIT(organ_owner, TRAIT_FREE_FLOAT_MOVEMENT, REF(src)) + +/obj/item/organ/external/wings/apid/can_soften_fall() + return TRUE + +///Check if we can flutter around +/obj/item/organ/external/wings/apid/proc/update_float_move() + SIGNAL_HANDLER + + if(!isspaceturf(owner.loc)) + var/datum/gas_mixture/current = owner.loc.return_air() + if(current && (current.return_pressure() >= ONE_ATMOSPHERE*0.85)) //as long as there's reasonable pressure and no gravity, flight is possible + ADD_TRAIT(owner, TRAIT_FREE_FLOAT_MOVEMENT, REF(src)) + return + + REMOVE_TRAIT(owner, TRAIT_FREE_FLOAT_MOVEMENT, REF(src)) + + +///Moth wing bodypart overlay, including burn functionality! +/datum/bodypart_overlay/mutant/wings/apid + feature_key = "apid_wings" + layers = EXTERNAL_BEHIND | EXTERNAL_FRONT + +/datum/bodypart_overlay/mutant/wings/apid/get_global_feature_list() + return GLOB.apid_wings_list + +/datum/bodypart_overlay/mutant/wings/apid/can_draw_on_bodypart(mob/living/carbon/human/human) + if(!(human.wear_suit?.flags_inv & HIDEMUTWINGS)) + return TRUE + return FALSE + +/datum/bodypart_overlay/mutant/wings/apid/get_base_icon_state() + return sprite_datum.icon_state + +/datum/sprite_accessory/apid_wings + icon = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + color_src = null + em_block = TRUE + +/datum/sprite_accessory/apid_wings/normal + name = "Normal" + icon_state = "normal" + diff --git a/monkestation/code/modules/botany/species/apid/hive/ability.dm b/monkestation/code/modules/botany/species/apid/hive/ability.dm new file mode 100644 index 000000000000..29da7ae69f03 --- /dev/null +++ b/monkestation/code/modules/botany/species/apid/hive/ability.dm @@ -0,0 +1,50 @@ + +/datum/action/cooldown/spell/build_hive + name = "Build Hive Home" + desc = "You construct a once home for you and your bee people." + button_icon = 'icons/obj/hydroponics/equipment.dmi' + button_icon_state = "beebox" + + cooldown_time = 25 MINUTES + spell_requirements = NONE + + var/obj/structure/beebox/hive/created_hive + +/datum/action/cooldown/spell/build_hive/can_cast_spell(feedback = TRUE) + . = ..() + if(created_hive) + return FALSE + +/datum/action/cooldown/spell/build_hive/cast(mob/living/carbon/human/user = usr) + . = ..() + if(is_species(user, /datum/species/apid)) + var/datum/species/apid/apid = user.dna.species + if(apid.stored_honey < 150) + to_chat(user, span_notice("Not enough stored honey")) + addtimer(CALLBACK(src, PROC_REF(reset_spell_cooldown)), 2 SECONDS) + return + + if(!do_after(user, 10 SECONDS, get_turf(user))) + addtimer(CALLBACK(src, PROC_REF(reset_spell_cooldown)), 2 SECONDS) + return + + if(is_species(user, /datum/species/apid)) + var/datum/species/apid/apid = user.dna.species + if(apid.stored_honey < 150) + addtimer(CALLBACK(src, PROC_REF(reset_spell_cooldown)), 2 SECONDS) + return + + apid.adjust_honeycount(-150) + created_hive = new(get_turf(user), user.real_name) + apid.owned_hive = created_hive + created_hive.current_stat = apid.current_stat + + RegisterSignals(created_hive, list(COMSIG_QDELETING, COMSIG_PREQDELETED), PROC_REF(remove_hive)) + +/datum/action/cooldown/spell/build_hive/proc/remove_hive() + UnregisterSignal(created_hive, list(COMSIG_QDELETING, COMSIG_PREQDELETED)) + created_hive = null + var/mob/living/carbon/human/user = owner + if(is_species(user, /datum/species/apid)) + var/datum/species/apid/apid = user.dna.species + apid.owned_hive = null diff --git a/monkestation/code/modules/botany/species/apid/hive/area.dm b/monkestation/code/modules/botany/species/apid/hive/area.dm new file mode 100644 index 000000000000..849e508f5a45 --- /dev/null +++ b/monkestation/code/modules/botany/species/apid/hive/area.dm @@ -0,0 +1,12 @@ +/area/station/hive + name = "Hive" + area_flags = NOTELEPORT | EVENT_PROTECTED | ABDUCTOR_PROOF + has_gravity = TRUE + +/area/station/hive/one + +/area/station/hive/two + +/area/station/hive/three + +/area/station/hive/four diff --git a/monkestation/code/modules/botany/species/apid/hive/hive_object.dm b/monkestation/code/modules/botany/species/apid/hive/hive_object.dm new file mode 100644 index 000000000000..9745794125ca --- /dev/null +++ b/monkestation/code/modules/botany/species/apid/hive/hive_object.dm @@ -0,0 +1,125 @@ +GLOBAL_LIST_INIT(hive_exits, list()) + +/obj/structure/beebox/hive + name = "generic hive" + desc = "A generic hive without an owner." + + icon = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + icon_state = "hive" + + var/obj/structure/hive_exit/linked_exit + var/stored_honey = 0 + var/current_stat = "potency" + +/obj/structure/beebox/hive/Initialize(mapload, created_name) + . = ..() + ADD_TRAIT(src, TRAIT_BANNED_FROM_CARGO_SHUTTLE, INNATE_TRAIT) // womp womp + + name = "[created_name]'s hive" + for(var/i = 1 to 3) + var/obj/item/honey_frame/HF = new(src) + honey_frames += HF + + for(var/obj/structure/hive_exit/exit as anything in GLOB.hive_exits) + if(exit.linked_hive) + continue + exit.linked_hive = src + linked_exit = exit + linked_exit.name = "[created_name]'s hive exit" + break + + if(!linked_exit) + var/datum/map_template/hive/hive = new() + var/datum/turf_reservation/roomReservation = SSmapping.request_turf_block_reservation(hive.width, hive.height, 1) + var/turf/bottom_left = roomReservation.bottom_left_turfs[1] + var/datum/map_template/load_from = hive + + load_from.load(bottom_left) + for(var/obj/structure/hive_exit/exit as anything in GLOB.hive_exits) + if(exit.linked_hive) + continue + exit.linked_hive = src + linked_exit = exit + break + +/obj/structure/beebox/hive/Destroy() + . = ..() + for(var/atom/movable/listed in linked_exit?.atoms_inside) + listed.forceMove(get_turf(src)) + linked_exit?.linked_hive = null + linked_exit.name = "generic hive exit" + linked_exit = null + +/obj/structure/beebox/hive/attack_hand(mob/living/user, list/modifiers) + . = ..() + if(!linked_exit) + return + + var/enter_time = 4 SECONDS + if(is_species(user, /datum/species/apid)) + enter_time = 2 SECONDS + + if(!do_after(user, enter_time, src)) + return + + if(user.pulling && user.pulling != src) + do_teleport(user.pulling, get_step(linked_exit, EAST), no_effects = TRUE, forced = TRUE) + do_teleport(user, get_step(linked_exit, EAST), no_effects = TRUE, forced = TRUE) + + +/obj/structure/hive_exit + name = "generic hive exit" + desc = "A generic hive exit without an owner" + + icon = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + icon_state = "hive_exit" + + var/obj/structure/beebox/hive/linked_hive + + var/list/atoms_inside = list() + +/obj/structure/hive_exit/Initialize(mapload) + . = ..() + GLOB.hive_exits += src + RegisterSignal(get_area(src), COMSIG_AREA_EXITED, PROC_REF(exit_area)) + RegisterSignal(get_area(src), COMSIG_AREA_ENTERED, PROC_REF(enter_area)) + +/obj/structure/hive_exit/Destroy() + . = ..() + for(var/atom/movable/listed in atoms_inside) + listed.forceMove(get_turf(linked_hive)) + GLOB.hive_exits -= src + linked_hive?.linked_exit = null + linked_hive = null + +/obj/structure/hive_exit/attack_hand(mob/living/user, list/modifiers) + . = ..() + if(!linked_hive) + return + + var/enter_time = 4 SECONDS + if(is_species(user, /datum/species/apid)) + enter_time = 2 SECONDS + + if(!do_after(user, enter_time, src)) + return + if(user.pulling) + do_teleport(user.pulling, get_turf(linked_hive), no_effects = TRUE, forced = TRUE) + do_teleport(user, get_turf(linked_hive), no_effects = TRUE, forced = TRUE) + +/obj/structure/hive_exit/proc/exit_area(datum/source, atom/removed) + if(isturf(removed)) + return + atoms_inside -= removed + +/obj/structure/hive_exit/proc/enter_area(datum/source, atom/added) + if(isturf(added)) + return + atoms_inside += added + + +/datum/map_template/hive + name = "Hive Template" + width = 15 + height = 15 + mappath = "_maps/~monkestation/templates/hives.dmm" diff --git a/monkestation/code/modules/botany/species/apid/hive/hive_turfs.dm b/monkestation/code/modules/botany/species/apid/hive/hive_turfs.dm new file mode 100644 index 000000000000..dd056c5d8429 --- /dev/null +++ b/monkestation/code/modules/botany/species/apid/hive/hive_turfs.dm @@ -0,0 +1,18 @@ +/turf/open/indestructible/hive + name = "wax floor" + desc = "A floor made of beeswax" + + icon = 'goon/icons/floors.dmi' + icon_state = "hive" + +/turf/closed/indestructible/hive + name = "wax wall" + desc = "A wall made of wax" + + icon = 'goon/icons/walls_beehive.dmi' + icon_state = "bee-0" + base_icon_state = "bee" + + smoothing_flags = SMOOTH_BITMASK + smoothing_groups = SMOOTH_GROUP_WAXWALL + canSmoothWith = SMOOTH_GROUP_WAXWALL diff --git a/monkestation/code/modules/botany/species/apid/organs.dm b/monkestation/code/modules/botany/species/apid/organs.dm new file mode 100644 index 000000000000..ab8a0ac7e872 --- /dev/null +++ b/monkestation/code/modules/botany/species/apid/organs.dm @@ -0,0 +1,5 @@ +/obj/item/organ/internal/eyes/apid + name = "apid eyes" + desc = "Designed for navigating dark hives, these eyes have improvement to low light vision." + lighting_cutoff = LIGHTING_CUTOFF_MEDIUM + eye_icon_state = "motheyes" diff --git a/monkestation/code/modules/botany/species/apid/species.dm b/monkestation/code/modules/botany/species/apid/species.dm new file mode 100644 index 000000000000..5424db2b397a --- /dev/null +++ b/monkestation/code/modules/botany/species/apid/species.dm @@ -0,0 +1,154 @@ +#define ui_honeydisplay "WEST,CENTER-2:15" +#define FORMAT_HONEY_CHARGES_TEXT(charges) MAPTEXT("
[round(charges)]
") + +/datum/language_holder/apid + understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM)) + spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM)) + + +/obj/item/food/meat/slab/human/mutant/apid + icon_state = "mothmeat" + desc = "Unpleasantly powdery and dry. Kind of pretty, though." + tastes = list("dust" = 1, "powder" = 1, "meat" = 2) + foodtypes = MEAT | RAW | BUGS | GORE + venue_value = FOOD_MEAT_MUTANT + +/atom/movable/screen/apid + icon = 'monkestation/code/modules/botany/icons/apid_sprites.dmi' + + +/atom/movable/screen/apid/honey + name = "honey storage" + icon_state = "honey_counter" + screen_loc = ui_honeydisplay + + +/datum/species/apid + name = "\improper Apid" + plural_form = "Apids" + id = SPECIES_APID + species_traits = list( + LIPS, + HAS_MARKINGS, + HAIR + ) + + /* + mutant_bodyparts = list( + "apid_stripes" = "None", + "apid_headstripes" = "None", + ) + */ + + mutanteyes = /obj/item/organ/internal/eyes/apid + + external_organs = list( + /obj/item/organ/external/wings/apid = "Normal", + /obj/item/organ/external/antennae_apid = "Moth", + ) + + inherent_traits = list( + TRAIT_TACKLING_WINGED_ATTACKER, + TRAIT_ANTENNAE, + ) + inherent_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_BUG + + meat = /obj/item/food/meat/slab/human/mutant/apid + liked_food = VEGETABLES | MEAT | FRUIT + disliked_food = GROSS | BUGS | GORE + toxic_food = RAW | SEAFOOD + + changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_MAGIC | MIRROR_PRIDE | ERT_SPAWN | RACE_SWAP | SLIME_EXTRACT + species_language_holder = /datum/language_holder/apid + + bodypart_overrides = list( + BODY_ZONE_HEAD = /obj/item/bodypart/head/apid, + BODY_ZONE_CHEST = /obj/item/bodypart/chest/apid, + BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/apid, + BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/apid, + BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/apid, + BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/apid, + ) + + var/obj/structure/beebox/hive/owned_hive + + var/datum/action/cooldown/spell/build_hive/build + var/datum/action/cooldown/spell/pointed/pollinate/pollinate + var/datum/action/cooldown/spell/change_pollination_stat/change_stat + + var/stored_honey = 0 + var/current_stat = "potency" + + /// UI displaying how much honey we have + var/atom/movable/screen/apid/honey/honeydisplay + + +/datum/species/apid/on_species_gain(mob/living/carbon/human/human_who_gained_species, datum/species/old_species, pref_load) + . = ..() + RegisterSignal(human_who_gained_species, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS, PROC_REF(damage_weakness)) + build = new + build.Grant(human_who_gained_species) + + pollinate = new + pollinate.Grant(human_who_gained_species) + + change_stat = new + change_stat.Grant(human_who_gained_species) + + if(human_who_gained_species.hud_used) + var/datum/hud/hud_used = human_who_gained_species.hud_used + + honeydisplay = new /atom/movable/screen/apid/honey() + honeydisplay.hud = hud_used + hud_used.infodisplay += honeydisplay + adjust_honeycount(0) + hud_used.show_hud(hud_used.hud_version) + +/datum/species/apid/proc/adjust_honeycount(amount) + stored_honey = max(0, amount + stored_honey) + honeydisplay?.maptext = FORMAT_HONEY_CHARGES_TEXT(stored_honey) + +/datum/species/apid/on_species_loss(mob/living/carbon/human/C, datum/species/new_species, pref_load) + . = ..() + UnregisterSignal(C, COMSIG_MOB_APPLY_DAMAGE_MODIFIERS) + if(build) + build.Remove(C) + if(pollinate) + pollinate.Remove(C) + if(change_stat) + change_stat.Remove(C) + + if(C.hud_used) + var/datum/hud/hud_used = C.hud_used + + hud_used.infodisplay -= honeydisplay + QDEL_NULL(honeydisplay) + +/datum/species/apid/spec_life(mob/living/carbon/human/H, seconds_per_tick, times_fired) + . = ..() + if(honeydisplay) + return + + if(H.hud_used) + var/datum/hud/hud_used = H.hud_used + + honeydisplay = new /atom/movable/screen/apid/honey() + honeydisplay.hud = hud_used + hud_used.infodisplay += honeydisplay + adjust_honeycount(0) + hud_used.show_hud(hud_used.hud_version) + +/datum/species/apid/proc/damage_weakness(datum/source, list/damage_mods, damage_amount, damagetype, def_zone, sharpness, attack_direction, obj/item/attacking_item) + SIGNAL_HANDLER + + if(istype(attacking_item, /obj/item/melee/flyswatter)) + damage_mods += 10 // Yes, a 10x damage modifier + +/datum/species/apid/get_scream_sound(mob/living/carbon/human/human) + return 'sound/voice/moth/scream_moth.ogg' + +/datum/species/apid/get_species_description() + return "Apids are a race of bipedal bees from the jungle planet of Saltu. Due to their large bodies, they have lost the ability to fly." + +#undef ui_honeydisplay +#undef FORMAT_HONEY_CHARGES_TEXT diff --git a/monkestation/code/modules/botany/tools.dm b/monkestation/code/modules/botany/tools.dm new file mode 100644 index 000000000000..9802dd996396 --- /dev/null +++ b/monkestation/code/modules/botany/tools.dm @@ -0,0 +1,29 @@ +/obj/item/shovel/spade/pre_attack(atom/A, mob/living/user, params) + if(A.GetComponent(/datum/component/plant_growing)) + if(!do_after(user, 3 SECONDS, A)) + return ..() + SEND_SIGNAL(A, COMSIG_PLANTER_REMOVE_PLANTS) + return TRUE + . = ..() + +/obj/item/cultivator/pre_attack(atom/A, mob/living/user, params) + if(SEND_SIGNAL(A, COMSIG_GROWING_ADJUST_WEED, -10)) + user.visible_message(span_notice("[user] uproots the weeds."), span_notice("You remove the weeds from [src].")) + return TRUE + . = ..() + +/obj/item/secateurs/pre_attack(atom/A, mob/living/user, params) + if(SEND_SIGNAL(A, COMSIG_GROWING_TRY_SECATEUR, user)) + return TRUE + . = ..() + +/obj/item/graft/pre_attack(atom/A, mob/living/user, params) + if(SEND_SIGNAL(A, COMSIG_GROWER_TRY_GRAFT, user, src)) + return TRUE + . = ..() + +/obj/item/bio_cube/pre_attack(atom/A, mob/living/user, params) + if(SEND_SIGNAL(A, COMSIG_ATTEMPT_BIOBOOST, total_duration)) + qdel(src) + return TRUE + . = ..() diff --git a/monkestation/code/modules/cassettes/track_folder/base_tracks.json b/monkestation/code/modules/cassettes/track_folder/base_tracks.json index 20c98475f40e..f55fa3d0da61 100644 --- a/monkestation/code/modules/cassettes/track_folder/base_tracks.json +++ b/monkestation/code/modules/cassettes/track_folder/base_tracks.json @@ -190,16 +190,6 @@ "genre": "Rock" }, { - "url": "https://files.catbox.moe/977069.m4a", - "title": "Miles Away", - "duration": 2520, - "artist": "Winger", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { "url": "https://files.catbox.moe/i5q08z.m4a", "title": "Run, Sally, Run!", "duration": 2870, @@ -210,16 +200,6 @@ "genre": "Electronic" }, { - "url": "https://files.catbox.moe/34kdfv.mp3", - "title": "Kickstart my Heart", - "duration": 2830, - "artist": "Motley Crue", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Metal" - }, - { "url": "https://files.catbox.moe/ul3m8e.mp3", "title": "Bizarre Love Triangle", "duration": 2610, @@ -340,16 +320,6 @@ "genre": "Rock" }, { - "url": "https://files.catbox.moe/1rgsqr.m4a", - "title": "A Night on Bald Mountain", - "duration": 6830, - "artist": "Modest Mussorgsky", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Classical and Orchestral" - }, - { "url": "https://files.catbox.moe/frq3un.m4a", "title": "Keep on Loving You", "duration": 2000, @@ -440,16 +410,6 @@ "genre": "Metal" }, { - "url": "https://files.catbox.moe/0e0wmd.m4a", - "title": "Gettin' Jiggy Wit It", - "duration": 2270, - "artist": "Will Smith", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Hip-Hop and Rap" - }, - { "url": "https://files.catbox.moe/fm20mk.m4a", "title": "Lay It Down", "duration": 2050, @@ -580,16 +540,6 @@ "genre": "Folk and Indie" }, { - "url": "https://files.catbox.moe/8c3n30.m4a", - "title": "New Sins for Old", - "duration": 1520, - "artist": "Leslie Fish", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Folk and Indie" - }, - { "url": "https://files.catbox.moe/t9i5vh.mp3", "title": "Space Hero", "duration": 1680, @@ -620,16 +570,6 @@ "genre": "Folk and Indie" }, { - "url": "https://files.catbox.moe/1vowpl.m4a", - "title": "Harder, Better, Faster, Stronger", - "duration": 2260, - "artist": "Daft Punk", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { "url": "https://files.catbox.moe/fnse07.m4a", "title": "Crazy Train", "duration": 2960, @@ -680,36 +620,6 @@ "genre": "Disco, Funk, Soul, and R&B" }, { - "url": "https://files.catbox.moe/u3ylxd.m4a", - "title": "Still D.R.E", - "duration": 2740, - "artist": "Dr. Dre (feat. Snoop Dogg)", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Hip-Hop and Rap" - }, - { - "url": "https://files.catbox.moe/2r3cbk.m4a", - "title": "It Was a Good Day", - "duration": 2600, - "artist": "Ice Cube", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Hip-Hop and Rap" - }, - { - "url": "https://files.catbox.moe/asthsu.m4a", - "title": "A Kiss to Build a Dream On", - "duration": 1840, - "artist": "Louis Armstrong", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Jazz" - }, - { "url": "https://files.catbox.moe/rze7qx.m4a", "title": "(I Always Kill) The Things I Love", "duration": 1750, @@ -780,16 +690,6 @@ "genre": "Electronic" }, { - "url": "https://files.catbox.moe/4vxedh.m4a", - "title": "Miami Disco", - "duration": 2700, - "artist": "Perturbator", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { "url": "https://files.catbox.moe/dw5h51.m4a", "title": "Shattered Dreams", "duration": 2050, @@ -840,16 +740,6 @@ "genre": "Electronic" }, { - "url": "https://files.catbox.moe/0y4tjc.m4a", - "title": "Angel", - "duration": 3790, - "artist": "Massive Attack", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { "url": "https://files.catbox.moe/ke8uk5.m4a", "title": "Beware the Beast", "duration": 2260, @@ -870,16 +760,6 @@ "genre": "Pop" }, { - "url": "https://cdn.discordapp.com/attachments/528973352728264714/956446080638283826/Deus_Ex_-_009_-_UNATCO_-_Ambient.mp3", - "title": "UNATCO - Ambience", - "duration": 1610, - "artist": "Michiel van den Bos", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { "url": "https://files.catbox.moe/gcej08.m4a", "title": "UNATCO - Conversation", "duration": 1720, @@ -890,26 +770,6 @@ "genre": "Electronic" }, { - "url": "https://cdn.discordapp.com/attachments/528973352728264714/956453078750593044/EdenShard_-_Deus_Ex-_Revision_Original_Soundtrack_-_06_The_Oath_of_Service.mp3", - "title": "Oath of Service", - "duration": 2160, - "artist": "EdenShard", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { - "url": "https://cdn.discordapp.com/attachments/528973352728264714/956453288600039434/EdenShard_-_Deus_Ex-_Revision_Original_Soundtrack_-_08_Mind_the_Synaptic_Gap.mp3", - "title": "Mind the Synaptic Gap", - "duration": 5460, - "artist": "EdenShard", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { "url": "https://files.catbox.moe/pf929m.m4a", "title": "Virtual Insanity", "duration": 3400, @@ -950,16 +810,6 @@ "genre": "Disco, Funk, Soul, and R&B" }, { - "url": "https://files.catbox.moe/cqqyzj.m4a", - "title": "Sexual Healing", - "duration": 2430, - "artist": "Marvin Gaye", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Disco, Funk, Soul, and R&B" - }, - { "url": "https://files.catbox.moe/t9mwa9.m4a", "title": "Sometimes", "duration": 2530, @@ -1010,16 +860,6 @@ "genre": "Rock" }, { - "url": "https://files.catbox.moe/50sksv.m4a", - "title": "Do It Again", - "duration": 3560, - "artist": "Steely Dan", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { "url": "https://files.catbox.moe/j3ntvh.m4a", "title": "Reelin' In The Years", "duration": 2750, @@ -1040,16 +880,6 @@ "genre": "Rock" }, { - "url": "https://files.catbox.moe/5sh98r.m4a", - "title": "Everlong", - "duration": 2500, - "artist": "Foo Fighters", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { "url": "https://files.catbox.moe/fdjdmj.m4a", "title": "Limelight", "duration": 2590, @@ -1080,16 +910,6 @@ "genre": "Rock" }, { - "url": "https://files.catbox.moe/c961k1.m4a", - "title": "Blue Monday", - "duration": 4430, - "artist": "New Order", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Pop" - }, - { "url": "https://files.catbox.moe/gldcwv.m4a", "title": "Dancin' In the Ruins", "duration": 2410, @@ -1100,16 +920,6 @@ "genre": "Rock" }, { - "url": "https://files.catbox.moe/1ckdtl.m4a", - "title": "The Boys Are Back In Town", - "duration": 2690, - "artist": "Thin Lizzy", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { "url": "https://files.catbox.moe/w8plzv.m4a", "title": "What It Takes", "duration": 3110, @@ -1200,16 +1010,6 @@ "genre": "Hip-Hop and Rap" }, { - "url": "https://files.catbox.moe/5qgnr9.m4a", - "title": "The Power Of Love", - "duration": 2340, - "artist": "Huey Lewis and The News", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { "url": "https://files.catbox.moe/whbte8.m4a", "title": "Just a Gigolo / I Ain't Got Nobody", "duration": 2780, @@ -1260,16 +1060,6 @@ "genre": "Folk and Indie" }, { - "url": "https://files.catbox.moe/6rbq2q.m4a", - "title": "All That Could Ever Be", - "duration": 3960, - "artist": "Rik Schaffer", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Folk and Indie" - }, - { "url": "https://files.catbox.moe/l8baty.m4a", "title": "Sleepwalking", "duration": 2170, @@ -1330,7 +1120,7 @@ "genre": "Disco, Funk, Soul, and R&B" }, { - "url": "https://files.catbox.moe/mt0w0u.mp3", + "url": "https://files.catbox.moe/tpab44.m4a", "title": "Wayfarer", "duration": 2710, "artist": "Kavinsky", @@ -1430,26 +1220,6 @@ "genre": "Metal" }, { - "url": "https://files.catbox.moe/4sdesv.m4a", - "title": "Ace of Spades", - "duration": 1670, - "artist": "Motorhead", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Metal" - }, - { - "url": "https://files.catbox.moe/3qqt2v.m4a", - "title": "Rise Of The Chaos Wizards", - "duration": 2370, - "artist": "Gloryhammer", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Metal" - }, - { "url": "https://files.catbox.moe/fgcdu2.m4a", "title": "Turn Up the Radio", "duration": 2780, @@ -1480,16 +1250,6 @@ "genre": "Metal" }, { - "url": "https://files.catbox.moe/gl22vl.m4a", - "title": "The Hellion / Electric Eye", - "duration": 2610, - "artist": "Judas Priest", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Metal" - }, - { "url": "https://files.catbox.moe/j4fvlc.mp3", "title": "2 Minutes To Midnight", "duration": 3600, @@ -1590,26 +1350,6 @@ "genre": "Hip-Hop and Rap" }, { - "url": "https://files.catbox.moe/8nyu9s.m4a", - "title": "Just a Friend", - "duration": 2380, - "artist": "Biz Markie", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Hip-Hop and Rap" - }, - { - "url": "https://files.catbox.moe/2b0wu2.m4a", - "title": "Intergalactic", - "duration": 2290, - "artist": "Beastie Boys", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Hip-Hop and Rap" - }, - { "url": "https://files.catbox.moe/nv66ck.mp3", "title": "Wild Child", "duration": 3120, @@ -1690,26 +1430,6 @@ "genre": "Classical and Orchestral" }, { - "url": "https://files.catbox.moe/15ybh9.mp3", - "title": "Handel This", - "duration": 890, - "artist": "Andreas Waldetoft", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Classical and Orchestral" - }, - { - "url": "https://files.catbox.moe/0294ze.m4a", - "title": "Lohengrin: Prelude", - "duration": 5930, - "artist": "Richard Wagner", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Classical and Orchestral" - }, - { "url": "https://files.catbox.moe/xgq1tr.m4a", "title": "Concerto Grosso no. 10 in B Minor - Allegro", "duration": 2150, @@ -1730,16 +1450,6 @@ "genre": "Classical and Orchestral" }, { - "url": "https://files.catbox.moe/8bdxq3.m4a", - "title": "Secunda", - "duration": 1230, - "artist": "Jeremy Soule", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Classical and Orchestral" - }, - { "url": "https://files.catbox.moe/lqgoiv.m4a", "title": "Far Horizons", "duration": 3310, @@ -1840,26 +1550,6 @@ "genre": "Rock" }, { - "url": "https://files.catbox.moe/231ksl.m4a", - "title": "Pjanoo (Club Mix)", - "duration": 4500, - "artist": "Eric Prydz", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { - "url": "https://files.catbox.moe/58hrry.m4a", - "title": "Inferno Galore", - "duration": 2250, - "artist": "Carpenter Brut", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { "url": "https://files.catbox.moe/vsdg7v.m4a", "title": "Low Rider", "duration": 1910, @@ -1890,16 +1580,6 @@ "genre": "Disco, Funk, Soul, and R&B" }, { - "url": "https://files.catbox.moe/24cqmw.m4a", - "title": "Every Rose Has It's Thorn", - "duration": 2600, - "artist": "Poison", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { "url": "https://files.catbox.moe/v3otpl.m4a", "title": "Fallen Angel", "duration": 2380, @@ -1930,26 +1610,6 @@ "genre": "Rock" }, { - "url": "https://files.catbox.moe/cavpff.m4a", - "title": "Take Me Home Tonight", - "duration": 2110, - "artist": "Eddie Money", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { - "url": "https://files.catbox.moe/n8l93a.m4a", - "title": "Same Ol' Situation (S.O.S.)", - "duration": 2530, - "artist": "Motely Crue", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Metal" - }, - { "url": "https://files.catbox.moe/gnr6py.m4a", "title": "Metal Health (Bang Your Head)", "duration": 3160, @@ -1960,16 +1620,6 @@ "genre": "Metal" }, { - "url": "https://files.catbox.moe/1drakx.m4a", - "title": "Shout At The Devil", - "duration": 1950, - "artist": "Motley Crue", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Metal" - }, - { "url": "https://files.catbox.moe/1kevft.m4a", "title": "Walk With Me in Hell", "duration": 3110, @@ -2120,16 +1770,6 @@ "genre": "Metal" }, { - "url": "https://files.catbox.moe/0gol3u.m4a", - "title": "Superbeast", - "duration": 2200, - "artist": "Rob Zombie", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Metal" - }, - { "url": "https://files.catbox.moe/irrxal.m4a", "title": "Perihelion", "duration": 1910, @@ -2160,16 +1800,6 @@ "genre": "Metal" }, { - "url": "https://files.catbox.moe/isa0lb.m4a", - "title": "On The Wind", - "duration": 2250, - "artist": "Dream Evil", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Metal" - }, - { "url": "https://files.catbox.moe/oi9dt1.m4a", "title": "Catch The Rainbow", "duration": 3960, @@ -2180,16 +1810,6 @@ "genre": "Rock" }, { - "url": "https://files.catbox.moe/1ozes3.m4a", - "title": "Peace Sells", - "duration": 2440, - "artist": "Megadeth", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Metal" - }, - { "url": "https://files.catbox.moe/iyi2ll.m4a", "title": "Never Gonna Give You Up", "duration": 2130, @@ -2210,16 +1830,6 @@ "genre": "Rock" }, { - "url": "https://files.catbox.moe/3blefo.m4a", - "title": "Piano Man", - "duration": 3400, - "artist": "Billy Joel", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { "url": "https://files.catbox.moe/qq4cbx.m4a", "title": "Mr. Blue Sky", "duration": 3030, @@ -2260,16 +1870,6 @@ "genre": "Rock" }, { - "url": "https://files.catbox.moe/2n2gdq.m4a", - "title": "Changes", - "duration": 2170, - "artist": "David Bowie", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { "url": "https://files.catbox.moe/jzucsv.mp3", "title": "A Horse With No Name", "duration": 2470, @@ -2430,16 +2030,6 @@ "genre": "Country and Western" }, { - "url": "https://files.catbox.moe/5mtinr.m4a", - "title": "The Devil Went Down to Georgia", - "duration": 2140, - "artist": "The Charlie Daniels Band", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Country and Western" - }, - { "url": "https://files.catbox.moe/qvd0qg.m4a", "title": "Cottonwood Tree", "duration": 2370, @@ -2640,26 +2230,6 @@ "genre": "Electronic" }, { - "url": "https://files.catbox.moe/36juno.mp3", - "title": "Head First", - "duration": 2130, - "artist": "HOME", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { - "url": "https://files.catbox.moe/bvwhm0.m4a", - "title": "Dust", - "duration": 3020, - "artist": "M|O|O|N", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { "url": "https://files.catbox.moe/b6urx6.m4a", "title": "Silent Strike", "duration": 2970, @@ -2710,16 +2280,6 @@ "genre": "Electronic" }, { - "url": "https://files.catbox.moe/3zb1hx.m4a", - "title": "Into The Labyrinth", - "duration": 3100, - "artist": "Kraddy", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { "url": "https://files.catbox.moe/qxadpn.m4a", "title": "Turbo Killer", "duration": 2080, @@ -2760,16 +2320,6 @@ "genre": "Electronic" }, { - "url": "https://files.catbox.moe/bem2ur.m4a", - "title": "Cheerleader Effect", - "duration": 2170, - "artist": "Carpenter Brut", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { "url": "https://files.catbox.moe/hwfg7e.m4a", "title": "Hairspray Hurricane", "duration": 3310, @@ -2790,26 +2340,6 @@ "genre": "Electronic" }, { - "url": "https://files.catbox.moe/4ju4p1.mp3", - "title": "Lone Digger", - "duration": 2300, - "artist": "Caravan Palace", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { - "url": "https://files.catbox.moe/4c0lmk.m4a", - "title": "Dessert", - "duration": 1840, - "artist": "Jun Chikuma", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { "url": "https://files.catbox.moe/g0ms06.m4a", "title": "Propane Nightmares", "duration": 3130, @@ -2850,26 +2380,6 @@ "genre": "Folk and Indie" }, { - "url": "https://files.catbox.moe/6ryyk0.m4a", - "title": "Apocalypse", - "duration": 2900, - "artist": "Cigarettes After Sex", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Folk and Indie" - }, - { - "url": "https://files.catbox.moe/2z6lpx.mp3", - "title": "Pioneer's Song", - "duration": 1790, - "artist": "Leslie Fish", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Folk and Indie" - }, - { "url": "https://files.catbox.moe/e55ey0.mp3", "title": "Bones", "duration": 1200, @@ -2940,26 +2450,6 @@ "genre": "Pop" }, { - "url": "https://files.catbox.moe/zifzgk.m4a", - "title": "Little Girls", - "duration": 2230, - "artist": "Oingo Boingo", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Pop" - }, - { - "url": "https://files.catbox.moe/5yk0mm.m4a", - "title": "Everything She Wants", - "duration": 3920, - "artist": "Wham!", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Pop" - }, - { "url": "https://files.catbox.moe/5ye8et.m4a", "title": "Careless Whisper", "duration": 3030, @@ -2970,16 +2460,6 @@ "genre": "Pop" }, { - "url": "https://files.catbox.moe/9a5dov.m4a", - "title": "Space Cowboy", - "duration": 2160, - "artist": "Jamiroquai", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Disco, Funk, Soul, and R&B" - }, - { "url": "https://files.catbox.moe/dpjb9r.m4a", "title": "Video Killed The Radio Star", "duration": 2530, @@ -3270,16 +2750,6 @@ "genre": "Rock" }, { - "url": "https://files.catbox.moe/20exnb.mp3", - "title": "Fortunate Son", - "duration": 1400, - "artist": "Creedence Clearwater Revival", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { "url": "https://files.catbox.moe/8xzmw5.mp3", "title": "Bohemian Rhapsody", "duration": 3580, @@ -3320,26 +2790,6 @@ "genre": "Hip-Hop and Rap" }, { - "url": "https://files.catbox.moe/6i31kh.m4a", - "title": "Rapper's Delight", - "duration": 8850, - "artist": "The Sugar Hill Gang", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Hip-Hop and Rap" - }, - { - "url": "https://files.catbox.moe/3y9k43.m4a", - "title": "Super Freak", - "duration": 2020, - "artist": "Rick James", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Hip-Hop and Rap" - }, - { "url": "https://files.catbox.moe/93ynna.m4a", "title": "Party All The Time", "duration": 2530, @@ -3390,16 +2840,6 @@ "genre": "Hip-Hop and Rap" }, { - "url": "https://files.catbox.moe/59nnfc.m4a", - "title": "Take_it_Back_v2", - "duration": 1690, - "artist": "Denzel Curry & Kenny Beats", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Hip-Hop and Rap" - }, - { "url": "https://files.catbox.moe/pkwkd1.m4a", "title": "All Caps", "duration": 1340, @@ -3539,397 +2979,6 @@ "jukebox": true }, { - "url" : "https://cdn.discordapp.com/attachments/404660884531707906/676726491806957568/Cassette.mp3", - "title" : "Cassette", - "duration" :2310, - "artist" : "Efence", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Ambience" - }, - { - "url" : "https://cdn.discordapp.com/attachments/676384242426707979/676730040624480264/Glowing_Red_Dust.mp3", - "title" : "Red Glowing Dust", - "duration" :3370, - "artist" : "Jón Hallur Haraldsson (also known as RealX)", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Ambience" - }, - { - - "url" : "https://cdn.discordapp.com/attachments/676384242426707979/676730820530274323/Stellardrone_-_Eternity.mp3", - "title" : "Eternity", - "duration" :3810, - "artist" : "Stellardrone", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Ambience" - }, - { - "url" : "https://cdn.discordapp.com/attachments/612993188655005697/676732888712216596/DJ-ZEK_-_Simulate.mp3", - "title" : "Simulate", - "duration" :4850, - "artist" : "Fawxtrot (now know as DJ Zek)", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Ambience" - }, - { - "url" : "https://cdn.discordapp.com/attachments/520929567540772873/707899715261562960/FitnessGram_20_Meter_PACER_Test_Full_Length_OFFICIAL_Audio_Version.mp3", - "title" : "The FitnessGram Pacer Test", - "duration" : 13720, - "artist" : "The Cooper Institute (FitnessGram)", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Ambience" - }, - { - "url" : "https://cdn.discordapp.com/attachments/575066308362895444/701639140109844530/Peaceful_Orbits.mp3", - "title" : "Peaceful Orbits", - "duration" : 2040, - "artist" : "TeknoAXE", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Ambience" - }, - { - "url" : "https://cdn.discordapp.com/attachments/575066308362895444/701639311896084511/Passing_Time_in_an_Escape_Pod.mp3", - "title" : "Passing Time in an Escape Pod", - "duration" : 2300, - "artist" : "TeknoAXE", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Ambience" - }, - { - "url" : "https://cdn.discordapp.com/attachments/331435060735508480/613310956927451137/Persona_5_48_-_Beneath_the_Mask_-rain-.mp3", - "title" : "Beneath the Mask -Rain Version-", - "duration" : 2790, - "artist" : "Persona 5", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Jazz" - }, - { - "url" : "https://cdn.discordapp.com/attachments/331037116094087168/543191420370944001/DragonSharkOriginal3.mp3", - "title" : "Terrorbyte/Dragon Shark theme", - "duration" : 2100, - "artist" : "RetroSpecter", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { - "url" : "https://cdn.discordapp.com/attachments/528973352728264714/583441087633555456/Prey_-_Everything_is_Going_to_Be_Ok_online-audio-converter.com.mp3", - "title" : "Everything Is Going To Be Ok", - "duration" : 1600, - "artist" : "Mick Gordon", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { - "url" : "https://cdn.discordapp.com/attachments/311691674130710528/463590234978910228/Saga_of_Tanya_the_Evil_-_Jingo_Jungle_Opening_ENGLISH_ver_AmaLee.mp3", - "title" : "Jingo Jungle", - "duration" : 2440, - "artist" : "Leeandlie http://bit.ly/Leeandlie", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { - "url" : "https://cdn.discordapp.com/attachments/311691674130710528/468345155498541066/TheMadnessOfFate.mp3", - "title" : "Madness of Fate", - "duration" :4030, - "artist" : "NIIC the singing dog - http://bit.ly/NIICDOGYT", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { - "url" : "https://cdn.discordapp.com/attachments/311691674130710528/449909217403469837/Hard_Bass_School_-_narkotik_kal.mp3", - "title" : "Narkotik kal", - "duration" : 2350, - "artist" : "Hardbass", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Ambience" - }, - { - "url" : "https://cdn.discordapp.com/attachments/528973352728264714/613807206047154217/TERRORBYTE_-_HELIOS.mp3", - "title" : "Helios", - "duration" : 2850, - "artist" : "TERRORBYTE", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Metal" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/453308819422838784/System_Of_A_Down_-_Shimmy_11.mp3", - "title" : "Shimmy", - "duration" : 1110, - "artist" : "System of a Down", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/453308919067049995/System_Of_A_Down_-_Science_10.mp3", - "title" : "Science", - "duration" : 1630, - "artist" : "System of a Down", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/453308944706699284/System_Of_A_Down_-_Deer_Dance_03.mp3", - "title" : "deer dance", - "duration" : 1750, - "artist" : "System of a Down", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/453308945411473428/System_Of_A_Down_-_Atwa.mp3", - "title" : "Atwa", - "duration" : 1790, - "artist" : "System of a Down", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/453308969772122112/System_Of_A_Down_-_Forest_08.mp3", - "title" : "Forest", - "duration" : 2400, - "artist" : "System of a Down", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/453308978089426985/System_Of_A_Down_-_Prison_Song_01.mp3", - "title" : "Prison song", - "duration" : 2110, - "artist" : "System of a Down", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/453308978089426985/System_Of_A_Down_-_Prison_Song_01.mp3", - "title" : "SPIDERS!", - "duration" : 2150, - "artist" : "System of a Down", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/453309000780611604/System_Of_A_Down_-_Aerials_14.mp3", - "title" : "Aerials", - "duration" : 2320, - "artist" : "System of a Down", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/453309015955472394/System_Of_A_Down_-_Jet_Pilot_04.mp3", - "title" : "Jet Pilot", - "duration" : 1260, - "artist" : "System of a Down", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/454653832945860608/Skeleton_Man-_The_Axis_of_Awesome.mp3", - "title" : "Skeleton Man", - "duration" : 1510, - "artist" : "AoW", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Classical and Orchestral" - }, - { - "url" : "https://cdn.discordapp.com/attachments/745916615606140938/775593951259918336/Nanook_Rubs_It.mp3", - "title" : "Nanook Rubs It", - "duration" :2770, - "artist" : "Frank Zappa", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Country and Western" - }, - { - "url" : "https://cdn.discordapp.com/attachments/745916615606140938/775593948307521566/Muffin_Man.mp3", - "title" : "Muffin Man", - "duration" :3370, - "artist" : "Frank Zappa", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Folk and Indie" - }, - { - "url" : "https://cdn.discordapp.com/attachments/404660884531707906/456271996314058752/Edgar_Rothermich_-01_Theme_from_Silent_Hill_jungle-vibe.com.mp3", - "title" : "Silent Hill", - "duration" : 1760, - "artist" : "Akira Yamaoka and Edgar Rothermich", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Country and Western" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/460490120240300052/Nightcore_-_Tetris.mp3", - "title" : "Tetris Remixed", - "duration" : 1910, - "artist" : "nightcore", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/460490219167154186/VERSACE_2017_-_TIX__The_Pssy_Project.mp3", - "title" : "Versance", - "duration" : 2030, - "artist" : "PussyProject", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Classical and Orchestral" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/460490229523021834/-_.mp3", - "title" : "Russian Techno", - "duration" : 2400, - "artist" : "Some russians", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Hip-Hop and Rap" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/460490231611654144/Rock_Dog_2017_Movie_Official_Lyric_Video_Glorious_by_Adam_Friedman.mp3", - "title" : "Gloriouse", - "duration" : 2200, - "artist" : "Rock Dog", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Rock" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/460490245725487104/Gigi_DAgostino_-_You_Spin_Me_Round__Tecno_Fes_2_.mp3", - "title" : "You spin me around", - "duration" : 2580, - "artist" : "Gigi DAgostino", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Pop" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/460490246610747402/-__Kyary_Pamyu_Pamyu_-.mp3", - "title" : "Pamyu Pamyu", - "duration" : 2640, - "artist" : "Kyary", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Pop" - }, - { - "url" : "https://cdn.discordapp.com/attachments/377217220041900034/457628201200648202/Berserk_-_S3RL_ft_Iceman.mp3", - "title" : "Beserk", - "duration" :1620, - "artist" : "S3RL ft Iceman", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Metal" - }, - { - "url" : "https://cdn.discordapp.com/attachments/458630047167807488/520025625852117002/Nick_Hakim_-_Pour_Another.mp3", - "title" : "Pour Another", - "duration" :3510, - "artist" : "Nick Hakim", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Jazz" - }, - { - "url" : "https://cdn.discordapp.com/attachments/526693916859170816/559236326264668190/Ocean_Man.mp3", - "title" : "Ocean Man", - "duration" : 1270, - "artist" : "Ween", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Classical and Orchestral" - }, - { - "url" : "https://cdn.discordapp.com/attachments/458630047167807488/520025672702492682/Nick_Hakim_-_I_Dont_Know.mp3", - "title" : "I dont know", - "duration" :2990, - "artist" : "Nick Hakim", - "secret": false, - "lobby": false, - "jukebox": true, - "genre": "Jazz" - }, - { - "url" : "https://cdn.discordapp.com/attachments/404660884531707906/614356720503881728/Underfell_Megalovania.mp3", - "title" : "Underfell", - "duration" :1830, - "artist" : "keno9988iii", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Metal" - }, - { - "url" : "https://cdn.discordapp.com/attachments/661630061983825938/663112074880352276/Headhunterz_-_Scrap_Attack_HQ.mp3", - "title" : "Scrap Attack", - "duration" :3730, - "artist" : "Headhunterz", - "secret": true, - "lobby": false, - "jukebox": true, - "genre": "Electronic" - }, - { "url" : "https://files.catbox.moe/h967s5.mp3", "title" : "MEGALOVANIA", "duration" : 1416, @@ -4355,7 +3404,6 @@ "lobby": false, "jukebox": true }, - { "url": "https://files.catbox.moe/qa6ahi.mp3", "title": "Deadlines", diff --git a/monkestation/code/modules/clothing/suits/costume.dm b/monkestation/code/modules/clothing/suits/costume.dm index b3fc4c6f60a7..61e535e67052 100644 --- a/monkestation/code/modules/clothing/suits/costume.dm +++ b/monkestation/code/modules/clothing/suits/costume.dm @@ -101,4 +101,63 @@ /datum/store_item/suit/helldiverarmor name = "Helldiver Armor" item_path = /obj/item/clothing/suit/helldiverarmor - item_cost = 10000 \ No newline at end of file + item_cost = 10000 + +/obj/item/clothing/suit/hooded/ashsuit + name = "ashsuit suit" + desc = "Whoever controls the Plasma, controls the Spinward Sector." + icon_state = "ashsuit" + icon = 'monkestation/icons/obj/clothing/suits.dmi' + worn_icon = 'monkestation/icons/mob/clothing/suit.dmi' + inhand_icon_state = null + body_parts_covered = CHEST|GROIN|LEGS|ARMS + cold_protection = CHEST|GROIN|LEGS|ARMS + min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + heat_protection = CHEST|GROIN|LEGS|ARMS + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + hoodtype = /obj/item/clothing/head/hooded/ashsuit + armor_type = /datum/armor/hooded_ashsuit + allowed = list( + /obj/item/flashlight, + /obj/item/gun/energy/recharge/kinetic_accelerator, + /obj/item/mining_scanner, + /obj/item/pickaxe, + /obj/item/resonator, + /obj/item/storage/bag/ore, + /obj/item/t_scanner/adv_mining_scanner, + /obj/item/tank/internals, + ) + resistance_flags = FIRE_PROOF + clothing_traits = list(TRAIT_SNOWSTORM_IMMUNE) + +/datum/armor/hooded_ashsuit + melee = 30 + bullet = 10 + laser = 10 + energy = 20 + bomb = 50 + fire = 50 + acid = 50 + +/obj/item/clothing/head/hooded/ashsuit + name = "ashsuit hood" + desc = "For covering your face when walking the ash dunes." + icon = 'monkestation/icons/obj/clothing/hats.dmi' + worn_icon = 'monkestation/icons/mob/clothing/head.dmi' + icon_state = "ashsuit" + body_parts_covered = HEAD + flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS + cold_protection = HEAD + min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + armor_type = /datum/armor/hooded_explorer + resistance_flags = FIRE_PROOF + +/obj/item/clothing/suit/hooded/ashsuit/Initialize(mapload) + . = ..() + AddComponent(/datum/component/armor_plate) + +/obj/item/clothing/head/hooded/ashsuit/Initialize(mapload) + . = ..() + AddComponent(/datum/component/armor_plate) diff --git a/monkestation/code/modules/datums/components/glitching_state.dm b/monkestation/code/modules/datums/components/glitching_state.dm new file mode 100644 index 000000000000..976c7180ade0 --- /dev/null +++ b/monkestation/code/modules/datums/components/glitching_state.dm @@ -0,0 +1,51 @@ +/datum/component/glitching_state + var/count = 5 + var/list/obj/effect/after_image/after_images + +/datum/component/glitching_state/Initialize(count = 5) + . = ..() + var/atom/movable/movable = parent + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + src.after_images = list() + src.count = count + if(count > 1) + for(var/number = 1 to count) + var/obj/effect/after_image/added_image = new /obj/effect/after_image(null, time_a = 1, time_b = 5, finalized_alpha = 128 - 64 * (number - 1) / (count - 1)) + after_images += added_image + movable.vis_contents += added_image + added_image.active = TRUE + added_image.sync_with_parent(parent, actual_loc = FALSE) + else + var/obj/effect/after_image/added_image = new /obj/effect/after_image(null, time_a = 1, time_b = 5, finalized_alpha = 64) + after_images |= added_image + movable.vis_contents += added_image + added_image.active = TRUE + added_image.sync_with_parent(parent, actual_loc = FALSE) + + START_PROCESSING(SSobj, src) + +/datum/component/glitching_state/Destroy(force, silent) + STOP_PROCESSING(SSobj, src) + var/atom/movable/movable = parent + movable?.vis_contents -= after_images + QDEL_LIST(after_images) + return ..() + +/datum/component/glitching_state/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, PROC_REF(on_dir_change)) + +/datum/component/glitching_state/UnregisterFromParent() + . = ..() + UnregisterSignal(parent, COMSIG_ATOM_DIR_CHANGE) + +/datum/component/glitching_state/process(seconds_per_tick) + for(var/obj/effect/after_image/image as anything in after_images) + image.sync_with_parent(parent, actual_loc = FALSE) + +/datum/component/glitching_state/proc/on_dir_change(datum/source, old_dir, new_dir) + SIGNAL_HANDLER + for(var/obj/effect/after_image/image as anything in after_images) + image.sync_with_parent(parent, actual_loc = FALSE, dir_override = new_dir) + diff --git a/monkestation/code/modules/datums/mood_events/generic_positive_events.dm b/monkestation/code/modules/datums/mood_events/generic_positive_events.dm index c6e60732859c..37f39c486364 100644 --- a/monkestation/code/modules/datums/mood_events/generic_positive_events.dm +++ b/monkestation/code/modules/datums/mood_events/generic_positive_events.dm @@ -5,3 +5,7 @@ /datum/mood_event/nanite_happiness/add_effects(message) description = "+++++++[message]+++++++" +/datum/mood_event/monster_hunter + description = "Glory to the hunt." + mood_change = 10 + hidden = TRUE diff --git a/monkestation/code/modules/donator/code/datum/loadout.dm b/monkestation/code/modules/donator/code/datum/loadout.dm index 06523f6e0351..13ae663f3a05 100644 --- a/monkestation/code/modules/donator/code/datum/loadout.dm +++ b/monkestation/code/modules/donator/code/datum/loadout.dm @@ -322,13 +322,13 @@ //quilark /datum/loadout_item/suit/coat_quilark - name = "discontinued winter coat" + name = "Discontinued Winter Coat" item_path = /obj/item/clothing/suit/toggle/quilark donator_only = TRUE requires_purchase = FALSE /datum/loadout_item/head/fur_cap_quilark - name = "discontinued cross hat" + name = "Discontinued Cross Hat" item_path = /obj/item/clothing/head/costume/fur_cap_quilark donator_only = TRUE requires_purchase = FALSE @@ -357,3 +357,34 @@ item_path = /obj/item/clothing/suit/hooded/org_thirteen donator_only = TRUE requires_purchase = FALSE + +//kumi +/datum/loadout_item/suit/kimono_kumi + name = "Shrine Keeper's Kimono" + item_path = /obj/item/clothing/suit/jacket/kimono_kumi + donator_only = TRUE + requires_purchase = FALSE + +/datum/loadout_item/under/miscellaneous/sarashi_kumi + name = "Shrine Keeper's Sarashi" + item_path = /obj/item/clothing/under/sarashi_kumi + donator_only = TRUE + requires_purchase = FALSE + +/datum/loadout_item/shoes/sandals_kumi + name = "Shrine Keeper's Sandals" + item_path = /obj/item/clothing/shoes/sandal/kumi + donator_only = TRUE + requires_purchase = FALSE + +/datum/loadout_item/gloves/fingerless_long + name = "Long Fingerless Gloves" + item_path = /obj/item/clothing/gloves/fingerless/long + donator_only = TRUE + requires_purchase = FALSE + +/datum/loadout_item/neck/bell + name = "Bell Necklace" + item_path = /obj/item/clothing/neck/bell + donator_only = TRUE + requires_purchase = FALSE diff --git a/monkestation/code/modules/donator/code/item/clothing.dm b/monkestation/code/modules/donator/code/item/clothing.dm index e2ae9a3d457a..4bbdb045b251 100644 --- a/monkestation/code/modules/donator/code/item/clothing.dm +++ b/monkestation/code/modules/donator/code/item/clothing.dm @@ -324,3 +324,41 @@ cold_protection = HEAD flags_inv = HIDEHAIR|HIDEEARS armor_type = /datum/armor/hooded_winterhood + +/obj/item/clothing/suit/jacket/kimono_kumi + name = "shrine keeper's kimono" + desc = "An ornately patterned shrine keeper's kimono, it seems a little big?" + icon = 'monkestation/code/modules/donator/icons/obj/clothing.dmi' + worn_icon = 'monkestation/code/modules/donator/icons/mob/clothing.dmi' + icon_state = "kimono_kumi" + +/obj/item/clothing/under/sarashi_kumi + name = "shrine keeper's sarashi" + desc = "Some chest wraps paired with a skirt. It digs at the waist a little." + icon = 'monkestation/code/modules/donator/icons/obj/clothing.dmi' + worn_icon = 'monkestation/code/modules/donator/icons/mob/clothing.dmi' + icon_state = "sarashi_kumi" + can_adjust = FALSE + +/obj/item/clothing/shoes/sandal/kumi + name = "shrine keeper's sandals" + desc = "A fancy pair of sandals made of hinoki." + icon = 'monkestation/code/modules/donator/icons/obj/clothing.dmi' + worn_icon = 'monkestation/code/modules/donator/icons/mob/clothing.dmi' + icon_state = "sandals_kumi" + +/obj/item/clothing/neck/bell + name = "bell necklace" + desc = "A bell attached to some string. We really are living in the future." + icon = 'monkestation/code/modules/donator/icons/obj/clothing.dmi' + worn_icon = 'monkestation/code/modules/donator/icons/mob/clothing.dmi' + icon_state = "neck_bell" + +/obj/item/clothing/gloves/fingerless/long + name = "long fingerless gloves" + desc = "A pair of fingerless gloves that reaches the elbow." + icon = 'monkestation/code/modules/donator/icons/obj/clothing.dmi' + worn_icon = 'monkestation/code/modules/donator/icons/mob/clothing.dmi' + icon_state = "fingerless_long" + + diff --git a/monkestation/code/modules/donator/icons/mob/clothing.dmi b/monkestation/code/modules/donator/icons/mob/clothing.dmi index 22f12dbda98f..66fc0d2ce907 100644 Binary files a/monkestation/code/modules/donator/icons/mob/clothing.dmi and b/monkestation/code/modules/donator/icons/mob/clothing.dmi differ diff --git a/monkestation/code/modules/donator/icons/obj/clothing.dmi b/monkestation/code/modules/donator/icons/obj/clothing.dmi index 02c934c0e240..9678d36e0c85 100644 Binary files a/monkestation/code/modules/donator/icons/obj/clothing.dmi and b/monkestation/code/modules/donator/icons/obj/clothing.dmi differ diff --git a/monkestation/code/modules/food_and_drinks/machinery/smartfridge.dm b/monkestation/code/modules/food_and_drinks/machinery/smartfridge.dm new file mode 100644 index 000000000000..48b3b47971ab --- /dev/null +++ b/monkestation/code/modules/food_and_drinks/machinery/smartfridge.dm @@ -0,0 +1,6 @@ +/obj/machinery/smartfridge + /// The tgui theme to use. Default is null, which means the Nanotrasen theme is used. + var/tgui_theme = null + +/obj/machinery/smartfridge/ui_static_data(mob/user) + return list("ui_theme" = tgui_theme) diff --git a/monkestation/code/modules/ghost_players/arena/maps/snow.dmm b/monkestation/code/modules/ghost_players/arena/maps/snow.dmm index 1bf45782f404..d59b33d9d9f3 100644 --- a/monkestation/code/modules/ghost_players/arena/maps/snow.dmm +++ b/monkestation/code/modules/ghost_players/arena/maps/snow.dmm @@ -19,7 +19,7 @@ dir = 1 }, /obj/effect/turf_decal/weather/snow/corner, -/obj/machinery/hydroponics/soil{ +/obj/machinery/growing/soil{ pixel_y = 7; color = " #DCDCDC" }, @@ -147,7 +147,7 @@ dir = 9 }, /obj/effect/turf_decal/weather/snow/corner, -/obj/machinery/hydroponics/soil{ +/obj/machinery/growing/soil{ pixel_y = 7; color = " #DCDCDC" }, @@ -258,7 +258,7 @@ dir = 1 }, /obj/effect/turf_decal/weather/snow/corner, -/obj/machinery/hydroponics/soil{ +/obj/machinery/growing/soil{ pixel_y = 7; color = " #DCDCDC" }, diff --git a/monkestation/code/modules/guns/laser.dm b/monkestation/code/modules/guns/laser.dm new file mode 100644 index 000000000000..40468a54bb69 --- /dev/null +++ b/monkestation/code/modules/guns/laser.dm @@ -0,0 +1,2 @@ +/obj/item/gun/energy/laser/captain + ammo_type = list(/obj/item/ammo_casing/energy/laser/hellfire/antique, /obj/item/ammo_casing/energy/disabler) diff --git a/monkestation/code/modules/hydroponics/grown/honeydew.dm b/monkestation/code/modules/hydroponics/grown/honeydew.dm index eb313b71cd09..f12e9d25d139 100644 --- a/monkestation/code/modules/hydroponics/grown/honeydew.dm +++ b/monkestation/code/modules/hydroponics/grown/honeydew.dm @@ -4,6 +4,8 @@ icon = 'monkestation/icons/obj/hydroponics/fruit.dmi' icon_state = "honeydew-seed" icon_dead = "honeydew-dead" + icon_grow = null + icon_harvest = null species = "honeydew" plantname = "Honeydew Melon Vines" product = /obj/item/food/grown/honeydew diff --git a/monkestation/code/modules/hydroponics/machines/composter.dm b/monkestation/code/modules/hydroponics/machines/composter.dm index e8b25ba4bf6c..927c16d3e389 100644 --- a/monkestation/code/modules/hydroponics/machines/composter.dm +++ b/monkestation/code/modules/hydroponics/machines/composter.dm @@ -126,6 +126,8 @@ . = ..() desc = "A cube made of pure biomatter, it seems to be denser than normal making it last [DisplayTimeText(total_duration)]. Does wonders on plant trays." + + /obj/item/bio_cube/attackby(obj/item/attacking_item, mob/living/user) . = ..() if(istype(attacking_item, /obj/item/bio_cube)) diff --git a/monkestation/code/modules/hydroponics/machines/splicer.dm b/monkestation/code/modules/hydroponics/machines/splicer.dm index 6c0879d60a5a..96b0a281b5fc 100644 --- a/monkestation/code/modules/hydroponics/machines/splicer.dm +++ b/monkestation/code/modules/hydroponics/machines/splicer.dm @@ -144,7 +144,7 @@ new_seed.icon_dead = first_seed.icon_dead new_seed.growthstages = first_seed.growthstages new_seed.growing_icon = first_seed.growing_icon - new_seed.seed_offset = first_seed.seed_offset + new_seed.plant_icon_offset = first_seed.plant_icon_offset new_seed.reagents_add = first_seed.reagents_add.Copy() diff --git a/monkestation/code/modules/hydroponics/seeds.dm b/monkestation/code/modules/hydroponics/seeds.dm index b271390e939c..e21075db3543 100644 --- a/monkestation/code/modules/hydroponics/seeds.dm +++ b/monkestation/code/modules/hydroponics/seeds.dm @@ -31,7 +31,7 @@ special_mutations = return_viable_mutations() /obj/item/seeds/spliced/harvest(mob/user) - var/obj/machinery/hydroponics/parent = loc //for ease of access + var/atom/movable/parent = loc //for ease of access var/t_amount = 0 var/list/result = list() var/output_loc = parent.Adjacent(user) ? user.loc : parent.loc //needed for TK @@ -66,7 +66,6 @@ product_name = t_prod.seed.plantname if(getYield() >= 1) SSblackbox.record_feedback("tally", "food_harvested", getYield(), product_name) - parent.update_tray(user, t_amount) return result @@ -100,7 +99,7 @@ S.icon_dead = icon_dead S.growthstages = growthstages S.growing_icon = growing_icon - S.seed_offset = seed_offset + S.plant_icon_offset = plant_icon_offset S.traits_in_progress = traits_in_progress if(istype(src, /obj/item/seeds/spliced)) diff --git a/monkestation/code/modules/hydroponics/seeds/coconut.dm b/monkestation/code/modules/hydroponics/seeds/coconut.dm index aa04ce9e64d4..3f51a8ef87f3 100644 --- a/monkestation/code/modules/hydroponics/seeds/coconut.dm +++ b/monkestation/code/modules/hydroponics/seeds/coconut.dm @@ -1,7 +1,7 @@ /obj/item/seeds/coconut name = "pack of Coconut seeds" desc = "These seeds grow into coconut trees." - seed_offset = -12 + plant_icon_offset = 0 icon = 'monkestation/icons/obj/hydroponics/fruit.dmi' icon_state = "coconut_seed" diff --git a/monkestation/code/modules/jobs/job_types/chaplain.dm b/monkestation/code/modules/jobs/job_types/chaplain.dm index 71e59f810752..b5a924f2d455 100644 --- a/monkestation/code/modules/jobs/job_types/chaplain.dm +++ b/monkestation/code/modules/jobs/job_types/chaplain.dm @@ -1,4 +1,4 @@ /datum/job/chaplain/after_spawn(mob/living/spawned, client/player_client) . = ..() if(spawned.mind) - ADD_TRAIT(spawned.mind, TRAIT_BLOODSUCKER_HUNTER, JOB_TRAIT) + ADD_TRAIT(spawned.mind, TRAIT_OCCULTIST, JOB_TRAIT) diff --git a/monkestation/code/modules/jobs/job_types/curator.dm b/monkestation/code/modules/jobs/job_types/curator.dm index 1748076be53f..08aaaea47b53 100644 --- a/monkestation/code/modules/jobs/job_types/curator.dm +++ b/monkestation/code/modules/jobs/job_types/curator.dm @@ -1,4 +1,4 @@ /datum/job/curator/after_spawn(mob/living/spawned, client/player_client) . = ..() if(spawned.mind) - ADD_TRAIT(spawned.mind, TRAIT_BLOODSUCKER_HUNTER, JOB_TRAIT) + ADD_TRAIT(spawned.mind, TRAIT_OCCULTIST, JOB_TRAIT) diff --git a/monkestation/code/modules/jobs/job_types/security_assistant.dm b/monkestation/code/modules/jobs/job_types/security_assistant.dm new file mode 100644 index 000000000000..57824251bdea --- /dev/null +++ b/monkestation/code/modules/jobs/job_types/security_assistant.dm @@ -0,0 +1,91 @@ +/datum/job/security_assistant + title = JOB_SECURITY_ASSISTANT + description = "Fine people for trivial things. Be an glorified hall monitor." + auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY + department_head = list(JOB_HEAD_OF_SECURITY) + faction = FACTION_STATION + total_positions = 5 + spawn_positions = 5 + supervisors = "the Head of Security, the Warden, and any proper security officers" + minimal_player_age = 7 + exp_requirements = 300 + exp_required_type = EXP_TYPE_CREW + exp_granted_type = EXP_TYPE_CREW + config_tag = "SECURITY_ASSISTANT" + + outfit = /datum/outfit/job/security_assistant + plasmaman_outfit = /datum/outfit/plasmaman/security + + paycheck = PAYCHECK_CREW + paycheck_department = ACCOUNT_SEC + + liver_traits = list(TRAIT_LAW_ENFORCEMENT_METABOLISM) + + display_order = JOB_DISPLAY_ORDER_SECURITY_ASSISTANT + bounty_types = CIV_JOB_SEC + departments_list = list(/datum/job_department/security) + + family_heirlooms = list(/obj/item/book/manual/wiki/security_space_law, /obj/item/clothing/head/beret/sec) + + mail_goodies = list( + /obj/item/food/donut/caramel = 10, + /obj/item/food/donut/matcha = 10, + /obj/item/food/donut/blumpkin = 5 + ) + rpg_title = "Guard" + job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN + + alt_titles = list( + "Security Assistant", + "Deputy", + "Hall Monitor", + "Assistant Officer", + "Professional Snitch" + ) + +/datum/outfit/job/security_assistant + name = "Security Assistant" + jobtype = /datum/job/security_assistant + + id_trim = /datum/id_trim/job/security_assistant + uniform = /obj/item/clothing/under/rank/security/officer/grey + backpack_contents = list( + /obj/item/restraints/handcuffs/cable/zipties = 1, + /obj/item/reagent_containers/spray/pepper = 1 + ) + belt = /obj/item/modular_computer/pda/security + ears = /obj/item/radio/headset/headset_sec + shoes = /obj/item/clothing/shoes/sneakers/black + l_pocket = /obj/item/citationinator + r_pocket = /obj/item/assembly/flash/handheld + glasses = /obj/item/clothing/glasses/hud/security + gloves = /obj/item/clothing/gloves/tackler/dolphin + + backpack = /obj/item/storage/backpack/security + satchel = /obj/item/storage/backpack/satchel/sec + duffelbag = /obj/item/storage/backpack/duffelbag/sec + + box = /obj/item/storage/box/survival/security + + implants = list(/obj/item/implant/mindshield) // i think this is stupid but this was apparently agreed upon ~lucy + +/datum/id_trim/job/security_assistant + assignment = "Security Assistant" + trim_state = "trim_secass" + department_color = COLOR_SECURITY_RED + subdepartment_color = COLOR_SECURITY_RED + sechud_icon_state = SECHUD_SECURITY_ASSISTANT + minimal_access = list( + ACCESS_BRIG_ENTRANCE, + ACCESS_SECURITY, + ACCESS_PERMABRIG + ) + extra_access = list( + ACCESS_MAINT_TUNNELS + ) + template_access = list( + ACCESS_CAPTAIN, + ACCESS_CHANGE_IDS, + ACCESS_HOS + ) + job = /datum/job/security_assistant diff --git a/monkestation/code/modules/mapping/access_helpers.dm b/monkestation/code/modules/mapping/access_helpers.dm index 6a7dac901dfa..e335d07792ce 100644 --- a/monkestation/code/modules/mapping/access_helpers.dm +++ b/monkestation/code/modules/mapping/access_helpers.dm @@ -27,3 +27,7 @@ /obj/effect/mapping_helpers/airlock/access/any/engineering/maintenance/west_offset offset_dir = WEST + +/obj/effect/mapping_helpers/airlock/access/any/security/permabrig/get_access() + . = ..() + . += list(ACCESS_PERMABRIG, ACCESS_BRIG) diff --git a/monkestation/code/modules/mech_comp/objects/collector.dm b/monkestation/code/modules/mech_comp/objects/collector.dm index ff26cb7266a7..47180d755b1b 100644 --- a/monkestation/code/modules/mech_comp/objects/collector.dm +++ b/monkestation/code/modules/mech_comp/objects/collector.dm @@ -29,6 +29,11 @@ for(var/obj/item/contained_item in src.loc) if(contained_item == src) continue + if(contained_item.anchored) + continue + if(contained_item.type in typesof(/obj/item/mcobject)) + continue + if(count >= max_transfer) break if(linked_storage.attempt_insert(contained_item, src)) diff --git a/monkestation/code/modules/mech_comp/objects/interactor.dm b/monkestation/code/modules/mech_comp/objects/interactor.dm index 7bfd38cd1556..06813ee541e3 100644 --- a/monkestation/code/modules/mech_comp/objects/interactor.dm +++ b/monkestation/code/modules/mech_comp/objects/interactor.dm @@ -120,7 +120,8 @@ for(var/atom/movable/listed_atom in selected_turf) if(dummy_human == listed_atom || src == listed_atom) continue - if(listed_atom in typesof(/obj/item/mcobject)) + + if(listed_atom.type in typesof(/obj/item/mcobject)) continue if(!held_item) @@ -140,10 +141,6 @@ held_item.melee_attack_chain(dummy_human, listed_atom) dummy_human.istate &= ~ISTATE_SECONDARY - for(var/atom/movable/listed_atom in src) - if(listed_atom == dummy_human) - continue - listed_atom.forceMove(src.loc) flash() /obj/item/mcobject/interactor/update_overlays() diff --git a/monkestation/code/modules/metrics/subsystem_analytics/generics.dm b/monkestation/code/modules/metrics/subsystem_analytics/generics.dm index 0a806c365c6e..f8154ed148b6 100644 --- a/monkestation/code/modules/metrics/subsystem_analytics/generics.dm +++ b/monkestation/code/modules/metrics/subsystem_analytics/generics.dm @@ -33,6 +33,14 @@ . = ..() var/list/cust = list() cust["processing_machines"] = length(processing) + cust["total_power_used"] = 0 + cust["total_excess_power"] = 0 + cust["total_power"] = 0 + for(var/datum/powernet/powernet in powernets) + cust["total_excess_power"] += powernet.netexcess + cust["total_power_used"] += powernet.load + cust["total_power"] += powernet.avail + .["custom"] = cust /datum/controller/subsystem/mobs/get_metrics() diff --git a/monkestation/code/modules/mob/living/carbon/human/species_type/abductors.dm b/monkestation/code/modules/mob/living/carbon/human/species_type/abductors.dm index 350f2924e7d9..d8a4a2a05ab6 100644 --- a/monkestation/code/modules/mob/living/carbon/human/species_type/abductors.dm +++ b/monkestation/code/modules/mob/living/carbon/human/species_type/abductors.dm @@ -1,3 +1,34 @@ +/datum/species/abductor + inherent_traits = list( + TRAIT_GENELESS, + TRAIT_NEVER_WOUNDED, + TRAIT_NOBLOOD, + TRAIT_NOBREATH, + TRAIT_NODISMEMBER, + TRAIT_NOHUNGER, + TRAIT_QUICKER_CARRY, + TRAIT_TRUE_NIGHT_VISION, + TRAIT_VIRUSIMMUNE, + TRAIT_CHUNKYFINGERS_IGNORE_BATON + ) + species_language_holder = /datum/language_holder/universal + coldmod = 0.5 + heatmod = 0.5 + siemens_coeff = 0.5 + var/datum/component/sign_language/signer + +/datum/species/abductor/on_species_gain(mob/living/carbon/user, datum/species/old_species) + . = ..() + user.update_sight() + if(!user.GetComponent(/datum/component/sign_language)) // if they're already capable of signing, don't clobber that + signer = user.AddComponent(/datum/component/sign_language) + +/datum/species/abductor/on_species_loss(mob/living/carbon/user) + . = ..() + user.update_sight() + if(!QDELETED(signer)) + QDEL_NULL(signer) + /datum/species/abductor/get_scream_sound(mob/living/carbon/human/human) return 'sound/weather/ashstorm/inside/weak_end.ogg' diff --git a/monkestation/code/modules/mob/living/emote.dm b/monkestation/code/modules/mob/living/emote.dm index 18d7dfec063b..0b135c2f56fa 100644 --- a/monkestation/code/modules/mob/living/emote.dm +++ b/monkestation/code/modules/mob/living/emote.dm @@ -227,3 +227,18 @@ return TRUE else return FALSE + +// The below function replaces the `/datum/emote/silicon/mob_type_allowed_typecache` variable. If +// you remove this function, be sure to replace the above variable. +/datum/emote/silicon/can_run_emote(mob/user, status_check, intentional) + // Silicons and simple bots can always use silicon emotes, assuming nothing else is wrong + if(issilicon(user) || isbot(user)) + return ..() + + // Allow users with synthetic voice boxes to use silicon emotes as well, because a synthetic + // voice box is more akin to a speaker than a human larynx. + var/tongue = user.get_organ_slot(ORGAN_SLOT_TONGUE) + if(istype(tongue, /obj/item/organ/internal/tongue/synth)) + return ..() + + return FALSE diff --git a/monkestation/code/modules/pollution/generic_pollutants.dm b/monkestation/code/modules/pollution/generic_pollutants.dm index da61911adc46..7c3abe36aedd 100644 --- a/monkestation/code/modules/pollution/generic_pollutants.dm +++ b/monkestation/code/modules/pollution/generic_pollutants.dm @@ -12,6 +12,9 @@ if(prob(20)) victim.emote("cough") + if(is_species(victim, /datum/species/apid)) + victim.adjust_drowsiness(2 SECONDS) + ///From smoking weed /datum/pollutant/smoke/cannabis name = "Cannabis" diff --git a/monkestation/code/modules/pollution/pollution.dm b/monkestation/code/modules/pollution/pollution.dm index 47c35eb04491..eb89ee58a933 100644 --- a/monkestation/code/modules/pollution/pollution.dm +++ b/monkestation/code/modules/pollution/pollution.dm @@ -80,7 +80,7 @@ to_chat(sniffer, span_notice(smell_string)) /datum/pollution/proc/scrub_amount(amount_to_scrub, update_active = TRUE, planetary_multiplier = FALSE) - if(amount_to_scrub >= total_amount) + if(amount_to_scrub >= total_amount || !isopenturf(my_turf)) qdel(src) return if(planetary_multiplier && my_turf.planetary_atmos) //Dissipate faster on planetary atmos diff --git a/monkestation/code/modules/ranching/chickens/ai/chicken_controller.dm b/monkestation/code/modules/ranching/chickens/ai/chicken_controller.dm index 573c858509ce..32e580c67c34 100644 --- a/monkestation/code/modules/ranching/chickens/ai/chicken_controller.dm +++ b/monkestation/code/modules/ranching/chickens/ai/chicken_controller.dm @@ -21,6 +21,7 @@ var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_entered), ) + can_idle = FALSE // we want these to be running always /datum/ai_controller/chicken/TryPossessPawn(atom/new_pawn) if(!isliving(new_pawn)) diff --git a/monkestation/code/modules/ranching/components/afterimage.dm b/monkestation/code/modules/ranching/components/afterimage.dm index eefbdf7d609b..900dedf8c6ab 100644 --- a/monkestation/code/modules/ranching/components/afterimage.dm +++ b/monkestation/code/modules/ranching/components/afterimage.dm @@ -1,7 +1,7 @@ /datum/component/after_image dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS var/rest_time - var/list/obj/after_image/after_images + var/list/obj/effect/after_image/after_images /datum/component/after_image/Initialize(count = 4, rest_time = 1) ..() @@ -11,11 +11,11 @@ src.after_images = list() if(count > 1) for(var/number = 1 to count) - var/obj/after_image/added_image = new /obj/after_image(null) + var/obj/effect/after_image/added_image = new /obj/effect/after_image(null) added_image.finalized_alpha = 200 - 100 * (number - 1) / (count - 1) after_images += added_image else - var/obj/after_image/added_image = new /obj/after_image(null) + var/obj/effect/after_image/added_image = new /obj/effect/after_image(null) added_image.finalized_alpha = 100 after_images |= added_image @@ -24,20 +24,20 @@ RegisterSignal(parent, COMSIG_MOVABLE_THROW_LANDED, PROC_REF(throw_landed)) /datum/component/after_image/RegisterWithParent() - for(var/obj/after_image/listed_image in src.after_images) + for(var/obj/effect/after_image/listed_image in src.after_images) listed_image.active = TRUE src.sync_after_images() /datum/component/after_image/UnregisterFromParent() UnregisterSignal(parent, list(COMSIG_MOVABLE_MOVED, COMSIG_ATOM_DIR_CHANGE, COMSIG_MOVABLE_THROW_LANDED)) - for(var/obj/after_image/listed_image in src.after_images) + for(var/obj/effect/after_image/listed_image in src.after_images) listed_image.active = FALSE qdel(listed_image) . = ..() /datum/component/after_image/Destroy() if(length(src.after_images)) - for(var/obj/after_image/listed_image in src.after_images) + for(var/obj/effect/after_image/listed_image in src.after_images) qdel(listed_image) src.after_images.Cut() src.after_images = null @@ -58,7 +58,7 @@ /datum/component/after_image/proc/sync_after_images(dir_override=null) set waitfor = FALSE - var/obj/after_image/targeted_image = new(null) + var/obj/effect/after_image/targeted_image = new(null) targeted_image.active = TRUE targeted_image.sync_with_parent(parent) targeted_image.loc = null @@ -66,44 +66,39 @@ targeted_image.setDir(dir_override) var/atom/movable/parent_am = parent var/atom/target_loc = parent_am.loc - for(var/obj/after_image/listed_image in src.after_images) + for(var/obj/effect/after_image/listed_image in src.after_images) sleep(src.rest_time) listed_image.sync_with_parent(targeted_image, target_loc) qdel(targeted_image) - - - - -/obj/after_image +/obj/effect/after_image mouse_opacity = FALSE anchored = 2 var/finalized_alpha = 100 var/appearance_ref = null var/active = FALSE -/obj/after_image/New() +/obj/effect/after_image/New(_loc, min_x = -3, max_x = 3, min_y = -3, max_y = 3, time_a = 0.5 SECONDS, time_b = 3 SECONDS, finalized_alpha = 100) . = ..() - animate(src, pixel_x=0, time=1, loop=-1) + src.finalized_alpha = finalized_alpha + animate(src, pixel_x=0, time=1, loop= -1) var/count = rand(5, 10) for(var/number = 1 to count) - var/time = 0.5 SECONDS + rand() * 3 SECONDS - var/pixel_x = number == count ? 0 : rand(-2, 2) - var/pixel_y = number == count ? 0 : rand(-2, 2) - animate(time = time, easing = pick(LINEAR_EASING, SINE_EASING, CIRCULAR_EASING, CUBIC_EASING), flags = ANIMATION_PARALLEL, pixel_x = pixel_x, pixel_y = pixel_y, loop =- 1) + var/time = time_a + rand() * time_b + var/pixel_x = number == count ? 0 : rand(min_x, max_y) + var/pixel_y = number == count ? 0 : rand(min_y, max_y) + animate(time = time, easing = pick(LINEAR_EASING, SINE_EASING, CIRCULAR_EASING, CUBIC_EASING), pixel_x = pixel_x, pixel_y = pixel_y, loop = -1) -/obj/after_image/proc/sync_with_parent(atom/movable/parent, loc_override=null) +/obj/effect/after_image/proc/sync_with_parent(atom/movable/parent, loc_override = null, actual_loc = TRUE, dir_override = null) if(!src.active) return src.name = parent.name src.desc = parent.desc src.glide_size = parent.glide_size - var/parent_appearance_ref = ref(parent.appearance) - if(istype(parent, /obj/after_image)) - var/obj/after_image/parent_after_image = parent + if(istype(parent, /obj/effect/after_image)) + var/obj/effect/after_image/parent_after_image = parent parent_appearance_ref = parent_after_image.appearance_ref - if(src.appearance_ref != parent_appearance_ref) src.appearance_ref = parent_appearance_ref src.appearance = parent.appearance @@ -111,15 +106,13 @@ src.plane = initial(src.plane) src.mouse_opacity = initial(src.mouse_opacity) src.anchored = initial(src.anchored) - var/atom/target_loc = loc_override ? loc_override : parent.loc - - if(target_loc != src.loc) + if(target_loc != src.loc && actual_loc) src.loc = target_loc + var/target_dir = isnull(dir_override) ? parent.dir : dir_override + if(src.dir != target_dir) + src.setDir(target_dir) - if(src.dir != parent.dir) - src.setDir(parent.dir) - -/obj/after_image/Destroy() +/obj/effect/after_image/Destroy() src.active = FALSE - . = ..() + return ..() diff --git a/monkestation/code/modules/research/techweb/all_nodes.dm b/monkestation/code/modules/research/techweb/all_nodes.dm index ae33bdedd7c4..41bd8f050650 100644 --- a/monkestation/code/modules/research/techweb/all_nodes.dm +++ b/monkestation/code/modules/research/techweb/all_nodes.dm @@ -170,9 +170,7 @@ "offline_nanites", "pyramid_nanites", ) - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000, TECHWEB_POINT_TYPE_NANITES = 2500) - hidden = TRUE - experimental = TRUE + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000, TECHWEB_POINT_TYPE_NANITES = 5000) /datum/techweb_node/nanite_storage_protocols id = "nanite_storage_protocols" @@ -185,9 +183,7 @@ "unsafe_storage_nanites", "zip_nanites", ) - research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000, TECHWEB_POINT_TYPE_NANITES = 2500) - hidden = TRUE - experimental = TRUE + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 5000, TECHWEB_POINT_TYPE_NANITES = 5000) /datum/techweb_node/adv_ballistics id = "adv_ballistics" diff --git a/monkestation/code/modules/security/code/citationinator.dm b/monkestation/code/modules/security/code/citationinator.dm new file mode 100644 index 000000000000..4d5d7baaf675 --- /dev/null +++ b/monkestation/code/modules/security/code/citationinator.dm @@ -0,0 +1,81 @@ +/obj/item/citationinator + name = "Citationinator" + desc = "A cheaply made plastic handheld doohickey, capable of issuing fines to ner-do-wells, and printing out a slip of paper with the details of the fine." + icon = 'monkestation/icons/obj/items/secass.dmi' + icon_state = "doohickey_closed" + inhand_icon_state = "doohickey" + worn_icon_state = "electronic" + lefthand_file = 'monkestation/icons/mob/inhands/equipment/secass_lefthand.dmi' + righthand_file = 'monkestation/icons/mob/inhands/equipment/secass_righthand.dmi' + w_class = WEIGHT_CLASS_SMALL + flags_1 = CONDUCT_1 + item_flags = NOBLUDGEON | NO_MAT_REDEMPTION + slot_flags = ITEM_SLOT_BELT + req_access = list(ACCESS_SECURITY) + +/obj/item/citationinator/attack_self(mob/living/user, modifiers) + if(!isliving(user)) + return + add_fingerprint(user) + icon_state = "doohickey_open" + issue_fine(user) + icon_state = initial(icon_state) + +/obj/item/citationinator/proc/issue_fine(mob/living/user) + var/obj/item/card/id/using_id = user.get_idcard() + if(!istype(using_id) || QDELING(using_id)) + return + if(!check_access(using_id)) + to_chat(user, span_warning("Insufficient access to issue citations!")) + return + var/list/choices = list() + for(var/datum/record/crew/person in GLOB.manifest.general) + if(!person.name) + continue + choices[person.name] = person + var/victim_name = tgui_input_list(user, "Select crew member to fine", "Do you got a loicense for that, mate?", choices, timeout = 1 MINUTES) + if(!victim_name) + return + var/datum/record/crew/victim = choices[victim_name] + if(!victim) + return + var/fine = tgui_input_number(user, "How many credits to fine?", "Civil Asset Forfeiture", default = 50, max_value = CONFIG_GET(number/maxfine), timeout = 1 MINUTES, round_value = 1) + if(!fine) + return + var/citation_name = trim(tgui_input_text(user, "What crime are they being fined for?", "Handling a fish in suspicious circumstances", max_length = MAX_NAME_LEN, encode = FALSE, timeout = 1 MINUTES)) + if(!citation_name) + return + var/reason = trim(tgui_input_text(user, "Provide details about the citation.", "Handling a fish in suspicious circumstances", multiline = TRUE, encode = FALSE, timeout = 1 MINUTES)) + var/issuer_name = using_id.registered_name || "Unknown" + var/datum/crime/citation/new_citation = new(name = citation_name, details = reason, author = issuer_name, fine = fine) + new_citation.alert_owner(user, src, victim.name, "You have been issued a [fine]cr citation for [citation_name] by [issuer_name]. Fines are payable at Security.") + victim.citations += new_citation + investigate_log("New Citation: [citation_name] Fine: [fine] | Added to [victim.name] by [key_name(user)]", INVESTIGATE_RECORDS) + SSblackbox.ReportCitation(REF(new_citation), user.ckey, user.real_name, victim.name, citation_name, fine) + + var/final_paper_text = "
Security Citation

" + final_paper_text += {" + + + + + + +
"} + final_paper_text += "" + final_paper_text += "" + final_paper_text += "" + final_paper_text += "" + final_paper_text += "" + final_paper_text += "" + final_paper_text += "
CitationDetailsAuthorTime AddedFine
[sanitize(new_citation.name)][sanitize(new_citation.details)][sanitize(new_citation.author)][new_citation.time][new_citation.fine]
" + + var/obj/item/paper/slip = new(drop_location()) + slip.name = "Citation - [victim.name] for [sanitize(new_citation.name)]" + slip.color = COLOR_FADED_PINK + slip.add_raw_text(final_paper_text) + slip.update_appearance() + user.put_in_hands(slip) + + user.visible_message(span_warning("[src] whirrs and clicks, before spitting out a slip of paper!")) + playsound(src, 'sound/machines/printer.ogg', vol = 40, vary = TRUE) diff --git a/monkestation/code/modules/slimecore/components/vac_tagged.dm b/monkestation/code/modules/slimecore/components/vac_tagged.dm index 317e3889cfa8..44db508e3ea0 100644 --- a/monkestation/code/modules/slimecore/components/vac_tagged.dm +++ b/monkestation/code/modules/slimecore/components/vac_tagged.dm @@ -8,9 +8,17 @@ creator = WEAKREF(creator_mob) +/datum/component/vac_tagged/Destroy(force, silent) + UnregisterSignal(parent, list(COMSIG_MOB_FED_ON, COMSIG_QDELETING)) + . = ..() + /datum/component/vac_tagged/RegisterWithParent() . = ..() RegisterSignal(parent, COMSIG_MOB_FED_ON, PROC_REF(on_fed_on)) + RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(on_deleting)) + +/datum/component/vac_tagged/proc/on_deleting() + qdel(src) /datum/component/vac_tagged/proc/on_fed_on(mob/living/source, mob/living/feeder, hunger_restored) SEND_SIGNAL(feeder, COMSIG_FRIENDSHIP_CHANGE, creator.resolve(), (hunger_restored * 0.1)) diff --git a/monkestation/code/modules/slimecore/corral/corral_data.dm b/monkestation/code/modules/slimecore/corral/corral_data.dm index c75b53e065da..97bc52ef5a89 100644 --- a/monkestation/code/modules/slimecore/corral/corral_data.dm +++ b/monkestation/code/modules/slimecore/corral/corral_data.dm @@ -21,6 +21,9 @@ if(!istype(slime)) continue managed_slimes |= slime + RegisterSignal(slime, COMSIG_ATOM_SUCKED, PROC_REF(remove_cause_sucked)) + RegisterSignal(slime, COMSIG_LIVING_DEATH, PROC_REF(remove_cause_sucked)) + RegisterSignals(slime, list(COMSIG_PREQDELETED, COMSIG_QDELETING), PROC_REF(try_remove)) /datum/corral_data/Destroy(force, ...) QDEL_LIST(corral_connectors) diff --git a/monkestation/code/modules/slimecore/mobs/_base_slime.dm b/monkestation/code/modules/slimecore/mobs/_base_slime.dm index 51a2d877d900..8e61f2a4754c 100644 --- a/monkestation/code/modules/slimecore/mobs/_base_slime.dm +++ b/monkestation/code/modules/slimecore/mobs/_base_slime.dm @@ -199,7 +199,7 @@ new_planning_subtree |= add_or_replace_tree(/datum/ai_planning_subtree/flee_target) if(slime_flags & CLEANER_SLIME) - new_planning_subtree |= add_or_replace_tree(/datum/ai_planning_subtree/cleaning_subtree) + new_planning_subtree |= add_or_replace_tree(/datum/ai_planning_subtree/cleaning_subtree_slime) if(!(slime_flags & PASSIVE_SLIME)) new_planning_subtree |= add_or_replace_tree(/datum/ai_planning_subtree/simple_find_target_no_trait/slime) diff --git a/monkestation/code/modules/slimecore/mobs/ai_controller/behaviours/clean_target.dm b/monkestation/code/modules/slimecore/mobs/ai_controller/behaviours/clean_target.dm index 36172a0bb356..13cf33e8046f 100644 --- a/monkestation/code/modules/slimecore/mobs/ai_controller/behaviours/clean_target.dm +++ b/monkestation/code/modules/slimecore/mobs/ai_controller/behaviours/clean_target.dm @@ -1,14 +1,14 @@ -/datum/ai_behavior/execute_clean +/datum/ai_behavior/execute_clean_slime behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH -/datum/ai_behavior/execute_clean/setup(datum/ai_controller/controller, target_key) +/datum/ai_behavior/execute_clean_slime/setup(datum/ai_controller/controller, target_key) . = ..() var/turf/target = controller.blackboard[target_key] if(isnull(target)) return FALSE set_movement_target(controller, target) -/datum/ai_behavior/execute_clean/perform(seconds_per_tick, datum/ai_controller/controller, target_key) +/datum/ai_behavior/execute_clean_slime/perform(seconds_per_tick, datum/ai_controller/controller, target_key) . = ..() var/mob/living/basic/living_pawn = controller.pawn var/atom/target = controller.blackboard[target_key] @@ -22,7 +22,7 @@ qdel(target) // Sent to the shadow realm to never be seen again finish_action(controller, TRUE, target_key) -/datum/ai_behavior/execute_clean/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) +/datum/ai_behavior/execute_clean_slime/finish_action(datum/ai_controller/controller, succeeded, target_key, targeting_strategy_key, hiding_location_key) . = ..() var/atom/target = controller.blackboard[target_key] if(QDELETED(target) || is_type_in_typecache(target, controller.blackboard[BB_HUNTABLE_TRASH])) @@ -32,10 +32,10 @@ return controller.clear_blackboard_key(target_key) -/datum/ai_behavior/find_and_set/in_list/clean_targets +/datum/ai_behavior/find_and_set/in_list/clean_targets_slime action_cooldown = 2 SECONDS -/datum/ai_behavior/find_and_set/in_list/clean_targets/search_tactic(datum/ai_controller/controller, locate_paths, search_range) +/datum/ai_behavior/find_and_set/in_list/clean_targets_slime/search_tactic(datum/ai_controller/controller, locate_paths, search_range) var/list/found = typecache_filter_list(oview(search_range, controller.pawn), locate_paths) if(length(found)) return pick(found) diff --git a/monkestation/code/modules/slimecore/mobs/ai_controller/controller.dm b/monkestation/code/modules/slimecore/mobs/ai_controller/controller.dm index c4b937fbb729..074598d14826 100644 --- a/monkestation/code/modules/slimecore/mobs/ai_controller/controller.dm +++ b/monkestation/code/modules/slimecore/mobs/ai_controller/controller.dm @@ -19,3 +19,4 @@ /datum/ai_planning_subtree/simple_find_target_no_trait/slime, /datum/ai_planning_subtree/basic_melee_attack_subtree/slime, ) + can_idle = FALSE // we want these to be running always diff --git a/monkestation/code/modules/slimecore/mobs/ai_controller/subtrees/cleaning_tree.dm b/monkestation/code/modules/slimecore/mobs/ai_controller/subtrees/cleaning_tree.dm index f8671b4e1c17..1f768cc92696 100644 --- a/monkestation/code/modules/slimecore/mobs/ai_controller/subtrees/cleaning_tree.dm +++ b/monkestation/code/modules/slimecore/mobs/ai_controller/subtrees/cleaning_tree.dm @@ -1,9 +1,9 @@ -/datum/ai_planning_subtree/cleaning_subtree +/datum/ai_planning_subtree/cleaning_subtree_slime -/datum/ai_planning_subtree/cleaning_subtree/SelectBehaviors(datum/ai_controller/basic_controller/controller, seconds_per_tick) +/datum/ai_planning_subtree/cleaning_subtree_slime/SelectBehaviors(datum/ai_controller/basic_controller/controller, seconds_per_tick) if(controller.blackboard_key_exists(BB_CLEAN_TARGET)) - controller.queue_behavior(/datum/ai_behavior/execute_clean, BB_CLEAN_TARGET) + controller.queue_behavior(/datum/ai_behavior/execute_clean_slime, BB_CLEAN_TARGET) return SUBTREE_RETURN_FINISH_PLANNING var/list/final_hunt_list = list() @@ -13,5 +13,5 @@ final_hunt_list += controller.blackboard[BB_HUNTABLE_PESTS] final_hunt_list += controller.blackboard[BB_HUNTABLE_TRASH] - controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list/clean_targets, BB_CLEAN_TARGET, final_hunt_list) + controller.queue_behavior(/datum/ai_behavior/find_and_set/in_list/clean_targets_slime, BB_CLEAN_TARGET, final_hunt_list) diff --git a/monkestation/code/modules/storytellers/converted_events/solo/bloodsuckers.dm b/monkestation/code/modules/storytellers/converted_events/solo/bloodsuckers.dm index 8d18788fb841..cf80b78d5757 100644 --- a/monkestation/code/modules/storytellers/converted_events/solo/bloodsuckers.dm +++ b/monkestation/code/modules/storytellers/converted_events/solo/bloodsuckers.dm @@ -12,6 +12,7 @@ JOB_HEAD_OF_SECURITY, JOB_PRISONER, JOB_SECURITY_OFFICER, + JOB_SECURITY_ASSISTANT, JOB_WARDEN, ) restricted_roles = list( diff --git a/monkestation/code/modules/storytellers/converted_events/solo/brother.dm b/monkestation/code/modules/storytellers/converted_events/solo/brother.dm index bb2cf73944fa..fffda04fdd40 100644 --- a/monkestation/code/modules/storytellers/converted_events/solo/brother.dm +++ b/monkestation/code/modules/storytellers/converted_events/solo/brother.dm @@ -13,7 +13,8 @@ JOB_HEAD_OF_SECURITY, JOB_PRISONER, JOB_SECURITY_OFFICER, - JOB_WARDEN + JOB_SECURITY_ASSISTANT, + JOB_WARDEN, ) restricted_roles = list( JOB_AI, diff --git a/monkestation/code/modules/storytellers/converted_events/solo/changeling.dm b/monkestation/code/modules/storytellers/converted_events/solo/changeling.dm index c655e25f9b84..b9ad1370c21c 100644 --- a/monkestation/code/modules/storytellers/converted_events/solo/changeling.dm +++ b/monkestation/code/modules/storytellers/converted_events/solo/changeling.dm @@ -12,6 +12,7 @@ JOB_HEAD_OF_SECURITY, JOB_PRISONER, JOB_SECURITY_OFFICER, + JOB_SECURITY_ASSISTANT, JOB_WARDEN, ) restricted_roles = list( diff --git a/monkestation/code/modules/storytellers/converted_events/solo/heretic.dm b/monkestation/code/modules/storytellers/converted_events/solo/heretic.dm index 54189f3e9c5e..e0a303497e8c 100644 --- a/monkestation/code/modules/storytellers/converted_events/solo/heretic.dm +++ b/monkestation/code/modules/storytellers/converted_events/solo/heretic.dm @@ -13,6 +13,7 @@ JOB_HEAD_OF_SECURITY, JOB_PRISONER, JOB_SECURITY_OFFICER, + JOB_SECURITY_ASSISTANT, JOB_WARDEN, ) restricted_roles = list( diff --git a/monkestation/code/modules/storytellers/converted_events/solo/monsterhunter.dm b/monkestation/code/modules/storytellers/converted_events/solo/monsterhunter.dm index 5947d45f23f6..be39ec01ca7d 100644 --- a/monkestation/code/modules/storytellers/converted_events/solo/monsterhunter.dm +++ b/monkestation/code/modules/storytellers/converted_events/solo/monsterhunter.dm @@ -2,9 +2,9 @@ /datum/round_event_control/antagonist/solo/monsterhunter name = "Monster Hunters" - track = EVENT_TRACK_MAJOR //being an anrtag event is for backend reasons, the event itself is major + track = EVENT_TRACK_MAJOR antag_flag = ROLE_MONSTERHUNTER - tags = list(TAG_MAGICAL, TAG_TARGETED, TAG_COMBAT) + tags = list(TAG_MAGICAL, TAG_TARGETED, TAG_COMBAT, TAG_CREW_ANTAG, TAG_DESTRUCTIVE) antag_datum = /datum/antagonist/monsterhunter protected_roles = list( JOB_CAPTAIN, @@ -14,6 +14,7 @@ JOB_RESEARCH_DIRECTOR, JOB_DETECTIVE, JOB_HEAD_OF_SECURITY, + JOB_SECURITY_ASSISTANT, JOB_PRISONER, JOB_SECURITY_OFFICER, JOB_WARDEN, @@ -22,7 +23,7 @@ JOB_AI, JOB_CYBORG, ) - min_players = 10 //no required enemies deu to instead needing enemy antags + min_players = 10 //no required enemies due to instead needing enemy antags weight = 25 // high weight as its a threat maximum_antags = 1 prompted_picking = TRUE @@ -35,10 +36,10 @@ var/count = 0 for(var/datum/antagonist/monster as anything in GLOB.antagonists) - if(!monster.owner || !monster.owner.current || monster.owner.current.stat == DEAD) + if(QDELETED(monster?.owner?.current) || monster.owner.current.stat == DEAD) continue - if(GLOB.monster_antagonist_types.Find(monster.type)) + if(is_type_in_typecache(monster, GLOB.monster_hunter_prey_antags)) count++ if(MINIMUM_MONSTERS_REQUIRED > count) diff --git a/monkestation/code/modules/storytellers/converted_events/solo/traitor.dm b/monkestation/code/modules/storytellers/converted_events/solo/traitor.dm index 7514064a56db..2709f0307626 100644 --- a/monkestation/code/modules/storytellers/converted_events/solo/traitor.dm +++ b/monkestation/code/modules/storytellers/converted_events/solo/traitor.dm @@ -13,6 +13,7 @@ JOB_PRISONER, JOB_SECURITY_OFFICER, JOB_WARDEN, + JOB_SECURITY_ASSISTANT, ) restricted_roles = list( JOB_AI, diff --git a/monkestation/code/modules/storytellers/gamemode_subsystem.dm b/monkestation/code/modules/storytellers/gamemode_subsystem.dm index 8f9cdde9e76d..27155fdadedd 100644 --- a/monkestation/code/modules/storytellers/gamemode_subsystem.dm +++ b/monkestation/code/modules/storytellers/gamemode_subsystem.dm @@ -271,9 +271,14 @@ SUBSYSTEM_DEF(gamemode) if(!observers) if(!ready_players && !isliving(candidate)) continue - if(no_antags && candidate.mind.special_role) - log_storyteller("Candidate([candidate]) removed from rolling poll due to special_role([candidate.mind.special_role])") - continue + if(no_antags && !isnull(candidate.mind.antag_datums)) + var/real = FALSE + for(var/datum/antagonist/antag_datum as anything in candidate.mind.antag_datums) + if(!(antag_datum.antag_flags & FLAG_FAKE_ANTAG)) + real = TRUE + break + if(real) + continue if(restricted_roles && (candidate.mind.assigned_role.title in restricted_roles)) continue if(length(required_roles) && !(candidate.mind.assigned_role.title in required_roles)) diff --git a/monkestation/code/modules/trading/lootbox_buying.dm b/monkestation/code/modules/trading/lootbox_buying.dm index 2d3d877b233c..bc66145a268d 100644 --- a/monkestation/code/modules/trading/lootbox_buying.dm +++ b/monkestation/code/modules/trading/lootbox_buying.dm @@ -34,6 +34,7 @@ if(!prefs.adjust_metacoins(ckey, -5000, donator_multipler = FALSE)) return prefs.lootboxes_owned++ + prefs.save_preferences() /client/proc/open_lootbox() message_admins("[ckey] opened a lootbox!") @@ -49,6 +50,7 @@ if(!prefs.lootboxes_owned) return prefs.lootboxes_owned-- + prefs.save_preferences() mob.trigger_lootbox_on_self() /proc/give_lootboxes_to_randoms(amount) @@ -63,3 +65,4 @@ return prefs.lootboxes_owned += amount to_chat(mob, span_notice("You have been given [amount] lootboxes! Open it using the escape menu.")) + prefs.save_preferences() diff --git a/monkestation/code/modules/trading/lootbox_odds.dm b/monkestation/code/modules/trading/lootbox_odds.dm index 18773422b2bb..dc939476fde6 100644 --- a/monkestation/code/modules/trading/lootbox_odds.dm +++ b/monkestation/code/modules/trading/lootbox_odds.dm @@ -62,33 +62,37 @@ /datum/loadout_item/proc/add_to_user(client/buyer) SHOULD_CALL_PARENT(TRUE) - var/fail_message ="Failed to add lootbox item to database. Will reattempt until added!" + var/replacement_lootbox_message = span_warning("The item you received will not be saved, but you have been granted a replacement lootbox to use at a later point.") if(!SSdbcore.IsConnected()) - to_chat(buyer, fail_message) + to_chat(buyer, span_warning("Database is not connected.")) + to_chat(buyer, replacement_lootbox_message) + buyer.prefs.lootboxes_owned++ + buyer.prefs.save_preferences() return FALSE if(!buyer?.prefs) return FALSE + buyer.prefs.inventory += item_path + var/datum/db_query/query_add_gear_purchase if(!buyer.prefs.inventory[item_path]) - buyer.prefs.inventory += item_path - var/datum/db_query/query_add_gear_purchase = SSdbcore.NewQuery({" + query_add_gear_purchase = SSdbcore.NewQuery({" INSERT INTO [format_table_name("metacoin_item_purchases")] (`ckey`, `item_id`, `amount`) VALUES (:ckey, :item_id, :amount)"}, list("ckey" = buyer.ckey, "item_id" = item_path, "amount" = 1)) - if(!query_add_gear_purchase.Execute()) - to_chat(buyer, fail_message) - qdel(query_add_gear_purchase) - addtimer(CALLBACK(src, PROC_REF(add_to_user), buyer), 15 SECONDS) - return FALSE - qdel(query_add_gear_purchase) else - buyer.prefs.inventory += item_path - var/datum/db_query/query_add_gear_purchase = SSdbcore.NewQuery({" + // Note from someone who didn't make this lootbox system: This seems to be related to + // duplicate lootbox items, but duplicate items don't appear to run this proc, making this + // seemingly useless. Even then, why are we setting the amount to 1? + query_add_gear_purchase = SSdbcore.NewQuery({" UPDATE [format_table_name("metacoin_item_purchases")] SET amount = :amount WHERE ckey = :ckey AND item_id = :item_id"}, list("ckey" = buyer.ckey, "item_id" = item_path, "amount" = 1)) - if(!query_add_gear_purchase.Execute()) - to_chat(buyer, fail_message) - qdel(query_add_gear_purchase) - return FALSE + if(!query_add_gear_purchase.Execute()) + // If the query fails to execute, notify the user and give them a replacement lootbox. + to_chat(buyer, span_warning("Failed to add lootbox item to database.")) + to_chat(buyer, replacement_lootbox_message) + buyer.prefs.lootboxes_owned++ + buyer.prefs.save_preferences() qdel(query_add_gear_purchase) + return FALSE + qdel(query_add_gear_purchase) return TRUE diff --git a/monkestation/code/modules/uplink/uplink_items/misc.dm b/monkestation/code/modules/uplink/uplink_items/misc.dm index 1c8feeb87fc1..ef0651d6c01a 100644 --- a/monkestation/code/modules/uplink/uplink_items/misc.dm +++ b/monkestation/code/modules/uplink/uplink_items/misc.dm @@ -16,3 +16,4 @@ purchasable_from = ~(UPLINK_NUKE_OPS | UPLINK_CLOWN_OPS) item = /obj/item/neutered_borer_spawner cost = 20 + refundable = TRUE diff --git a/monkestation/code/modules/uplink/uplink_items/weapons.dm b/monkestation/code/modules/uplink/uplink_items/weapons.dm index 39d9d9f3fefe..73a5cad23a5a 100644 --- a/monkestation/code/modules/uplink/uplink_items/weapons.dm +++ b/monkestation/code/modules/uplink/uplink_items/weapons.dm @@ -26,3 +26,9 @@ cost = 12 surplus = 40 purchasable_from = ~(UPLINK_CLOWN_OPS | UPLINK_GANGS) + +/datum/uplink_item/dangerous/venom_knife + name = "Poisoned Knife" + desc = "A knife that is made of two razor sharp blades, it has a secret compartment in the handle to store liquids which are injected when stabbing something. Can hold up to forty units of reagents but comes empty." + item = /obj/item/knife/venom + cost = 6 // all in all it's not super stealthy and you have to get some chemicals yourself diff --git a/monkestation/code/modules/virology/disease/_disease.dm b/monkestation/code/modules/virology/disease/_disease.dm index 1916fc30782c..9578d3cdae78 100644 --- a/monkestation/code/modules/virology/disease/_disease.dm +++ b/monkestation/code/modules/virology/disease/_disease.dm @@ -133,8 +133,9 @@ GLOBAL_LIST_INIT(virusDB, list()) machine = dish.loc if(specified_stage) - for(var/datum/symptom/e in symptoms) - if(e.stage == specified_stage) + for(var/x in symptoms.len) + if(x == specified_stage) + var/datum/symptom/e = symptoms[x] e.multiplier_tweak(0.1 * rand(1, 3)) minormutate(specified_stage) if(e.chance == e.max_chance && prob(strength) && e.max_chance <= initial(e.max_chance) * 3) diff --git a/monkestation/code/modules/virology/disease/symtoms/stage2.dm b/monkestation/code/modules/virology/disease/symtoms/stage2.dm index d6ded963ea9d..284ca249e342 100644 --- a/monkestation/code/modules/virology/disease/symtoms/stage2.dm +++ b/monkestation/code/modules/virology/disease/symtoms/stage2.dm @@ -189,7 +189,7 @@ blood.diseases |= virus_copylist(mob.diseases) -/* //commented out until i can figure out how to make this work without shoving static lights on moving objects +//commented out until i can figure out how to make this work without shoving static lights on moving objects /datum/symptom/lantern name = "Lantern Syndrome" desc = "Causes the infected to glow." @@ -197,13 +197,21 @@ badness = EFFECT_DANGER_HELPFUL multiplier = 4 max_multiplier = 10 + chance = 10 + max_chance = 15 var/uncolored = 0 var/flavortext = 0 var/color = rgb(255, 255, 255) + var/obj/effect/dummy/lighting_obj/moblight /datum/symptom/lantern/activate(mob/living/mob) + if(moblight) + qdel(moblight) + moblight = new(mob) if(ismouse(mob)) - mob.set_light(multiplier, multiplier/3, l_color = color) + moblight.set_light_range(multiplier) + moblight.set_light_power(multiplier / 3) + moblight.set_light_color(color) return if(mob.reagents.has_reagent(/datum/reagent/space_cleaner)) uncolored = 1 //Having spacecleaner in your system when the effect activates will permanently make the color white. @@ -214,13 +222,15 @@ if(!flavortext) to_chat(mob, span_notice("You are glowing!")) flavortext = 1 - mob.set_light(multiplier, multiplier, multiplier/3, l_color = color) + moblight.set_light_range(multiplier) + moblight.set_light_power(multiplier / 3) + moblight.set_light_color(color) /datum/symptom/lantern/deactivate(mob/living/mob) - mob.set_light(0, 0, 0, l_color = rgb(0,0,0)) + QDEL_NULL(moblight) to_chat(mob, span_notice("You don't feel as bright.")) flavortext = 0 -*/ + /datum/symptom/vitreous name = "Vitreous resonance" @@ -374,20 +384,6 @@ if(ispodperson(victim)) //Plantmen take a LOT of damage victim.adjustCloneLoss(5 * multiplier) - for(var/obj/machinery/hydroponics/plantbox in range(3*multiplier,mob)) - switch(rand(1,3)) - if(1) - plantbox.adjust_waterlevel(-rand(1,10)) - plantbox.adjust_plant_nutriments(-rand(1,5)) - if(2) - plantbox.adjust_toxic(rand(1,50)) - if(3) - plantbox.adjust_weedlevel(10) - plantbox.adjust_pestlevel(10) - if(prob(5)) - plantbox.plantdies() - - for(var/obj/item/food/grown/crop in range(2*multiplier,mob)) crop.visible_message("\The [crop] rots at an alarming rate!") new /obj/item/food/badrecipe(get_turf(crop)) @@ -418,7 +414,7 @@ sleep(100) var/list/possible_bots = list( /mob/living/simple_animal/bot/cleanbot, - /mob/living/simple_animal/bot/medbot, + /mob/living/basic/bot/medbot, /mob/living/simple_animal/bot/secbot, /mob/living/simple_animal/bot/floorbot, /mob/living/simple_animal/bot/buttbot diff --git a/monkestation/code/modules/virology/disease/symtoms/stage4.dm b/monkestation/code/modules/virology/disease/symtoms/stage4.dm index 68e71556eda7..1679cf6c530d 100644 --- a/monkestation/code/modules/virology/disease/symtoms/stage4.dm +++ b/monkestation/code/modules/virology/disease/symtoms/stage4.dm @@ -423,3 +423,26 @@ .=..() disease.process_dead = FALSE +/datum/symptom/oxygen + name = "Self-Respiration" + desc = "The virus synthesizes oxygen, which can remove the need for breathing at high symptom strength." + stage = 4 + max_multiplier = 5 + badness = EFFECT_DANGER_HELPFUL + var/breathing = TRUE + +/datum/symptom/oxygen/activate(mob/living/carbon/mob, datum/disease/advanced/disease) + mob.losebreath = max(0, mob.losebreath - multiplier) + mob.adjustOxyLoss(-2 * multiplier) + if(multiplier >= 4) + to_chat(mob, span_notice("[pick("Your lungs feel great.", "You realize you haven't been breathing.", "You don't feel the need to breathe.")]")) + if(breathing) + breathing = FALSE + ADD_TRAIT(mob, TRAIT_NOBREATH, DISEASE_TRAIT) + +/datum/symptom/oxygen/deactivate(mob/living/carbon/mob, datum/disease/advanced/disease) + if(!breathing) + breathing = TRUE + REMOVE_TRAIT(mob, TRAIT_NOBREATH, DISEASE_TRAIT) + mob.emote("gasp") + to_chat(mob, span_notice("You feel the need to breathe again.")) diff --git a/monkestation/code/modules/virology/items/virusdish.dm b/monkestation/code/modules/virology/items/virusdish.dm index b16989728bc3..3a045c745b0d 100644 --- a/monkestation/code/modules/virology/items/virusdish.dm +++ b/monkestation/code/modules/virology/items/virusdish.dm @@ -15,7 +15,6 @@ GLOBAL_LIST_INIT(virusdishes, list()) var/cloud_delay = 8 SECONDS//similar to a mob's breathing var/last_cloud_time = 0 var/mob/last_openner - var/takes_left = 2 /obj/item/weapon/virusdish/New(loc) ..() @@ -105,13 +104,16 @@ GLOBAL_LIST_INIT(virusdishes, list()) ..() if(istype(I,/obj/item/hand_labeler)) return - if(istype(I, /obj/item/reagent_containers/syringe) && takes_left) - takes_left-- - var/obj/item/reagent_containers/syringe/B = I - var/list/data = list("viruses"=null,"blood_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"viruses"=list(),"immunity"=list()) - data["viruses"] |= list(contained_virus) - B.reagents.add_reagent(/datum/reagent/blood, B.volume, data) - to_chat(user, span_notice("You take some blood from the [src]")) + if(istype(I, /obj/item/reagent_containers/syringe)) + if(growth < 50) + to_chat(user, span_warning("There isn't enough growth in the [src].")) + else + growth = growth - 50 + var/obj/item/reagent_containers/syringe/B = I + var/list/data = list("viruses"=null,"blood_DNA"=null,"blood_type"="O-","resistances"=null,"trace_chem"=null,"viruses"=list(),"immunity"=list()) + data["viruses"] |= list(contained_virus) + B.reagents.add_reagent(/datum/reagent/blood, B.volume, data) + to_chat(user, span_notice("You take some blood from the [src].")) if (open) if (istype(I,/obj/item/reagent_containers)) var/success = 0 diff --git a/monkestation/code/modules/virology/machines/splicer.dm b/monkestation/code/modules/virology/machines/splicer.dm index dfd69e0af61b..94647c6f49de 100644 --- a/monkestation/code/modules/virology/machines/splicer.dm +++ b/monkestation/code/modules/virology/machines/splicer.dm @@ -180,8 +180,8 @@ var/list/effects = dish.contained_virus.symptoms for(var/x = 1 to effects.len) - var/datum/symptom/e = effects[x] - if(e.stage == target_stage) + if(x == target_stage) + var/datum/symptom/e = effects[x] effects[x] = memorybank.Copy(dish.contained_virus) dish.contained_virus.log += "
[ROUND_TIME()] [memorybank.name] spliced in by [key_name(usr)] (replaces [e.name])" break diff --git a/monkestation/code/modules/virology/readme.md b/monkestation/code/modules/virology/readme.md index 7eaa3bce0137..d87a0c7738b6 100644 --- a/monkestation/code/modules/virology/readme.md +++ b/monkestation/code/modules/virology/readme.md @@ -13,6 +13,7 @@ This PR aims to make virology not ass to play with - code\modules\mob\living\carbon\life.dm + - code\modules\mob\living\carbon\init_signals.dm - code\datums\diseases\_disease.dm - code\game\objects\effects\decals\cleanable.dm - code\game\atoms.dm diff --git a/monkestation/icons/hud/screen_alert.dmi b/monkestation/icons/hud/screen_alert.dmi index 967caecb662d..60df15794a67 100644 Binary files a/monkestation/icons/hud/screen_alert.dmi and b/monkestation/icons/hud/screen_alert.dmi differ diff --git a/monkestation/icons/mob/clothing/head.dmi b/monkestation/icons/mob/clothing/head.dmi index c7921069bbc5..7143b2dae137 100644 Binary files a/monkestation/icons/mob/clothing/head.dmi and b/monkestation/icons/mob/clothing/head.dmi differ diff --git a/monkestation/icons/mob/clothing/suit.dmi b/monkestation/icons/mob/clothing/suit.dmi index 0150c152aa30..4536c506ca6d 100644 Binary files a/monkestation/icons/mob/clothing/suit.dmi and b/monkestation/icons/mob/clothing/suit.dmi differ diff --git a/monkestation/icons/mob/huds/antags/monster_hunter.dmi b/monkestation/icons/mob/huds/antags/monster_hunter.dmi new file mode 100644 index 000000000000..6bae8ea82cae Binary files /dev/null and b/monkestation/icons/mob/huds/antags/monster_hunter.dmi differ diff --git a/monkestation/icons/mob/inhands/equipment/secass_lefthand.dmi b/monkestation/icons/mob/inhands/equipment/secass_lefthand.dmi new file mode 100644 index 000000000000..1d533d9f5026 Binary files /dev/null and b/monkestation/icons/mob/inhands/equipment/secass_lefthand.dmi differ diff --git a/monkestation/icons/mob/inhands/equipment/secass_righthand.dmi b/monkestation/icons/mob/inhands/equipment/secass_righthand.dmi new file mode 100644 index 000000000000..c35281cf59a1 Binary files /dev/null and b/monkestation/icons/mob/inhands/equipment/secass_righthand.dmi differ diff --git a/monkestation/icons/mob/species/misc/feet_digi.dmi b/monkestation/icons/mob/species/misc/feet_digi.dmi index c1cc99be86d6..e46ddfe67adf 100644 Binary files a/monkestation/icons/mob/species/misc/feet_digi.dmi and b/monkestation/icons/mob/species/misc/feet_digi.dmi differ diff --git a/monkestation/icons/mob/species/misc/suit_digi.dmi b/monkestation/icons/mob/species/misc/suit_digi.dmi index 5f3ccb4adf62..6c06633ca500 100644 Binary files a/monkestation/icons/mob/species/misc/suit_digi.dmi and b/monkestation/icons/mob/species/misc/suit_digi.dmi differ diff --git a/monkestation/icons/mob/species/misc/uniform_digi.dmi b/monkestation/icons/mob/species/misc/uniform_digi.dmi index 84fc3c50013a..fe7835409e99 100644 Binary files a/monkestation/icons/mob/species/misc/uniform_digi.dmi and b/monkestation/icons/mob/species/misc/uniform_digi.dmi differ diff --git a/monkestation/icons/obj/clothing/hats.dmi b/monkestation/icons/obj/clothing/hats.dmi index f394e9161053..8d039b341602 100644 Binary files a/monkestation/icons/obj/clothing/hats.dmi and b/monkestation/icons/obj/clothing/hats.dmi differ diff --git a/monkestation/icons/obj/clothing/suits.dmi b/monkestation/icons/obj/clothing/suits.dmi index d0ee109ee212..2ec9a3624132 100644 Binary files a/monkestation/icons/obj/clothing/suits.dmi and b/monkestation/icons/obj/clothing/suits.dmi differ diff --git a/monkestation/icons/obj/items/secass.dmi b/monkestation/icons/obj/items/secass.dmi new file mode 100644 index 000000000000..133c1676b7df Binary files /dev/null and b/monkestation/icons/obj/items/secass.dmi differ diff --git a/monkestation/icons/obj/items_and_weapons.dmi b/monkestation/icons/obj/items_and_weapons.dmi index 549e16e92b15..5a0f852f3350 100644 Binary files a/monkestation/icons/obj/items_and_weapons.dmi and b/monkestation/icons/obj/items_and_weapons.dmi differ diff --git a/sound/voice/medbot/i_am_chicken.ogg b/sound/voice/medbot/i_am_chicken.ogg new file mode 100644 index 000000000000..d1c4465505f3 Binary files /dev/null and b/sound/voice/medbot/i_am_chicken.ogg differ diff --git a/strings/abductee_objectives.txt b/strings/abductee_objectives.txt index f9ff87016699..ad423675f857 100644 --- a/strings/abductee_objectives.txt +++ b/strings/abductee_objectives.txt @@ -2,8 +2,6 @@ All the pets around here suck. You need to make them cooler. Replace them with e Being alone and in large groups are both frightening. Try to be alone with only one other person whenever possible. Call forth a spirit from the other side. Climb the corporate ladder all the way to the top! -Convince the crew that you are a paraplegic. -Death should be final and modern medicine disrupts the natural order. Don't allow anyone to be revived. Ensure your department prospers over all else. Expand the station. Flood the station's powernet with as much electricity as you can. @@ -11,16 +9,13 @@ Fuck the system! Defect from the station and start an independent colony in spac Go have a good conversation with the singularity/tesla/supermatter crystal. Bonus points if it responds. It's all an entirely virtual simulation within an underground vault. Convince the crew to escape the shackles of VR. Nanotrasen is abusing the animals! Save as many as you can! -No matter how they say it, other people keep mispronouncing your name. Be sure to correct them whenever possible. Replace all the floor tiles with wood, carpeting, grass or bling. So much lies undiscovered. Look deeper into the machinations of the universe. Start a collection of corpses. Don't kill people to get these corpses. -The CEO of Nanotrasen is coming! Ensure the station is in absolutely pristine condition. The clown is not funny. You can do better! Steal his audience and make the crew laugh! The crew must get to know one another better. Break down the walls inside the station! The elder gods hunger. Gather a cult and conduct a ritual to summon one. The leaders of this station are hiding a grand, evil conspiracy. Only you can learn what it is, and expose it to the people! -The oxygen is killing them all and they don't even know it. Make sure no oxygen is on the station. The Syndicate has hired you to compile dossiers on all important members of the crew. Be sure they don't know you're doing it. There are alien parasites masquerading as people's hair. Save people from this invasion. There is only one other person in existence, he is just really good at pretending to be multiple people. diff --git a/tgstation.dme b/tgstation.dme index de80455de934..320fbf275865 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -266,6 +266,7 @@ #include "code\__DEFINES\xenobiology.dm" #include "code\__DEFINES\ai\ai.dm" #include "code\__DEFINES\ai\ai_blackboard.dm" +#include "code\__DEFINES\ai\bot_keys.dm" #include "code\__DEFINES\ai\carp.dm" #include "code\__DEFINES\ai\haunted.dm" #include "code\__DEFINES\ai\monkey.dm" @@ -389,6 +390,8 @@ #include "code\__DEFINES\~monkestation\antagonists.dm" #include "code\__DEFINES\~monkestation\artifact.dm" #include "code\__DEFINES\~monkestation\asteroids.dm" +#include "code\__DEFINES\~monkestation\atom_hud.dm" +#include "code\__DEFINES\~monkestation\botany.dm" #include "code\__DEFINES\~monkestation\chat.dm" #include "code\__DEFINES\~monkestation\clock_cult.dm" #include "code\__DEFINES\~monkestation\colors.dm" @@ -401,6 +404,7 @@ #include "code\__DEFINES\~monkestation\guns.dm" #include "code\__DEFINES\~monkestation\interaction_particles.dm" #include "code\__DEFINES\~monkestation\ipcs.dm" +#include "code\__DEFINES\~monkestation\jobs.dm" #include "code\__DEFINES\~monkestation\keybinding.dm" #include "code\__DEFINES\~monkestation\level_traits.dm" #include "code\__DEFINES\~monkestation\logging.dm" @@ -548,6 +552,7 @@ #include "code\__HELPERS\sorts\MergeSort.dm" #include "code\__HELPERS\sorts\TimSort.dm" #include "code\__HELPERS\~monkestation-helpers\announcements.dm" +#include "code\__HELPERS\~monkestation-helpers\antags.dm" #include "code\__HELPERS\~monkestation-helpers\icon_smoothing.dm" #include "code\__HELPERS\~monkestation-helpers\time.dm" #include "code\__HELPERS\~monkestation-helpers\virology.dm" @@ -677,7 +682,6 @@ #include "code\controllers\subsystem\garbage.dm" #include "code\controllers\subsystem\icon_smooth.dm" #include "code\controllers\subsystem\id_access.dm" -#include "code\controllers\subsystem\idlenpcpool.dm" #include "code\controllers\subsystem\init_profiler.dm" #include "code\controllers\subsystem\input.dm" #include "code\controllers\subsystem\ipintel.dm" @@ -889,6 +893,7 @@ #include "code\datums\ai\basic_mobs\base_basic_controller.dm" #include "code\datums\ai\basic_mobs\generic_controllers.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\basic_attacking.dm" +#include "code\datums\ai\basic_mobs\basic_ai_behaviors\befriend_target.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\climb_tree.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\find_parent.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\nearest_targeting.dm" @@ -1360,6 +1365,8 @@ #include "code\datums\elements\footstep_override.dm" #include "code\datums\elements\forced_gravity.dm" #include "code\datums\elements\frozen.dm" +#include "code\datums\elements\give_turf_traits.dm" +#include "code\datums\elements\hat_wearer.dm" #include "code\datums\elements\haunted.dm" #include "code\datums\elements\high_fiver.dm" #include "code\datums\elements\honkspam.dm" @@ -1553,6 +1560,7 @@ #include "code\datums\mutations\webbing.dm" #include "code\datums\proximity_monitor\field.dm" #include "code\datums\proximity_monitor\proximity_monitor.dm" +#include "code\datums\proximity_monitor\fields\ai_target_tracking.dm" #include "code\datums\proximity_monitor\fields\gravity.dm" #include "code\datums\proximity_monitor\fields\projectile_dampener.dm" #include "code\datums\proximity_monitor\fields\timestop.dm" @@ -2507,6 +2515,7 @@ #include "code\modules\admin\force_event.dm" #include "code\modules\admin\fun_balloon.dm" #include "code\modules\admin\greyscale_modify_menu.dm" +#include "code\modules\admin\hardelete_toggle.dm" #include "code\modules\admin\holder2.dm" #include "code\modules\admin\ipintel.dm" #include "code\modules\admin\IsBanned.dm" @@ -2636,14 +2645,17 @@ #include "code\modules\antagonists\_common\antag_spawner.dm" #include "code\modules\antagonists\_common\antag_team.dm" #include "code\modules\antagonists\abductor\abductor.dm" +#include "code\modules\antagonists\abductor\abductor_structures.dm" #include "code\modules\antagonists\abductor\ice_abductor.dm" #include "code\modules\antagonists\abductor\abductee\abductee.dm" #include "code\modules\antagonists\abductor\abductee\abductee_objectives.dm" -#include "code\modules\antagonists\abductor\equipment\abduction_gear.dm" #include "code\modules\antagonists\abductor\equipment\abduction_outfits.dm" #include "code\modules\antagonists\abductor\equipment\abduction_surgery.dm" #include "code\modules\antagonists\abductor\equipment\gland.dm" #include "code\modules\antagonists\abductor\equipment\orderable_gear.dm" +#include "code\modules\antagonists\abductor\equipment\gear\abductor_clothing.dm" +#include "code\modules\antagonists\abductor\equipment\gear\abductor_items.dm" +#include "code\modules\antagonists\abductor\equipment\gear\abductor_posters.dm" #include "code\modules\antagonists\abductor\equipment\glands\access.dm" #include "code\modules\antagonists\abductor\equipment\glands\blood.dm" #include "code\modules\antagonists\abductor\equipment\glands\chem.dm" @@ -3997,6 +4009,7 @@ #include "code\modules\library\skill_learning\skillchip.dm" #include "code\modules\library\skill_learning\job_skillchips\_job.dm" #include "code\modules\library\skill_learning\job_skillchips\chef.dm" +#include "code\modules\library\skill_learning\job_skillchips\janitor.dm" #include "code\modules\library\skill_learning\job_skillchips\psychologist.dm" #include "code\modules\library\skill_learning\job_skillchips\research_director.dm" #include "code\modules\library\skill_learning\job_skillchips\roboticist.dm" @@ -4210,6 +4223,16 @@ #include "code\modules\mob\living\basic\blob_minions\blob_spore.dm" #include "code\modules\mob\living\basic\blob_minions\blob_zombie.dm" #include "code\modules\mob\living\basic\blob_minions\blobbernaut.dm" +#include "code\modules\mob\living\basic\bots\_bots.dm" +#include "code\modules\mob\living\basic\bots\bot_ai.dm" +#include "code\modules\mob\living\basic\bots\bot_hud.dm" +#include "code\modules\mob\living\basic\bots\cleanbot\cleanbot.dm" +#include "code\modules\mob\living\basic\bots\cleanbot\cleanbot_abilities.dm" +#include "code\modules\mob\living\basic\bots\cleanbot\cleanbot_ai.dm" +#include "code\modules\mob\living\basic\bots\hygienebot\hygienebot.dm" +#include "code\modules\mob\living\basic\bots\hygienebot\hygienebot_ai.dm" +#include "code\modules\mob\living\basic\bots\medbot\medbot.dm" +#include "code\modules\mob\living\basic\bots\medbot\medbot_ai.dm" #include "code\modules\mob\living\basic\clown\clown.dm" #include "code\modules\mob\living\basic\clown\clown_ai.dm" #include "code\modules\mob\living\basic\cult\shade.dm" @@ -4598,14 +4621,12 @@ #include "code\modules\mob\living\simple_animal\damage_procs.dm" #include "code\modules\mob\living\simple_animal\simple_animal.dm" #include "code\modules\mob\living\simple_animal\bot\bot.dm" -#include "code\modules\mob\living\simple_animal\bot\cleanbot.dm" +#include "code\modules\mob\living\simple_animal\bot\bot_announcement.dm" #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\firebot.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\hygienebot.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" #include "code\modules\mob\living\simple_animal\bot\SuperBeepsky.dm" @@ -5755,6 +5776,7 @@ #include "monkestation\code\game\objects\items\recursive_gift.dm" #include "monkestation\code\game\objects\items\stickers.dm" #include "monkestation\code\game\objects\items\superglue.dm" +#include "monkestation\code\game\objects\items\venom_knife.dm" #include "monkestation\code\game\objects\items\AI_modules\monke_lawsets.dm" #include "monkestation\code\game\objects\items\circuitboards\computer_circuitboards.dm" #include "monkestation\code\game\objects\items\circuitboards\holy_weapons.dm" @@ -5805,6 +5827,12 @@ #include "monkestation\code\modules\aesthetics\walls\iron.dm" #include "monkestation\code\modules\antagonists\_common\antag_datum.dm" #include "monkestation\code\modules\antagonists\_common\antag_hud.dm" +#include "monkestation\code\modules\antagonists\abductor\abductor.dm" +#include "monkestation\code\modules\antagonists\abductor\equipment\glands\blood.dm" +#include "monkestation\code\modules\antagonists\abductor\equipment\glands\plasma.dm" +#include "monkestation\code\modules\antagonists\abductor\equipment\glands\slime.dm" +#include "monkestation\code\modules\antagonists\abductor\equipment\glands\trauma.dm" +#include "monkestation\code\modules\antagonists\abductor\machinery\dispenser.dm" #include "monkestation\code\modules\antagonists\battlecruiser\battlecruiser.dm" #include "monkestation\code\modules\antagonists\borers\code\cortical_borer_chems.dm" #include "monkestation\code\modules\antagonists\borers\code\focus_datum.dm" @@ -6107,11 +6135,12 @@ #include "monkestation\code\modules\bloodsuckers\clans\tremere.dm" #include "monkestation\code\modules\bloodsuckers\clans\venture.dm" #include "monkestation\code\modules\bloodsuckers\controllers\sunlight.dm" -#include "monkestation\code\modules\bloodsuckers\monster_hunters\hunter_abilities.dm" #include "monkestation\code\modules\bloodsuckers\monster_hunters\hunter_datum.dm" #include "monkestation\code\modules\bloodsuckers\monster_hunters\hunter_rulesets.dm" -#include "monkestation\code\modules\bloodsuckers\monster_hunters\hunter_weapons.dm" #include "monkestation\code\modules\bloodsuckers\monster_hunters\hunting_contracts.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\abilities\bloodsilver.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\abilities\paradox.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\abilities\wonderland.dm" #include "monkestation\code\modules\bloodsuckers\monster_hunters\events\wonderland.dm" #include "monkestation\code\modules\bloodsuckers\monster_hunters\events\wonderland_apocalypse.dm" #include "monkestation\code\modules\bloodsuckers\monster_hunters\monsters\killer_rabbits.dm" @@ -6121,6 +6150,17 @@ #include "monkestation\code\modules\bloodsuckers\monster_hunters\monsters\monster_effects\killer_rabbit_effects.dm" #include "monkestation\code\modules\bloodsuckers\monster_hunters\monsters\monster_effects\white_rabbit.dm" #include "monkestation\code\modules\bloodsuckers\monster_hunters\monsters\monster_powers\killer_rabbit_powers.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\tools\bnuuy_mask.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\tools\jack_bomb.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\tools\rabbit_eye.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\tools\rabbit_locator.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\tools\weaponsmith.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\weapons\_trick_weapon.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\weapons\beast_claw.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\weapons\darkmoon.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\weapons\hunter_axe.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\weapons\hunter_revolver.dm" +#include "monkestation\code\modules\bloodsuckers\monster_hunters\weapons\threaded_cane.dm" #include "monkestation\code\modules\bloodsuckers\powers\_base_power.dm" #include "monkestation\code\modules\bloodsuckers\powers\cloak.dm" #include "monkestation\code\modules\bloodsuckers\powers\feed.dm" @@ -6152,6 +6192,48 @@ #include "monkestation\code\modules\bloodsuckers\vassals\vassal_pinpointer.dm" #include "monkestation\code\modules\bloodsuckers\vassals\types\favorite.dm" #include "monkestation\code\modules\bloodsuckers\vassals\types\revenge.dm" +#include "monkestation\code\modules\botany\hydrotray.dm" +#include "monkestation\code\modules\botany\plant_processing.dm" +#include "monkestation\code\modules\botany\potty.dm" +#include "monkestation\code\modules\botany\tools.dm" +#include "monkestation\code\modules\botany\components\plant_growing.dm" +#include "monkestation\code\modules\botany\components\plant_growth_holder.dm" +#include "monkestation\code\modules\botany\components\plant_tray_overlays.dm" +#include "monkestation\code\modules\botany\new_seeds\mutations.dm" +#include "monkestation\code\modules\botany\new_seeds\seeds.dm" +#include "monkestation\code\modules\botany\plant_icon_overrides\crops.dm" +#include "monkestation\code\modules\botany\plant_icon_overrides\flowers.dm" +#include "monkestation\code\modules\botany\plant_icon_overrides\fruits.dm" +#include "monkestation\code\modules\botany\plant_icon_overrides\herbs.dm" +#include "monkestation\code\modules\botany\plant_icon_overrides\veggies.dm" +#include "monkestation\code\modules\botany\plant_icon_overrides\weeds.dm" +#include "monkestation\code\modules\botany\reagents\_base.dm" +#include "monkestation\code\modules\botany\reagents\growing_component_apply\cat2_medicine_reagents.dm" +#include "monkestation\code\modules\botany\reagents\growing_component_apply\drug_reagents.dm" +#include "monkestation\code\modules\botany\reagents\growing_component_apply\food_reagents.dm" +#include "monkestation\code\modules\botany\reagents\growing_component_apply\medicine.dm" +#include "monkestation\code\modules\botany\reagents\growing_component_apply\other_reagents.dm" +#include "monkestation\code\modules\botany\reagents\growing_component_apply\pyrotechnics.dm" +#include "monkestation\code\modules\botany\reagents\growing_component_apply\toxins.dm" +#include "monkestation\code\modules\botany\reagents\plant_apply\drink_reagents.dm" +#include "monkestation\code\modules\botany\reagents\plant_apply\ethanol.dm" +#include "monkestation\code\modules\botany\reagents\plant_apply\food_reagents.dm" +#include "monkestation\code\modules\botany\reagents\plant_apply\medicine.dm" +#include "monkestation\code\modules\botany\reagents\plant_apply\other_reagents.dm" +#include "monkestation\code\modules\botany\reagents\plant_apply\pyrotechnics.dm" +#include "monkestation\code\modules\botany\reagents\plant_apply\toxins.dm" +#include "monkestation\code\modules\botany\species\apid\abilities.dm" +#include "monkestation\code\modules\botany\species\apid\bee.dm" +#include "monkestation\code\modules\botany\species\apid\bodyparts.dm" +#include "monkestation\code\modules\botany\species\apid\organs.dm" +#include "monkestation\code\modules\botany\species\apid\species.dm" +#include "monkestation\code\modules\botany\species\apid\external_organs\apid_antennae.dm" +#include "monkestation\code\modules\botany\species\apid\external_organs\preferences.dm" +#include "monkestation\code\modules\botany\species\apid\external_organs\wings.dm" +#include "monkestation\code\modules\botany\species\apid\hive\ability.dm" +#include "monkestation\code\modules\botany\species\apid\hive\area.dm" +#include "monkestation\code\modules\botany\species\apid\hive\hive_object.dm" +#include "monkestation\code\modules\botany\species\apid\hive\hive_turfs.dm" #include "monkestation\code\modules\bunny_wizard\outfits.dm" #include "monkestation\code\modules\bunny_wizard\wizard_items.dm" #include "monkestation\code\modules\cargo\bounties\pathology.dm" @@ -6272,6 +6354,7 @@ #include "monkestation\code\modules\cryopods\persistance\base_persistance.dm" #include "monkestation\code\modules\cryopods\trackers\cryo_chaplain.dm" #include "monkestation\code\modules\datums\brain_damage\mild.dm" +#include "monkestation\code\modules\datums\components\glitching_state.dm" #include "monkestation\code\modules\datums\components\nanites.dm" #include "monkestation\code\modules\datums\diseases\advanced\symptoms\nanites.dm" #include "monkestation\code\modules\datums\mood_events\generic_negative_events.dm" @@ -6295,6 +6378,7 @@ #include "monkestation\code\modules\events\summon_wizard_event.dm" #include "monkestation\code\modules\events\ghost_role\drifting_contractor.dm" #include "monkestation\code\modules\events\wizard\summon_gifts.dm" +#include "monkestation\code\modules\food_and_drinks\machinery\smartfridge.dm" #include "monkestation\code\modules\food_and_drinks\recipes\boiling.dm" #include "monkestation\code\modules\food_and_drinks\recipes\recipes_meat.dm" #include "monkestation\code\modules\ghost_players\area_changes.dm" @@ -6321,6 +6405,7 @@ #include "monkestation\code\modules\ghost_players\job_helpers\organ_printer.dm" #include "monkestation\code\modules\goonimizations\goon_keybinds.dm" #include "monkestation\code\modules\goonimizations\shuttle_votes.dm" +#include "monkestation\code\modules\guns\laser.dm" #include "monkestation\code\modules\hydroponics\botanical_lexicon.dm" #include "monkestation\code\modules\hydroponics\plant_genes.dm" #include "monkestation\code\modules\hydroponics\seeds.dm" @@ -6345,6 +6430,7 @@ #include "monkestation\code\modules\jobs\job_types\ghost.dm" #include "monkestation\code\modules\jobs\job_types\godzilla.dm" #include "monkestation\code\modules\jobs\job_types\gorilla.dm" +#include "monkestation\code\modules\jobs\job_types\security_assistant.dm" #include "monkestation\code\modules\jobs\job_types\skeleton.dm" #include "monkestation\code\modules\jobs\job_types\yellowclown.dm" #include "monkestation\code\modules\library\skill_learning\job_skillchips\shaft_miner.dm" @@ -6794,6 +6880,7 @@ #include "monkestation\code\modules\research\nanites\nanite_programs\utility.dm" #include "monkestation\code\modules\research\nanites\nanite_programs\weapon.dm" #include "monkestation\code\modules\research\techweb\all_nodes.dm" +#include "monkestation\code\modules\security\code\citationinator.dm" #include "monkestation\code\modules\security\code\holographic_handcuffs.dm" #include "monkestation\code\modules\security\code\weapons\lawbringer.dm" #include "monkestation\code\modules\security\code\weapons\paco.dm" diff --git a/tgui/packages/tgui/interfaces/SmartVend.js b/tgui/packages/tgui/interfaces/SmartVend.js index 9ca12a87b7de..2426d356ef49 100644 --- a/tgui/packages/tgui/interfaces/SmartVend.js +++ b/tgui/packages/tgui/interfaces/SmartVend.js @@ -6,7 +6,10 @@ import { Window } from '../layouts'; export const SmartVend = (props, context) => { const { act, data } = useBackend(context); return ( - +