Step One: Nerd Co SpySpeks tm upon your face.
-Step Two: Place the included "ProfitProtektor tm" camera assembly in a place of your choosing - make sure to make heavy use of it's inconspicous design!
+Step Two: Place the included "ProfitProtektor tm" camera assembly in a place of your choosing - make sure to make heavy use of its inconspicous design!
Step Three: Press the "Activate Remote View" Button on the side of your SpySpeks tm to open a movable camera display in the corner of your vision, it's just that easy!
TROUBLESHOOTING
My SpySpeks tm Make a shrill beep while attempting to use!
diff --git a/code/game/objects/items/devices/swapper.dm b/code/game/objects/items/devices/swapper.dm
index dee9198c93296..fc5a9d39f9742 100644
--- a/code/game/objects/items/devices/swapper.dm
+++ b/code/game/objects/items/devices/swapper.dm
@@ -97,7 +97,7 @@
return teleportable
/obj/item/swapper/proc/swap(mob/user)
- if(QDELETED(linked_swapper) || world.time < linked_swapper.cooldown)
+ if(QDELETED(linked_swapper) || isnull(linked_swapper.loc) || world.time < linked_swapper.cooldown)
return
var/atom/movable/A = get_teleportable_container()
diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm
index 0dc69cb9c8117..8327e185ea612 100644
--- a/code/game/objects/items/devices/traitordevices.dm
+++ b/code/game/objects/items/devices/traitordevices.dm
@@ -138,7 +138,7 @@ effective or pretty fucking useless.
data["cooldown"] = DisplayTimeText(get_cooldown())
return data
-/obj/item/healthanalyzer/rad_laser/ui_act(action, params)
+/obj/item/healthanalyzer/rad_laser/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index 598c16c9041a8..d17530c801085 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -291,7 +291,7 @@
data["valve"] = valve_open
return data
-/obj/item/transfer_valve/ui_act(action, params)
+/obj/item/transfer_valve/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/game/objects/items/dice.dm b/code/game/objects/items/dice.dm
index f32b2ee30ec21..b06dd737654d0 100644
--- a/code/game/objects/items/dice.dm
+++ b/code/game/objects/items/dice.dm
@@ -181,7 +181,7 @@
name = "knucklebones rules"
default_raw_text = "How to play knucklebones \
\
-
Make two 3x3 grids right next to eachother using anything you can find to mark the ground. I like using the bartenders hologram projector.
\
+
Make two 3x3 grids right next to each other using anything you can find to mark the ground. I like using the bartenders hologram projector.
\
Take turns rolling the dice and moving the dice into one of the three rows on your 3x3 grid.
\
Your goal is to get the most points by putting die of the same number in the same row.
\
If you have two of the same die in the same row, you will add them together and then times the sum by two. Then add that to the rest of the die.
\
diff --git a/code/game/objects/items/dna_probe.dm b/code/game/objects/items/dna_probe.dm
index ee6a32766522f..57718ca217e0b 100644
--- a/code/game/objects/items/dna_probe.dm
+++ b/code/game/objects/items/dna_probe.dm
@@ -17,8 +17,6 @@
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
icon_state = "sampler"
item_flags = NOBLUDGEON
- ///Whether we have Carp DNA
- var/carp_dna_loaded = FALSE
///What sources of DNA this sampler can extract from.
var/allowed_scans = DNA_PROBE_SCAN_PLANTS | DNA_PROBE_SCAN_ANIMALS | DNA_PROBE_SCAN_HUMANS
///List of all Animal DNA scanned with this sampler.
@@ -35,10 +33,14 @@
if(dna_vault_ref?.resolve())
// Weirdly we can upload to any existing DNA vault so long as we're linked to any other existing DNA vault.
return try_upload_dna(interacting_with, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
- else
- return try_linking_vault(interacting_with, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
+ return try_linking_vault(interacting_with, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
+
+ if (!valid_scan_target(interacting_with))
+ return NONE
+
+ if (scan_dna(interacting_with, user))
+ return ITEM_INTERACT_SUCCESS
- scan_dna(interacting_with, user)
return ITEM_INTERACT_BLOCKING
/obj/item/dna_probe/proc/try_linking_vault(obj/machinery/dna_vault/target, mob/user)
@@ -78,7 +80,7 @@
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))
+ if(istype(target, /obj/machinery/hydroponics))
var/obj/machinery/hydroponics/hydro_tray = target
if(!hydro_tray.myseed)
return
@@ -90,11 +92,12 @@
return
if(hydro_tray.plant_status != 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 .
+ return
stored_dna_plants[hydro_tray.myseed.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))
+ return TRUE
+ else if(ishuman(target))
var/mob/living/carbon/human/human_target = target
if(our_vault.human_dna[human_target.dna.unique_identity])
to_chat(user, span_notice("Humanoid data already present in vault storage."))
@@ -108,23 +111,40 @@
stored_dna_human[human_target.dna.unique_identity] = TRUE
playsound(src, 'sound/misc/compiler-stage2.ogg', 50)
balloon_alert(user, "data added")
+ return TRUE
+
+ if(!isliving(target))
+ return
+
+ var/static/list/non_simple_animals = typecacheof(list(/mob/living/carbon/alien))
+ if(!isanimal_or_basicmob(target) && !is_type_in_typecache(target, non_simple_animals) && !ismonkey(target))
+ return
+
+ var/mob/living/living_target = target
+ if(our_vault.animal_dna[living_target.type])
+ to_chat(user, span_notice("Animal data already present in vault storage."))
+ return
+ if(stored_dna_animal[living_target.type])
+ to_chat(user, span_notice("Animal data already present in local storage."))
+ return
+ if(!(living_target.mob_biotypes & MOB_ORGANIC))
+ to_chat(user, span_alert("No compatible DNA detected."))
+ return .
+ stored_dna_animal[living_target.type] = TRUE
+ playsound(src, 'sound/misc/compiler-stage2.ogg', 50)
+ balloon_alert(user, "data added")
+ return TRUE
- else if((allowed_scans & DNA_PROBE_SCAN_ANIMALS) && isliving(target))
+/obj/item/dna_probe/proc/valid_scan_target(atom/target)
+ if((allowed_scans & DNA_PROBE_SCAN_PLANTS) && istype(target, /obj/machinery/hydroponics))
+ return TRUE
+ if((allowed_scans & DNA_PROBE_SCAN_HUMANS) && ishuman(target))
+ return TRUE
+ if((allowed_scans & DNA_PROBE_SCAN_ANIMALS) && isliving(target))
var/static/list/non_simple_animals = typecacheof(list(/mob/living/carbon/alien))
if(isanimal_or_basicmob(target) || is_type_in_typecache(target, non_simple_animals) || ismonkey(target))
- var/mob/living/living_target = target
- if(our_vault.animal_dna[living_target.type])
- to_chat(user, span_notice("Animal data already present in vault storage."))
- return
- if(stored_dna_animal[living_target.type])
- to_chat(user, span_notice("Animal data already present in local storage."))
- return
- if(!(living_target.mob_biotypes & MOB_ORGANIC))
- to_chat(user, span_alert("No compatible DNA detected."))
- return .
- stored_dna_animal[living_target.type] = TRUE
- playsound(src, 'sound/misc/compiler-stage2.ogg', 50)
- balloon_alert(user, "data added")
+ return TRUE
+ return FALSE
#define CARP_MIX_DNA_TIMER (15 SECONDS)
@@ -132,6 +152,8 @@
/obj/item/dna_probe/carp_scanner
name = "Carp DNA Sampler"
desc = "Can be used to take chemical and genetic samples of animals."
+ ///Whether we have Carp DNA
+ var/carp_dna_loaded = FALSE
/obj/item/dna_probe/carp_scanner/examine_more(mob/user)
. = ..()
@@ -145,6 +167,11 @@
else
return ..()
+/obj/item/dna_probe/carp_scanner/valid_scan_target(atom/target)
+ if (istype(target, /mob/living/basic/carp))
+ return TRUE
+ return ..()
+
/obj/item/dna_probe/carp_scanner/attack_self(mob/user, modifiers)
. = ..()
if(!carp_dna_loaded)
diff --git a/code/game/objects/items/dualsaber.dm b/code/game/objects/items/dualsaber.dm
index 8f838c0c0412f..86b99e8c47e4e 100644
--- a/code/game/objects/items/dualsaber.dm
+++ b/code/game/objects/items/dualsaber.dm
@@ -56,10 +56,9 @@
/// Triggered on wield of two handed item
/// Specific hulk checks due to reflection chance for balance issues and switches hitsounds.
/obj/item/dualsaber/proc/on_wield(obj/item/source, mob/living/carbon/user)
- if(user?.has_dna())
- if(user.dna.check_mutation(/datum/mutation/human/hulk))
- to_chat(user, span_warning("You lack the grace to wield this!"))
- return COMPONENT_TWOHANDED_BLOCK_WIELD
+ if(user && HAS_TRAIT(user, TRAIT_HULK))
+ to_chat(user, span_warning("You lack the grace to wield this!"))
+ return COMPONENT_TWOHANDED_BLOCK_WIELD
update_weight_class(w_class_on)
hitsound = 'sound/weapons/blade1.ogg'
START_PROCESSING(SSobj, src)
@@ -123,12 +122,11 @@
. = ..()
/obj/item/dualsaber/attack(mob/target, mob/living/carbon/human/user)
- if(user.has_dna())
- if(user.dna.check_mutation(/datum/mutation/human/hulk))
- to_chat(user, span_warning("You grip the blade too hard and accidentally drop it!"))
- if(HAS_TRAIT(src, TRAIT_WIELDED))
- user.dropItemToGround(src, force=TRUE)
- return
+ if(HAS_TRAIT(user, TRAIT_HULK))
+ to_chat(user, span_warning("You grip the blade too hard and accidentally drop it!"))
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
+ user.dropItemToGround(src, force=TRUE)
+ return
..()
if(!HAS_TRAIT(src, TRAIT_WIELDED))
return
diff --git a/code/game/objects/items/eightball.dm b/code/game/objects/items/eightball.dm
index 719789b2a936c..4445cdd1b7277 100644
--- a/code/game/objects/items/eightball.dm
+++ b/code/game/objects/items/eightball.dm
@@ -63,7 +63,9 @@
shaking = TRUE
- start_shaking(user)
+ if (!start_shaking(user))
+ return
+
if(do_after(user, shake_time))
say(get_answer())
@@ -73,7 +75,7 @@
shaking = FALSE
/obj/item/toy/eightball/proc/start_shaking(mob/user)
- return
+ return TRUE
/obj/item/toy/eightball/proc/get_answer()
return pick(possible_answers)
@@ -98,8 +100,7 @@
/obj/item/toy/eightball/haunted
shake_time = 30 SECONDS
cooldown_time = 3 MINUTES
- var/last_message = "Nothing!"
- var/selected_message
+ var/selected_message = "Nothing!"
//these kind of store the same thing but one is easier to work with.
var/list/votes = list()
var/list/voted = list()
@@ -137,7 +138,6 @@
for (var/answer in haunted_answers)
votes[answer] = 0
SSpoints_of_interest.make_point_of_interest(src)
- become_hearing_sensitive()
/obj/item/toy/eightball/haunted/MakeHaunted()
return FALSE
@@ -150,20 +150,19 @@
interact(user)
return ..()
-/obj/item/toy/eightball/haunted/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, list/message_mods = list(), message_range)
- . = ..()
- last_message = raw_message
-
/obj/item/toy/eightball/haunted/start_shaking(mob/user)
// notify ghosts that someone's shaking a haunted eightball
// and inform them of the message, (hopefully a yes/no question)
- selected_message = last_message
+ selected_message = tgui_input_text(user, "What is your question?", "Eightball") || initial(selected_message)
+ if (!(src in user.held_items))
+ return FALSE
notify_ghosts(
"[user] is shaking [src], hoping to get an answer to \"[selected_message]\"",
source = src,
header = "Magic eightball",
click_interact = TRUE,
)
+ return TRUE
/obj/item/toy/eightball/haunted/get_answer()
var/top_amount = 0
@@ -211,16 +210,16 @@
data["question"] = selected_message
data["answers"] = list()
- for(var/pa in haunted_answers)
- var/list/L = list()
- L["answer"] = pa
- L["amount"] = votes[pa]
- L["selected"] = voted[user.ckey]
+ for(var/vote in haunted_answers)
+ var/list/answer_data = list()
+ answer_data["answer"] = vote
+ answer_data["amount"] = votes[vote]
+ answer_data["selected"] = voted[user.ckey]
- data["answers"] += list(L)
+ data["answers"] += list(answer_data)
return data
-/obj/item/toy/eightball/haunted/ui_act(action, params)
+/obj/item/toy/eightball/haunted/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm
index 764e2fc6173bf..433b528fbcebd 100644
--- a/code/game/objects/items/extinguisher.dm
+++ b/code/game/objects/items/extinguisher.dm
@@ -47,6 +47,8 @@
var/cooling_power = 2
/// Icon state when inside a tank holder.
var/tank_holder_icon_state = "holder_extinguisher"
+ ///Icon state when inside an extinguisher cabinet.
+ var/cabinet_icon_state = "extinguisher_common"
/obj/item/extinguisher/Initialize(mapload)
. = ..()
@@ -84,6 +86,7 @@
max_water = 30
sprite_name = "miniFE"
dog_fashion = null
+ cabinet_icon_state = "extinguisher_mini"
/obj/item/extinguisher/mini/empty
starting_water = FALSE
@@ -141,6 +144,7 @@
)
sprite_name = "foam_extinguisher"
precision = TRUE
+ cabinet_icon_state = "extinguisher_adv"
max_water = 100
/obj/item/extinguisher/advanced/empty
diff --git a/code/game/objects/items/fireaxe.dm b/code/game/objects/items/fireaxe.dm
index 38e4c840694f5..8a8f26b2c8995 100644
--- a/code/game/objects/items/fireaxe.dm
+++ b/code/game/objects/items/fireaxe.dm
@@ -77,7 +77,7 @@
icon_state = "metalh2_axe0"
base_icon_state = "metalh2_axe"
name = "metallic hydrogen axe"
- desc = "A lightweight crowbar with an extreme sharp fire axe head attached. It trades it's hefty as a weapon by making it easier to carry around when holstered to suits without having to sacrifice your backpack."
+ desc = "A lightweight crowbar with an extreme sharp fire axe head attached. It trades its heft as a weapon by making it easier to carry around when holstered to suits without having to sacrifice your backpack."
force_unwielded = 5
force_wielded = 15
demolition_mod = 2
diff --git a/code/game/objects/items/food/bait.dm b/code/game/objects/items/food/bait.dm
index 047a8a7cd58ce..f31eb44f308eb 100644
--- a/code/game/objects/items/food/bait.dm
+++ b/code/game/objects/items/food/bait.dm
@@ -60,9 +60,23 @@
*/
/obj/item/food/bait/doughball/synthetic
name = "synthetic doughball"
- icon_state = "doughball"
+ icon_state = "doughball_blue"
preserved_food = TRUE
/obj/item/food/bait/doughball/synthetic/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_OMNI_BAIT, INNATE_TRAIT)
+
+///Found in the can of omni-baits, only available from the super fishing toolbox, from the fishing mystery box.
+/obj/item/food/bait/doughball/synthetic/super
+ name = "super-doughball"
+ desc = "No fish will be able to resist this."
+ bait_quality = TRAIT_GREAT_QUALITY_BAIT
+
+///Used by the advanced fishing rod
+/obj/item/food/bait/doughball/syntethic/unconsumable
+
+/obj/item/food/bait/doughball/synthetic/unconsumable/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_BAIT_UNCONSUMABLE, INNATE_TRAIT)
+
diff --git a/code/game/objects/items/food/cake.dm b/code/game/objects/items/food/cake.dm
index 0b443554bb3b6..3bcb35376ab88 100644
--- a/code/game/objects/items/food/cake.dm
+++ b/code/game/objects/items/food/cake.dm
@@ -521,6 +521,7 @@
foodtypes = GRAIN | SUGAR | DAIRY
slice_type = /obj/item/food/cakeslice/clown_slice
crafting_complexity = FOOD_COMPLEXITY_5
+ crafted_food_buff = /datum/status_effect/food/trait/waddle
/obj/item/food/cakeslice/clown_slice
name = "clown cake slice"
@@ -534,6 +535,7 @@
tastes = list("cake" = 1, "sugar" = 1, "joy" = 10)
foodtypes = GRAIN | SUGAR | DAIRY
crafting_complexity = FOOD_COMPLEXITY_5
+ crafted_food_buff = /datum/status_effect/food/trait/waddle
/obj/item/food/cake/trumpet
name = "spaceman's cake"
diff --git a/code/game/objects/items/food/donkpocket.dm b/code/game/objects/items/food/donkpocket.dm
index d4b4636f15c9b..b21149da8d044 100644
--- a/code/game/objects/items/food/donkpocket.dm
+++ b/code/game/objects/items/food/donkpocket.dm
@@ -259,3 +259,105 @@
)
tastes = list("meat" = 2, "dough" = 2, "inner peace" = 1)
foodtypes = GRAIN
+
+/obj/item/food/donkpocket/deluxe
+ name = "\improper Donk-pocket Deluxe"
+ desc = "Donk Co's latest product. Its recipe is a closely guarded secret."
+ icon_state = "donkpocketdeluxe"
+ food_reagents = list(
+ /datum/reagent/consumable/nutriment = 4,
+ /datum/reagent/consumable/nutriment/protein = 2,
+ /datum/reagent/consumable/nutriment/vitamin = 1,
+ /datum/reagent/medicine/omnizine = 2,
+ )
+ tastes = list("quality meat" = 2, "dough" = 2, "raw fanciness" = 1)
+ foodtypes = GRAIN | MEAT
+ crafting_complexity = FOOD_COMPLEXITY_4
+
+ warm_type = /obj/item/food/donkpocket/warm/deluxe
+ var/static/list/deluxe_added_reagents = list(
+ /datum/reagent/medicine/omnizine = 8,
+ )
+
+/obj/item/food/donkpocket/deluxe/make_bakeable()
+ AddComponent(/datum/component/bakeable, warm_type, rand(baking_time_short, baking_time_long), TRUE, TRUE, deluxe_added_reagents)
+
+/obj/item/food/donkpocket/deluxe/make_microwaveable()
+ AddElement(/datum/element/microwavable, warm_type, deluxe_added_reagents)
+
+/obj/item/food/donkpocket/warm/deluxe
+ name = "warm Donk-pocket Deluxe"
+ desc = "Donk Co's latest product. It's crispy warm and oh-so perfectly toasted. Damn, that's a good looking Donk."
+ icon_state = "donkpocketdeluxe"
+ food_reagents = list(
+ /datum/reagent/consumable/nutriment = 4,
+ /datum/reagent/consumable/nutriment/protein = 2,
+ /datum/reagent/consumable/nutriment/vitamin = 1,
+ /datum/reagent/medicine/omnizine = 10,
+ )
+ tastes = list("quality meat" = 2, "dough" = 2, "fanciness" = 1)
+ foodtypes = GRAIN | MEAT | FRIED
+
+/obj/item/food/donkpocket/deluxe/nocarb
+ name = "/improper Meat-pocket"
+ desc = "The food of choice for the carnivorous traitor."
+ icon_state = "donkpocketdeluxenocarb"
+ food_reagents = list(
+ /datum/reagent/consumable/nutriment = 4,
+ /datum/reagent/consumable/nutriment/protein = 3,
+ /datum/reagent/medicine/omnizine = 2,
+ )
+ tastes = list("raw meat" = 2, "more meat" = 2, "no carbs" = 1)
+ foodtypes = MEAT | RAW
+ crafting_complexity = FOOD_COMPLEXITY_4
+
+ warm_type = /obj/item/food/donkpocket/warm/deluxe/nocarb
+
+/obj/item/food/donkpocket/deluxe/meat/make_bakeable()
+ AddComponent(/datum/component/bakeable, warm_type, rand(baking_time_short, baking_time_long), TRUE, TRUE, deluxe_added_reagents)
+
+/obj/item/food/donkpocket/deluxe/meat/make_microwaveable()
+ AddElement(/datum/element/microwavable, warm_type, deluxe_added_reagents)
+
+/obj/item/food/donkpocket/warm/deluxe/nocarb
+ name = "warm Meat-pocket"
+ desc = "The warm food of choice for the carnivorous traitor."
+ icon_state = "donkpocketdeluxenocarb"
+ food_reagents = list(
+ /datum/reagent/consumable/nutriment = 4,
+ /datum/reagent/consumable/nutriment/protein = 3,
+ /datum/reagent/medicine/omnizine = 10,
+ )
+ tastes = list("meat" = 2, "more meat" = 2, "no carbs" = 1)
+ foodtypes = MEAT
+
+/obj/item/food/donkpocket/deluxe/vegan
+ name = "/improper Donk-roll"
+ desc = "The classic station snack, now with rice! Certified vegan and cruelty free by the Animal Liberation Front."
+ icon_state = "donkpocketdeluxevegan"
+ food_reagents = list(
+ /datum/reagent/consumable/nutriment = 4,
+ /datum/reagent/consumable/nutriment/vitamin = 3,
+ /datum/reagent/medicine/omnizine = 2,
+ )
+ tastes = list("rice patty" = 2, "dough" = 2, "peppery kick" = 1)
+ foodtypes = GRAIN | VEGETABLES
+ crafting_complexity = FOOD_COMPLEXITY_4
+
+/obj/item/food/donkpocket/deluxe/vegan/make_bakeable()
+ AddComponent(/datum/component/bakeable, warm_type, rand(baking_time_short, baking_time_long), TRUE, TRUE, deluxe_added_reagents)
+
+/obj/item/food/donkpocket/deluxe/vegan/make_microwaveable()
+ AddElement(/datum/element/microwavable, warm_type, deluxe_added_reagents)
+
+/obj/item/food/donkpocket/warm/deluxe/vegan
+ name = "warm Donk-roll"
+ desc = "The classic station snack, now with rice! It's been fried to perfection."
+ icon_state = "donkpocketdeluxevegan"
+ food_reagents = list(
+ /datum/reagent/consumable/nutriment = 4,
+ /datum/reagent/consumable/nutriment/vitamin = 3,
+ /datum/reagent/medicine/omnizine = 10,
+ )
+ tastes = list("rice patty" = 2, "fried dough" = 2, "peppery kick" = 1)
+ foodtypes = GRAIN | VEGETABLES
diff --git a/code/game/objects/items/food/egg.dm b/code/game/objects/items/food/egg.dm
index b669e16b103cd..bcc61e721e211 100644
--- a/code/game/objects/items/food/egg.dm
+++ b/code/game/objects/items/food/egg.dm
@@ -277,6 +277,7 @@ GLOBAL_VAR_INIT(chicks_from_eggs, 0)
foodtypes = MEAT | BREAKFAST | DAIRY
venue_value = FOOD_PRICE_CHEAP
crafting_complexity = FOOD_COMPLEXITY_2
+ crafted_food_buff = /datum/status_effect/food/speech/french
/obj/item/food/omelette/attackby(obj/item/item, mob/user, params)
if(istype(item, /obj/item/kitchen/fork))
diff --git a/code/game/objects/items/food/lizard.dm b/code/game/objects/items/food/lizard.dm
index 47b5ff7510916..2048c997ef9ad 100644
--- a/code/game/objects/items/food/lizard.dm
+++ b/code/game/objects/items/food/lizard.dm
@@ -156,6 +156,7 @@
desc = "Another example of cultural crossover between lizards and humans, desert snail escargot is closer to the Roman dish cocleas than the contemporary French escargot. It's a common street food in the desert cities."
icon = 'icons/obj/food/lizard.dmi'
icon_state = "lizard_escargot"
+ trash_type = /obj/item/reagent_containers/cup/bowl
food_reagents = list(
/datum/reagent/consumable/nutriment/protein = 6,
/datum/reagent/consumable/nutriment/vitamin = 4,
@@ -188,6 +189,7 @@
desc = "One of the many human foods to make its way to the lizards was french fries, which are called poms-franzisks in Draconic. When topped with barbecued meat and sauce, they make a hearty meal."
icon = 'icons/obj/food/lizard.dmi'
icon_state = "lizard_fries"
+ trash_type = /obj/item/plate
food_reagents = list(
/datum/reagent/consumable/nutriment = 4,
/datum/reagent/consumable/nutriment/protein = 6,
@@ -887,7 +889,7 @@
/obj/item/food/burger/rootrib
name = "rootrib"
- desc = "An elusive rib shaped burger with limited availablity across the galaxy. Now meeting subhuman requirements."
+ desc = "An elusive rib shaped burger with limited availability across the galaxy. Now meeting subhuman requirements."
icon_state = "rootrib"
icon = 'icons/obj/food/lizard.dmi'
food_reagents = list(
diff --git a/code/game/objects/items/food/martian.dm b/code/game/objects/items/food/martian.dm
index 7ceaf1878176c..748610457ec41 100644
--- a/code/game/objects/items/food/martian.dm
+++ b/code/game/objects/items/food/martian.dm
@@ -102,6 +102,7 @@
desc = "A spice paste from Indonesia, used widely in cooking throughout South East Asia."
icon = 'icons/obj/food/martian.dmi'
icon_state = "sambal"
+ trash_type = /obj/item/reagent_containers/cup/bowl
food_reagents = list(
/datum/reagent/consumable/nutriment/vitamin = 5,
/datum/reagent/consumable/capsaicin = 2
@@ -519,7 +520,7 @@
crafting_complexity = FOOD_COMPLEXITY_3
/obj/item/food/takoyaki/russian
- name = "russian takoyaki"
+ name = "Russian takoyaki"
desc = "A dangerous twist on a classic dish, that makes for the perfect cover for evading the police."
icon = 'icons/obj/food/martian.dmi'
icon_state = "russian_takoyaki"
@@ -535,7 +536,7 @@
/obj/item/food/takoyaki/taco
name = "tacoyaki"
- desc = "Straight outta Mars' most innovative street food stands, it's tacoyaki- trading octopus for taco meat and corn, and worcestershire sauce for queso. ¡Tan sabroso!"
+ desc = "Straight outta Mars' most innovative street food stands, it's tacoyaki- trading octopus for taco meat and corn, and Worcestershire sauce for queso. ¡Tan sabroso!"
icon = 'icons/obj/food/martian.dmi'
icon_state = "tacoyaki"
food_reagents = list(
diff --git a/code/game/objects/items/food/meatdish.dm b/code/game/objects/items/food/meatdish.dm
index 537c7688d2dd4..298ab54c78204 100644
--- a/code/game/objects/items/food/meatdish.dm
+++ b/code/game/objects/items/food/meatdish.dm
@@ -34,6 +34,37 @@
foodtypes = SEAFOOD
eatverbs = list("bite", "chew", "gnaw", "swallow", "chomp")
w_class = WEIGHT_CLASS_SMALL
+ starting_reagent_purity = 1.0
+
+/obj/item/food/fishmeat/quality
+ name = "quality fish fillet"
+ desc = "A fillet of some precious fish meat."
+ food_reagents = list(
+ /datum/reagent/consumable/nutriment/protein = 4,
+ /datum/reagent/consumable/nutriment/vitamin = 3,
+ )
+ bite_consumption = 7
+ crafting_complexity = FOOD_COMPLEXITY_1
+
+/obj/item/food/fishmeat/quality/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/quality_food_ingredient, FOOD_COMPLEXITY_1)
+
+/obj/item/food/fishmeat/salmon
+ name = "salmon fillet"
+ desc = "a chunky, fatty fillet of salmon meat."
+ icon_state = "salmon"
+ food_reagents = list(
+ /datum/reagent/consumable/nutriment/protein = 4,
+ /datum/reagent/consumable/nutriment/vitamin = 3,
+ /datum/reagent/consumable/nutriment/fat/oil = 2,
+ )
+ bite_consumption = 4.5
+ crafting_complexity = FOOD_COMPLEXITY_1
+
+/obj/item/food/fishmeat/salmon/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/quality_food_ingredient, FOOD_COMPLEXITY_1)
/obj/item/food/fishmeat/carp
name = "carp fillet"
@@ -88,6 +119,7 @@
name = "donkfillet"
desc = "The dreaded donkfish fillet. No sane spaceman would eat this, and it does not get better when cooked."
icon_state = "donkfillet"
+ starting_reagent_purity = 0.3
/obj/item/food/fishmeat/octopus
name = "octopus tentacle"
@@ -498,7 +530,7 @@
w_class = WEIGHT_CLASS_SMALL
crafting_complexity = FOOD_COMPLEXITY_1
-///Exists purely for the crafting recipe (because itll take subtypes)
+///Exists purely for the crafting recipe (because it'll take subtypes)
/obj/item/food/patty/plain
/obj/item/food/patty/human
@@ -893,9 +925,10 @@
/obj/item/food/beef_stroganoff
name = "beef stroganoff"
- desc = "A russian dish that consists of beef and sauce. Really popular in japan, or at least that's what my animes would allude to."
+ desc = "A Russian dish that consists of beef and sauce. Really popular in Japan, or at least that's what my animes would allude to."
icon = 'icons/obj/food/meat.dmi'
icon_state = "beefstroganoff"
+ trash_type = /obj/item/reagent_containers/cup/bowl
food_reagents = list(
/datum/reagent/consumable/nutriment/protein = 16,
/datum/reagent/consumable/nutriment/vitamin = 4,
@@ -1074,7 +1107,7 @@
crafting_complexity = FOOD_COMPLEXITY_5
/obj/item/food/full_english
- name = "full english breakfast"
+ name = "full English breakfast"
desc = "A hearty plate with all the trimmings, representing the pinnacle of the breakfast art."
icon = 'icons/obj/food/meat.dmi'
icon_state = "full_english"
diff --git a/code/game/objects/items/food/meatslab.dm b/code/game/objects/items/food/meatslab.dm
index a5fe073e56f3f..ed391a57bd255 100644
--- a/code/game/objects/items/food/meatslab.dm
+++ b/code/game/objects/items/food/meatslab.dm
@@ -150,7 +150,7 @@
blood_decal_type = null
/obj/item/food/meat/slab/human/mutant/skeleton/make_processable()
- return //skeletons dont have cutlets
+ return //skeletons don't have cutlets
/obj/item/food/meat/slab/human/mutant/zombie
name = "meat (rotten)"
@@ -353,7 +353,7 @@
/obj/item/food/meat/rawbacon
name = "raw piece of bacon"
desc = "A raw piece of bacon."
- icon_state = "baconb"
+ icon_state = "bacon"
bite_consumption = 2
food_reagents = list(
/datum/reagent/consumable/nutriment/protein = 2,
@@ -369,7 +369,7 @@
/obj/item/food/meat/bacon
name = "piece of bacon"
desc = "A delicious piece of bacon."
- icon_state = "baconcookedb"
+ icon_state = "baconcooked"
food_reagents = list(
/datum/reagent/consumable/nutriment/protein = 2,
/datum/reagent/consumable/nutriment/vitamin = 1,
diff --git a/code/game/objects/items/food/mexican.dm b/code/game/objects/items/food/mexican.dm
index 396e351ff4bde..fa66db1450c8f 100644
--- a/code/game/objects/items/food/mexican.dm
+++ b/code/game/objects/items/food/mexican.dm
@@ -196,12 +196,14 @@
w_class = WEIGHT_CLASS_SMALL
venue_value = FOOD_PRICE_LEGENDARY
crafting_complexity = FOOD_COMPLEXITY_5
+ crafted_food_buff = /datum/status_effect/food/trait/ashstorm_immune
/obj/item/food/chipsandsalsa
name = "chips and salsa"
desc = "Some tortilla chips with a cup of zesty salsa. Highly addictive!"
icon = 'icons/obj/food/mexican.dmi'
icon_state = "chipsandsalsa"
+ trash_type = /obj/item/reagent_containers/cup/bowl
food_reagents = list(
/datum/reagent/consumable/nutriment = 4,
/datum/reagent/consumable/capsaicin = 2,
@@ -331,6 +333,7 @@
desc = "A not-so liquid salsa made of pineapples, tomatoes, onions, and chilis. Makes for delightfully contrasting flavors."
icon = 'icons/obj/food/mexican.dmi'
icon_state = "pineapple_salsa"
+ trash_type = /obj/item/reagent_containers/cup/bowl
food_reagents = list(
/datum/reagent/consumable/nutriment = 6,
/datum/reagent/consumable/nutriment/vitamin = 6,
diff --git a/code/game/objects/items/food/misc.dm b/code/game/objects/items/food/misc.dm
index 31ac87c0ff690..26852066bae28 100644
--- a/code/game/objects/items/food/misc.dm
+++ b/code/game/objects/items/food/misc.dm
@@ -16,6 +16,87 @@
juice_typepath = /datum/reagent/consumable/watermelonjuice
w_class = WEIGHT_CLASS_SMALL
+/obj/item/food/watermelonmush
+ name = "watermelon mush"
+ desc = "A plop of watery goodness."
+ icon = 'icons/obj/service/hydroponics/harvest.dmi'
+ icon_state = "watermelonpulp"
+ food_reagents = list(
+ /datum/reagent/water = 2,
+ /datum/reagent/consumable/nutriment/vitamin = 0.1,
+ /datum/reagent/consumable/nutriment = 0.5,
+ )
+ tastes = list("watermelon" = 1)
+ foodtypes = FRUIT
+ food_flags = FOOD_FINGER_FOOD
+ juice_typepath = /datum/reagent/consumable/watermelonjuice
+ w_class = WEIGHT_CLASS_SMALL
+
+/obj/item/food/holymelonslice
+ name = "holymelon slice"
+ desc = "A slice of holy goodness."
+ icon = 'icons/obj/service/hydroponics/harvest.dmi'
+ icon_state = "holymelonslice"
+ food_reagents = list(
+ /datum/reagent/water/holywater = 0.5,
+ /datum/reagent/consumable/nutriment/vitamin = 0.2,
+ /datum/reagent/consumable/nutriment = 1,
+ )
+ tastes = list("holymelon" = 1)
+ foodtypes = FRUIT
+ food_flags = FOOD_FINGER_FOOD
+ juice_typepath = /datum/reagent/water/holywater
+ w_class = WEIGHT_CLASS_SMALL
+
+/obj/item/food/holymelonmush
+ name = "holymelon mush"
+ desc = "A plop of holy goodness."
+ icon = 'icons/obj/service/hydroponics/harvest.dmi'
+ icon_state = "holymelonpulp"
+ food_reagents = list(
+ /datum/reagent/water/holywater = 1,
+ /datum/reagent/consumable/nutriment/vitamin = 0.1,
+ /datum/reagent/consumable/nutriment = 0.5,
+ )
+ tastes = list("holymelon" = 1)
+ foodtypes = FRUIT
+ food_flags = FOOD_FINGER_FOOD
+ juice_typepath = /datum/reagent/water/holywater
+ w_class = WEIGHT_CLASS_SMALL
+
+/obj/item/food/barrelmelonslice
+ name = "barrelmelon slice"
+ desc = "A slice of beery goodness."
+ icon = 'icons/obj/service/hydroponics/harvest.dmi'
+ icon_state = "barrelmelonslice"
+ food_reagents = list(
+ /datum/reagent/consumable/ethanol/beer = 1,
+ /datum/reagent/consumable/nutriment/vitamin = 0.2,
+ /datum/reagent/consumable/nutriment = 1,
+ )
+ tastes = list("beer" = 1)
+ foodtypes = FRUIT
+ food_flags = FOOD_FINGER_FOOD
+ juice_typepath = /datum/reagent/consumable/ethanol/beer
+ w_class = WEIGHT_CLASS_SMALL
+
+/obj/item/food/barrelmelonmush
+ name = "barrelmelon mush"
+ desc = "A plop of beery goodness."
+ icon = 'icons/obj/service/hydroponics/harvest.dmi'
+ icon_state = "barrelmelonpulp"
+ food_reagents = list(
+ /datum/reagent/consumable/ethanol/beer = 2,
+ /datum/reagent/consumable/nutriment/vitamin = 0.1,
+ /datum/reagent/consumable/nutriment = 0.5,
+ )
+ tastes = list("beer" = 1)
+ foodtypes = FRUIT
+ food_flags = FOOD_FINGER_FOOD
+ juice_typepath = /datum/reagent/consumable/ethanol/beer
+ w_class = WEIGHT_CLASS_SMALL
+
+
/obj/item/food/appleslice
name = "apple slice"
desc = "The perfect after-school snack."
diff --git a/code/game/objects/items/food/monkeycube.dm b/code/game/objects/items/food/monkeycube.dm
index a364a251a8344..02882bb3776f4 100644
--- a/code/game/objects/items/food/monkeycube.dm
+++ b/code/game/objects/items/food/monkeycube.dm
@@ -82,7 +82,7 @@
/obj/item/food/monkeycube/gorilla
name = "gorilla cube"
- desc = "A Waffle Co. brand gorilla cube. Now with extra molecules!"
+ desc = "A Waffle Corp. brand gorilla cube. Now with extra molecules!"
bite_consumption = 20
food_reagents = list(
/datum/reagent/monkey_powder = 30,
diff --git a/code/game/objects/items/food/moth.dm b/code/game/objects/items/food/moth.dm
index b2d9dfdb8fe37..367a3f29b5cc4 100644
--- a/code/game/objects/items/food/moth.dm
+++ b/code/game/objects/items/food/moth.dm
@@ -9,6 +9,7 @@
Herbs are one such addition, and are particularly beloved."
icon = 'icons/obj/food/moth.dmi'
icon_state = "herby_cheese"
+ trash_type = /obj/item/reagent_containers/cup/bowl
food_reagents = list(/datum/reagent/consumable/nutriment/protein = 6)
tastes = list("cheese" = 1, "herbs" = 1)
foodtypes = DAIRY | VEGETABLES
@@ -461,6 +462,7 @@
it's just sorta what it's always been called."
icon = 'icons/obj/food/moth.dmi'
icon_state = "hua_mulan_congee"
+ trash_type = /obj/item/reagent_containers/cup/bowl
food_reagents = list(
/datum/reagent/consumable/nutriment = 6,
/datum/reagent/consumable/nutriment/vitamin = 10,
@@ -476,6 +478,7 @@
desc = "Polenta loaded with cheese, served with a few discs of fried eggplant and some tomato sauce. Lække!"
icon = 'icons/obj/food/moth.dmi'
icon_state = "fried_eggplant_polenta"
+ trash_type = /obj/item/reagent_containers/cup/bowl
food_reagents = list(
/datum/reagent/consumable/nutriment/protein = 12,
/datum/reagent/consumable/nutriment/vitamin = 6,
diff --git a/code/game/objects/items/food/pastries.dm b/code/game/objects/items/food/pastries.dm
index 1024e44c625f3..0b96101a99d51 100644
--- a/code/game/objects/items/food/pastries.dm
+++ b/code/game/objects/items/food/pastries.dm
@@ -67,7 +67,6 @@
name = "waffles"
desc = "Mmm, waffles."
icon_state = "waffles"
- trash_type = /obj/item/trash/waffles
food_reagents = list(
/datum/reagent/consumable/nutriment = 8,
/datum/reagent/consumable/nutriment/vitamin = 2,
@@ -85,7 +84,6 @@
name = "\improper Soylent Green"
desc = "Not made of people. Honest." //Totally people.
icon_state = "soylent_green"
- trash_type = /obj/item/trash/waffles
food_reagents = list(
/datum/reagent/consumable/nutriment = 10,
/datum/reagent/consumable/nutriment/vitamin = 2,
@@ -100,7 +98,6 @@
name = "\improper Soylent Virdians"
desc = "Not made of people. Honest." //Actually honest for once.
icon_state = "soylent_yellow"
- trash_type = /obj/item/trash/waffles
food_reagents = list(
/datum/reagent/consumable/nutriment = 10,
/datum/reagent/consumable/nutriment/vitamin = 2,
@@ -115,7 +112,6 @@
name = "roffle waffles"
desc = "Waffles from Roffle. Co."
icon_state = "rofflewaffles"
- trash_type = /obj/item/trash/waffles
bite_consumption = 4
food_reagents = list(
/datum/reagent/consumable/nutriment = 8,
@@ -327,6 +323,20 @@
tastes = list("cake" = 3, "blue cherry" = 1)
crafting_complexity = FOOD_COMPLEXITY_3
+/obj/item/food/jupitercupcake
+ name = "jupiter-cup-cake"
+ desc = "A static dessert."
+ icon_state = "jupitercupcake"
+ food_reagents = list(
+ /datum/reagent/consumable/nutriment = 6,
+ /datum/reagent/consumable/nutriment/vitamin = 2,
+ /datum/reagent/consumable/caramel = 3,
+ /datum/reagent/consumable/liquidelectricity/enriched = 3,
+ )
+ tastes = list("cake" = 3, "caramel" = 2, "zap" = 1)
+ crafting_complexity = FOOD_COMPLEXITY_3
+ crafted_food_buff = /datum/status_effect/food/trait/shockimmune
+
/obj/item/food/honeybun
name = "honey bun"
desc = "A sticky pastry bun glazed with honey."
diff --git a/code/game/objects/items/food/pie.dm b/code/game/objects/items/food/pie.dm
index e57759915208d..4a44f5e5cb6fd 100644
--- a/code/game/objects/items/food/pie.dm
+++ b/code/game/objects/items/food/pie.dm
@@ -11,7 +11,7 @@
crafting_complexity = FOOD_COMPLEXITY_2
/// type is spawned 5 at a time and replaces this pie when processed by cutting tool
var/obj/item/food/pieslice/slice_type
- /// so that the yield can change if it isnt 5
+ /// so that the yield can change if it isn't 5
var/yield = 5
/obj/item/food/pie/make_processable()
@@ -316,6 +316,7 @@
)
tastes = list("nothing" = 3)
foodtypes = GRAIN
+ crafted_food_buff = /datum/status_effect/food/trait/mute
/obj/item/food/pie/berrytart
name = "berry tart"
diff --git a/code/game/objects/items/food/pizza.dm b/code/game/objects/items/food/pizza.dm
index 71dd87af8e183..bdaab0a72851f 100644
--- a/code/game/objects/items/food/pizza.dm
+++ b/code/game/objects/items/food/pizza.dm
@@ -169,7 +169,7 @@
/obj/item/food/pizza/vegetable
name = "vegetable pizza"
- desc = "No one of Tomatos Sapiens were harmed during making this pizza."
+ desc = "No one of Tomatoes Sapiens were harmed during making this pizza."
icon_state = "vegetablepizza"
food_reagents = list(
/datum/reagent/consumable/nutriment = 25,
diff --git a/code/game/objects/items/food/snacks.dm b/code/game/objects/items/food/snacks.dm
index cb64c6df52204..722663cf265e0 100644
--- a/code/game/objects/items/food/snacks.dm
+++ b/code/game/objects/items/food/snacks.dm
@@ -400,7 +400,7 @@ GLOBAL_LIST_INIT(safe_peanut_types, populate_safe_peanut_types())
/obj/item/food/semki
name = "\improper Semki Sunflower Seeds"
- desc = "A pack of roasted sunflower seeds. Beloved by space russians and babushka alike."
+ desc = "A pack of roasted sunflower seeds. Beloved by space Russians and babushka alike."
icon_state = "semki"
trash_type = /obj/item/trash/semki
food_reagents = list(
diff --git a/code/game/objects/items/food/spaghetti.dm b/code/game/objects/items/food/spaghetti.dm
index cab4a62a29f3b..bf1fca9332a08 100644
--- a/code/game/objects/items/food/spaghetti.dm
+++ b/code/game/objects/items/food/spaghetti.dm
@@ -249,3 +249,17 @@
tastes = list("noodles" = 5, "fried tofu" = 4, "lime" = 2, "peanut" = 3, "onion" = 2)
foodtypes = GRAIN | VEGETABLES | NUTS | FRUIT
crafting_complexity = FOOD_COMPLEXITY_4
+
+/obj/item/food/spaghetti/carbonara
+ name = "spaghetti carbonara"
+ desc = "Silky eggs, crispy pork, cheesy bliss. Mamma mia!"
+ icon_state = "carbonara"
+ food_reagents = list(
+ /datum/reagent/consumable/nutriment = 10,
+ /datum/reagent/consumable/nutriment/protein = 6,
+ /datum/reagent/consumable/nutriment/vitamin = 4,
+ )
+ tastes = list("spaghetti" = 1, "parmigiano reggiano" = 1, "guanciale" = 1)
+ foodtypes = GRAIN | MEAT | DAIRY
+ crafting_complexity = FOOD_COMPLEXITY_4
+ crafted_food_buff = /datum/status_effect/food/speech/italian
diff --git a/code/game/objects/items/granters/crafting/advanced_donk_recipes.dm b/code/game/objects/items/granters/crafting/advanced_donk_recipes.dm
new file mode 100644
index 0000000000000..1ba2502f37eec
--- /dev/null
+++ b/code/game/objects/items/granters/crafting/advanced_donk_recipes.dm
@@ -0,0 +1,21 @@
+
+/obj/item/book/granter/crafting_recipe/donk_secret_recipe
+ name = "Donk Co. Secret Recipe"
+ desc = "Documents detailing how to make several Donk Co. branded prototypes."
+ crafting_recipe_types = list(
+ /datum/crafting_recipe/food/donkpocket/deluxe,
+ /datum/crafting_recipe/food/donkpocket/deluxe/nocarb,
+ /datum/crafting_recipe/food/donkpocket/deluxe/vegan,
+ )
+ icon = 'icons/obj/service/bureaucracy.dmi'
+ icon_state = "docs_part"
+ uses = INFINITY
+ remarks = list(
+ "It's written in code...",
+ "Decyphering...",
+ "Studying...",
+ "Got to get the steps in order...",
+ "The six basic food groups...",
+ "The secret formula!",
+ "Three different variants...",
+ )
diff --git a/code/game/objects/items/granters/magic/knock.dm b/code/game/objects/items/granters/magic/knock.dm
index 11bdfeeadbfa2..0e4aaf9ee6c4d 100644
--- a/code/game/objects/items/granters/magic/knock.dm
+++ b/code/game/objects/items/granters/magic/knock.dm
@@ -9,7 +9,7 @@
"Slow down, book. I still haven't finished this page...",
"The book won't stop moving!",
"I think this is hurting the spine of the book...",
- "I can't get to the next page, it's stuck t- I'm good, it just turned to the next page on it's own.",
+ "I can't get to the next page, it's stuck t- I'm good, it just turned to the next page on its own.",
"Yeah, staff of doors does the same thing. Go figure...",
)
diff --git a/code/game/objects/items/granters/magic/lightning_bolt.dm b/code/game/objects/items/granters/magic/lightning_bolt.dm
index 3e3cbade46fa6..9fb6494bcc38c 100644
--- a/code/game/objects/items/granters/magic/lightning_bolt.dm
+++ b/code/game/objects/items/granters/magic/lightning_bolt.dm
@@ -13,4 +13,3 @@
"UNLIMITED power? Well... maybe if I practice...",
"Wait until they're grouped up...",
)
-
diff --git a/code/game/objects/items/grenades/_grenade.dm b/code/game/objects/items/grenades/_grenade.dm
index ec16b4c22fe95..a9fe7e65809ab 100644
--- a/code/game/objects/items/grenades/_grenade.dm
+++ b/code/game/objects/items/grenades/_grenade.dm
@@ -17,15 +17,17 @@
obj_flags = CONDUCTS_ELECTRICITY
slot_flags = ITEM_SLOT_BELT
max_integrity = 40
+ pickup_sound = 'sound/items/grenade_pick_up.ogg'
+ drop_sound = 'sound/items/grenade_drop.ogg'
/// Bitfields which prevent the grenade from detonating if set. Includes ([GRENADE_DUD]|[GRENADE_USED])
var/dud_flags = NONE
///Is this grenade currently armed?
var/active = FALSE
- ///Is it a cluster grenade? We dont wanna spam admin logs with these.
+ ///Is it a cluster grenade? We don't wanna spam admin logs with these.
var/type_cluster = FALSE
///How long it takes for a grenade to explode after being armed
var/det_time = 5 SECONDS
- ///Will this state what it's det_time is when examined?
+ ///Will this state what its det_time is when examined?
var/display_timer = TRUE
///Used in botch_check to determine how a user's clumsiness affects that user's ability to prime a grenade correctly.
var/clumsy_check = GRENADE_CLUMSY_FUMBLE
@@ -46,7 +48,7 @@
var/shrapnel_type
/// the higher this number, the more projectiles are created as shrapnel
var/shrapnel_radius
- ///Did we add the component responsible for spawning sharpnel to this?
+ ///Did we add the component responsible for spawning shrapnel to this?
var/shrapnel_initialized
/obj/item/grenade/Initialize(mapload)
@@ -59,7 +61,7 @@
playsound(src, 'sound/items/eatfood.ogg', 50, TRUE)
arm_grenade(user, det_time)
user.transferItemToLoc(src, user, TRUE)//>eat a grenade set to 5 seconds >rush captain
- sleep(det_time)//so you dont die instantly
+ sleep(det_time)//so you don't die instantly
return dud_flags ? SHAME : BRUTELOSS
/obj/item/grenade/atom_deconstruct(disassembled = TRUE)
diff --git a/code/game/objects/items/grenades/chem_grenade.dm b/code/game/objects/items/grenades/chem_grenade.dm
index 2c01ad53edee9..5aadc9fbb3f23 100644
--- a/code/game/objects/items/grenades/chem_grenade.dm
+++ b/code/game/objects/items/grenades/chem_grenade.dm
@@ -35,7 +35,6 @@
set_wires(new /datum/wires/explosive/chem_grenade(src))
/obj/item/grenade/chem_grenade/Destroy(force)
- QDEL_NULL(wires)
QDEL_NULL(landminemode)
QDEL_LIST(beakers)
return ..()
@@ -502,7 +501,7 @@
/obj/item/grenade/chem_grenade/ez_clean
name = "cleaner grenade"
- desc = "Waffle Co.-brand foaming space cleaner. In a special applicator for rapid cleaning of wide areas."
+ desc = "Waffle Corp. brand foaming space cleaner. In a special applicator for rapid cleaning of wide areas."
stage = GRENADE_READY
/obj/item/grenade/chem_grenade/ez_clean/Initialize(mapload)
diff --git a/code/game/objects/items/grenades/plastic.dm b/code/game/objects/items/grenades/plastic.dm
index 49f66b06896b8..c9090912cc7e6 100644
--- a/code/game/objects/items/grenades/plastic.dm
+++ b/code/game/objects/items/grenades/plastic.dm
@@ -51,8 +51,6 @@
set_wires(new /datum/wires/explosive/c4(src))
/obj/item/grenade/c4/Destroy()
- qdel(wires)
- set_wires(null)
target = null
return ..()
diff --git a/code/game/objects/items/hand_items.dm b/code/game/objects/items/hand_items.dm
index 371ecee6ff803..cf752bf82accb 100644
--- a/code/game/objects/items/hand_items.dm
+++ b/code/game/objects/items/hand_items.dm
@@ -143,7 +143,7 @@
affix_desc = "on [target.p_their()] sensitive antennae"
affix_desc_target = "on your highly sensitive antennae"
brutal_noogie = TRUE
- if(user.dna?.check_mutation(/datum/mutation/human/hulk))
+ if(HAS_TRAIT(user, TRAIT_HULK))
prefix_desc = "sickeningly brutal"
brutal_noogie = TRUE
@@ -178,7 +178,7 @@
var/damage = rand(1, 5)
if(HAS_TRAIT(target, TRAIT_ANTENNAE))
damage += rand(3,7)
- if(user.dna?.check_mutation(/datum/mutation/human/hulk))
+ if(HAS_TRAIT(user, TRAIT_HULK))
damage += rand(3,7)
if(damage >= 5)
@@ -532,6 +532,12 @@
color = COLOR_BLACK
kiss_type = /obj/projectile/kiss/death
+/obj/item/hand_item/kisser/syndie
+ name = "syndie kiss"
+ desc = "oooooo you like syndicate ur a syndiekisser"
+ color = COLOR_SYNDIE_RED
+ kiss_type = /obj/projectile/kiss/syndie
+
/obj/projectile/kiss
name = "kiss"
icon = 'icons/mob/simple/animal.dmi'
@@ -543,13 +549,21 @@
damage_type = BRUTE
damage = 0 // love can't actually hurt you
armour_penetration = 100 // but if it could, it would cut through even the thickest plate
+ var/silent_blown = FALSE
+
+/obj/projectile/kiss/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/parriable_projectile)
/obj/projectile/kiss/fire(angle, atom/direct_target)
- if(firer)
+ if(firer && !silent_blown)
name = "[name] blown by [firer]"
+
return ..()
/obj/projectile/kiss/Impact(atom/A)
+ def_zone = BODY_ZONE_HEAD // let's keep it PG, people
+
if(damage > 0 || !isliving(A)) // if we do damage or we hit a nonliving thing, we don't have to worry about a harmless hit because we can't wrongly do damage anyway
return ..()
@@ -603,7 +617,6 @@
living_target.visible_message("[living_target] [other_msg]", span_userdanger("Whoa! [self_msg]"))
/obj/projectile/kiss/on_hit(atom/target, blocked, pierce_hit)
- def_zone = BODY_ZONE_HEAD // let's keep it PG, people
. = ..()
if(isliving(target))
var/mob/living/living_target = target
@@ -625,6 +638,18 @@
var/obj/item/organ/internal/heart/dont_go_breakin_my_heart = heartbreakee.get_organ_slot(ORGAN_SLOT_HEART)
dont_go_breakin_my_heart.apply_organ_damage(999)
+// Based on energy gun characteristics
+/obj/projectile/kiss/syndie
+ name = "syndie kiss"
+ color = COLOR_SYNDIE_RED
+ impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser
+ damage_type = BURN
+ armor_flag = LASER
+ armour_penetration = 0
+ damage = 25
+ wound_bonus = -20
+ bare_wound_bonus = 40
+ silent_blown = TRUE
/obj/projectile/kiss/french
name = "french kiss (is that a hint of garlic?)"
diff --git a/code/game/objects/items/handcuffs.dm b/code/game/objects/items/handcuffs.dm
index c5a68260abade..7edc000920454 100644
--- a/code/game/objects/items/handcuffs.dm
+++ b/code/game/objects/items/handcuffs.dm
@@ -48,6 +48,8 @@
breakouttime = 1 MINUTES
armor_type = /datum/armor/restraints_handcuffs
custom_price = PAYCHECK_COMMAND * 0.35
+ pickup_sound = 'sound/items/handcuffs_pick_up.ogg'
+ drop_sound = 'sound/items/handcuffs_drop.ogg'
///How long it takes to handcuff someone
var/handcuff_time = 4 SECONDS
@@ -55,6 +57,8 @@
var/handcuff_time_mod = 1
///Sound that plays when starting to put handcuffs on someone
var/cuffsound = 'sound/weapons/handcuffs.ogg'
+ ///Sound that plays when restrain is successful
+ var/cuffsuccesssound = 'sound/items/handcuff_finish.ogg'
///If set, handcuffs will be destroyed on application and leave behind whatever this is set to.
var/trashtype = null
/// How strong the cuffs are. Weak cuffs can be broken with wirecutters or boxcutters.
@@ -120,6 +124,7 @@
return
apply_cuffs(victim, user, dispense = iscyborg(user))
+ playsound(loc, cuffsuccesssound, 30, TRUE, -2)
victim.visible_message(
span_notice("[user] handcuffs [victim]."),
@@ -195,6 +200,8 @@
custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 1.5, /datum/material/glass= SMALL_MATERIAL_AMOUNT * 0.75)
breakouttime = 30 SECONDS
cuffsound = 'sound/weapons/cablecuff.ogg'
+ pickup_sound = null
+ drop_sound = null
restraint_strength = HANDCUFFS_TYPE_WEAK
/obj/item/restraints/handcuffs/cable/Initialize(mapload, new_color)
diff --git a/code/game/objects/items/holosign_creator.dm b/code/game/objects/items/holosign_creator.dm
index 926131151e92b..049ea8928feff 100644
--- a/code/game/objects/items/holosign_creator.dm
+++ b/code/game/objects/items/holosign_creator.dm
@@ -38,10 +38,11 @@
/obj/item/holosign_creator/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!check_allowed_items(interacting_with, not_inside = TRUE))
return NONE
+
var/turf/target_turf = get_turf(interacting_with)
var/obj/structure/holosign/target_holosign = locate(holosign_type) in target_turf
+
if(target_holosign)
- qdel(target_holosign)
return ITEM_INTERACT_BLOCKING
if(target_turf.is_blocked_turf(TRUE)) //can't put holograms on a tile that has dense stuff
return ITEM_INTERACT_BLOCKING
@@ -51,7 +52,9 @@
if(LAZYLEN(signs) >= max_signs)
balloon_alert(user, "max capacity!")
return ITEM_INTERACT_BLOCKING
+
playsound(src, 'sound/machines/click.ogg', 20, TRUE)
+
if(creation_time)
holocreator_busy = TRUE
if(!do_after(user, creation_time, target = interacting_with))
@@ -62,6 +65,7 @@
return ITEM_INTERACT_BLOCKING
if(target_turf.is_blocked_turf(TRUE)) //don't try to sneak dense stuff on our tile during the wait.
return ITEM_INTERACT_BLOCKING
+
target_holosign = create_holosign(interacting_with, user)
return ITEM_INTERACT_SUCCESS
@@ -105,15 +109,15 @@
/obj/item/holosign_creator/security
name = "security holobarrier projector"
- desc = "A holographic projector that creates holographic security barriers."
+ desc = "A holographic projector that creates holographic security barriers. You can remotely open barriers with it."
icon_state = "signmaker_sec"
holosign_type = /obj/structure/holosign/barrier
- creation_time = 3 SECONDS
+ creation_time = 2 SECONDS
max_signs = 6
/obj/item/holosign_creator/engineering
name = "engineering holobarrier projector"
- desc = "A holographic projector that creates holographic engineering barriers."
+ desc = "A holographic projector that creates holographic engineering barriers. You can remotely open barriers with it."
icon_state = "signmaker_engi"
holosign_type = /obj/structure/holosign/barrier/engineering
creation_time = 1 SECONDS
diff --git a/code/game/objects/items/implants/implant_explosive.dm b/code/game/objects/items/implants/implant_explosive.dm
index 687b8db014e04..b8f55b0085529 100644
--- a/code/game/objects/items/implants/implant_explosive.dm
+++ b/code/game/objects/items/implants/implant_explosive.dm
@@ -109,7 +109,7 @@
/**
* Merges two explosive implants together, adding the stats of the latter to the former before qdeling the latter implant.
* kept_implant = the implant that is kept
- * stat_implant = the implant which has it's stats added to kept_implant, before being deleted.
+ * stat_implant = the implant which has its stats added to kept_implant, before being deleted.
*/
/obj/item/implant/explosive/proc/merge_implants(obj/item/implant/explosive/kept_implant, obj/item/implant/explosive/stat_implant)
kept_implant.explosion_devastate += stat_implant.explosion_devastate
diff --git a/code/game/objects/items/implants/implant_mindshield.dm b/code/game/objects/items/implants/implant_mindshield.dm
index 67b1f4c6d92ff..9f7e507f1c343 100644
--- a/code/game/objects/items/implants/implant_mindshield.dm
+++ b/code/game/objects/items/implants/implant_mindshield.dm
@@ -20,7 +20,7 @@
if(!.)
return FALSE
if(target.mind)
- if((SEND_SIGNAL(target.mind, COMSIG_PRE_MINDSHIELD_IMPLANT, user) & COMPONENT_MINDSHIELD_RESISTED) || target.mind.unconvertable)
+ if((SEND_SIGNAL(target.mind, COMSIG_PRE_MINDSHIELD_IMPLANT, user) & COMPONENT_MINDSHIELD_RESISTED))
if(!silent)
target.visible_message(span_warning("[target] seems to resist the implant!"), span_warning("You feel something interfering with your mental conditioning, but you resist it!"))
removed(target, TRUE)
@@ -30,7 +30,7 @@
if(prob(1) || check_holidays(APRIL_FOOLS))
target.say("I'm out! I quit! Whose kidneys are these?", forced = "They're out! They quit! Whose kidneys do they have?")
- ADD_TRAIT(target, TRAIT_MINDSHIELD, IMPLANT_TRAIT)
+ target.add_traits(list(TRAIT_MINDSHIELD, TRAIT_UNCONVERTABLE), IMPLANT_TRAIT)
target.sec_hud_set_implants()
if(!silent)
to_chat(target, span_notice("You feel a sense of peace and security. You are now protected from brainwashing."))
@@ -42,7 +42,7 @@
return FALSE
if(isliving(target))
var/mob/living/L = target
- REMOVE_TRAIT(L, TRAIT_MINDSHIELD, IMPLANT_TRAIT)
+ target.remove_traits(list(TRAIT_MINDSHIELD, TRAIT_UNCONVERTABLE), IMPLANT_TRAIT)
L.sec_hud_set_implants()
if(target.stat != DEAD && !silent)
to_chat(target, span_boldnotice("Your mind suddenly feels terribly vulnerable. You are no longer safe from brainwashing."))
diff --git a/code/game/objects/items/implants/implantchair.dm b/code/game/objects/items/implants/implantchair.dm
index 5f833e3264875..50302173a796f 100644
--- a/code/game/objects/items/implants/implantchair.dm
+++ b/code/game/objects/items/implants/implantchair.dm
@@ -55,7 +55,7 @@
return data
-/obj/machinery/implantchair/ui_act(action, params)
+/obj/machinery/implantchair/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -191,7 +191,7 @@
objective = tgui_input_text(user, "What order do you want to imprint on [C]?", "Brainwashing", max_length = 120)
message_admins("[ADMIN_LOOKUPFLW(user)] set brainwash machine objective to '[objective]'.")
user.log_message("set brainwash machine objective to '[objective]'.", LOG_GAME)
- if(HAS_TRAIT(C, TRAIT_MINDSHIELD))
+ if(HAS_MIND_TRAIT(C, TRAIT_UNCONVERTABLE))
return FALSE
brainwash(C, objective)
message_admins("[ADMIN_LOOKUPFLW(user)] brainwashed [key_name_admin(C)] with objective '[objective]'.")
diff --git a/code/game/objects/items/inducer.dm b/code/game/objects/items/inducer.dm
index d74bb7aa8d10e..2404974d69970 100644
--- a/code/game/objects/items/inducer.dm
+++ b/code/game/objects/items/inducer.dm
@@ -81,9 +81,9 @@
return
/obj/item/inducer/attackby(obj/item/used_item, mob/user)
+ var/obj/item/stock_parts/power_store/our_cell = get_cell()
if(istype(used_item, /obj/item/stock_parts/power_store))
if(opened)
- var/obj/item/stock_parts/power_store/our_cell = get_cell()
if(isnull(our_cell))
if(!user.transferItemToLoc(used_item, src))
return
@@ -95,6 +95,15 @@
to_chat(user, span_warning("[src] already has \a [our_cell] installed!"))
return
+ if (istype(used_item, /obj/item/stack/sheet/mineral/plasma) && !isnull(our_cell))
+ if(our_cell.charge == our_cell.maxcharge)
+ balloon_alert(user, "already fully charged!")
+ return
+ used_item.use(1)
+ our_cell.give(1.5 * STANDARD_CELL_CHARGE)
+ balloon_alert(user, "cell recharged")
+ return
+
if(cantbeused(user))
return
@@ -187,7 +196,7 @@
opened = TRUE
/obj/item/inducer/orderable
- cell_type = /obj/item/stock_parts/power_store/cell/inducer_supply
+ cell_type = /obj/item/stock_parts/power_store/battery/upgraded
opened = FALSE
/obj/item/inducer/sci
diff --git a/code/game/objects/items/inspector.dm b/code/game/objects/items/inspector.dm
index b73917bfba8b6..4aff1f03388ab 100644
--- a/code/game/objects/items/inspector.dm
+++ b/code/game/objects/items/inspector.dm
@@ -8,7 +8,7 @@
*/
/obj/item/inspector
name = "\improper N-spect scanner"
- desc = "Central Command standard issue inspection device. Can perform either wide area scans that central command can use to verify the security of the station, or detailed scans to determine if an item is contraband."
+ desc = "Central Command standard issue inspection device. Can perform either wide area scans that central command can use to verify the security of the station, or detailed scan. Can scan people for contraband on their person or items being contraband."
icon = 'icons/obj/devices/scanner.dmi'
icon_state = "inspector"
worn_icon_state = "salestagger"
@@ -20,6 +20,7 @@
interaction_flags_click = NEED_DEXTERITY
throw_range = 1
throw_speed = 1
+ COOLDOWN_DECLARE(scanning_person) //Cooldown for scanning a carbon
///How long it takes to print on time each mode, ordered NORMAL, FAST, HONK
var/list/time_list = list(5 SECONDS, 1 SECONDS, 0.1 SECONDS)
///Which print time mode we're on.
@@ -91,7 +92,7 @@
if(!cell_cover_open)
. += "Its cell cover is closed. It looks like it could be pried out, but doing so would require an appropriate tool."
return
- . += "It's cell cover is open, exposing the cell slot. It looks like it could be pried in, but doing so would require an appropriate tool."
+ . += "Its cell cover is open, exposing the cell slot. It looks like it could be pried in, but doing so would require an appropriate tool."
if(!cell)
. += "The slot for a cell is empty."
else
@@ -106,18 +107,28 @@
if(!cell || !cell.use(INSPECTOR_ENERGY_USAGE_LOW))
balloon_alert(user, "check cell!")
return ITEM_INTERACT_BLOCKING
- if(!isitem(interacting_with))
- return ITEM_INTERACT_BLOCKING
- var/obj/item/contraband_item = interacting_with
- var/contraband_status = contraband_item.is_contraband()
- if((!contraband_status && scans_correctly) || (contraband_status && !scans_correctly))
+
+ if(iscarbon(interacting_with)) //Prevents insta scanning people
+ if(!COOLDOWN_FINISHED(src, scanning_person))
+ return ITEM_INTERACT_BLOCKING
+
+ visible_message(span_warning("[user] starts scanning [interacting_with] with [src]"))
+ to_chat(interacting_with, span_userdanger("[user] is trying to scan you for contraband!"))
+ balloon_alert_to_viewers("scanning...")
+ playsound(src, 'sound/effects/genetics.ogg', 40, FALSE)
+ COOLDOWN_START(src, scanning_person, 4 SECONDS)
+ if(!do_after(user, 4 SECONDS, interacting_with))
+ return ITEM_INTERACT_BLOCKING
+
+ if(contraband_scan(interacting_with, user))
+ playsound(src, 'sound/machines/uplinkerror.ogg', 40)
+ balloon_alert(user, "contraband detected!")
+ return ITEM_INTERACT_SUCCESS
+ else
playsound(src, 'sound/machines/ping.ogg', 20)
balloon_alert(user, "clear")
return ITEM_INTERACT_SUCCESS
- playsound(src, 'sound/machines/uplinkerror.ogg', 40)
- balloon_alert(user, "contraband detected!")
- return ITEM_INTERACT_SUCCESS
/obj/item/inspector/add_context(atom/source, list/context, obj/item/held_item, mob/user)
var/update_context = FALSE
@@ -145,6 +156,29 @@
return CONTEXTUAL_SCREENTIP_SET
return NONE
+/**
+ * Scans the carbon or item for contraband.
+ *
+ * Arguments:
+ * - scanned - what or who is scanned?
+ * - user - who is performing the scanning?
+ */
+/obj/item/inspector/proc/contraband_scan(scanned, user)
+ if(iscarbon(scanned))
+ var/mob/living/carbon/scanned_carbon = scanned
+ for(var/obj/item/content in scanned_carbon.get_all_contents_skipping_traits(TRAIT_CONTRABAND_BLOCKER))
+ var/contraband_content = content.is_contraband()
+ if((contraband_content && scans_correctly) || (!contraband_content && !scans_correctly))
+ return TRUE
+
+ if(isitem(scanned))
+ var/obj/item/contraband_item = scanned
+ var/contraband_status = contraband_item.is_contraband()
+ if((contraband_status && scans_correctly) || (!contraband_status && !scans_correctly))
+ return TRUE
+
+ return FALSE
+
/**
* Create our report
*
diff --git a/code/game/objects/items/kirby_plants/kirbyplants.dm b/code/game/objects/items/kirby_plants/kirbyplants.dm
index 26199bcd42c9c..3f2e007f05ba1 100644
--- a/code/game/objects/items/kirby_plants/kirbyplants.dm
+++ b/code/game/objects/items/kirby_plants/kirbyplants.dm
@@ -1,5 +1,5 @@
-
/obj/item/kirbyplants
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "potted plant"
icon = 'icons/obj/fluff/flora/plants.dmi'
icon_state = "plant-01"
diff --git a/code/game/objects/items/kitchen.dm b/code/game/objects/items/kitchen.dm
index 9a55b1cd57464..d3011e8680364 100644
--- a/code/game/objects/items/kitchen.dm
+++ b/code/game/objects/items/kitchen.dm
@@ -104,7 +104,7 @@
/obj/item/knife/kitchen/silicon
name = "Kitchen Toolset"
- icon = 'icons/obj/items_cyborg.dmi'
+ icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "sili_knife"
desc = "A breakthrough in synthetic engineering, this tool is a knife programmed to dull when not used for cooking purposes, and can exchange the blade for a rolling pin"
force = 0
diff --git a/code/game/objects/items/knives.dm b/code/game/objects/items/knives.dm
index 1a16b08eb866b..6ea12e72f347c 100644
--- a/code/game/objects/items/knives.dm
+++ b/code/game/objects/items/knives.dm
@@ -166,6 +166,19 @@
force = 15
throwforce = 15
+/obj/item/knife/combat/root
+ name = "cahn'root dagger"
+ icon = 'icons/obj/weapons/stabby.dmi'
+ icon_state = "rootdagger"
+ worn_icon_state = "root_dagger"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ inhand_icon_state = "rootshiv"
+ embed_type = /datum/embed_data/combat_knife/weak
+ desc = "A root dagger, deceptively sharp. Perfect to hide and stab someone with, or make a couple and throw them at enemies."
+ force = 15
+ throwforce = 15
+
/obj/item/knife/combat/bone
name = "bone dagger"
inhand_icon_state = "bone_dagger"
@@ -177,7 +190,6 @@
desc = "A sharpened bone. The bare minimum in survival."
embed_type = /datum/embed_data/combat_knife/weak
obj_flags = parent_type::obj_flags & ~CONDUCTS_ELECTRICITY
- slot_flags = NONE
force = 15
throwforce = 15
custom_materials = null
@@ -187,9 +199,11 @@
/obj/item/knife/combat/cyborg
name = "cyborg knife"
- icon = 'icons/obj/items_cyborg.dmi'
+ icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "knife_cyborg"
+ worn_icon_state = "knife_cyborg" //error sprite - this shouldn't have been dropped
desc = "A cyborg-mounted plasteel knife. Extremely sharp and durable."
+ slot_flags = NONE //you can't put this in your mouth
/obj/item/knife/shiv
name = "glass shiv"
@@ -279,3 +293,18 @@
/obj/item/knife/shiv/carrot/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] forcefully drives \the [src] into [user.p_their()] eye! It looks like [user.p_theyre()] trying to commit suicide!"))
return BRUTELOSS
+
+/obj/item/knife/shiv/parsnip
+ name = "parsnip shiv"
+ icon_state = "parsnipshiv"
+ inhand_icon_state = "parsnipshiv"
+ desc = "Truly putting 'snip' in the 'parsnip', and it's not sub-par either!"
+ custom_materials = null
+
+/obj/item/knife/shiv/root
+ name = "cahn'root shiv"
+ icon_state = "rootshiv"
+ inhand_icon_state = "rootshiv"
+ desc = "A root sharpened into a shiv. A root source of someone's stab wounds soon, most likely."
+ custom_materials = null
+
diff --git a/code/game/objects/items/machine_wand.dm b/code/game/objects/items/machine_wand.dm
index fd0c730fff811..75c765730f78b 100644
--- a/code/game/objects/items/machine_wand.dm
+++ b/code/game/objects/items/machine_wand.dm
@@ -24,6 +24,18 @@
bug_appearance = mutable_appearance('icons/effects/effects.dmi', "fly-surrounding", ABOVE_WINDOW_LAYER)
register_context()
+/obj/item/machine_remote/equipped(mob/user, slot, initial)
+ . = ..()
+ if(user.get_active_held_item() == src)
+ ADD_TRAIT(user, TRAIT_AI_ACCESS, HELD_ITEM_TRAIT)
+ ADD_TRAIT(user, TRAIT_SILICON_ACCESS, HELD_ITEM_TRAIT)
+
+/obj/item/machine_remote/dropped(mob/user, silent)
+ . = ..()
+ if(user.get_active_held_item() != src)
+ REMOVE_TRAIT(user, TRAIT_AI_ACCESS, HELD_ITEM_TRAIT)
+ REMOVE_TRAIT(user, TRAIT_SILICON_ACCESS, HELD_ITEM_TRAIT)
+
/obj/item/machine_remote/Destroy(force)
. = ..()
if(controlling_machine_or_bot)
diff --git a/code/game/objects/items/melee/baton.dm b/code/game/objects/items/melee/baton.dm
index 253a40be69d54..4af5c4c923e75 100644
--- a/code/game/objects/items/melee/baton.dm
+++ b/code/game/objects/items/melee/baton.dm
@@ -318,6 +318,8 @@
bare_wound_bonus = 5
clumsy_knockdown_time = 15 SECONDS
active = FALSE
+ pickup_sound = 'sound/items/stun_baton_pick_up.ogg'
+ drop_sound = 'sound/items/stun_baton_drop.ogg'
/// The sound effecte played when our baton is extended.
var/on_sound = 'sound/weapons/batonextend.ogg'
@@ -436,6 +438,8 @@
light_on = FALSE
light_color = LIGHT_COLOR_ORANGE
light_power = 0.5
+ pickup_sound = 'sound/items/stun_baton_pick_up.ogg'
+ drop_sound = 'sound/items/stun_baton_drop.ogg'
var/throw_stun_chance = 35
@@ -504,7 +508,6 @@
/obj/item/melee/baton/security/Exited(atom/movable/mov_content)
. = ..()
if(mov_content == cell)
- cell.update_appearance()
cell = null
active = FALSE
update_appearance()
diff --git a/code/game/objects/items/melee/misc.dm b/code/game/objects/items/melee/misc.dm
index 80b1e66b2e3e9..855484c3066d4 100644
--- a/code/game/objects/items/melee/misc.dm
+++ b/code/game/objects/items/melee/misc.dm
@@ -164,6 +164,47 @@
user.death(FALSE)
REMOVE_TRAIT(src, TRAIT_NODROP, SABRE_SUICIDE_TRAIT)
+
+/obj/item/melee/parsnip_sabre
+ name = "parsnip sabre"
+ desc = "A weird, yet elegant weapon. Suprisingly sharp for something made from a parsnip."
+ icon = 'icons/obj/weapons/sword.dmi'
+ icon_state = "parsnip_sabre"
+ inhand_icon_state = "parsnip_sabre"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ force = 15
+ throwforce = 10
+ demolition_mod = 0.3
+ w_class = WEIGHT_CLASS_BULKY
+ block_chance = 40
+ armour_penetration = 40
+ sharpness = SHARP_EDGED
+ attack_verb_continuous = list("slashes", "cuts")
+ attack_verb_simple = list("slash", "cut")
+ block_sound = 'sound/weapons/parry.ogg'
+ hitsound = 'sound/weapons/rapierhit.ogg'
+ custom_materials = null
+ wound_bonus = 5
+ bare_wound_bonus = 15
+
+/obj/item/melee/sabre/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/jousting)
+
+/obj/item/melee/parsnip_sabre/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)
+ if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK)
+ final_block_chance = 0 //Don't bring a sword to a gunfight, and also you aren't going to really block someone full body tackling you with a sword
+ return ..()
+
+/obj/item/melee/parsnip_sabre/on_exit_storage(datum/storage/container)
+ . = ..()
+ playsound(container.parent, 'sound/items/unsheath.ogg', 25, TRUE)
+
+/obj/item/melee/parsnip_sabre/on_enter_storage(datum/storage/container)
+ . = ..()
+ playsound(container.parent, 'sound/items/sheath.ogg', 25, TRUE)
+
/obj/item/melee/beesword
name = "The Stinger"
desc = "Taken from a giant bee and folded over one thousand times in pure honey. Can sting through anything."
diff --git a/code/game/objects/items/nitrium_crystals.dm b/code/game/objects/items/nitrium_crystals.dm
deleted file mode 100644
index 828f437ade2fd..0000000000000
--- a/code/game/objects/items/nitrium_crystals.dm
+++ /dev/null
@@ -1,18 +0,0 @@
-/obj/item/nitrium_crystal
- desc = "A weird brown crystal, it smokes when broken"
- name = "nitrium crystal"
- icon = 'icons/obj/pipes_n_cables/atmos.dmi'
- icon_state = "nitrium_crystal"
- var/cloud_size = 1
-
-/obj/item/nitrium_crystal/attack_self(mob/user)
- . = ..()
- var/datum/effect_system/fluid_spread/smoke/chem/smoke = new
- var/turf/location = get_turf(src)
- create_reagents(5)
- reagents.add_reagent(/datum/reagent/nitrium_low_metabolization, 3)
- reagents.add_reagent(/datum/reagent/nitrium_high_metabolization, 2)
- smoke.attach(location)
- smoke.set_up(cloud_size, holder = src, location = location, carry = reagents, silent = TRUE)
- smoke.start()
- qdel(src)
diff --git a/code/game/objects/items/pet_carrier.dm b/code/game/objects/items/pet_carrier.dm
index 2e2e622ff6c1b..8f17704b8c776 100644
--- a/code/game/objects/items/pet_carrier.dm
+++ b/code/game/objects/items/pet_carrier.dm
@@ -5,7 +5,7 @@
/obj/item/pet_carrier
name = "pet carrier"
desc = "A big white-and-blue pet carrier. Good for carrying meat to the chef cute animals around."
- icon = 'icons/obj/pet_carrier.dmi'
+ icon = 'icons/obj/storage/pet_carrier.dmi'
base_icon_state = "pet_carrier"
icon_state = "pet_carrier_open"
inhand_icon_state = "pet_carrier"
diff --git a/code/game/objects/items/pillow.dm b/code/game/objects/items/pillow.dm
index f699dd34b3bfa..d92c317356ce2 100644
--- a/code/game/objects/items/pillow.dm
+++ b/code/game/objects/items/pillow.dm
@@ -2,7 +2,7 @@
/obj/item/pillow
name = "pillow"
desc = "A soft and fluffy pillow. You can smack someone with this!"
- icon = 'icons/obj/bed.dmi'
+ icon = 'icons/obj/structures/bed.dmi'
icon_state = "pillow_1_t"
inhand_icon_state = "pillow_t"
lefthand_file = 'icons/mob/inhands/items/pillow_lefthand.dmi'
@@ -131,7 +131,7 @@
icon_state = "pillow_[variation]_t"
inhand_icon_state = "pillow_t"
-/// Puts a brick inside the pillow, increasing it's damage
+/// Puts a brick inside the pillow, increasing its damage
/obj/item/pillow/proc/become_bricked()
bricked = TRUE
var/datum/component/two_handed/two_handed = GetComponent(/datum/component/two_handed)
@@ -166,7 +166,7 @@
body_parts_covered = CHEST|GROIN|ARMS|LEGS|FEET
cold_protection = CHEST|GROIN|ARMS|LEGS //a pillow suit must be hella warm
allowed = list(/obj/item/pillow) //moar pillow carnage
- icon = 'icons/obj/bed.dmi'
+ icon = 'icons/obj/structures/bed.dmi'
worn_icon = 'icons/mob/clothing/suits/pillow.dmi'
icon_state = "pillow_suit"
armor_type = /datum/armor/suit_pillow_suit
@@ -189,7 +189,7 @@
name = "pillow hood"
desc = "The final piece of the pillow juggernaut"
body_parts_covered = HEAD
- icon = 'icons/obj/bed.dmi'
+ icon = 'icons/obj/structures/bed.dmi'
worn_icon = 'icons/mob/clothing/suits/pillow.dmi'
icon_state = "pillowcase_hat"
body_parts_covered = HEAD
@@ -203,7 +203,7 @@
/obj/item/clothing/neck/pillow_tag
name = "pillow tag"
desc = "A price tag for the pillow. It appears to have space to fill names in."
- icon = 'icons/obj/bed.dmi'
+ icon = 'icons/obj/structures/bed.dmi'
icon_state = "pillow_tag"
worn_icon = 'icons/mob/clothing/neck.dmi'
worn_icon_state = "pillow_tag"
diff --git a/code/game/objects/items/plushes.dm b/code/game/objects/items/plushes.dm
index 35e4c7ff72e9f..95f46da2e10ce 100644
--- a/code/game/objects/items/plushes.dm
+++ b/code/game/objects/items/plushes.dm
@@ -44,7 +44,7 @@
/obj/item/toy/plush/Initialize(mapload)
. = ..()
AddComponent(/datum/component/squeak, squeak_override)
- AddElement(/datum/element/bed_tuckable, mapload, 6, -5, 90)
+ AddElement(/datum/element/bed_tuckable, mapload, 6, 7, 90)
AddElement(/datum/element/toy_talk)
//have we decided if Pinocchio goes in the blue or pink aisle yet?
@@ -793,3 +793,10 @@
inhand_icon_state = "blahaj"
attack_verb_continuous = list("gnaws", "gnashes", "chews")
attack_verb_simple = list("gnaw", "gnash", "chew")
+
+/obj/item/toy/plush/donkpocket
+ name = "donk pocket plushie"
+ desc = "The stuffed companion of choice for the seasoned traitor."
+ icon_state = "donkpocket"
+ attack_verb_continuous = list("donks")
+ attack_verb_simple = list("donk")
diff --git a/code/game/objects/items/puzzle_pieces.dm b/code/game/objects/items/puzzle_pieces.dm
index 7e7076e842c64..46b3d16dba475 100644
--- a/code/game/objects/items/puzzle_pieces.dm
+++ b/code/game/objects/items/puzzle_pieces.dm
@@ -40,7 +40,7 @@
name = "locked door"
desc = "This door only opens under certain conditions. It looks virtually indestructible."
icon = 'icons/obj/doors/puzzledoor/default.dmi'
- icon_state = "door_closed"
+ icon_state = "closed"
explosion_block = 3
heat_proof = TRUE
max_integrity = 600
@@ -75,6 +75,24 @@
if(!isnull(puzzle_id) && uses_queuelinks)
SSqueuelinks.add_to_queue(src, puzzle_id)
AddElement(/datum/element/empprotection, EMP_PROTECT_ALL)
+ update_appearance()
+
+/obj/machinery/door/puzzle/update_icon_state()
+ . = ..()
+ switch(animation)
+ if("opening")
+ icon_state = "opening"
+ if("closing")
+ icon_state = "closing"
+ else
+ icon_state = density ? "closed" : "open_top"
+
+/obj/machinery/door/puzzle/update_overlays()
+ . = ..()
+ if(!density)
+ // If we're open we layer the bit below us "above" any mobs so they can walk through
+ . += mutable_appearance(icon, "open_bottom", ABOVE_MOB_LAYER, appearance_flags = KEEP_APART)
+ . += emissive_blocker(icon, "open_bottom", src, ABOVE_MOB_LAYER)
/obj/machinery/door/puzzle/MatchedLinks(id, list/partners)
for(var/partner in partners)
@@ -89,14 +107,14 @@
/obj/machinery/door/puzzle/animation_length(animation)
switch(animation)
if(DOOR_OPENING_ANIMATION)
- return 1.0 SECONDS
+ return 0.7 SECONDS
/obj/machinery/door/puzzle/animation_segment_delay(animation)
switch(animation)
if(DOOR_OPENING_PASSABLE)
- return 0.8 SECONDS
+ return 0.5 SECONDS
if(DOOR_OPENING_FINISHED)
- return 1.0 SECONDS
+ return 0.7 SECONDS
/obj/machinery/door/puzzle/Bumped(atom/movable/AM)
return !density && ..()
@@ -169,7 +187,7 @@
trigger_item = TRUE
specific_item = /obj/structure/holobox
removable_signaller = FALSE //Being a pressure plate subtype, this can also use signals.
- roundstart_signaller_freq = FREQ_HOLOGRID_SOLUTION //Frequency is kept on it's own default channel however.
+ roundstart_signaller_freq = FREQ_HOLOGRID_SOLUTION //Frequency is kept on its own default channel however.
active = TRUE
trigger_delay = 10
protected = TRUE
@@ -502,7 +520,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/puzzle/password/pin, 32)
/obj/structure/puzzle_blockade/oneway
name = "one-way gate"
desc = "A wall of solid light, likely defending something important. Virtually indestructible."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/fluff/general.dmi'
icon_state = "oneway"
base_icon_state = "oneway"
light_color = COLOR_BIOLUMINESCENCE_BLUE
diff --git a/code/game/objects/items/rcd/RCD.dm b/code/game/objects/items/rcd/RCD.dm
index cf254e447818d..2a815a9b02e02 100644
--- a/code/game/objects/items/rcd/RCD.dm
+++ b/code/game/objects/items/rcd/RCD.dm
@@ -16,6 +16,9 @@
item_flags = NO_MAT_REDEMPTION | NOBLUDGEON
has_ammobar = TRUE
actions_types = list(/datum/action/item_action/rcd_scan)
+ drop_sound = 'sound/items/handling/rcd_drop.ogg'
+ pickup_sound = 'sound/items/handling/rcd_pickup.ogg'
+ sound_vary = TRUE
/// main category of currently selected design[Structures, Airlocks, Airlock Access]
var/root_category
@@ -29,7 +32,8 @@
var/construction_mode
/// The path of the structure the rcd is currently creating
var/atom/movable/rcd_design_path
-
+ ///our currently selected direction
+ var/selected_direction
/// Owner of this rcd. It can either be a construction console, player, or mech.
var/atom/owner
/// used by arcd, can this rcd work from a range
@@ -65,8 +69,9 @@
var/list/design = GLOB.rcd_designs[root_category][design_category][1]
rcd_design_path = design["[RCD_DESIGN_PATH]"]
- design_title = initial(rcd_design_path.name)
+ design_title = RCD_SPRITESHEET_PATH_KEY(rcd_design_path)
mode = design["[RCD_DESIGN_MODE]"]
+
construction_mode = mode
GLOB.rcd_list += src
@@ -77,6 +82,10 @@
GLOB.rcd_list -= src
. = ..()
+/obj/item/construction/rcd/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ playsound(src, SFX_TOOL_SWITCH, 20, TRUE)
+
/obj/item/construction/rcd/ui_action_click(mob/user, actiontype)
if (!COOLDOWN_FINISHED(src, destructive_scan_cooldown))
to_chat(user, span_warning("[src] lets out a low buzz."))
@@ -95,15 +104,15 @@
mode = RCD_TURF
user.visible_message(span_suicide("[user] sets the RCD to 'Wall' and points it down [user.p_their()] throat! It looks like [user.p_theyre()] trying to commit suicide!"))
if(checkResource(16, user)) // It takes 16 resources to construct a wall
- var/success = T.rcd_act(user, src, list("[RCD_DESIGN_MODE]" = RCD_TURF, "[RCD_DESIGN_PATH]" = /turf/open/floor/plating/rcd))
+ var/success = T.rcd_act(user, src, list(RCD_DESIGN_MODE = RCD_TURF, RCD_DESIGN_PATH = /turf/open/floor/plating/rcd, RCD_BUILD_DIRECTION = selected_direction))
T = get_turf(user)
// If the RCD placed a floor instead of a wall, having a wall without plating under it is cursed
// There isn't an easy programmatical way to check if rcd_act will place a floor or a wall, so just repeat using it for free
if(success && isopenturf(T))
- T.rcd_act(user, src, list("[RCD_DESIGN_MODE]" = RCD_TURF, "[RCD_DESIGN_PATH]" = /turf/open/floor/plating/rcd))
+ T.rcd_act(user, src, list(RCD_DESIGN_MODE = RCD_TURF, RCD_DESIGN_PATH = /turf/open/floor/plating/rcd))
useResource(16, user)
activate()
- playsound(loc, 'sound/machines/click.ogg', 50, 1)
+ playsound(get_turf(user), SFX_TOOL_SWITCH, 20, TRUE)
user.gib(DROP_ALL_REMAINS)
return MANUAL_SUICIDE
@@ -119,8 +128,8 @@
* * [mob][user]- the user
*/
/obj/item/construction/rcd/proc/can_place(atom/target, list/rcd_results, mob/user)
- var/rcd_mode = rcd_results["[RCD_DESIGN_MODE]"]
- var/atom/movable/rcd_structure = rcd_results["[RCD_DESIGN_PATH]"]
+ var/rcd_mode = rcd_results[RCD_DESIGN_MODE]
+ var/atom/movable/rcd_structure = rcd_results[RCD_DESIGN_PATH]
/**
*For anything that does not go an a wall we have to make sure that turf is clear for us to put the structure on it
*If we are just trying to destory something then this check is not nessassary
@@ -134,17 +143,17 @@
var/is_full_tile = initial(window_type.fulltile)
var/list/structures_to_ignore
- if(istype(target, /obj/structure/grille))
+ if(istype(target, /obj/structure/window_frame))
if(is_full_tile) //if we are trying to build full-tile windows we ignore the grille
- structures_to_ignore = list(/obj/structure/grille)
+ structures_to_ignore = list(/obj/structure/window_frame)
else //when building directional windows we ignore the grill and other directional windows
- structures_to_ignore = list(/obj/structure/grille, /obj/structure/window)
+ structures_to_ignore = list(/obj/structure/window_frame, /obj/structure/window)
else //for directional windows we ignore other directional windows as they can be in diffrent directions on the turf.
structures_to_ignore = list(/obj/structure/window)
//check if we can build our window on the grill
if(target_turf.is_blocked_turf(exclude_mobs = !is_full_tile, source_atom = null, ignore_atoms = structures_to_ignore, type_list = TRUE))
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ playsound(get_turf(user), SFX_TOOL_SWITCH, 20, TRUE)
balloon_alert(user, "something is blocking the turf")
return FALSE
@@ -155,7 +164,7 @@
else if(rcd_mode == RCD_TURF && rcd_structure == /turf/open/floor/plating/rcd && (!istype(target_turf, /turf/open/floor) || istype(target, /obj/structure/girder)))
//if a player builds a wallgirder on top of himself manually with iron sheets he can't finish the wall if he is still on the girder. Exclude the girder itself when checking for other dense objects on the turf
if(istype(target, /obj/structure/girder) && target_turf.is_blocked_turf(exclude_mobs = FALSE, source_atom = null, ignore_atoms = list(target)))
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ playsound(get_turf(user), SFX_TOOL_SWITCH, 20, TRUE)
balloon_alert(user, "something is on the girder!")
return FALSE
@@ -190,7 +199,7 @@
//check if the structure can fit on this turf
if(target_turf.is_blocked_turf(exclude_mobs = ignore_mobs, source_atom = null, ignore_atoms = ignored_types, type_list = TRUE))
- playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
+ playsound(get_turf(user), SFX_TOOL_SWITCH, 20, TRUE)
balloon_alert(user, "something is on the tile!")
return FALSE
@@ -213,8 +222,9 @@
var/list/rcd_results = target.rcd_vals(user, src)
if(!rcd_results)
return FALSE
- rcd_results["[RCD_DESIGN_MODE]"] = mode
- rcd_results["[RCD_DESIGN_PATH]"] = rcd_design_path
+ rcd_results[RCD_DESIGN_MODE] = mode
+ rcd_results[RCD_DESIGN_PATH] = rcd_design_path
+ rcd_results[RCD_BUILD_DIRECTION] = selected_direction
var/delay = rcd_results["delay"] * delay_mod
if (
@@ -228,10 +238,10 @@
var/target_name = target.name //Store this information before it gets mutated by the rcd.
var/target_path = target.type
- var/atom/design_path = rcd_results["[RCD_DESIGN_PATH]"]
+ var/atom/design_path = rcd_results[RCD_DESIGN_PATH]
var/location = AREACOORD(target)
if(_rcd_create_effect(target, user, delay, rcd_results))
- log_tool("[key_name(user)] used [src] to [rcd_results["[RCD_DESIGN_MODE]"] != RCD_DECONSTRUCT ? "construct [initial(design_path.name)]([design_path])" : "deconstruct [target_name]([target_path])"] at [location]")
+ log_tool("[key_name(user)] used [src] to [rcd_results[RCD_DESIGN_MODE] != RCD_DECONSTRUCT ? "construct [initial(design_path.name)]([design_path])" : "deconstruct [target_name]([target_path])"] at [location]")
current_active_effects -= 1
@@ -245,7 +255,7 @@
* * rcd_results- list of params which contains the cost & build mode to create the structure
*/
/obj/item/construction/rcd/proc/_rcd_create_effect(atom/target, mob/user, delay, list/rcd_results)
- var/obj/effect/constructing_effect/rcd_effect = new(get_turf(target), delay, rcd_results["[RCD_DESIGN_MODE]"], upgrade)
+ var/obj/effect/constructing_effect/rcd_effect = new(get_turf(target), delay, rcd_results[RCD_DESIGN_MODE], upgrade)
//resource & structure placement sanity checks before & after delay along with beam effects
if(!checkResource(rcd_results["cost"], user) || !can_place(target, rcd_results, user))
@@ -303,6 +313,8 @@
data["root_categories"] += category
data["selected_root"] = root_category
+ var/datum/asset/spritesheet/rcd/rcd_sheet = get_asset_datum(/datum/asset/spritesheet/rcd)
+
data["categories"] = list()
for(var/sub_category as anything in GLOB.rcd_designs[root_category])
var/list/target_category = GLOB.rcd_designs[root_category][sub_category]
@@ -319,9 +331,9 @@
for(var/list/design as anything in target_category)
var/atom/movable/design_path = design[RCD_DESIGN_PATH]
- var/design_name = initial(design_path.name)
-
- designs += list(list("title" = design_name, "icon" = sanitize_css_class_name(design_name)))
+ var/design_name = RCD_SPRITESHEET_PATH_KEY(design_path)
+ var/icon_id = rcd_sheet.icon_size_id(sanitize_css_class_name(design_name))
+ designs += list(list("title" = design_name, "icon" = sanitize_css_class_name(design_name), "icon_id" = icon_id))
data["categories"] += list(list("cat_name" = sub_category, "designs" = designs))
return data
@@ -332,6 +344,7 @@
//main categories
data["selected_category"] = design_category
data["selected_design"] = design_title
+ data["selected_direction"] = dir2text(selected_direction)
//merge airlock_electronics ui data with this
var/list/airlock_data = airlock_electronics.ui_data(user)
@@ -349,6 +362,11 @@
root_category = new_root
update_static_data_for_all_viewers()
+ if("select_direction")
+ var/new_dir = text2dir(params["selected_direction"])
+ selected_direction = (isnull(new_dir) || new_dir == selected_direction || !(new_dir in GLOB.cardinals)) ? null : new_dir
+ return TRUE
+
if("design")
//read and validate params from UI
var/category_name = params["category"]
@@ -376,10 +394,10 @@
if(design == null) //not a valid design
return TRUE
design_category = category_name
- mode = design["[RCD_DESIGN_MODE]"]
+ mode = design[RCD_DESIGN_MODE]
construction_mode = mode
- rcd_design_path = design["[RCD_DESIGN_PATH]"]
- design_title = initial(rcd_design_path.name)
+ rcd_design_path = design[RCD_DESIGN_PATH]
+ design_title = RCD_SPRITESHEET_PATH_KEY(rcd_design_path)
blueprint_changed = TRUE
else
diff --git a/code/game/objects/items/rcd/RLD.dm b/code/game/objects/items/rcd/RLD.dm
index 2a99f535f42b5..510fe43a33321 100644
--- a/code/game/objects/items/rcd/RLD.dm
+++ b/code/game/objects/items/rcd/RLD.dm
@@ -36,9 +36,9 @@
///reference to thr original icons
var/static/list/original_options = list(
"Color Pick" = icon(icon = 'icons/hud/radial.dmi', icon_state = "omni"),
- "Glow Stick" = icon(icon = 'icons/obj/lighting.dmi', icon_state = "glowstick"),
+ "Glow Stick" = icon(icon = 'icons/obj/toys/toy.dmi', icon_state = "glowstick"),
"Deconstruct" = icon(icon = 'icons/obj/tools.dmi', icon_state = "wrench"),
- "Light Fixture" = icon(icon = 'icons/obj/lighting.dmi', icon_state = "ltube"),
+ "Light Fixture" = icon(icon = 'icons/obj/service/janitor.dmi', icon_state = "tube"),
)
///will contain the original icons modified with the color choice
var/list/display_options = list()
@@ -166,7 +166,7 @@
return ITEM_INTERACT_BLOCKING
activate()
var/obj/machinery/light/L = new /obj/machinery/light(get_turf(winner))
- L.setDir(get_dir(winner, interacting_with))
+ L.setDir(get_dir(interacting_with, winner))
L.color = color_choice
L.set_light_color(color_choice)
return ITEM_INTERACT_SUCCESS
diff --git a/code/game/objects/items/rcd/RPD.dm b/code/game/objects/items/rcd/RPD.dm
index 41a962ed7e7de..cd97911c28df6 100644
--- a/code/game/objects/items/rcd/RPD.dm
+++ b/code/game/objects/items/rcd/RPD.dm
@@ -142,6 +142,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
/datum/pipe_info/meter
icon_state = "meter"
dirtype = PIPE_ONEDIR
+ all_layers = TRUE
/datum/pipe_info/meter/New(label)
name = label
@@ -182,6 +183,9 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT*37.5, /datum/material/glass=SHEET_MATERIAL_AMOUNT*18.75)
armor_type = /datum/armor/item_pipe_dispenser
resistance_flags = FIRE_PROOF
+ drop_sound = 'sound/items/handling/rpd_drop.ogg'
+ pickup_sound = 'sound/items/handling/rpd_pickup.ogg'
+ sound_vary = TRUE
///Sparks system used when changing device in the UI
var/datum/effect_system/spark_spread/spark_system
///Direction of the device we are going to spawn, set up in the UI
@@ -293,7 +297,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
/obj/item/pipe_dispenser/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] points the end of the RPD down [user.p_their()] throat and presses a button! It looks like [user.p_theyre()] trying to commit suicide..."))
- playsound(get_turf(user), 'sound/machines/click.ogg', 50, TRUE)
+ playsound(get_turf(user), SFX_TOOL_SWITCH, 20, TRUE)
playsound(get_turf(user), RPD_USE_SOUND, 50, TRUE)
return BRUTELOSS
@@ -357,8 +361,9 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
data["init_directions"] = init_directions
return data
-/obj/item/pipe_dispenser/ui_act(action, params)
+/obj/item/pipe_dispenser/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
+ playsound(src, SFX_TOOL_SWITCH, 20, TRUE)
if(.)
return
@@ -456,7 +461,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
. = TRUE
if((mode & DESTROY_MODE) && istype(attack_target, /obj/item/pipe) || istype(attack_target, /obj/structure/disposalconstruct) || istype(attack_target, /obj/structure/c_transit_tube) || istype(attack_target, /obj/structure/c_transit_tube_pod) || istype(attack_target, /obj/item/pipe_meter) || istype(attack_target, /obj/structure/disposalpipe/broken))
- playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
+ playsound(get_turf(src), SFX_TOOL_SWITCH, 20, TRUE)
playsound(get_turf(src), RPD_USE_SOUND, 50, TRUE)
qdel(attack_target)
return
@@ -478,7 +483,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
balloon_alert(user, "already configured for its directions!")
return
- playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
+ playsound(get_turf(src), SFX_TOOL_SWITCH, 20, TRUE)
// Something else could have changed the target's state while we were waiting in do_after
// Most of the edge cases don't matter, but atmos components being able to have live connections not described by initializable directions sounds like a headache at best and an exploit at worst
@@ -542,7 +547,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
if(isclosedturf(attack_target))
balloon_alert(user, "target is blocked!")
return
- playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
+ playsound(get_turf(src), SFX_TOOL_SWITCH, 20, TRUE)
if(do_after(user, disposal_build_speed, target = attack_target))
var/obj/structure/disposalconstruct/new_disposals_segment = new (attack_target, queued_pipe_type, queued_pipe_dir, queued_pipe_flipped)
@@ -572,7 +577,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
balloon_alert(user, "something in the way!")
return
- playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE)
+ playsound(get_turf(src), SFX_TOOL_SWITCH, 20, TRUE)
if(do_after(user, transit_build_speed, target = attack_target))
playsound(get_turf(src), RPD_USE_SOUND, 50, TRUE)
if(queued_pipe_type == /obj/structure/c_transit_tube_pod)
@@ -619,7 +624,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
return FALSE
if(!can_make_pipe)
return FALSE
- playsound(get_turf(src), 'sound/machines/click.ogg', 50, vary = TRUE)
+ playsound(get_turf(src), SFX_TOOL_SWITCH, 20, vary = TRUE)
if(!continued_build && !do_after(user, atmos_build_speed, target = atom_to_target))
return FALSE
if(!recipe.all_layers && (layer_to_build == 1 || layer_to_build == 5))
diff --git a/code/game/objects/items/rcd/RPLD.dm b/code/game/objects/items/rcd/RPLD.dm
index 56452e2e452b1..e9a5fcc66dbc9 100644
--- a/code/game/objects/items/rcd/RPLD.dm
+++ b/code/game/objects/items/rcd/RPLD.dm
@@ -152,6 +152,10 @@
return data
+/obj/item/construction/plumbing/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ playsound(src, SFX_TOOL_SWITCH, 20, TRUE)
+
/obj/item/construction/plumbing/handle_ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
switch(action)
if("color")
@@ -178,8 +182,6 @@
blueprint = design
blueprint_changed = TRUE
- playsound(src, 'sound/effects/pop.ogg', 50, vary = FALSE)
-
return TRUE
diff --git a/code/game/objects/items/rcd/RSF.dm b/code/game/objects/items/rcd/RSF.dm
index ef0be22acc476..ee85994143a00 100644
--- a/code/game/objects/items/rcd/RSF.dm
+++ b/code/game/objects/items/rcd/RSF.dm
@@ -30,7 +30,7 @@ RSF
///The cost of the object we are going to dispense
var/dispense_cost = 0
w_class = WEIGHT_CLASS_NORMAL
- ///An associated list of atoms and charge costs. This can contain a separate list, as long as it's associated item is an object
+ ///An associated list of atoms and charge costs. This can contain a separate list, as long as its associated item is an object
///The RSF item list below shows in the player facing ui in this order, this is why it isn't in alphabetical order, but instead sorted by category
var/list/cost_by_item = list(
/obj/item/reagent_containers/cup/glass/drinkingglass = 20,
@@ -47,7 +47,7 @@ RSF
/obj/item/pen = 50,
/obj/item/cigarette = 10,
)
- ///An associated list of fuel and it's value
+ ///An associated list of fuel and its value
var/list/matter_by_item = list(/obj/item/rcd_ammo = 10,)
///A list of surfaces that we are allowed to place things on.
var/list/allowed_surfaces = list(/turf/open/floor, /obj/structure/table)
diff --git a/code/game/objects/items/rcd/RTD.dm b/code/game/objects/items/rcd/RTD.dm
index 45b9c9e3687dd..52fd723691b87 100644
--- a/code/game/objects/items/rcd/RTD.dm
+++ b/code/game/objects/items/rcd/RTD.dm
@@ -135,7 +135,7 @@
/// re create the appearance
/datum/overlay_info/proc/add_decal(turf/the_turf)
- the_turf.AddElement(/datum/element/decal, icon, icon_state, direction, null, null, alpha, color, null, FALSE, null)
+ the_turf.AddElement(/datum/element/decal, icon, icon_state, direction, null, null, alpha, color, null, null, null, FALSE, null)
/obj/item/construction/rtd/Initialize(mapload)
. = ..()
diff --git a/code/game/objects/items/religion.dm b/code/game/objects/items/religion.dm
index f25c022b5808a..232de4c461930 100644
--- a/code/game/objects/items/religion.dm
+++ b/code/game/objects/items/religion.dm
@@ -386,6 +386,7 @@
icon_state = "crusader"
w_class = WEIGHT_CLASS_NORMAL
armor_type = /datum/armor/shoes_plate
+ body_parts_covered = FEET|LEGS
clothing_traits = list(TRAIT_NO_SLIP_WATER)
cold_protection = FEET
min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT
diff --git a/code/game/objects/items/robot/items/hypo.dm b/code/game/objects/items/robot/items/hypo.dm
index 9a29ccbd9ef97..96a0c0d36b220 100644
--- a/code/game/objects/items/robot/items/hypo.dm
+++ b/code/game/objects/items/robot/items/hypo.dm
@@ -90,6 +90,9 @@
/datum/reagent/consumable/ethanol/fernet,\
)
+#define REAGENT_CONTAINER_INTERNAL "internal_beaker"
+#define REAGENT_CONTAINER_BEVAPPARATUS "beverage_apparatus"
+
///Borg Hypospray
/obj/item/reagent_containers/borghypo
name = "cyborg hypospray"
@@ -222,7 +225,7 @@
/obj/item/reagent_containers/borghypo/attack_self(mob/user)
ui_interact(user)
-/obj/item/reagent_containers/borghypo/ui_act(action, params)
+/obj/item/reagent_containers/borghypo/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -325,6 +328,7 @@
dispensed_temperature = WATER_MATTERSTATE_CHANGE_TEMP //Water stays wet, ice stays ice
default_reagent_types = BASE_SERVICE_REAGENTS
expanded_reagent_types = EXPANDED_SERVICE_REAGENTS
+ var/reagent_search_container = REAGENT_CONTAINER_BEVAPPARATUS
/obj/item/reagent_containers/borghypo/borgshaker/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
@@ -332,6 +336,27 @@
ui = new(user, src, "BorgShaker", name)
ui.open()
+/obj/item/reagent_containers/borghypo/borgshaker/ui_act(action, params)
+ . = ..()
+ if(.)
+ return
+ var/mob/living/silicon/robot/user = usr
+ switch(action)
+ if("reaction_lookup")
+ if(!iscyborg(usr))
+ return
+ if (reagent_search_container == REAGENT_CONTAINER_BEVAPPARATUS)
+ var/obj/item/borg/apparatus/beaker/service/beverage_apparatus = (locate() in user.model.modules) || (locate() in user.held_items)
+ if (!isnull(beverage_apparatus) && !isnull(beverage_apparatus.stored))
+ beverage_apparatus.stored.reagents.ui_interact(user)
+ else if (reagent_search_container == REAGENT_CONTAINER_INTERNAL)
+ var/obj/item/reagent_containers/cup/beaker/large/internal_beaker = (locate() in user.model.modules) || (locate() in user.held_items)
+ if (!isnull(internal_beaker))
+ internal_beaker.reagents.ui_interact(user)
+ if ("set_preferred_container")
+ reagent_search_container = params["value"]
+ return TRUE
+
/obj/item/reagent_containers/borghypo/borgshaker/ui_data(mob/user)
var/list/drink_reagents = list()
var/list/alcohol_reagents = list()
@@ -354,6 +379,17 @@
data["sodas"] = drink_reagents
data["alcohols"] = alcohol_reagents
data["selectedReagent"] = selected_reagent?.name
+ data["reagentSearchContainer"] = reagent_search_container
+
+ if(iscyborg(user))
+ var/mob/living/silicon/robot/cyborg = user
+ var/obj/item/borg/apparatus/beaker/service/beverage_apparatus = (locate() in cyborg.model.modules) || (locate() in cyborg.held_items)
+
+ if (isnull(beverage_apparatus))
+ to_chat(user, span_warning("This unit has no beverage apparatus. This shouldn't be possible. Delete yourself, NOW!"))
+ data["apparatusHasItem"] = FALSE
+ else
+ data["apparatusHasItem"] = !isnull(beverage_apparatus.stored)
return data
/obj/item/reagent_containers/borghypo/borgshaker/attack(mob/M, mob/user)
@@ -452,6 +488,8 @@
dispensed_temperature = WATER_MATTERSTATE_CHANGE_TEMP
default_reagent_types = HACKED_SERVICE_REAGENTS
+#undef REAGENT_CONTAINER_INTERNAL
+#undef REAGENT_CONTAINER_BEVAPPARATUS
#undef BASE_MEDICAL_REAGENTS
#undef EXPANDED_MEDICAL_REAGENTS
#undef HACKED_MEDICAL_REAGENTS
diff --git a/code/game/objects/items/robot/items/storage.dm b/code/game/objects/items/robot/items/storage.dm
index 2d91128adb68d..3e030a9bd8f23 100644
--- a/code/game/objects/items/robot/items/storage.dm
+++ b/code/game/objects/items/robot/items/storage.dm
@@ -41,6 +41,11 @@
stored.forceMove(get_turf(usr))
return
+/obj/item/borg/apparatus/get_proxy_attacker_for(atom/target, mob/user)
+ if(stored) // Use the stored item if available
+ return stored
+ return ..()
+
/**
* Attack_self will pass for the stored item.
*/
@@ -57,10 +62,6 @@
return CLICK_ACTION_SUCCESS
/obj/item/borg/apparatus/pre_attack(atom/atom, mob/living/user, params)
- if(stored)
- stored.melee_attack_chain(user, atom, params)
- return TRUE
-
if(istype(atom.loc, /mob/living/silicon/robot) || istype(atom.loc, /obj/item/robot_model) || HAS_TRAIT(atom, TRAIT_NODROP))
return ..() // Borgs should not be grabbing their own modules
@@ -132,7 +133,6 @@
else
. += "Nothing."
- . += span_notice(" Right-clicking will splash the beaker on the ground.")
. += span_notice(" Alt-click will drop the currently stored beaker. ")
/obj/item/borg/apparatus/beaker/update_overlays()
@@ -151,15 +151,6 @@
arm.pixel_y = arm.pixel_y - 5
. += arm
-/// Secondary attack spills the content of the beaker.
-/obj/item/borg/apparatus/beaker/pre_attack_secondary(atom/target, mob/living/silicon/robot/user)
- var/obj/item/reagent_containers/stored_beaker = stored
- if(!stored_beaker)
- return ..()
- stored_beaker.SplashReagents(drop_location(user))
- loc.visible_message(span_notice("[user] spills the contents of [stored_beaker] all over the ground."))
- return ..()
-
/obj/item/borg/apparatus/beaker/extra
name = "secondary beaker storage apparatus"
desc = "A supplementary beaker storage apparatus."
diff --git a/code/game/objects/items/robot/items/tools.dm b/code/game/objects/items/robot/items/tools.dm
index f16cd0844901d..fcdc43818dca7 100644
--- a/code/game/objects/items/robot/items/tools.dm
+++ b/code/game/objects/items/robot/items/tools.dm
@@ -288,7 +288,7 @@
/obj/item/borg/cyborg_omnitool/engineering
name = "engineering omni-toolset"
desc = "A set of engineering tools used by cyborgs to conduct various engineering tasks."
- icon = 'icons/obj/items_cyborg.dmi'
+ icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "toolkit_engiborg"
omni_toolkit = list(
diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm
index 25c83d1b9635a..7a8196799d87d 100644
--- a/code/game/objects/items/robot/robot_parts.dm
+++ b/code/game/objects/items/robot/robot_parts.dm
@@ -30,7 +30,7 @@
/// If the cyborg starts movement free and not under lockdown
var/locomotion = TRUE
- /// If the cyborg synchronizes it's laws with it's master AI
+ /// If the cyborg synchronizes its laws with its master AI
var/lawsync = TRUE
/// If the cyborg starts with a master AI
var/aisync = TRUE
@@ -409,7 +409,7 @@
data["lawsync"] = lawsync
return data
-/obj/item/robot_suit/ui_act(action, list/params)
+/obj/item/robot_suit/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm
index 9a3e45fac4213..e10f6778038b6 100644
--- a/code/game/objects/items/robot/robot_upgrades.dm
+++ b/code/game/objects/items/robot/robot_upgrades.dm
@@ -2,10 +2,10 @@
// Contains various borg upgrades.
/obj/item/borg/upgrade
- name = "borg upgrade module."
+ name = "borg upgrade module"
desc = "Protected by FRM."
- icon = 'icons/obj/devices/circuitry_n_data.dmi'
- icon_state = "cyborg_upgrade"
+ icon = 'icons/mob/silicon/robot_items.dmi'
+ icon_state = "module_general"
w_class = WEIGHT_CLASS_SMALL
var/locked = FALSE
var/installed = FALSE
@@ -73,6 +73,7 @@
/obj/item/borg/upgrade/rename
name = "cyborg reclassification board"
desc = "Used to rename a cyborg."
+ icon = 'icons/obj/devices/circuitry_n_data.dmi'
icon_state = "cyborg_upgrade1"
var/heldname = ""
one_use = TRUE
@@ -99,7 +100,7 @@
/obj/item/borg/upgrade/disablercooler
name = "cyborg rapid disabler cooling module"
desc = "Used to cool a mounted disabler, increasing the potential current in it and thus its recharge rate."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_security"
require_model = TRUE
model_type = list(/obj/item/robot_model/security)
model_flags = BORG_MODEL_SECURITY
@@ -134,7 +135,7 @@
/obj/item/borg/upgrade/thrusters
name = "ion thruster upgrade"
desc = "An energy-operated thruster system for cyborgs."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_general"
/obj/item/borg/upgrade/thrusters/action(mob/living/silicon/robot/borg, mob/living/user = usr)
. = ..()
@@ -156,7 +157,7 @@
/obj/item/borg/upgrade/diamond_drill
name = "mining cyborg diamond drill"
desc = "A diamond drill replacement for the mining model's standard drill."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_miner"
require_model = TRUE
model_type = list(/obj/item/robot_model/miner)
model_flags = BORG_MODEL_MINER
@@ -167,7 +168,7 @@
/obj/item/borg/upgrade/soh
name = "mining cyborg satchel of holding"
desc = "A satchel of holding replacement for mining cyborg's ore satchel module."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_miner"
require_model = TRUE
model_type = list(/obj/item/robot_model/miner)
model_flags = BORG_MODEL_MINER
@@ -178,7 +179,7 @@
/obj/item/borg/upgrade/tboh
name = "janitor cyborg trash bag of holding"
desc = "A trash bag of holding replacement for the janiborg's standard trash bag."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_janitor"
require_model = TRUE
model_type = list(/obj/item/robot_model/janitor)
model_flags = BORG_MODEL_JANITOR
@@ -189,7 +190,7 @@
/obj/item/borg/upgrade/amop
name = "janitor cyborg advanced mop"
desc = "An advanced mop replacement for the janiborg's standard mop."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_janitor"
require_model = TRUE
model_type = list(/obj/item/robot_model/janitor)
model_flags = BORG_MODEL_JANITOR
@@ -200,17 +201,27 @@
/obj/item/borg/upgrade/prt
name = "janitor cyborg plating repair tool"
desc = "A tiny heating device to repair burnt and damaged hull platings with."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_janitor"
require_model = TRUE
model_type = list(/obj/item/robot_model/janitor)
model_flags = BORG_MODEL_JANITOR
items_to_add = list(/obj/item/cautery/prt)
+/obj/item/borg/upgrade/plunger
+ name = "janitor cyborg plunging tool"
+ desc = "An integrated cyborg retractable plunger. It's meant for plunging things, duh."
+ icon_state = "module_janitor"
+ require_model = TRUE
+ model_type = list(/obj/item/robot_model/janitor)
+ model_flags = BORG_MODEL_JANITOR
+
+ items_to_add = list(/obj/item/plunger/cyborg)
+
/obj/item/borg/upgrade/syndicate
name = "illegal equipment module"
desc = "Unlocks the hidden, deadlier functions of a cyborg."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_illegal"
require_model = TRUE
/obj/item/borg/upgrade/syndicate/Initialize(mapload)
@@ -239,7 +250,7 @@
/obj/item/borg/upgrade/lavaproof
name = "mining cyborg lavaproof chassis"
desc = "An upgrade kit to apply specialized coolant systems and insulation layers to a mining cyborg's chassis, enabling them to withstand exposure to molten rock and liquid plasma."
- icon_state = "cyborg_ash_tracks"
+ icon_state = "module_miner"
resistance_flags = LAVA_PROOF | FIRE_PROOF | FREEZE_PROOF
require_model = TRUE
model_type = list(/obj/item/robot_model/miner)
@@ -260,7 +271,7 @@
/obj/item/borg/upgrade/selfrepair
name = "self-repair module"
desc = "This module will repair the cyborg over time."
- icon_state = "cyborg_upgrade5"
+ icon_state = "module_general"
require_model = TRUE
var/repair_amount = -1
/// world.time of next repair
@@ -360,7 +371,7 @@
name = "medical cyborg hypospray advanced synthesiser"
desc = "An upgrade to the Medical model cyborg's hypospray, allowing it \
to produce more advanced and complex medical reagents."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_medical"
require_model = TRUE
model_type = list(/obj/item/robot_model/medical)
model_flags = BORG_MODEL_MEDICAL
@@ -389,7 +400,7 @@
name = "cyborg piercing hypospray"
desc = "An upgrade to a cyborg's hypospray, allowing it to \
pierce armor and thick material."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_medical"
/obj/item/borg/upgrade/piercing_hypospray/action(mob/living/silicon/robot/borg, mob/living/user = usr)
. = ..()
@@ -419,8 +430,9 @@
/obj/item/borg/upgrade/surgery_omnitool
name = "cyborg surgical omni-tool upgrade"
desc = "An upgrade to the Medical model, upgrading the built-in \
- surgical omnitool, to be on par with advanced surgical tools"
- icon_state = "cyborg_upgrade3"
+ surgical omnitool, to be on par with advanced surgical tools, allowing for faster surgery. \
+ It also upgrades their scanner."
+ icon_state = "module_medical"
require_model = TRUE
model_type = list(/obj/item/robot_model/medical, /obj/item/robot_model/syndicate_medical)
model_flags = BORG_MODEL_MEDICAL
@@ -452,7 +464,7 @@
name = "cyborg engineering omni-tool upgrade"
desc = "An upgrade to the Engineering model, upgrading the built-in \
engineering omnitool, to be on par with advanced engineering tools"
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_engineer"
require_model = TRUE
model_type = list(/obj/item/robot_model/engineering, /obj/item/robot_model/saboteur)
model_flags = BORG_MODEL_ENGINEERING
@@ -479,7 +491,7 @@
name = "medical cyborg defibrillator"
desc = "An upgrade to the Medical model, installing a built-in \
defibrillator, for on the scene revival."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_medical"
require_model = TRUE
model_type = list(/obj/item/robot_model/medical)
model_flags = BORG_MODEL_MEDICAL
@@ -530,7 +542,7 @@
desc = "An upgrade to the Medical model, installing a processor \
capable of scanning surgery disks and carrying \
out procedures"
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_medical"
require_model = TRUE
model_type = list(/obj/item/robot_model/medical, /obj/item/robot_model/syndicate_medical)
model_flags = BORG_MODEL_MEDICAL
@@ -540,6 +552,7 @@
/obj/item/borg/upgrade/ai
name = "B.O.R.I.S. module"
desc = "Bluespace Optimized Remote Intelligence Synchronization. An uplink device which takes the place of an MMI in cyborg endoskeletons, creating a robotic shell controlled by an AI."
+ icon = 'icons/obj/devices/circuitry_n_data.dmi'
icon_state = "boris"
/obj/item/borg/upgrade/ai/action(mob/living/silicon/robot/borg, mob/living/user = usr)
@@ -563,7 +576,7 @@
/obj/item/borg/upgrade/expand
name = "borg expander"
desc = "A cyborg resizer, it makes a cyborg huge."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_general"
/obj/item/borg/upgrade/expand/action(mob/living/silicon/robot/borg, mob/living/user = usr)
. = ..()
@@ -603,8 +616,7 @@
/obj/item/borg/upgrade/rped
name = "engineering cyborg RPED"
desc = "A rapid part exchange device for the engineering cyborg."
- icon = 'icons/obj/storage/storage.dmi'
- icon_state = "borgrped"
+ icon_state = "module_engineer"
require_model = TRUE
model_type = list(/obj/item/robot_model/engineering, /obj/item/robot_model/saboteur)
model_flags = BORG_MODEL_ENGINEERING
@@ -614,6 +626,7 @@
/obj/item/borg/upgrade/inducer
name = "engineering integrated power inducer"
desc = "An integrated inducer that can charge a device's internal cell from power provided by the cyborg."
+ icon_state = "module_engineer"
require_model = TRUE
model_type = list(/obj/item/robot_model/engineering, /obj/item/robot_model/saboteur)
model_flags = BORG_MODEL_ENGINEERING
@@ -638,8 +651,7 @@
/obj/item/borg/upgrade/pinpointer
name = "medical cyborg crew pinpointer"
desc = "A crew pinpointer module for the medical cyborg. Permits remote access to the crew monitor."
- icon = 'icons/obj/devices/tracker.dmi'
- icon_state = "pinpointer_crew"
+ icon_state = "module_medical"
require_model = TRUE
model_type = list(/obj/item/robot_model/medical, /obj/item/robot_model/syndicate_medical)
model_flags = BORG_MODEL_MEDICAL
@@ -676,7 +688,7 @@
/obj/item/borg/upgrade/transform
name = "borg model picker (Standard)"
desc = "Allows you to to turn a cyborg into a standard cyborg."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_general"
var/obj/item/robot_model/new_model = null
/obj/item/borg/upgrade/transform/action(mob/living/silicon/robot/borg, mob/living/user = usr)
@@ -687,13 +699,13 @@
/obj/item/borg/upgrade/transform/clown
name = "borg model picker (Clown)"
desc = "Allows you to to turn a cyborg into a clown, honk."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_honk"
new_model = /obj/item/robot_model/clown
/obj/item/borg/upgrade/circuit_app
name = "circuit manipulation apparatus"
desc = "An engineering cyborg upgrade allowing for manipulation of circuit boards."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_engineer"
require_model = TRUE
model_type = list(/obj/item/robot_model/engineering, /obj/item/robot_model/saboteur)
model_flags = BORG_MODEL_ENGINEERING
@@ -703,7 +715,7 @@
/obj/item/borg/upgrade/beaker_app
name = "beaker storage apparatus"
desc = "A supplementary beaker storage apparatus for medical cyborgs."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_medical"
require_model = TRUE
model_type = list(/obj/item/robot_model/medical)
model_flags = BORG_MODEL_MEDICAL
@@ -713,7 +725,7 @@
/obj/item/borg/upgrade/drink_app
name = "glass storage apparatus"
desc = "A supplementary drinking glass storage apparatus for service cyborgs."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_service"
require_model = TRUE
model_type = list(/obj/item/robot_model/service)
model_flags = BORG_MODEL_SERVICE
@@ -723,7 +735,7 @@
/obj/item/borg/upgrade/broomer
name = "experimental push broom"
desc = "An experimental push broom used for efficiently pushing refuse."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_janitor"
require_model = TRUE
model_type = list(/obj/item/robot_model/janitor)
model_flags = BORG_MODEL_JANITOR
@@ -733,7 +745,7 @@
/obj/item/borg/upgrade/condiment_synthesizer
name = "Service Cyborg Condiment Synthesiser"
desc = "An upgrade to the service model cyborg, allowing it to produce solid condiments."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_service"
require_model = TRUE
model_type = list(/obj/item/robot_model/service)
model_flags = BORG_MODEL_SERVICE
@@ -743,7 +755,7 @@
/obj/item/borg/upgrade/silicon_knife
name = "Service Cyborg Kitchen Toolset"
desc = "An upgrade to the service model cyborg, to help process foods."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_service"
require_model = TRUE
model_type = list(/obj/item/robot_model/service)
model_flags = BORG_MODEL_SERVICE
@@ -753,7 +765,7 @@
/obj/item/borg/upgrade/service_apparatus
name = "Service Cyborg Service Apparatus"
desc = "An upgrade to the service model cyborg, to help handle foods and paper."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_service"
require_model = TRUE
model_type = list(/obj/item/robot_model/service)
model_flags = BORG_MODEL_SERVICE
@@ -763,7 +775,7 @@
/obj/item/borg/upgrade/rolling_table
name = "Service Cyborg Rolling Table Dock"
desc = "An upgrade to the service model cyborg, to help provide mobile service."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_service"
require_model = TRUE
model_type = list(/obj/item/robot_model/service)
model_flags = BORG_MODEL_SERVICE
@@ -773,7 +785,7 @@
/obj/item/borg/upgrade/service_cookbook
name = "Service Cyborg Cookbook"
desc = "An upgrade to the service model cyborg, that lets them create more foods."
- icon_state = "cyborg_upgrade3"
+ icon_state = "module_service"
require_model = TRUE
model_type = list(/obj/item/robot_model/service)
model_flags = BORG_MODEL_SERVICE
diff --git a/code/game/objects/items/rollertable_dock.dm b/code/game/objects/items/rollertable_dock.dm
index d0067e8c67596..a140d64d80f3c 100644
--- a/code/game/objects/items/rollertable_dock.dm
+++ b/code/game/objects/items/rollertable_dock.dm
@@ -1,7 +1,7 @@
/obj/item/rolling_table_dock
name = "rolling table dock"
desc = "A collapsed roller table that can be ejected for service on the go. Must be collected or replaced after use."
- icon = 'icons/obj/smooth_structures/rollingtable.dmi'
+ icon = 'icons/obj/structures/smooth/rollingtable.dmi'
icon_state = "rollingtable"
var/obj/structure/table/rolling/loaded = null
diff --git a/code/game/objects/items/sharpener.dm b/code/game/objects/items/sharpener.dm
index a0f41574e4b47..709747efc6663 100644
--- a/code/game/objects/items/sharpener.dm
+++ b/code/game/objects/items/sharpener.dm
@@ -3,7 +3,7 @@
*
* Items used for sharpening stuff
*
-* Whetstones can be used to increase an item's force, throw_force and wound_bonus and it's change it's sharpness to SHARP_EDGED. Whetstones do not work with energy weapons. Two-handed weapons will only get the throw_force bonus. A whetstone can only be used once.
+* Whetstones can be used to increase an item's force, throw_force and wound_bonus and it changes its sharpness to SHARP_EDGED. Whetstones do not work with energy weapons. Two-handed weapons will only get the throw_force bonus. A whetstone can only be used once.
*
*/
/obj/item/sharpener
diff --git a/code/game/objects/items/shields.dm b/code/game/objects/items/shields.dm
index 1553f0e9c8f29..c980fe0dbb359 100644
--- a/code/game/objects/items/shields.dm
+++ b/code/game/objects/items/shields.dm
@@ -99,6 +99,15 @@
max_integrity = 55
w_class = WEIGHT_CLASS_NORMAL
+/obj/item/shield/buckler/moonflower
+ name = "moonflower buckler"
+ desc = "A buckler made from a steel-cap reinforced moonflower."
+ icon_state = "moonflower_buckler"
+ inhand_icon_state = "moonflower_buckler"
+ block_chance = 40
+ max_integrity = 40
+ w_class = WEIGHT_CLASS_NORMAL
+
/obj/item/shield/kite
name = "kite shield"
desc = "Protect your internal organs with this almond shaped shield."
@@ -140,6 +149,8 @@
shield_break_sound = 'sound/effects/glassbr3.ogg'
shield_break_leftover = /obj/item/shard
armor_type = /datum/armor/item_shield/riot
+ pickup_sound = 'sound/items/plastic_shield_pick_up.ogg'
+ drop_sound = 'sound/items/plastic_shield_drop.ogg'
/obj/item/shield/riot/Initialize(mapload)
. = ..()
diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm
index ff08886e7d3ed..dd2ab6008245c 100644
--- a/code/game/objects/items/shooting_range.dm
+++ b/code/game/objects/items/shooting_range.dm
@@ -1,7 +1,8 @@
/obj/item/target
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "shooting target"
desc = "A shooting target."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/art/cardboard_cutout.dmi'
icon_state = "target_h"
density = FALSE
max_integrity = 1800
diff --git a/code/game/objects/items/spear.dm b/code/game/objects/items/spear.dm
index 96fbde554b79e..53e307e31d6e0 100644
--- a/code/game/objects/items/spear.dm
+++ b/code/game/objects/items/spear.dm
@@ -221,7 +221,7 @@
icon_state = "military_spear0"
base_icon_state = "military_spear0"
icon_prefix = "military_spear"
- name = "military Javelin"
+ name = "military javelin"
desc = "A stick with a seemingly blunt spearhead on its end. Looks like it might break bones easily."
attack_verb_continuous = list("attacks", "pokes", "jabs")
attack_verb_simple = list("attack", "poke", "jab")
diff --git a/code/game/objects/items/stacks/bscrystal.dm b/code/game/objects/items/stacks/bscrystal.dm
index 410724862f3b5..20c974ad0632c 100644
--- a/code/game/objects/items/stacks/bscrystal.dm
+++ b/code/game/objects/items/stacks/bscrystal.dm
@@ -2,7 +2,7 @@
/obj/item/stack/ore/bluespace_crystal
name = "bluespace crystal"
desc = "A glowing bluespace crystal, not much is known about how they work. It looks very delicate."
- icon = 'icons/obj/ore.dmi'
+ icon = 'icons/obj/mining_zones/ore.dmi'
icon_state = "bluespace_crystal"
singular_name = "bluespace crystal"
dye_color = DYE_COSMIC
@@ -21,6 +21,8 @@
points = 0
refined_type = null
merge_type = /obj/item/stack/ore/bluespace_crystal/refined
+ drop_sound = null //till I make a better one
+ pickup_sound = null
/obj/item/stack/ore/bluespace_crystal/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
@@ -60,6 +62,8 @@
refined_type = null
grind_results = list(/datum/reagent/bluespace = 10, /datum/reagent/silicon = 20)
merge_type = /obj/item/stack/ore/bluespace_crystal/artificial
+ drop_sound = null //till I make a better one
+ pickup_sound = null
//Polycrystals, aka stacks
/obj/item/stack/sheet/bluespace_crystal
@@ -79,6 +83,7 @@
material_type = /datum/material/bluespace
var/crystal_type = /obj/item/stack/ore/bluespace_crystal/refined
+
/obj/item/stack/sheet/bluespace_crystal/attack_self(mob/user)// to prevent the construction menu from ever happening
to_chat(user, span_warning("You cannot crush the polycrystal in-hand, try breaking one off."))
diff --git a/code/game/objects/items/stacks/golem_food/golem_hand_actions.dm b/code/game/objects/items/stacks/golem_food/golem_hand_actions.dm
index e4b65e033975e..fbae20ead1d74 100644
--- a/code/game/objects/items/stacks/golem_food/golem_hand_actions.dm
+++ b/code/game/objects/items/stacks/golem_food/golem_hand_actions.dm
@@ -7,7 +7,7 @@
/obj/item/gibtonite_hand
name = "stabilised gibtonite fist"
desc = "You had better launch this at something before it comes out the other end of your body."
- icon = 'icons/obj/ore.dmi'
+ icon = 'icons/obj/mining_zones/ore.dmi'
icon_state = "Gibtonite ore"
lefthand_file = 'icons/mob/inhands/golem_lefthand.dmi'
righthand_file = 'icons/mob/inhands/golem_righthand.dmi'
diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm
index 5a8e1361c28c3..69dbe87cd5cc9 100644
--- a/code/game/objects/items/stacks/rods.dm
+++ b/code/game/objects/items/stacks/rods.dm
@@ -39,6 +39,9 @@ GLOBAL_LIST_INIT(rod_recipes, list ( \
cost = HALF_SHEET_MATERIAL_AMOUNT
source = /datum/robot_energy_storage/material/iron
merge_type = /obj/item/stack/rods
+ pickup_sound = 'sound/items/iron_rod_pick_up.ogg'
+ drop_sound = 'sound/items/metal_drop.ogg'
+ sound_vary = TRUE
/datum/embed_data/rods
embed_chance = 50
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index a9d71fa127abb..cb711105995bc 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -10,6 +10,7 @@
*/
GLOBAL_LIST_INIT(glass_recipes, list ( \
new/datum/stack_recipe("directional window", /obj/structure/window/unanchored, time = 0.5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ON_SOLID_GROUND | CRAFT_CHECK_DIRECTION, category = CAT_WINDOWS), \
+ new/datum/stack_recipe("short directional window", /obj/structure/window/half/unanchored, time = 0.5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ON_SOLID_GROUND | CRAFT_CHECK_DIRECTION, category = CAT_WINDOWS), \
new/datum/stack_recipe("fulltile window", /obj/structure/window/fulltile/unanchored, 2, time = 1 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ON_SOLID_GROUND | CRAFT_IS_FULLTILE, category = CAT_WINDOWS), \
new/datum/stack_recipe("glass shard", /obj/item/shard, time = 0, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ON_SOLID_GROUND, category = CAT_MISC), \
new/datum/stack_recipe("glass tile", /obj/item/stack/tile/glass, 1, 4, 20, category = CAT_TILES) \
@@ -32,6 +33,8 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \
cost = SHEET_MATERIAL_AMOUNT
source = /datum/robot_energy_storage/material/glass
sniffable = TRUE
+ pickup_sound = 'sound/items/glass_pick_up.ogg'
+ drop_sound = 'sound/items/glass_drop.ogg'
/datum/armor/sheet_glass
fire = 50
@@ -102,6 +105,8 @@ GLOBAL_LIST_INIT(pglass_recipes, list ( \
grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/toxin/plasma = 10)
material_flags = NONE
tableVariant = /obj/structure/table/glass/plasmaglass
+ pickup_sound = 'sound/items/glass_pick_up.ogg'
+ drop_sound = 'sound/items/glass_drop.ogg'
/obj/item/stack/sheet/plasmaglass/fifty
amount = 50
@@ -139,8 +144,10 @@ GLOBAL_LIST_INIT(pglass_recipes, list ( \
*/
GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \
new/datum/stack_recipe("windoor frame", /obj/structure/windoor_assembly, 5, time = 0, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ON_SOLID_GROUND | CRAFT_CHECK_DIRECTION, category = CAT_WINDOWS), \
+ new/datum/stack_recipe("short windoor frame", /obj/structure/windoor_assembly/half, 5, time = 0, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ON_SOLID_GROUND | CRAFT_CHECK_DIRECTION, category = CAT_WINDOWS), \
null, \
new/datum/stack_recipe("directional reinforced window", /obj/structure/window/reinforced/unanchored, time = 0.5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ON_SOLID_GROUND | CRAFT_CHECK_DIRECTION, category = CAT_WINDOWS), \
+ new/datum/stack_recipe("short directional reinforced window", /obj/structure/window/reinforced/half/unanchored, time = 0.5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ON_SOLID_GROUND | CRAFT_CHECK_DIRECTION, category = CAT_WINDOWS), \
new/datum/stack_recipe("fulltile reinforced window", /obj/structure/window/reinforced/fulltile/unanchored, 2, time = 2 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ON_SOLID_GROUND | CRAFT_IS_FULLTILE, category = CAT_WINDOWS), \
new/datum/stack_recipe("glass shard", /obj/item/shard, time = 10, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ON_SOLID_GROUND, category = CAT_MISC), \
new/datum/stack_recipe("reinforced glass tile", /obj/item/stack/tile/rglass, 1, 4, 20, category = CAT_TILES) \
@@ -160,6 +167,8 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \
grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/iron = 10)
matter_amount = 6
tableVariant = /obj/structure/table/reinforced/rglass
+ pickup_sound = 'sound/items/glass_pick_up.ogg'
+ drop_sound = 'sound/items/glass_drop.ogg'
/obj/item/stack/sheet/rglass/fifty
amount = 50
@@ -198,6 +207,8 @@ GLOBAL_LIST_INIT(prglass_recipes, list ( \
gulag_valid = TRUE
matter_amount = 8
tableVariant = /obj/structure/table/reinforced/plasmarglass
+ pickup_sound = 'sound/items/glass_pick_up.ogg'
+ drop_sound = 'sound/items/glass_drop.ogg'
/datum/armor/sheet_plasmarglass
melee = 20
@@ -228,6 +239,8 @@ GLOBAL_LIST_INIT(titaniumglass_recipes, list(
resistance_flags = ACID_PROOF
merge_type = /obj/item/stack/sheet/titaniumglass
tableVariant = /obj/structure/table/reinforced/titaniumglass
+ pickup_sound = 'sound/items/glass_pick_up.ogg'
+ drop_sound = 'sound/items/glass_drop.ogg'
/obj/item/stack/sheet/titaniumglass/fifty
amount = 50
@@ -258,6 +271,8 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
resistance_flags = ACID_PROOF
merge_type = /obj/item/stack/sheet/plastitaniumglass
tableVariant = /obj/structure/table/reinforced/plastitaniumglass
+ pickup_sound = 'sound/items/glass_pick_up.ogg'
+ drop_sound = 'sound/items/glass_drop.ogg'
/obj/item/stack/sheet/plastitaniumglass/fifty
amount = 50
diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm
index fbd69dbd20d4b..10ce7e7fa1c12 100644
--- a/code/game/objects/items/stacks/sheets/leather.dm
+++ b/code/game/objects/items/stacks/sheets/leather.dm
@@ -5,6 +5,8 @@
inhand_icon_state = null
novariants = TRUE
merge_type = /obj/item/stack/sheet/animalhide
+ pickup_sound = 'sound/items/skin_pick_up.ogg'
+ drop_sound = 'sound/items/skin_drop.ogg'
/obj/item/stack/sheet/animalhide/human
name = "human skin"
@@ -191,6 +193,8 @@ GLOBAL_LIST_INIT(carp_recipes, list ( \
icon_state = "sheet-leather"
inhand_icon_state = null
merge_type = /obj/item/stack/sheet/leather
+ pickup_sound = 'sound/items/skin_pick_up.ogg'
+ drop_sound = 'sound/items/skin_drop.ogg'
GLOBAL_LIST_INIT(leather_recipes, list ( \
new/datum/stack_recipe("wallet", /obj/item/storage/wallet, 1, crafting_flags = NONE, category = CAT_CONTAINERS), \
@@ -235,12 +239,14 @@ GLOBAL_LIST_INIT(leather_recipes, list ( \
*/
/obj/item/stack/sheet/sinew
name = "watcher sinew"
- icon = 'icons/obj/mining.dmi'
+ icon = 'icons/obj/mining_zones/equipment.dmi'
desc = "Long stringy filaments which presumably came from a watcher's wings."
singular_name = "watcher sinew"
icon_state = "sinew"
novariants = TRUE
merge_type = /obj/item/stack/sheet/sinew
+ drop_sound = 'sound/effects/meatslap.ogg'
+ pickup_sound = 'sound/effects/meatslap.ogg'
/obj/item/stack/sheet/sinew/Initialize(mapload, new_amount, merge, list/mat_override, mat_amt)
. = ..()
diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm
index 0d4393efea6a4..114222b4312d0 100644
--- a/code/game/objects/items/stacks/sheets/mineral.dm
+++ b/code/game/objects/items/stacks/sheets/mineral.dm
@@ -444,7 +444,7 @@ GLOBAL_LIST_INIT(abductor_recipes, list ( \
/obj/item/stack/sheet/mineral/coal
name = "coal"
desc = "Someone's gotten on the naughty list."
- icon = 'icons/obj/ore.dmi'
+ icon = 'icons/obj/mining_zones/ore.dmi'
icon_state = "slag"
singular_name = "coal lump"
merge_type = /obj/item/stack/sheet/mineral/coal
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 0ec790fe74349..ba0c10bc9f514 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -20,6 +20,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
new/datum/stack_recipe("bed", /obj/structure/bed, 2, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_FURNITURE), \
new/datum/stack_recipe("double bed", /obj/structure/bed/double, 4, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_FURNITURE), \
null, \
+ new/datum/stack_recipe("window frame", /obj/structure/window_frame, 2, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND | CRAFT_APPLIES_MATS), \
new/datum/stack_recipe_list("office chairs", list( \
new/datum/stack_recipe("dark office chair", /obj/structure/chair/office, 5, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_FURNITURE), \
new/datum/stack_recipe("light office chair", /obj/structure/chair/office/light, 5, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_FURNITURE), \
@@ -124,7 +125,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
new/datum/stack_recipe("apc frame", /obj/item/wallframe/apc, 2, crafting_flags = NONE, category = CAT_STRUCTURE), \
new/datum/stack_recipe("air alarm frame", /obj/item/wallframe/airalarm, 2, crafting_flags = NONE, category = CAT_STRUCTURE), \
new/datum/stack_recipe("fire alarm frame", /obj/item/wallframe/firealarm, 2, crafting_flags = NONE, category = CAT_STRUCTURE), \
- new/datum/stack_recipe("extinguisher cabinet frame", /obj/item/wallframe/extinguisher_cabinet, 2, crafting_flags = NONE, category = CAT_STRUCTURE), \
+ new/datum/stack_recipe("extinguisher rack frame", /obj/item/wallframe/extinguisher_cabinet, 2, crafting_flags = NONE, category = CAT_STRUCTURE), \
new/datum/stack_recipe("button frame", /obj/item/wallframe/button, 1, crafting_flags = NONE, category = CAT_STRUCTURE), \
new/datum/stack_recipe("light switch frame", /obj/item/wallframe/light_switch, 1, crafting_flags = NONE, category = CAT_STRUCTURE), \
new/datum/stack_recipe("sparker frame", /obj/item/wallframe/sparker, 1, crafting_flags = NONE, category = CAT_STRUCTURE), \
@@ -328,7 +329,7 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
new/datum/stack_recipe("cat house", /obj/structure/cat_house, 5, time = 5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_STRUCTURE), \
new/datum/stack_recipe("coffin", /obj/structure/closet/crate/coffin, 5, time = 1.5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_FURNITURE), \
new/datum/stack_recipe("book case", /obj/structure/bookcase, 4, time = 1.5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_FURNITURE), \
- new/datum/stack_recipe("drying rack", /obj/machinery/smartfridge/drying_rack, 10, time = 1.5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_TOOLS), \
+ new/datum/stack_recipe("drying rack", /obj/machinery/smartfridge/drying/rack, 10, time = 1.5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_TOOLS), \
new/datum/stack_recipe("wooden barrel", /obj/structure/fermenting_barrel, 8, time = 5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_CONTAINERS), \
new/datum/stack_recipe("dog bed", /obj/structure/bed/dogbed, 10, time = 1 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_FURNITURE), \
new/datum/stack_recipe("dresser", /obj/structure/dresser, 10, time = 1.5 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_FURNITURE), \
@@ -385,6 +386,8 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
grind_results = list(/datum/reagent/cellulose = 20) //no lignocellulose or lignin reagents yet,
walltype = /turf/closed/wall/mineral/wood
stairs_type = /obj/structure/stairs/wood
+ pickup_sound = 'sound/items/wood_pick_up.ogg'
+ drop_sound = 'sound/items/wood_drop.ogg'
/datum/armor/mineral_wood
fire = 50
@@ -448,6 +451,8 @@ GLOBAL_LIST_INIT(bamboo_recipes, list ( \
grind_results = list(/datum/reagent/cellulose = 10)
material_type = /datum/material/bamboo
walltype = /turf/closed/wall/mineral/bamboo
+ drop_sound = null
+ pickup_sound = null
/datum/armor/mineral_bamboo
fire = 50
@@ -573,6 +578,8 @@ GLOBAL_LIST_INIT(durathread_recipes, list ( \
grind_results = list(/datum/reagent/cellulose = 20)
var/loom_result = /obj/item/stack/sheet/cloth
var/loom_time = 1 SECONDS
+ drop_sound = 'sound/items/handling/cloth_drop.ogg'
+ pickup_sound = 'sound/items/handling/cloth_pickup.ogg'
/obj/item/stack/sheet/cotton/Initialize(mapload)
. = ..()
@@ -678,6 +685,8 @@ GLOBAL_LIST_INIT(cardboard_recipes, list ( \
merge_type = /obj/item/stack/sheet/cardboard
grind_results = list(/datum/reagent/cellulose = 10)
material_type = /datum/material/cardboard
+ pickup_sound = 'sound/items/cardboard_pick_up.ogg'
+ drop_sound = 'sound/items/cardboard_drop.ogg'
/obj/item/stack/sheet/cardboard/Initialize(mapload, new_amount, merge, list/mat_override, mat_amt)
. = ..()
@@ -809,6 +818,8 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \
grind_results = list(/datum/reagent/carbon = 10)
merge_type = /obj/item/stack/sheet/bone
material_type = /datum/material/bone
+ drop_sound = null
+ pickup_sound = null
/obj/item/stack/sheet/bone/Initialize(mapload, new_amount, merge, list/mat_override, mat_amt)
. = ..()
@@ -853,6 +864,8 @@ GLOBAL_LIST_INIT(plastic_recipes, list(
throwforce = 7
material_type = /datum/material/plastic
merge_type = /obj/item/stack/sheet/plastic
+ pickup_sound = 'sound/items/plastic_pick_up.ogg'
+ drop_sound = 'sound/items/plastic_drop.ogg'
/obj/item/stack/sheet/plastic/fifty
amount = 50
@@ -879,6 +892,8 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra
resistance_flags = FLAMMABLE
grind_results = list(/datum/reagent/cellulose = 20)
material_type = /datum/material/paper
+ drop_sound = null
+ pickup_sound = null
/obj/item/stack/sheet/paperframes/get_main_recipes()
. = ..()
@@ -900,6 +915,9 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra
merge_type = /obj/item/stack/sheet/meat
material_type = /datum/material/meat
material_modifier = 1 //None of that wussy stuff
+ drop_sound = null
+ pickup_sound = null
+ walltype = /turf/closed/wall/mineral/meat
/obj/item/stack/sheet/meat/fifty
amount = 50
@@ -917,6 +935,9 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra
merge_type = /obj/item/stack/sheet/pizza
material_type = /datum/material/pizza
material_modifier = 1
+ drop_sound = null
+ pickup_sound = null
+ walltype = /turf/closed/wall/mineral/pizza
/obj/item/stack/sheet/pizza/fifty
amount = 50
@@ -934,6 +955,8 @@ new /datum/stack_recipe("paper frame door", /obj/structure/mineral_door/paperfra
merge_type = /obj/item/stack/sheet/sandblock
material_type = /datum/material/sand
material_modifier = 1
+ drop_sound = SFX_STONE_DROP
+ pickup_sound = SFX_STONE_PICKUP
/obj/item/stack/sheet/sandblock/fifty
amount = 50
diff --git a/code/game/objects/items/stacks/sheets/sheets.dm b/code/game/objects/items/stacks/sheets/sheets.dm
index 8b81953528ee1..568fd2f49aa29 100644
--- a/code/game/objects/items/stacks/sheets/sheets.dm
+++ b/code/game/objects/items/stacks/sheets/sheets.dm
@@ -13,6 +13,8 @@
attack_verb_simple = list("bash", "batter", "bludgeon", "thrash", "smash")
novariants = FALSE
material_flags = MATERIAL_EFFECTS
+ pickup_sound = 'sound/items/metal_pick_up.ogg'
+ drop_sound = 'sound/items/metal_drop.ogg'
var/sheettype = null //this is used for girders in the creation of walls/false walls
///If true, this is worth points in the gulag labour stacker
var/gulag_valid = FALSE
@@ -22,6 +24,8 @@
var/walltype
/// whether this sheet can be sniffed by the material sniffer
var/sniffable = FALSE
+ /// this makes pickup and drop sounds vary
+ sound_vary = TRUE
/obj/item/stack/sheet/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1)
. = ..()
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index c748ba4c494a1..6246136d2687b 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -34,7 +34,7 @@
var/merge_type = null
/// The weight class the stack has at amount > 2/3rds max_amount
var/full_w_class = WEIGHT_CLASS_NORMAL
- /// Determines whether the item should update it's sprites based on amount.
+ /// Determines whether the item should update its sprites based on amount.
var/novariants = TRUE
/// List that tells you how much is in a single unit.
var/list/mats_per_unit
@@ -302,7 +302,7 @@
data["recipes"] = recursively_build_recipes(recipes)
return data
-/obj/item/stack/ui_act(action, params)
+/obj/item/stack/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/game/objects/items/stacks/tape.dm b/code/game/objects/items/stacks/tape.dm
index 0e054ba8d1062..acfc59b64d037 100644
--- a/code/game/objects/items/stacks/tape.dm
+++ b/code/game/objects/items/stacks/tape.dm
@@ -127,12 +127,15 @@
singular_name = "surgical tape"
desc = "Made for patching broken bones back together alongside bone gel, not for playing pranks."
prefix = "surgical"
- conferred_embed = list("embed_chance" = 30, "pain_mult" = 0, "jostle_pain_mult" = 0, "ignore_throwspeed_threshold" = TRUE)
+ conferred_embed = /datum/embed_data/sticky_tape/surgical
splint_factor = 0.5
custom_price = PAYCHECK_CREW
merge_type = /obj/item/stack/sticky_tape/surgical
greyscale_colors = "#70BAE7#BD6A62"
tape_gag = /obj/item/clothing/mask/muzzle/tape/surgical
+/datum/embed_data/sticky_tape/surgical
+ embed_chance = 30
+
/obj/item/stack/sticky_tape/surgical/get_surgery_tool_overlay(tray_extended)
return "tape" + (tray_extended ? "" : "_out")
diff --git a/code/game/objects/items/storage/backpack.dm b/code/game/objects/items/storage/backpack.dm
index 77ca77e8ba0bf..a88da6537b64e 100644
--- a/code/game/objects/items/storage/backpack.dm
+++ b/code/game/objects/items/storage/backpack.dm
@@ -377,7 +377,7 @@
/obj/item/storage/backpack/satchel/flat
name = "smuggler's satchel"
- desc = "A very slim satchel that can easily fit into tight spaces."
+ desc = "A very slim satchel that can easily fit into tight spaces. Its contents cannot be detected by contraband scanners."
icon_state = "satchel-flat"
inhand_icon_state = "satchel-flat"
w_class = WEIGHT_CLASS_NORMAL //Can fit in backpacks itself.
diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm
index 996cd933647a1..5c4037577c258 100644
--- a/code/game/objects/items/storage/bags.dm
+++ b/code/game/objects/items/storage/bags.dm
@@ -119,7 +119,7 @@
/obj/item/storage/bag/ore
name = "mining satchel"
desc = "This little bugger can be used to store and transport ores."
- icon = 'icons/obj/mining.dmi'
+ icon = 'icons/obj/mining_zones/equipment.dmi'
icon_state = "satchel"
worn_icon_state = "satchel"
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKETS
@@ -286,7 +286,7 @@
/obj/item/storage/bag/sheetsnatcher
name = "sheet snatcher"
desc = "A patented Nanotrasen storage system designed for any kind of mineral sheet."
- icon = 'icons/obj/mining.dmi'
+ icon = 'icons/obj/mining_zones/equipment.dmi'
icon_state = "sheetsnatcher"
worn_icon_state = "satchel"
diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm
index 1bf97712ec566..1235ab3afbd9d 100644
--- a/code/game/objects/items/storage/belt.dm
+++ b/code/game/objects/items/storage/belt.dm
@@ -79,7 +79,7 @@
))
/obj/item/storage/belt/utility/chief
- name = "\improper Chief Engineer's toolbelt" //"the Chief Engineer's toolbelt", because "Chief Engineer's toolbelt" is not a proper noun
+ name = "chief engineer's toolbelt"
desc = "Holds tools, looks snazzy."
icon_state = "utility_ce"
inhand_icon_state = "utility_ce"
@@ -373,6 +373,9 @@
/obj/item/restraints/handcuffs,
/obj/item/restraints/legcuffs/bola,
))
+ atom_storage.open_sound = 'sound/items/holster.ogg'
+ atom_storage.open_sound_vary = TRUE
+ atom_storage.rustle_sound = FALSE
/obj/item/storage/belt/security/full/PopulateContents()
new /obj/item/reagent_containers/spray/pepper(src)
@@ -529,7 +532,7 @@
/obj/item/storage/belt/military/snack/Initialize(mapload)
. = ..()
- var/sponsor = pick("Donk Co.", "Waffle Co.", "Roffle Co.", "Gorlax Marauders", "Tiger Cooperative")
+ var/sponsor = pick("Donk Co.", "Waffle Corp.", "Roffle Co.", "Gorlex Marauders", "Tiger Cooperative")
desc = "A set of snack-tical webbing worn by athletes of the [sponsor] VR sports division."
atom_storage.max_slots = 6
atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
@@ -821,7 +824,7 @@
AddElement(/datum/element/update_icon_updates_onmob)
atom_storage.max_slots = 1
- atom_storage.rustle_sound = FALSE
+ atom_storage.do_rustle = FALSE
atom_storage.max_specific_storage = WEIGHT_CLASS_BULKY
atom_storage.set_holdable(/obj/item/melee/sabre)
atom_storage.click_alt_open = FALSE
@@ -855,6 +858,50 @@
new /obj/item/melee/sabre(src)
update_appearance()
+/obj/item/storage/belt/grass_sabre
+ name = "sabre sheath"
+ desc = "An simple grass sheath designed to hold a sabre of... some sort. Actual metal one might be too sharp, though..."
+ icon_state = "grass_sheath"
+ inhand_icon_state = "grass_sheath"
+ worn_icon_state = "grass_sheath"
+ w_class = WEIGHT_CLASS_BULKY
+ interaction_flags_click = parent_type::interaction_flags_click | NEED_DEXTERITY | NEED_HANDS
+
+/obj/item/storage/belt/grass_sabre/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+
+ atom_storage.max_slots = 1
+ atom_storage.do_rustle = FALSE
+ atom_storage.max_specific_storage = WEIGHT_CLASS_BULKY
+ atom_storage.set_holdable(/obj/item/melee/parsnip_sabre)
+ atom_storage.click_alt_open = FALSE
+
+/obj/item/storage/belt/grass_sabre/examine(mob/user)
+ . = ..()
+ if(length(contents))
+ . += span_notice("Alt-click it to quickly draw the blade.")
+
+/obj/item/storage/belt/grass_sabre/click_alt(mob/user)
+ if(length(contents))
+ var/obj/item/I = contents[1]
+ user.visible_message(span_notice("[user] takes [I] out of [src]."), span_notice("You take [I] out of [src]."))
+ user.put_in_hands(I)
+ update_appearance()
+ else
+ balloon_alert(user, "it's empty!")
+ return CLICK_ACTION_SUCCESS
+
+/obj/item/storage/belt/grass_sabre/update_icon_state()
+ icon_state = initial(inhand_icon_state)
+ inhand_icon_state = initial(inhand_icon_state)
+ worn_icon_state = initial(worn_icon_state)
+ if(contents.len)
+ icon_state += "-sabre"
+ inhand_icon_state += "-sabre"
+ worn_icon_state += "-sabre"
+ return ..()
+
/obj/item/storage/belt/plant
name = "botanical belt"
desc = "A sturdy leather belt used to hold most hydroponics supplies."
diff --git a/code/game/objects/items/storage/boxes/_boxes.dm b/code/game/objects/items/storage/boxes/_boxes.dm
index 56915d8a5fced..9401527299689 100644
--- a/code/game/objects/items/storage/boxes/_boxes.dm
+++ b/code/game/objects/items/storage/boxes/_boxes.dm
@@ -19,6 +19,8 @@
. = ..()
atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
update_appearance()
+ atom_storage.open_sound = 'sound/items/cardboard_box_open.ogg'
+ atom_storage.rustle_sound = 'sound/items/cardboard_box_rustle.ogg'
/obj/item/storage/box/suicide_act(mob/living/carbon/user)
var/obj/item/bodypart/head/myhead = user.get_bodypart(BODY_ZONE_HEAD)
diff --git a/code/game/objects/items/storage/boxes/job_boxes.dm b/code/game/objects/items/storage/boxes/job_boxes.dm
index ea9189cc5f2b0..a8ced4fdad48e 100644
--- a/code/game/objects/items/storage/boxes/job_boxes.dm
+++ b/code/game/objects/items/storage/boxes/job_boxes.dm
@@ -43,7 +43,7 @@
if(HAS_TRAIT(SSstation, STATION_TRAIT_RADIOACTIVE_NEBULA))
new /obj/item/storage/pill_bottle/potassiodide(src)
- if(SSmapping.is_planetary() && LAZYLEN(SSmapping.multiz_levels))
+ if(length(SSmapping.levels_by_trait(ZTRAIT_STATION)) > 1)
new /obj/item/climbing_hook/emergency(src)
/obj/item/storage/box/survival/radio/PopulateContents()
diff --git a/code/game/objects/items/storage/boxes/science_boxes.dm b/code/game/objects/items/storage/boxes/science_boxes.dm
index f0654cdf024bc..63e1c1ee3762d 100644
--- a/code/game/objects/items/storage/boxes/science_boxes.dm
+++ b/code/game/objects/items/storage/boxes/science_boxes.dm
@@ -39,6 +39,7 @@
illustration = null
/// Which type of cube are we spawning in this box?
var/cube_type = /obj/item/food/monkeycube
+ custom_price = PAYCHECK_CREW * 2
/obj/item/storage/box/monkeycubes/Initialize(mapload)
. = ..()
@@ -50,12 +51,12 @@
new cube_type(src)
/obj/item/storage/box/monkeycubes/syndicate
- desc = "Waffle Co. brand monkey cubes. Just add water and a dash of subterfuge!"
+ desc = "Waffle Corp. brand monkey cubes. Just add water and a dash of subterfuge!"
cube_type = /obj/item/food/monkeycube/syndicate
/obj/item/storage/box/gorillacubes
name = "gorilla cube box"
- desc = "Waffle Co. brand gorilla cubes. Do not taunt."
+ desc = "Waffle Corp. brand gorilla cubes. Do not taunt."
icon_state = "monkeycubebox"
illustration = null
diff --git a/code/game/objects/items/storage/boxes/security_boxes.dm b/code/game/objects/items/storage/boxes/security_boxes.dm
index 459c0ab7ce29e..935ead8f93e95 100644
--- a/code/game/objects/items/storage/boxes/security_boxes.dm
+++ b/code/game/objects/items/storage/boxes/security_boxes.dm
@@ -204,6 +204,16 @@
for(var/i in 1 to 7)
new /obj/item/ammo_casing/shotgun/breacher(src)
+/obj/item/storage/box/large_dart
+ name = "box of XL shotgun darts"
+ name = "A box full of shotgun darts with increased chemical storage capacity."
+ icon_state = "shotdart_box"
+ illustration = null
+
+/obj/item/storage/box/large_dart/PopulateContents()
+ for(var/i in 1 to 7)
+ new /obj/item/ammo_casing/shotgun/dart/large(src)
+
/obj/item/storage/box/emptysandbags
name = "box of empty sandbags"
illustration = "sandbag"
diff --git a/code/game/objects/items/storage/holsters.dm b/code/game/objects/items/storage/holsters.dm
index afb7b0f750b26..f8dee3afdc7f4 100644
--- a/code/game/objects/items/storage/holsters.dm
+++ b/code/game/objects/items/storage/holsters.dm
@@ -33,6 +33,8 @@
/obj/item/gun/energy/laser/captain,
/obj/item/gun/energy/e_gun/hos,
))
+ atom_storage.open_sound = 'sound/items/holster.ogg'
+ atom_storage.open_sound_vary = TRUE
/obj/item/storage/belt/holster/energy
name = "energy shoulder holsters"
diff --git a/code/game/objects/items/storage/lockbox.dm b/code/game/objects/items/storage/lockbox.dm
index a1dbe0e690c71..8fa147252514c 100644
--- a/code/game/objects/items/storage/lockbox.dm
+++ b/code/game/objects/items/storage/lockbox.dm
@@ -199,7 +199,7 @@
req_access = list(ACCESS_QM)
/obj/item/storage/lockbox/medal/cargo/PopulateContents()
- new /obj/item/clothing/accessory/medal/ribbon/cargo(src)
+ new /obj/item/clothing/accessory/medal/ribbon/cargo(src)
/obj/item/storage/lockbox/medal/service
name = "service award box"
@@ -207,7 +207,7 @@
req_access = list(ACCESS_HOP)
/obj/item/storage/lockbox/medal/service/PopulateContents()
- new /obj/item/clothing/accessory/medal/silver/excellence(src)
+ new /obj/item/clothing/accessory/medal/silver/excellence(src)
/obj/item/storage/lockbox/medal/sci
name = "science medal box"
diff --git a/code/game/objects/items/storage/medkit.dm b/code/game/objects/items/storage/medkit.dm
index 944289598d17b..368ef9c0b406a 100644
--- a/code/game/objects/items/storage/medkit.dm
+++ b/code/game/objects/items/storage/medkit.dm
@@ -18,6 +18,9 @@
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
throw_speed = 3
throw_range = 7
+ drop_sound = 'sound/items/medkit_drop.ogg'
+ pickup_sound = 'sound/items/medkit_pick_up.ogg'
+ sound_vary = TRUE
var/empty = FALSE
/// Defines damage type of the medkit. General ones stay null. Used for medibot healing bonuses
var/damagetype_healed
@@ -79,6 +82,9 @@
/obj/item/storage/medkit/Initialize(mapload)
. = ..()
atom_storage.max_specific_storage = WEIGHT_CLASS_SMALL
+ atom_storage.open_sound = 'sound/items/medkit_open.ogg'
+ atom_storage.open_sound_vary = TRUE
+ atom_storage.rustle_sound = 'sound/items/medkit_rustle.ogg'
/obj/item/storage/medkit/regular
icon_state = "medkit"
@@ -454,6 +460,8 @@
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
w_class = WEIGHT_CLASS_SMALL
+ pickup_sound = 'sound/items/handling/pill_bottle_pickup.ogg'
+ drop_sound = 'sound/items/handling/pill_bottle_place.ogg'
/obj/item/storage/pill_bottle/Initialize(mapload)
. = ..()
@@ -462,6 +470,8 @@
/obj/item/reagent_containers/pill,
/obj/item/food/bait/natural,
))
+ atom_storage.open_sound = 'sound/items/handling/pill_bottle_open.ogg'
+ atom_storage.open_sound_vary = FALSE
/obj/item/storage/pill_bottle/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is trying to get the cap off [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
diff --git a/code/game/objects/items/storage/toolbox.dm b/code/game/objects/items/storage/toolbox.dm
index 7c5bc74e07550..d2bb90e69e445 100644
--- a/code/game/objects/items/storage/toolbox.dm
+++ b/code/game/objects/items/storage/toolbox.dm
@@ -33,7 +33,8 @@
if(prob(1))
latches = "triple_latch"
update_appearance()
-
+ atom_storage.open_sound = 'sound/items/toolbox_open.ogg'
+ atom_storage.rustle_sound = 'sound/items/toolbox_rustle.ogg'
AddElement(/datum/element/falling_hazard, damage = force, wound_bonus = wound_bonus, hardhat_safety = TRUE, crushes = FALSE, impact_sound = hitsound)
/obj/item/storage/toolbox/update_overlays()
@@ -348,6 +349,11 @@
weapon_to_spawn = /obj/item/gun/ballistic/automatic/c20r
extra_to_spawn = /obj/item/ammo_box/magazine/smgm45
+/obj/item/storage/toolbox/guncase/smartgun
+ name = "adielle smartgun case"
+ weapon_to_spawn = /obj/item/gun/ballistic/automatic/smartgun
+ extra_to_spawn = /obj/item/ammo_box/magazine/smartgun
+
/obj/item/storage/toolbox/guncase/clandestine
name = "clandestine gun case"
weapon_to_spawn = /obj/item/gun/ballistic/automatic/pistol/clandestine
@@ -442,12 +448,6 @@
weapon_to_spawn = /obj/effect/spawner/random/sakhno
extra_to_spawn = /obj/effect/spawner/random/sakhno/ammo
-/obj/item/storage/toolbox/guncase/soviet/plastikov
- name = "ancient surplus gun case"
- desc = "A gun case. Has the symbol of the Third Soviet Union stamped on the side."
- weapon_to_spawn = /obj/item/gun/ballistic/automatic/plastikov
- extra_to_spawn = /obj/item/food/rationpack //sorry comrade, cannot get you more ammo, here, have lunch
-
/obj/item/storage/toolbox/guncase/monkeycase
name = "monkey gun case"
desc = "Everything a monkey needs to truly go ape-shit. There's a paw-shaped hand scanner lock on the front of the case."
diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm
index d3e41b21a986b..acbe9c017aaae 100644
--- a/code/game/objects/items/storage/uplink_kits.dm
+++ b/code/game/objects/items/storage/uplink_kits.dm
@@ -267,7 +267,7 @@
if(KIT_MR_FREEZE)
new /obj/item/clothing/glasses/cold(src)
- new /obj/item/clothing/gloves/color/black(src)
+ new /obj/item/clothing/gloves/color/black/security/blu(src)
new /obj/item/clothing/mask/chameleon(src)
new /obj/item/clothing/suit/hooded/wintercoat(src)
new /obj/item/clothing/shoes/winterboots(src)
@@ -584,6 +584,7 @@
new /obj/item/storage/backpack/satchel(src)
new /obj/item/modular_computer/pda/heads(src)
new /obj/item/clipboard(src)
+ new /obj/item/skillchip/big_pointer(src)
/obj/item/storage/box/syndie_kit/chameleon/broken/PopulateContents()
new /obj/item/clothing/under/chameleon/broken(src)
diff --git a/code/game/objects/items/storage/wallets.dm b/code/game/objects/items/storage/wallets.dm
index e485d6997f05f..e8c1fc6d19245 100644
--- a/code/game/objects/items/storage/wallets.dm
+++ b/code/game/objects/items/storage/wallets.dm
@@ -9,6 +9,7 @@
var/obj/item/card/id/front_id = null
var/list/combined_access
var/cached_flat_icon
+ var/overlay_icon_state = "wallet_overlay"
/obj/item/storage/wallet/Initialize(mapload)
. = ..()
@@ -103,7 +104,7 @@
return
. += mutable_appearance(front_id.icon, front_id.icon_state)
. += front_id.overlays
- . += mutable_appearance(icon, "wallet_overlay")
+ . += mutable_appearance(icon, overlay_icon_state)
/obj/item/storage/wallet/proc/get_cached_flat_icon()
if(!cached_flat_icon)
@@ -117,9 +118,9 @@
/obj/item/storage/wallet/proc/update_label()
if(front_id)
- name = "wallet displaying [front_id]"
+ name = "[src::name] displaying [front_id]"
else
- name = "wallet"
+ name = src::name
/obj/item/storage/wallet/examine()
. = ..()
diff --git a/code/game/objects/items/surgery_tray.dm b/code/game/objects/items/surgery_tray.dm
index e156bf8a0a862..4e84bd3ac3559 100644
--- a/code/game/objects/items/surgery_tray.dm
+++ b/code/game/objects/items/surgery_tray.dm
@@ -42,7 +42,7 @@
. = ..()
if(is_portable)
desc = "The wheels and bottom storage of this medical cart have been stowed away, \
- leaving a cumbersome tray in it's place."
+ leaving a cumbersome tray in its place."
else
desc = initial(desc)
diff --git a/code/game/objects/items/tanks/tanks.dm b/code/game/objects/items/tanks/tanks.dm
index bdbfa79001ddf..9c7138e00dee0 100644
--- a/code/game/objects/items/tanks/tanks.dm
+++ b/code/game/objects/items/tanks/tanks.dm
@@ -22,6 +22,9 @@
slot_flags = ITEM_SLOT_BACK
worn_icon = 'icons/mob/clothing/back.dmi' //since these can also get thrown into suit storage slots. if something goes on the belt, set this to null.
hitsound = 'sound/weapons/smash.ogg'
+ pickup_sound = 'sound/items/gas_tank_pick_up.ogg'
+ drop_sound = 'sound/items/gas_tank_drop.ogg'
+ sound_vary = TRUE
pressure_resistance = ONE_ATMOSPHERE * 5
force = 5
throwforce = 10
@@ -69,7 +72,7 @@
if (QDELETED(breathing_mob))
breathing_mob = null
return
- // Close open air tank if it got dropped by it's current user.
+ // Close open air tank if it got dropped by its current user.
if (loc != breathing_mob)
breathing_mob.cutoff_internals()
@@ -253,7 +256,7 @@
if(istype(carbon_user) && (carbon_user.external == src || carbon_user.internal == src))
.["connected"] = TRUE
-/obj/item/tank/ui_act(action, params)
+/obj/item/tank/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/game/objects/items/tools/crowbar.dm b/code/game/objects/items/tools/crowbar.dm
index c6b0d52cdc1c3..776dd16d78175 100644
--- a/code/game/objects/items/tools/crowbar.dm
+++ b/code/game/objects/items/tools/crowbar.dm
@@ -189,9 +189,9 @@
/obj/item/crowbar/cyborg
name = "hydraulic crowbar"
desc = "A hydraulic prying tool, simple but powerful."
- icon = 'icons/obj/items_cyborg.dmi'
+ icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "toolkit_engiborg_crowbar"
- worn_icon_state = "crowbar"
+ worn_icon_state = "toolkit_engiborg_crowbar" //error sprite - this shouldn't have been dropped
usesound = 'sound/items/jaws_pry.ogg'
force = 10
toolspeed = 0.5
diff --git a/code/game/objects/items/tools/screwdriver.dm b/code/game/objects/items/tools/screwdriver.dm
index b9e0d15e69f6e..63ee56be7f17c 100644
--- a/code/game/objects/items/tools/screwdriver.dm
+++ b/code/game/objects/items/tools/screwdriver.dm
@@ -148,7 +148,7 @@
/obj/item/screwdriver/cyborg
name = "automated screwdriver"
desc = "A powerful automated screwdriver, designed to be both precise and quick."
- icon = 'icons/obj/items_cyborg.dmi'
+ icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "toolkit_engiborg_screwdriver"
hitsound = 'sound/items/drill_hit.ogg'
usesound = 'sound/items/drill_use.ogg'
diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm
index b2b0109c04c88..a0a3c33fff34d 100644
--- a/code/game/objects/items/tools/weldingtool.dm
+++ b/code/game/objects/items/tools/weldingtool.dm
@@ -145,22 +145,31 @@
if(user.combat_mode)
return NONE
+ return try_heal_loop(interacting_with, user)
+
+/obj/item/weldingtool/proc/try_heal_loop(atom/interacting_with, mob/living/user, repeating = FALSE)
var/mob/living/carbon/human/attacked_humanoid = interacting_with
var/obj/item/bodypart/affecting = attacked_humanoid.get_bodypart(check_zone(user.zone_selected))
if(isnull(affecting) || !IS_ROBOTIC_LIMB(affecting))
return NONE
- var/use_delay = 0
+ if (!affecting.brute_dam)
+ balloon_alert(user, "limb not damaged")
+ return ITEM_INTERACT_BLOCKING
+ user.visible_message(span_notice("[user] starts to fix some of the dents on [attacked_humanoid == user ? user.p_their() : "[attacked_humanoid]'s"] [affecting.name]."),
+ span_notice("You start fixing some of the dents on [attacked_humanoid == user ? "your" : "[attacked_humanoid]'s"] [affecting.name]."))
+ var/use_delay = repeating ? 1 SECONDS : 0
if(user == attacked_humanoid)
- user.visible_message(span_notice("[user] starts to fix some of the dents on [attacked_humanoid]'s [affecting.name]."),
- span_notice("You start fixing some of the dents on [attacked_humanoid == user ? "your" : "[attacked_humanoid]'s"] [affecting.name]."))
use_delay = 5 SECONDS
if(!use_tool(attacked_humanoid, user, use_delay, volume=50, amount=1))
return ITEM_INTERACT_BLOCKING
- attacked_humanoid.item_heal(user, brute_heal = 15, burn_heal = 0, heal_message_brute = "dents", heal_message_burn = "burnt wires", required_bodytype = BODYTYPE_ROBOTIC)
+ if (!attacked_humanoid.item_heal(user, brute_heal = 15, burn_heal = 0, heal_message_brute = "dents", heal_message_burn = "burnt wires", required_bodytype = BODYTYPE_ROBOTIC))
+ return ITEM_INTERACT_BLOCKING
+
+ INVOKE_ASYNC(src, PROC_REF(try_heal_loop), interacting_with, user, TRUE)
return ITEM_INTERACT_SUCCESS
/obj/item/weldingtool/afterattack(atom/target, mob/user, click_parameters)
@@ -332,7 +341,7 @@
/obj/item/weldingtool/largetank/cyborg
name = "integrated welding tool"
desc = "An advanced welder designed to be used in robotic systems. Custom framework doubles the speed of welding."
- icon = 'icons/obj/items_cyborg.dmi'
+ icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "indwelder_cyborg"
toolspeed = 0.5
diff --git a/code/game/objects/items/tools/wirecutters.dm b/code/game/objects/items/tools/wirecutters.dm
index 7f2b11777d8f5..9ce1bc2409132 100644
--- a/code/game/objects/items/tools/wirecutters.dm
+++ b/code/game/objects/items/tools/wirecutters.dm
@@ -77,7 +77,7 @@
/obj/item/wirecutters/cyborg
name = "powered wirecutters"
desc = "Cuts wires with the power of ELECTRICITY. Faster than normal wirecutters."
- icon = 'icons/obj/items_cyborg.dmi'
+ icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "toolkit_engiborg_cutters"
worn_icon_state = "cutters"
toolspeed = 0.5
diff --git a/code/game/objects/items/tools/wrench.dm b/code/game/objects/items/tools/wrench.dm
index aa72b5d257ac6..fce0a6b16f518 100644
--- a/code/game/objects/items/tools/wrench.dm
+++ b/code/game/objects/items/tools/wrench.dm
@@ -87,7 +87,7 @@
/obj/item/wrench/cyborg
name = "hydraulic wrench"
desc = "An advanced robotic wrench, powered by internal hydraulics. Twice as fast as the handheld version."
- icon = 'icons/obj/items_cyborg.dmi'
+ icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "toolkit_engiborg_wrench"
toolspeed = 0.5
diff --git a/code/game/objects/items/toy_mechs.dm b/code/game/objects/items/toy_mechs.dm
index c50908738fe94..1b3367032c190 100644
--- a/code/game/objects/items/toy_mechs.dm
+++ b/code/game/objects/items/toy_mechs.dm
@@ -606,7 +606,7 @@
/obj/item/toy/mecha/deathripley/super_special_attack(obj/item/toy/mecha/victim)
playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 20, TRUE)
- if(victim.combat_health < combat_health) //Instantly kills the other mech if it's health is below our's.
+ if(victim.combat_health < combat_health) //Instantly kills the other mech if its health is below ours.
say("EXECUTE!!")
victim.combat_health = 0
else //Otherwise, just deal one damage.
diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm
index ff540932f31b7..daafdd447b646 100644
--- a/code/game/objects/items/trash.dm
+++ b/code/game/objects/items/trash.dm
@@ -85,10 +85,6 @@
desc = "In the Mothic Fleet every individual wrapper is carefully recycled and repurposed into fresh material. Over here they are more commonly dropped directly onto the floor."
icon_state = "moth_ration"
-/obj/item/trash/waffles
- name = "waffles tray"
- icon_state = "waffles"
-
/obj/item/trash/pistachios
name = "pistachios pack"
icon_state = "pistachios_pack"
@@ -113,7 +109,7 @@
/obj/item/trash/flare
name = "burnt flare"
- icon = 'icons/obj/lighting.dmi'
+ icon = 'icons/obj/devices/lighting.dmi'
icon_state = "flare-empty"
/obj/item/trash/can
diff --git a/code/game/objects/items/wall_mounted.dm b/code/game/objects/items/wall_mounted.dm
index ef19205cf8063..b5a9ec28355a6 100644
--- a/code/game/objects/items/wall_mounted.dm
+++ b/code/game/objects/items/wall_mounted.dm
@@ -6,9 +6,14 @@
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
w_class = WEIGHT_CLASS_SMALL
+ /// What do we create?
var/result_path
- var/wall_external = FALSE // For frames that are external to the wall they are placed on, like light fixtures and cameras.
- var/pixel_shift //The amount of pixels
+ /// For frames that are external to the wall they are placed on, like light fixtures and cameras.
+ var/wall_external = FALSE
+ /// Do we used inverse directionals?
+ var/inverse_dir = TRUE
+ /// Can we only hang this on walls North of us? Preferably don't enable unless you absolutely have to
+ var/north_only = FALSE
/obj/item/wallframe/proc/try_build(turf/on_wall, mob/user)
if(get_dist(on_wall,user) > 1)
@@ -18,6 +23,11 @@
if(!(floor_to_wall in GLOB.cardinals))
balloon_alert(user, "stand in line with wall!")
return
+
+ if (north_only && floor_to_wall != NORTH)
+ balloon_alert(user, "cannot place here!")
+ return
+
var/turf/T = get_turf(user)
var/area/A = get_area(T)
if(!isfloorturf(T))
@@ -38,20 +48,13 @@
user.visible_message(span_notice("[user.name] attaches [src] to the wall."),
span_notice("You attach [src] to the wall."),
span_hear("You hear clicking."))
- var/floor_to_wall = get_dir(user, on_wall)
+ var/facing_dir = get_dir(user, on_wall)
+
+ if(inverse_dir) // I'm sorry about this switch, O.turn wouldn't work for whatever reason.
+ facing_dir = REVERSE_DIR(facing_dir)
+ var/obj/hanging_object = new result_path(get_turf(user), facing_dir, TRUE)
+ hanging_object.setDir(facing_dir)
- var/obj/hanging_object = new result_path(get_turf(user), floor_to_wall, TRUE)
- hanging_object.setDir(floor_to_wall)
- if(pixel_shift)
- switch(floor_to_wall)
- if(NORTH)
- hanging_object.pixel_y = pixel_shift
- if(SOUTH)
- hanging_object.pixel_y = -pixel_shift
- if(EAST)
- hanging_object.pixel_x = pixel_shift
- if(WEST)
- hanging_object.pixel_x = -pixel_shift
after_attach(hanging_object)
qdel(src)
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index 5384a11cf3bbe..6b6e8f3d28eeb 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -160,9 +160,17 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
throw_range = 5
armour_penetration = 35
+/obj/item/claymore/cutlass/old
+ name = "old cutlass"
+ desc = parent_type::desc + " This one seems a tad old."
+ force = 24
+ throwforce = 17
+ armour_penetration = 20
+ block_chance = 30
+
/obj/item/claymore/carrot
name = "carrot sword"
- desc = "A full-sized carrot sword. Definitely \not\ good for the eyes, not anymore."
+ desc = "A full-sized carrot sword. Definitely not good for the eyes, not anymore."
icon_state = "carrot_sword"
inhand_icon_state = "carrot_sword"
worn_icon_state = "carrot_sword"
@@ -327,7 +335,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
playsound(user, 'sound/items/screwdriver2.ogg', 50, TRUE)
/obj/item/claymore/highlander/robot //BLOODTHIRSTY BORGS NOW COME IN PLAID
- icon = 'icons/obj/items_cyborg.dmi'
+ icon = 'icons/mob/silicon/robot_items.dmi'
icon_state = "claymore_cyborg"
var/mob/living/silicon/robot/robot
@@ -505,6 +513,36 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
user.visible_message(span_suicide("[user] is strangling [user.p_them()]self with [src]'s cord! It looks like [user.p_theyre()] trying to commit suicide!"))
return OXYLOSS
+/obj/item/bambostaff
+ name = "Bamboo Staff"
+ desc = "A long bamboo-made staff with steel-capped ends. It is rumoured that initiates of Spider Clan train with such before getting to learn how to use a katana."
+ force = 10
+ block_chance = 45
+ block_sound = 'sound/weapons/genhit.ogg'
+ slot_flags = ITEM_SLOT_BACK
+ w_class = WEIGHT_CLASS_BULKY
+ hitsound = SFX_SWING_HIT
+ attack_verb_continuous = list("smashes", "slams", "whacks", "thwacks")
+ attack_verb_simple = list("smash", "slam", "whack", "thwack")
+ icon = 'icons/obj/weapons/staff.dmi'
+ icon_state = "bambostaff0"
+ base_icon_state = "bambostaff"
+ inhand_icon_state = "bambostaff0"
+ worn_icon_state = "bambostaff0"
+ lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
+
+/obj/item/bambostaff/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed, \
+ force_unwielded = 10, \
+ force_wielded = 14, \
+ )
+
+/obj/item/bambostaff/update_icon_state()
+ icon_state = inhand_icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]"
+ return ..()
+
/obj/item/cane
name = "cane"
desc = "A cane used by a true gentleman. Or a clown."
@@ -1202,7 +1240,6 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
alpha = 150
duration = 0.5 SECONDS
layer = ABOVE_ALL_MOB_LAYER
- plane = ABOVE_GAME_PLANE
/obj/effect/temp_visual/slash/Initialize(mapload, atom/target, x_slashed, y_slashed, slash_color)
. = ..()
diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index d07f7ad21c5f5..be4924de56226 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -1,4 +1,3 @@
-
/obj/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
..()
take_damage(AM.throwforce, BRUTE, MELEE, 1, get_dir(src, AM))
@@ -96,7 +95,7 @@
/obj/proc/collision_damage(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
var/amt = max(0, ((force - (move_resist * MOVE_FORCE_CRUSH_RATIO)) / (move_resist * MOVE_FORCE_CRUSH_RATIO)) * 10)
- take_damage(amt, BRUTE)
+ take_damage(amt, BRUTE, attack_dir = REVERSE_DIR(direction))
/obj/singularity_act()
SSexplosions.high_mov_atom += src
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index bfdd0f71426c5..1b9282be66182 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -7,12 +7,12 @@
/// Extra examine line to describe controls, such as right-clicking, left-clicking, etc.
var/desc_controls
- /// The context returned when an attack against this object doesnt deal any traditional damage to the object.
+ /// The context returned when an attack against this object doesn't deal any traditional damage to the object.
var/no_damage_feedback = "without leaving a mark"
/// Icon to use as a 32x32 preview in crafting menus and such
var/icon_preview
var/icon_state_preview
- /// The vertical pixel offset applied when the object is anchored on a tile with table
+ /// The vertical pixel_z offset applied when the object is anchored on a tile with table
/// Ignored when set to 0 - to avoid shifting directional wall-mounted objects above tables
var/anchored_tabletop_offset = 0
@@ -24,7 +24,7 @@
/// If this attacks a human with no wound armor on the affected body part, add this to the wound mod. Some attacks may be significantly worse at wounding if there's even a slight layer of armor to absorb some of it vs bare flesh
var/bare_wound_bonus = 0
- /// A multiplier to an objecet's force when used against a stucture, vechicle, machine, or robot.
+ /// A multiplier to an object's force when used against a structure, vehicle, machine, or robot.
var/demolition_mod = 1
/// Custom fire overlay icon, will just use the default overlay if this is null
@@ -32,7 +32,7 @@
/// Particles this obj uses when burning, if any
var/burning_particles
- var/drag_slowdown // Amont of multiplicative slowdown applied if pulled. >1 makes you slower, <1 makes you faster.
+ var/drag_slowdown // Amount of multiplicative slowdown applied if pulled. >1 makes you slower, <1 makes you faster.
/// Map tag for something. Tired of it being used on snowflake items. Moved here for some semblance of a standard.
/// Next pr after the network fix will have me refactor door interactions, so help me god.
@@ -70,7 +70,7 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag)
var/total_force = (attacking_item.force * attacking_item.demolition_mod)
- var/damage = take_damage(total_force, attacking_item.damtype, MELEE, 1)
+ var/damage = take_damage(total_force, attacking_item.damtype, MELEE, 1, get_dir(src, user))
var/damage_verb = "hit"
@@ -199,7 +199,7 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag)
/obj/proc/plunger_act(obj/item/plunger/attacking_plunger, mob/living/user, reinforced)
return SEND_SIGNAL(src, COMSIG_PLUNGER_ACT, attacking_plunger, user, reinforced)
-// Should move all contained objects to it's location.
+// Should move all contained objects to its location.
/obj/proc/dump_contents()
SHOULD_CALL_PARENT(FALSE)
CRASH("Unimplemented.")
@@ -278,7 +278,14 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag)
return FALSE
return TRUE
-/// Adjusts the vertical pixel offset when the object is anchored on a tile with table
+/// Adjusts the vertical pixel_z offset when the object is anchored on a tile with table
/obj/proc/check_on_table()
- if(anchored_tabletop_offset != 0 && !istype(src, /obj/structure/table) && locate(/obj/structure/table) in loc)
- pixel_y = anchored ? anchored_tabletop_offset : initial(pixel_y)
+ if(anchored_tabletop_offset == 0)
+ return
+ if(istype(src, /obj/structure/table))
+ return
+
+ if(anchored && locate(/obj/structure/table) in loc)
+ pixel_z = anchored_tabletop_offset
+ else
+ pixel_z = initial(pixel_z)
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index ff6e6e171bb06..0484b823fd281 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -1,6 +1,6 @@
/// Inert structures, such as girders, machine frames, and crates/lockers.
/obj/structure
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/structures/tall.dmi'
pressure_resistance = 8
max_integrity = 300
interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT
@@ -32,7 +32,7 @@
QUEUE_SMOOTH_NEIGHBORS(src)
return ..()
-/obj/structure/ui_act(action, params)
+/obj/structure/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
add_fingerprint(usr)
return ..()
diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm
index f0c855e7c74d9..a550efd48032b 100644
--- a/code/game/objects/structures/aliens.dm
+++ b/code/game/objects/structures/aliens.dm
@@ -50,15 +50,13 @@
/obj/structure/alien/resin
name = "resin"
desc = "Looks like some kind of thick resin."
- icon = 'icons/obj/smooth_structures/alien/resin_wall.dmi'
- icon_state = "resin_wall-0"
- base_icon_state = "resin_wall"
+ icon = 'icons/obj/structures/smooth/alien/resin_wall_1.dmi'
density = TRUE
opacity = TRUE
anchored = TRUE
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_ALIEN_RESIN
- canSmoothWith = SMOOTH_GROUP_ALIEN_RESIN
+ smoothing_groups = SMOOTH_GROUP_ALIEN_WALLS + SMOOTH_GROUP_TALL_WALLS
+ canSmoothWith = SMOOTH_GROUP_ALIEN_WALLS
max_integrity = 200
var/resintype = null
can_atmos_pass = ATMOS_PASS_DENSITY
@@ -66,7 +64,9 @@
/obj/structure/alien/resin/Initialize(mapload)
. = ..()
+ icon = get_icon()
air_update_turf(TRUE, TRUE)
+ make_splitvis()
/obj/structure/alien/resin/Destroy()
air_update_turf(TRUE, FALSE)
@@ -77,15 +77,17 @@
. = ..()
move_update_air(T)
+/obj/structure/alien/resin/proc/make_splitvis()
+ AddElement(/datum/element/split_visibility, icon, color)
+
+/obj/structure/alien/resin/proc/get_icon()
+ if(prob(50))
+ return 'icons/obj/structures/smooth/alien/resin_wall_1.dmi'
+ return 'icons/obj/structures/smooth/alien/resin_wall_2.dmi'
+
/obj/structure/alien/resin/wall
name = "resin wall"
desc = "Thick resin solidified into a wall."
- icon = 'icons/obj/smooth_structures/alien/resin_wall.dmi'
- icon_state = "resin_wall-0"
- base_icon_state = "resin_wall"
- resintype = "wall"
- smoothing_groups = SMOOTH_GROUP_ALIEN_WALLS + SMOOTH_GROUP_ALIEN_RESIN
- canSmoothWith = SMOOTH_GROUP_ALIEN_WALLS
/obj/structure/alien/resin/wall/block_superconductivity()
return 1
@@ -103,15 +105,21 @@
/obj/structure/alien/resin/membrane
name = "resin membrane"
desc = "Resin just thin enough to let light pass through."
- icon = 'icons/obj/smooth_structures/alien/resin_membrane.dmi'
+ icon = 'icons/obj/structures/smooth/alien/resin_membrane.dmi'
icon_state = "resin_membrane-0"
base_icon_state = "resin_membrane"
opacity = FALSE
max_integrity = 160
resintype = "membrane"
- smoothing_groups = SMOOTH_GROUP_ALIEN_WALLS + SMOOTH_GROUP_ALIEN_RESIN
+ smoothing_groups = SMOOTH_GROUP_ALIEN_WALLS
canSmoothWith = SMOOTH_GROUP_ALIEN_WALLS
+/obj/structure/alien/resin/membrane/make_splitvis()
+ return
+
+/obj/structure/alien/resin/membrane/get_icon()
+ return 'icons/obj/structures/smooth/alien/resin_membrane.dmi'
+
/obj/structure/alien/resin/attack_paw(mob/user, list/modifiers)
return attack_hand(user, modifiers)
@@ -135,12 +143,12 @@
density = FALSE
layer = MID_TURF_LAYER
plane = FLOOR_PLANE
- icon = 'icons/obj/smooth_structures/alien/weeds1.dmi'
+ icon = 'icons/obj/structures/smooth/alien/weeds1.dmi'
icon_state = "weeds1-0"
base_icon_state = "weeds1"
max_integrity = 15
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_ALIEN_WEEDS + SMOOTH_GROUP_ALIEN_RESIN
+ smoothing_groups = SMOOTH_GROUP_ALIEN_WEEDS
canSmoothWith = SMOOTH_GROUP_ALIEN_WEEDS + SMOOTH_GROUP_WALLS
///the range of the weeds going to be affected by the node
var/node_range = NODERANGE
@@ -177,13 +185,13 @@
. = base_icon_state
switch(rand(1,3))
if(1)
- icon = 'icons/obj/smooth_structures/alien/weeds1.dmi'
+ icon = 'icons/obj/structures/smooth/alien/weeds1.dmi'
base_icon_state = "weeds1"
if(2)
- icon = 'icons/obj/smooth_structures/alien/weeds2.dmi'
+ icon = 'icons/obj/structures/smooth/alien/weeds2.dmi'
base_icon_state = "weeds2"
if(3)
- icon = 'icons/obj/smooth_structures/alien/weeds3.dmi'
+ icon = 'icons/obj/structures/smooth/alien/weeds3.dmi'
base_icon_state = "weeds3"
set_smoothed_icon_state(smoothing_junction)
@@ -253,7 +261,7 @@
/obj/structure/alien/weeds/node
name = "glowing resin"
desc = "Blue bioluminescence shines from beneath the surface."
- icon = 'icons/obj/smooth_structures/alien/weednode.dmi'
+ icon = 'icons/obj/structures/smooth/alien/weednode.dmi'
icon_state = "weednode-0"
base_icon_state = "weednode"
light_color = LIGHT_COLOR_BLUE
diff --git a/code/game/objects/structures/beds_chairs/alien_nest.dm b/code/game/objects/structures/beds_chairs/alien_nest.dm
index 77759c9e309b0..a7e5c68a2bc9f 100644
--- a/code/game/objects/structures/beds_chairs/alien_nest.dm
+++ b/code/game/objects/structures/beds_chairs/alien_nest.dm
@@ -3,7 +3,7 @@
/obj/structure/bed/nest
name = "alien nest"
desc = "It's a gruesome pile of thick, sticky resin shaped like a nest."
- icon = 'icons/obj/smooth_structures/alien/nest.dmi'
+ icon = 'icons/obj/structures/smooth/alien/nest.dmi'
icon_state = "nest-0"
base_icon_state = "nest"
max_integrity = 120
@@ -13,6 +13,7 @@
canSmoothWith = SMOOTH_GROUP_ALIEN_NEST
build_stack_type = null
elevation = 0
+ can_deconstruct = FALSE
var/static/mutable_appearance/nest_overlay = mutable_appearance('icons/mob/nonhuman-player/alien.dmi', "nestoverlay", LYING_MOB_LAYER)
/obj/structure/bed/nest/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
@@ -21,10 +22,6 @@
return ..()
-/obj/structure/bed/nest/wrench_act_secondary(mob/living/user, obj/item/weapon)
- return ITEM_INTERACT_BLOCKING
-
-
/obj/structure/bed/nest/user_unbuckle_mob(mob/living/captive, mob/living/hero)
if(!length(buckled_mobs))
return
@@ -41,7 +38,7 @@
unbuckle_mob(captive)
add_fingerprint(hero)
return
-
+
captive.visible_message(span_warning("[captive.name] struggles to break free from the gelatinous resin!"),
span_notice("You struggle to break free from the gelatinous resin... (Stay still for about a minute and a half.)"),
span_hear("You hear squelching..."))
diff --git a/code/game/objects/structures/beds_chairs/bed.dm b/code/game/objects/structures/beds_chairs/bed.dm
index 4cfd6355eb0b2..cb57446dc301a 100644
--- a/code/game/objects/structures/beds_chairs/bed.dm
+++ b/code/game/objects/structures/beds_chairs/bed.dm
@@ -8,10 +8,11 @@
/// Beds
/obj/structure/bed
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "bed"
desc = "This is used to lie in, sleep in or strap on."
icon_state = "bed"
- icon = 'icons/obj/bed.dmi'
+ icon = 'icons/obj/structures/bed.dmi'
anchored = TRUE
can_buckle = TRUE
buckle_lying = 90
@@ -24,6 +25,8 @@
var/build_stack_amount = 2
/// Mobs standing on it are nudged up by this amount. Also used to align the person back when buckled to it after init.
var/elevation = 8
+ /// If this bed can be deconstructed using a wrench
+ var/can_deconstruct = TRUE
/obj/structure/bed/Initialize(mapload)
. = ..()
@@ -34,7 +37,8 @@
/obj/structure/bed/examine(mob/user)
. = ..()
- . += span_notice("It's held together by a couple of bolts.")
+ if (can_deconstruct)
+ . += span_notice("It's held together by a couple of bolts.")
/obj/structure/bed/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
if(held_item)
@@ -56,6 +60,8 @@
return attack_hand(user, modifiers)
/obj/structure/bed/wrench_act_secondary(mob/living/user, obj/item/weapon)
+ if (!can_deconstruct)
+ return NONE
..()
weapon.play_tool_sound(src)
deconstruct(disassembled = TRUE)
diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm
index 1783d4b236a63..b36790ec7846b 100644
--- a/code/game/objects/structures/beds_chairs/chair.dm
+++ b/code/game/objects/structures/beds_chairs/chair.dm
@@ -1,7 +1,8 @@
/obj/structure/chair
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "chair"
desc = "You sit in this. Either by will or force."
- icon = 'icons/obj/chairs.dmi'
+ icon = 'icons/obj/structures/chairs.dmi'
icon_state = "chair"
anchored = TRUE
can_buckle = TRUE
@@ -66,7 +67,7 @@
if(!user.temporarilyRemoveItemFromInventory(input_shock_kit))
return
if(!overlays_from_child_procs || overlays_from_child_procs.len == 0)
- var/image/echair_over_overlay = image('icons/obj/chairs.dmi', loc, "echair_over")
+ var/image/echair_over_overlay = image('icons/obj/structures/chairs.dmi', loc, "echair_over")
AddComponent(/datum/component/electrified_buckle, (SHOCK_REQUIREMENT_ITEM | SHOCK_REQUIREMENT_LIVE_CABLE | SHOCK_REQUIREMENT_SIGNAL_RECEIVED_TOGGLE), input_shock_kit, list(echair_over_overlay), FALSE)
else
AddComponent(/datum/component/electrified_buckle, (SHOCK_REQUIREMENT_ITEM | SHOCK_REQUIREMENT_LIVE_CABLE | SHOCK_REQUIREMENT_SIGNAL_RECEIVED_TOGGLE), input_shock_kit, overlays_from_child_procs, FALSE)
@@ -158,14 +159,6 @@
gen_armrest()
return ..()
-/obj/structure/chair/comfy/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
- if(same_z_layer)
- return ..()
- cut_overlay(armrest)
- QDEL_NULL(armrest)
- gen_armrest()
- return ..()
-
/obj/structure/chair/comfy/proc/gen_armrest()
armrest = GetArmrest()
armrest.layer = ABOVE_MOB_LAYER
@@ -215,7 +208,7 @@
/obj/structure/chair/comfy/shuttle/electrify_self(obj/item/assembly/shock_kit/input_shock_kit, mob/user, list/overlays_from_child_procs)
if(!overlays_from_child_procs)
- overlays_from_child_procs = list(image('icons/obj/chairs.dmi', loc, "echair_over", pixel_x = -1))
+ overlays_from_child_procs = list(image('icons/obj/structures/chairs.dmi', loc, "echair_over", pixel_x = -1))
. = ..()
/obj/structure/chair/comfy/shuttle/tactical
@@ -239,7 +232,7 @@
/obj/structure/chair/office/electrify_self(obj/item/assembly/shock_kit/input_shock_kit, mob/user, list/overlays_from_child_procs)
if(!overlays_from_child_procs)
- overlays_from_child_procs = list(image('icons/obj/chairs.dmi', loc, "echair_over", pixel_x = -1))
+ overlays_from_child_procs = list(image('icons/obj/structures/chairs.dmi', loc, "echair_over", pixel_x = -1))
. = ..()
/obj/structure/chair/office/tactical
@@ -258,21 +251,22 @@
buildstackamount = 1
item_chair = /obj/item/chair/stool
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool, 0)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/chair/stool)
/obj/structure/chair/stool/narsie_act()
return
/obj/structure/chair/mouse_drop_dragged(atom/over_object, mob/user, src_location, over_location, params)
- if(over_object == user)
- if(!item_chair || has_buckled_mobs())
- return
- user.visible_message(span_notice("[user] grabs \the [src.name]."), span_notice("You grab \the [src.name]."))
- var/obj/item/C = new item_chair(loc)
- C.set_custom_materials(custom_materials)
- TransferComponents(C)
- user.put_in_hands(C)
- qdel(src)
+ if(!isliving(user) || over_object != user)
+ return
+ if(!item_chair || has_buckled_mobs())
+ return
+ user.visible_message(span_notice("[user] grabs \the [src.name]."), span_notice("You grab \the [src.name]."))
+ var/obj/item/C = new item_chair(loc)
+ C.set_custom_materials(custom_materials)
+ TransferComponents(C)
+ user.put_in_hands(C)
+ qdel(src)
/obj/structure/chair/user_buckle_mob(mob/living/M, mob/user, check_loc = TRUE)
return ..()
@@ -282,8 +276,15 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool, 0)
desc = "It has some unsavory stains on it..."
icon_state = "bar"
item_chair = /obj/item/chair/stool/bar
+ can_buckle = TRUE
+
+/obj/structure/chair/stool/bar/post_buckle_mob(mob/living/M)
+ M.pixel_y += 4
+
+/obj/structure/chair/stool/bar/post_unbuckle_mob(mob/living/M)
+ M.pixel_y -= 4
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/chair/stool/bar)
/obj/structure/chair/stool/bamboo
name = "bamboo stool"
@@ -298,7 +299,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0)
/obj/item/chair
name = "chair"
desc = "Bar brawl essential."
- icon = 'icons/obj/chairs.dmi'
+ icon = 'icons/obj/structures/chairs.dmi'
icon_state = "chair_toppled"
inhand_icon_state = "chair"
lefthand_file = 'icons/mob/inhands/items/chairs_lefthand.dmi'
@@ -424,6 +425,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0)
origin_type = /obj/structure/chair/wood/wings
/obj/structure/chair/old
+ SET_BASE_VISUAL_PIXEL(0, 0)
name = "strange chair"
desc = "You sit in this. Either by will or force. Looks REALLY uncomfortable."
icon_state = "chairold"
@@ -518,7 +520,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0)
/obj/item/chair/plastic
name = "folding plastic chair"
desc = "Somehow, you can always find one under the wrestling ring."
- icon = 'icons/obj/chairs.dmi'
+ icon = 'icons/obj/structures/chairs.dmi'
icon_state = "folded_chair"
inhand_icon_state = "folded_chair"
lefthand_file = 'icons/mob/inhands/items/chairs_lefthand.dmi'
diff --git a/code/game/objects/structures/beds_chairs/pew.dm b/code/game/objects/structures/beds_chairs/pew.dm
index 6388247e8c426..31915aba2d0eb 100644
--- a/code/game/objects/structures/beds_chairs/pew.dm
+++ b/code/game/objects/structures/beds_chairs/pew.dm
@@ -1,7 +1,7 @@
/obj/structure/chair/pew
name = "wooden pew"
desc = "Kneel here and pray."
- icon = 'icons/obj/chairs_wide.dmi'
+ icon = 'icons/obj/structures/chairs_wide.dmi'
icon_state = "pewmiddle"
resistance_flags = FLAMMABLE
max_integrity = 70
@@ -22,21 +22,13 @@
gen_armrest()
return ..()
-/obj/structure/chair/pew/left/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
- if(same_z_layer)
- return ..()
- cut_overlay(leftpewarmrest)
- QDEL_NULL(leftpewarmrest)
- gen_armrest()
- return ..()
-
/obj/structure/chair/pew/left/proc/gen_armrest()
leftpewarmrest = GetLeftPewArmrest()
leftpewarmrest.layer = ABOVE_MOB_LAYER
update_leftpewarmrest()
/obj/structure/chair/pew/left/proc/GetLeftPewArmrest()
- return mutable_appearance('icons/obj/chairs_wide.dmi', "pewend_left_armrest")
+ return mutable_appearance('icons/obj/structures/chairs_wide.dmi', "pewend_left_armrest")
/obj/structure/chair/pew/left/Destroy()
QDEL_NULL(leftpewarmrest)
@@ -65,19 +57,13 @@
gen_armrest()
return ..()
-/obj/structure/chair/pew/right/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
- cut_overlay(rightpewarmrest)
- QDEL_NULL(rightpewarmrest)
- gen_armrest()
- return ..()
-
/obj/structure/chair/pew/right/proc/gen_armrest()
rightpewarmrest = GetRightPewArmrest()
rightpewarmrest.layer = ABOVE_MOB_LAYER
update_rightpewarmrest()
/obj/structure/chair/pew/right/proc/GetRightPewArmrest()
- return mutable_appearance('icons/obj/chairs_wide.dmi', "pewend_right_armrest")
+ return mutable_appearance('icons/obj/structures/chairs_wide.dmi', "pewend_right_armrest")
/obj/structure/chair/pew/right/Destroy()
QDEL_NULL(rightpewarmrest)
diff --git a/code/game/objects/structures/beds_chairs/sofa.dm b/code/game/objects/structures/beds_chairs/sofa.dm
index bf9a221929b67..6701529eeb3cc 100644
--- a/code/game/objects/structures/beds_chairs/sofa.dm
+++ b/code/game/objects/structures/beds_chairs/sofa.dm
@@ -16,7 +16,7 @@ path/corner/color_name {\
/obj/structure/chair/sofa
name = "old ratty sofa"
icon_state = "error"
- icon = 'icons/obj/chairs_wide.dmi'
+ icon = 'icons/obj/structures/chairs_wide.dmi'
buildstackamount = 1
item_chair = null
var/mutable_appearance/armrest
@@ -26,21 +26,13 @@ path/corner/color_name {\
gen_armrest()
AddElement(/datum/element/soft_landing)
-/obj/structure/chair/sofa/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
- if(same_z_layer)
- return ..()
- cut_overlay(armrest)
- QDEL_NULL(armrest)
- gen_armrest()
- return ..()
-
/obj/structure/chair/sofa/proc/gen_armrest()
armrest = mutable_appearance(initial(icon), "[icon_state]_armrest", ABOVE_MOB_LAYER)
update_armrest()
/obj/structure/chair/sofa/electrify_self(obj/item/assembly/shock_kit/input_shock_kit, mob/user, list/overlays_from_child_procs)
if(!overlays_from_child_procs)
- overlays_from_child_procs = list(image('icons/obj/chairs.dmi', loc, "echair_over", pixel_x = -1))
+ overlays_from_child_procs = list(image('icons/obj/structures/chairs.dmi', loc, "echair_over", pixel_x = -1))
. = ..()
/obj/structure/chair/sofa/post_buckle_mob(mob/living/M)
diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm
index a843c3e3e4bff..a8faf485478d7 100644
--- a/code/game/objects/structures/bedsheet_bin.dm
+++ b/code/game/objects/structures/bedsheet_bin.dm
@@ -38,12 +38,16 @@ LINEN BINS
/obj/item/bedsheet/Initialize(mapload)
. = ..()
AddComponent(/datum/component/surgery_initiator)
- AddElement(/datum/element/bed_tuckable, mapload, 0, 0, 0)
+ AddElement(/datum/element/bed_tuckable, mapload, 0, 12, 0)
if(bedsheet_type == BEDSHEET_DOUBLE)
stack_amount *= 2
dying_key = DYE_REGISTRY_DOUBLE_BEDSHEET
register_context()
register_item_context()
+ for(var/stuff as anything in loc)
+ if(istype(stuff, /obj/structure/bed))
+ pixel_y = DEPTH_OFFSET
+ return
/obj/item/bedsheet/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
if(istype(held_item) && (held_item.tool_behaviour == TOOL_WIRECUTTER || held_item.get_sharpness()))
@@ -69,6 +73,7 @@ LINEN BINS
return ITEM_INTERACT_BLOCKING
forceMove(get_turf(to_cover))
+ pixel_z = to_cover.pixel_z
balloon_alert(user, "covered")
coverup(to_cover)
add_fingerprint(user)
@@ -131,7 +136,6 @@ LINEN BINS
UnregisterSignal(sleeper, COMSIG_QDELETING)
balloon_alert(sleeper, "smoothed sheets")
layer = initial(layer)
- SET_PLANE_IMPLICIT(src, initial(plane))
pixel_z = 0
signal_sleeper = null
@@ -574,7 +578,7 @@ LINEN BINS
/obj/structure/bedsheetbin
name = "linen bin"
desc = "It looks rather cosy."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/fluff/general.dmi'
icon_state = "linenbin-full"
base_icon_state = "linenbin"
anchored = TRUE
diff --git a/code/game/objects/structures/billboard.dm b/code/game/objects/structures/billboard.dm
index c6786dd84e4a1..8f831854c8937 100644
--- a/code/game/objects/structures/billboard.dm
+++ b/code/game/objects/structures/billboard.dm
@@ -3,7 +3,6 @@
desc = "A blank billboard, with space for all kinds of advertising."
icon = 'icons/obj/fluff/billboard.dmi'
icon_state = "billboard_blank"
- plane = ABOVE_GAME_PLANE
max_integrity = 1000
bound_width = 96
bound_height = 32
diff --git a/code/game/objects/structures/broken_flooring.dm b/code/game/objects/structures/broken_flooring.dm
index 9b2e47290802f..1b0953d365389 100644
--- a/code/game/objects/structures/broken_flooring.dm
+++ b/code/game/objects/structures/broken_flooring.dm
@@ -63,13 +63,13 @@
/obj/structure/broken_flooring/plating/always_floorplane
always_floorplane = TRUE
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/broken_flooring/singular, 0)
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/broken_flooring/pile, 0)
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/broken_flooring/side, 0)
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/broken_flooring/corner, 0)
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/broken_flooring/plating, 0)
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/broken_flooring/singular/always_floorplane, 0)
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/broken_flooring/pile/always_floorplane, 0)
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/broken_flooring/side/always_floorplane, 0)
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/broken_flooring/corner/always_floorplane, 0)
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/broken_flooring/plating/always_floorplane, 0)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/broken_flooring/singular)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/broken_flooring/pile)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/broken_flooring/side)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/broken_flooring/corner)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/broken_flooring/plating)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/broken_flooring/singular/always_floorplane)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/broken_flooring/pile/always_floorplane)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/broken_flooring/side/always_floorplane)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/broken_flooring/corner/always_floorplane)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/broken_flooring/plating/always_floorplane)
diff --git a/code/game/objects/structures/cannons/cannon.dm b/code/game/objects/structures/cannons/cannon.dm
index 3408ade8283d0..3a10cc17189f8 100644
--- a/code/game/objects/structures/cannons/cannon.dm
+++ b/code/game/objects/structures/cannons/cannon.dm
@@ -140,5 +140,17 @@
new /obj/item/stack/rods(src.loc)
. = ..()
+///A cannon found from the fishing mystery box.
+/obj/structure/cannon/mystery_box
+ icon_state = "mystery_box_cannon" //east facing sprite for the presented item, it'll be changed back to normal on init
+ dir = EAST
+ anchored = FALSE
+
+/obj/structure/cannon/mystery_box/Initialize(mapload)
+ . = ..()
+ icon_state = "falconet_patina"
+ reagents.add_reagent(/datum/reagent/gunpowder, charge_size)
+ loaded_cannonball = new(src)
+
#undef BAD_FUEL_DAMAGE_TAX
#undef BAD_FUEL_EXPLODE_PROBABILTY
diff --git a/code/game/objects/structures/cat_house.dm b/code/game/objects/structures/cat_house.dm
index bfaa464ff6d09..5283d77c6deac 100644
--- a/code/game/objects/structures/cat_house.dm
+++ b/code/game/objects/structures/cat_house.dm
@@ -1,4 +1,5 @@
/obj/structure/cat_house
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "cat house"
desc = "Cozy home for cats."
icon = 'icons/mob/simple/pets.dmi'
diff --git a/code/game/objects/structures/construction_console/construction_console.dm b/code/game/objects/structures/construction_console/construction_console.dm
index d3ae2984e3170..fabb8c8068e61 100644
--- a/code/game/objects/structures/construction_console/construction_console.dm
+++ b/code/game/objects/structures/construction_console/construction_console.dm
@@ -99,7 +99,7 @@
name = "construction holo-drone"
//Allows any curious crew to watch the base after it leaves. (This is safe as the base cannot be modified once it leaves)
move_on_shuttle = TRUE
- icon = 'icons/obj/mining.dmi'
+ icon = 'icons/obj/mining_zones/equipment.dmi'
icon_state = "construction_drone"
invisibility = INVISIBILITY_MAXIMUM
///Reference to the camera console controlling this drone
@@ -119,7 +119,7 @@
return ..()
/mob/camera/ai_eye/remote/base_construction/relaymove(mob/living/user, direction)
- //This camera eye is visible, and as such needs to keep it's dir updated
+ //This camera eye is visible, and as such needs to keep its dir updated
dir = direction
return ..()
diff --git a/code/game/objects/structures/containers.dm b/code/game/objects/structures/containers.dm
index bf56f7850f82d..87e6e4d5010a3 100644
--- a/code/game/objects/structures/containers.dm
+++ b/code/game/objects/structures/containers.dm
@@ -9,7 +9,6 @@
density = TRUE
anchored = TRUE
layer = ABOVE_ALL_MOB_LAYER
- plane = ABOVE_GAME_PLANE
/obj/structure/shipping_container/Initialize(mapload)
. = ..()
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index 4484e3c512ef3..9a872b27b79a4 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -5,6 +5,7 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
/obj/structure/closet
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "closet"
desc = "It's a basic storage unit."
icon = 'icons/obj/storage/closet.dmi'
@@ -457,6 +458,8 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
var/atom/L = drop_location()
for(var/atom/movable/AM in src)
AM.forceMove(L)
+ AM.pixel_w = pixel_w
+ AM.pixel_z = pixel_z
if(throwing) // you keep some momentum when getting out of a thrown closet
step(AM, dir)
if(throwing)
@@ -1193,6 +1196,8 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets)
if(!toggle())
return
if(was_opened)
+ if (!target.Move(get_turf(src), get_dir(target, src)))
+ return
target.forceMove(src)
else
target.Knockdown(SHOVE_KNOCKDOWN_SOLID)
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index baf239284ac2f..7b64b6569d6cc 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -62,6 +62,7 @@
)
if(paint_jobs)
paint_jobs = crate_paint_jobs
+ AddComponent(/datum/component/soapbox)
/obj/structure/closet/crate/Destroy()
QDEL_NULL(manifest)
diff --git a/code/game/objects/structures/crates_lockers/crates/secure.dm b/code/game/objects/structures/crates_lockers/crates/secure.dm
index 81f7b97a3c159..1a102fdb512fd 100644
--- a/code/game/objects/structures/crates_lockers/crates/secure.dm
+++ b/code/game/objects/structures/crates_lockers/crates/secure.dm
@@ -122,6 +122,28 @@
icon_state = "robo_secure"
base_icon_state = "robo_secure"
+/obj/structure/closet/crate/secure/trashcart
+ desc = "A heavy, metal trashcart with wheels. It has an electronic lock on it."
+ name = "secure trash cart"
+ max_integrity = 250
+ damage_deflection = 10
+ icon_state = "securetrashcart"
+ base_icon_state = "securetrashcart"
+ paint_jobs = null
+ req_access = list(ACCESS_JANITOR)
+
+/obj/structure/closet/crate/secure/trashcart/filled
+
+/obj/structure/closet/crate/secure/trashcart/filled/PopulateContents()
+ . = ..()
+ for(var/i in 1 to rand(8,12))
+ new /obj/effect/spawner/random/trash/deluxe_garbage(src)
+ if(prob(35))
+ new /obj/effect/spawner/random/trash/garbage(src)
+ for(var/i in 1 to rand(4,6))
+ if(prob(30))
+ new /obj/item/storage/bag/trash/filled(src)
+
/obj/structure/closet/crate/secure/owned
name = "private crate"
desc = "A crate cover designed to only open for who purchased its contents."
diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm
index aead6fafb017c..1282864aa3381 100644
--- a/code/game/objects/structures/curtains.dm
+++ b/code/game/objects/structures/curtains.dm
@@ -4,7 +4,7 @@
/obj/structure/curtain
name = "curtain"
desc = "Contains less than 1% mercury."
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "bathroom-open"
color = "#ACD1E9" //Default color, didn't bother hardcoding other colors, mappers can and should easily change it.
alpha = 200 //Mappers can also just set this to 255 if they want curtains that can't be seen through
@@ -146,7 +146,7 @@
set_opacity(TRUE)
/obj/structure/curtain/cloth/fancy/mechanical/attack_hand(mob/user, list/modifiers)
- return
+ return
/obj/structure/curtain/cloth/fancy/mechanical/start_closed
icon_state = "cur_fancy-closed"
diff --git a/code/game/objects/structures/deployable_turret.dm b/code/game/objects/structures/deployable_turret.dm
index f24df392dedef..2b1a90500ccf8 100644
--- a/code/game/objects/structures/deployable_turret.dm
+++ b/code/game/objects/structures/deployable_turret.dm
@@ -213,7 +213,7 @@
/obj/machinery/deployable_turret/hmg
name = "heavy machine gun turret"
- desc = "A heavy caliber machine gun commonly used by Nanotrasen forces, famed for it's ability to give people on the receiving end more holes than normal."
+ desc = "A heavy caliber machine gun commonly used by Nanotrasen forces, famed for its ability to give people on the receiving end more holes than normal."
icon_state = "hmg"
max_integrity = 250
projectile_type = /obj/projectile/bullet/manned_turret/hmg
diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm
index 012e8e8a544e9..6577e2e667785 100644
--- a/code/game/objects/structures/displaycase.dm
+++ b/code/game/objects/structures/displaycase.dm
@@ -1,6 +1,7 @@
/obj/structure/displaycase
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "display case"
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/storage/storage.dmi'
icon_state = "glassbox"
desc = "A display case for prized possessions."
density = TRUE
@@ -222,7 +223,7 @@
/obj/structure/displaycase_chassis
name = "display case chassis"
desc = "The wooden base of a display case."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/storage/storage.dmi'
icon_state = "glassbox_chassis"
resistance_flags = FLAMMABLE
anchored = TRUE
@@ -411,7 +412,7 @@
data["showpiece_icon"] = icon2base64(getFlatIcon(showpiece, no_anim=TRUE))
return data
-/obj/structure/displaycase/trophy/ui_act(action, params)
+/obj/structure/displaycase/trophy/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -526,7 +527,7 @@
data["product_icon"] = showpiece ? icon2base64(getFlatIcon(showpiece, no_anim=TRUE)) : null
return data
-/obj/structure/displaycase/forsale/ui_act(action, params)
+/obj/structure/displaycase/forsale/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm
index 9493a7c941407..9b54afa10f93e 100644
--- a/code/game/objects/structures/door_assembly.dm
+++ b/code/game/objects/structures/door_assembly.dm
@@ -6,16 +6,17 @@
anchored = FALSE
density = TRUE
max_integrity = 200
+ greyscale_config = /datum/greyscale_config/airlocks/custom
/// Airlock's current construction state
var/state = AIRLOCK_ASSEMBLY_NEEDS_WIRES
- var/base_name = "Airlock"
+ var/base_name = null
var/created_name = null
var/mineral = null
var/obj/item/electronics/airlock/electronics = null
/// Do we perform the extra checks required for multi-tile (large) airlocks
var/multi_tile = FALSE
/// The type path of the airlock once completed (solid version)
- var/airlock_type = /obj/machinery/door/airlock
+ var/obj/machinery/door/airlock/airlock_type = /obj/machinery/door/airlock
/// The type path of the airlock once completed (glass version)
var/glass_type = /obj/machinery/door/airlock/glass
/// FALSE = glass can be installed. TRUE = glass is already installed.
@@ -35,9 +36,7 @@
/obj/structure/door_assembly/multi_tile
name = "large airlock assembly"
- icon = 'icons/obj/doors/airlocks/multi_tile/public/glass.dmi'
- overlays_file = 'icons/obj/doors/airlocks/multi_tile/public/overlays.dmi'
- base_name = "large airlock"
+ icon = /obj/machinery/door/airlock/multi_tile/public/glass::icon
glass_type = /obj/machinery/door/airlock/multi_tile/public/glass
airlock_type = /obj/machinery/door/airlock/multi_tile/public/glass
dir = EAST
@@ -47,6 +46,8 @@
material_amt = 8
/obj/structure/door_assembly/Initialize(mapload)
+ base_name = base_name || initial(airlock_type.name) || "Airlock"
+ overlays_file = initial(airlock_type.overlays_file)
. = ..()
update_appearance()
update_name()
@@ -325,14 +326,6 @@
qdel(src)
return door
-/obj/structure/door_assembly/update_overlays()
- . = ..()
- if(!glass)
- . += get_airlock_overlay("fill_construction", icon, src, TRUE)
- else
- . += get_airlock_overlay("glass_construction", overlays_file, src, TRUE)
- . += get_airlock_overlay("panel_c[state+1]", overlays_file, src, TRUE)
-
/obj/structure/door_assembly/update_name()
name = ""
switch(state)
@@ -384,7 +377,7 @@
return FALSE
/obj/structure/door_assembly/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- if(rcd_data["[RCD_DESIGN_MODE]"] == RCD_DECONSTRUCT)
+ if(rcd_data[RCD_DESIGN_MODE] == RCD_DECONSTRUCT)
qdel(src)
return TRUE
return FALSE
diff --git a/code/game/objects/structures/door_assembly_types.dm b/code/game/objects/structures/door_assembly_types.dm
index d62fb1bec7676..febdff708689a 100644
--- a/code/game/objects/structures/door_assembly_types.dm
+++ b/code/game/objects/structures/door_assembly_types.dm
@@ -1,123 +1,102 @@
/obj/structure/door_assembly/door_assembly_public
name = "public airlock assembly"
- icon = 'icons/obj/doors/airlocks/public/glass.dmi'
- overlays_file = 'icons/obj/doors/airlocks/public/overlays.dmi'
+ icon = /obj/machinery/door/airlock/public::icon
glass_type = /obj/machinery/door/airlock/public/glass
airlock_type = /obj/machinery/door/airlock/public
/obj/structure/door_assembly/door_assembly_com
name = "command airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/command.dmi'
- base_name = "command airlock"
+ icon = /obj/machinery/door/airlock/command::icon
glass_type = /obj/machinery/door/airlock/command/glass
airlock_type = /obj/machinery/door/airlock/command
/obj/structure/door_assembly/door_assembly_sec
name = "security airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/security.dmi'
- base_name = "security airlock"
+ icon = /obj/machinery/door/airlock/security::icon
glass_type = /obj/machinery/door/airlock/security/glass
airlock_type = /obj/machinery/door/airlock/security
/obj/structure/door_assembly/door_assembly_eng
name = "engineering airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/engineering.dmi'
- base_name = "engineering airlock"
+ icon = /obj/machinery/door/airlock/engineering::icon
glass_type = /obj/machinery/door/airlock/engineering/glass
airlock_type = /obj/machinery/door/airlock/engineering
/obj/structure/door_assembly/door_assembly_min
name = "mining airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/mining.dmi'
- base_name = "mining airlock"
+ icon = /obj/machinery/door/airlock/mining::icon
glass_type = /obj/machinery/door/airlock/mining/glass
airlock_type = /obj/machinery/door/airlock/mining
/obj/structure/door_assembly/door_assembly_atmo
name = "atmospherics airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/atmos.dmi'
- base_name = "atmospherics airlock"
+ icon = /obj/machinery/door/airlock/atmos::icon
glass_type = /obj/machinery/door/airlock/atmos/glass
airlock_type = /obj/machinery/door/airlock/atmos
/obj/structure/door_assembly/door_assembly_research
name = "research airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/research.dmi'
- base_name = "research airlock"
+ icon = /obj/machinery/door/airlock/research::icon
glass_type = /obj/machinery/door/airlock/research/glass
airlock_type = /obj/machinery/door/airlock/research
/obj/structure/door_assembly/door_assembly_science
name = "science airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/science.dmi'
- base_name = "science airlock"
+ icon = /obj/machinery/door/airlock/science::icon
glass_type = /obj/machinery/door/airlock/science/glass
airlock_type = /obj/machinery/door/airlock/science
/obj/structure/door_assembly/door_assembly_med
name = "medical airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/medical.dmi'
- base_name = "medical airlock"
+ icon = /obj/machinery/door/airlock/medical::icon
glass_type = /obj/machinery/door/airlock/medical/glass
airlock_type = /obj/machinery/door/airlock/medical
/obj/structure/door_assembly/door_assembly_hydro
name = "hydroponics airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/hydroponics.dmi'
- base_name = "hydroponics airlock"
+ icon = /obj/machinery/door/airlock/hydroponics::icon
glass_type = /obj/machinery/door/airlock/hydroponics/glass
airlock_type = /obj/machinery/door/airlock/hydroponics
/obj/structure/door_assembly/door_assembly_mai
name = "maintenance airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/maintenance.dmi'
- base_name = "maintenance airlock"
+ icon = /obj/machinery/door/airlock/maintenance::icon
glass_type = /obj/machinery/door/airlock/maintenance/glass
airlock_type = /obj/machinery/door/airlock/maintenance
/obj/structure/door_assembly/door_assembly_extmai
name = "external maintenance airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/maintenanceexternal.dmi'
- base_name = "external maintenance airlock"
+ icon = /obj/machinery/door/airlock/maintenance/external::icon
glass_type = /obj/machinery/door/airlock/maintenance/external/glass
airlock_type = /obj/machinery/door/airlock/maintenance/external
/obj/structure/door_assembly/door_assembly_ext
name = "external airlock assembly"
- icon = 'icons/obj/doors/airlocks/external/external.dmi'
- base_name = "external airlock"
- overlays_file = 'icons/obj/doors/airlocks/external/overlays.dmi'
+ icon = /obj/machinery/door/airlock/external::icon
glass_type = /obj/machinery/door/airlock/external/glass
airlock_type = /obj/machinery/door/airlock/external
/obj/structure/door_assembly/door_assembly_fre
name = "freezer airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/freezer.dmi'
- base_name = "freezer airlock"
+ icon = /obj/machinery/door/airlock/freezer::icon
airlock_type = /obj/machinery/door/airlock/freezer
noglass = TRUE
/obj/structure/door_assembly/door_assembly_hatch
name = "airtight hatch assembly"
- icon = 'icons/obj/doors/airlocks/hatch/centcom.dmi'
- base_name = "airtight hatch"
- overlays_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi'
+ icon = /obj/machinery/door/airlock/hatch::icon
airlock_type = /obj/machinery/door/airlock/hatch
noglass = TRUE
/obj/structure/door_assembly/door_assembly_mhatch
name = "maintenance hatch assembly"
- icon = 'icons/obj/doors/airlocks/hatch/maintenance.dmi'
- base_name = "maintenance hatch"
- overlays_file = 'icons/obj/doors/airlocks/hatch/overlays.dmi'
+ icon = /obj/machinery/door/airlock/maintenance_hatch::icon
airlock_type = /obj/machinery/door/airlock/maintenance_hatch
noglass = TRUE
/obj/structure/door_assembly/door_assembly_highsecurity
name = "high security airlock assembly"
- icon = 'icons/obj/doors/airlocks/highsec/highsec.dmi'
- base_name = "high security airlock"
- overlays_file = 'icons/obj/doors/airlocks/highsec/overlays.dmi'
+ icon = /obj/machinery/door/airlock/highsecurity::icon
airlock_type = /obj/machinery/door/airlock/highsecurity
noglass = TRUE
material_type = /obj/item/stack/sheet/plasteel
@@ -125,9 +104,7 @@
/obj/structure/door_assembly/door_assembly_vault
name = "vault door assembly"
- icon = 'icons/obj/doors/airlocks/vault/vault.dmi'
- base_name = "vault door"
- overlays_file = 'icons/obj/doors/airlocks/vault/overlays.dmi'
+ icon = /obj/machinery/door/airlock/vault::icon
airlock_type = /obj/machinery/door/airlock/vault
noglass = TRUE
material_type = /obj/item/stack/sheet/plasteel
@@ -135,81 +112,68 @@
/obj/structure/door_assembly/door_assembly_shuttle
name = "shuttle airlock assembly"
- icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi'
- base_name = "shuttle airlock"
- overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi'
+ icon = /obj/machinery/door/airlock/shuttle::icon
airlock_type = /obj/machinery/door/airlock/shuttle
glass_type = /obj/machinery/door/airlock/shuttle/glass
/obj/structure/door_assembly/door_assembly_cult
name = "cult airlock assembly"
- icon = 'icons/obj/doors/airlocks/cult/runed/cult.dmi'
- base_name = "cult airlock"
- overlays_file = 'icons/obj/doors/airlocks/cult/runed/overlays.dmi'
+ icon = /obj/machinery/door/airlock/cult::icon
airlock_type = /obj/machinery/door/airlock/cult
glass_type = /obj/machinery/door/airlock/cult/glass
/obj/structure/door_assembly/door_assembly_cult/unruned
- icon = 'icons/obj/doors/airlocks/cult/unruned/cult.dmi'
- overlays_file = 'icons/obj/doors/airlocks/cult/unruned/overlays.dmi'
+ icon = /obj/machinery/door/airlock/cult/unruned::icon
airlock_type = /obj/machinery/door/airlock/cult/unruned
glass_type = /obj/machinery/door/airlock/cult/unruned/glass
/obj/structure/door_assembly/door_assembly_viro
name = "virology airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/virology.dmi'
- base_name = "virology airlock"
+ icon = /obj/machinery/door/airlock/virology::icon
glass_type = /obj/machinery/door/airlock/virology/glass
airlock_type = /obj/machinery/door/airlock/virology
/obj/structure/door_assembly/door_assembly_centcom
- icon = 'icons/obj/doors/airlocks/centcom/centcom.dmi'
- overlays_file = 'icons/obj/doors/airlocks/centcom/overlays.dmi'
+ icon = /obj/machinery/door/airlock/centcom::icon
airlock_type = /obj/machinery/door/airlock/centcom
noglass = TRUE
/obj/structure/door_assembly/door_assembly_grunge
- icon = 'icons/obj/doors/airlocks/centcom/centcom.dmi'
- overlays_file = 'icons/obj/doors/airlocks/centcom/overlays.dmi'
+ icon = /obj/machinery/door/airlock/grunge::icon
airlock_type = /obj/machinery/door/airlock/grunge
noglass = TRUE
/obj/structure/door_assembly/door_assembly_gold
name = "gold airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/gold.dmi'
- base_name = "gold airlock"
+ icon = /obj/machinery/door/airlock/gold::icon
airlock_type = /obj/machinery/door/airlock/gold
mineral = "gold"
glass_type = /obj/machinery/door/airlock/gold/glass
/obj/structure/door_assembly/door_assembly_silver
name = "silver airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/silver.dmi'
- base_name = "silver airlock"
+ icon = /obj/machinery/door/airlock/silver::icon
airlock_type = /obj/machinery/door/airlock/silver
mineral = "silver"
glass_type = /obj/machinery/door/airlock/silver/glass
/obj/structure/door_assembly/door_assembly_diamond
name = "diamond airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/diamond.dmi'
- base_name = "diamond airlock"
+ icon = /obj/machinery/door/airlock/diamond::icon
airlock_type = /obj/machinery/door/airlock/diamond
mineral = "diamond"
glass_type = /obj/machinery/door/airlock/diamond/glass
/obj/structure/door_assembly/door_assembly_uranium
name = "uranium airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/uranium.dmi'
- base_name = "uranium airlock"
+ icon = /obj/machinery/door/airlock/uranium::icon
airlock_type = /obj/machinery/door/airlock/uranium
mineral = "uranium"
glass_type = /obj/machinery/door/airlock/uranium/glass
/obj/structure/door_assembly/door_assembly_plasma
name = "plasma airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/plasma.dmi'
- base_name = "plasma airlock"
+ icon = /obj/machinery/door/airlock/plasma::icon
airlock_type = /obj/machinery/door/airlock/plasma
mineral = "plasma"
glass_type = /obj/machinery/door/airlock/plasma/glass
@@ -217,41 +181,35 @@
/obj/structure/door_assembly/door_assembly_bananium
name = "bananium airlock assembly"
desc = "Honk."
- icon = 'icons/obj/doors/airlocks/station/bananium.dmi'
- base_name = "bananium airlock"
+ icon = /obj/machinery/door/airlock/bananium::icon
airlock_type = /obj/machinery/door/airlock/bananium
mineral = "bananium"
glass_type = /obj/machinery/door/airlock/bananium/glass
/obj/structure/door_assembly/door_assembly_sandstone
name = "sandstone airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/sandstone.dmi'
- base_name = "sandstone airlock"
+ icon = /obj/machinery/door/airlock/sandstone::icon
airlock_type = /obj/machinery/door/airlock/sandstone
mineral = "sandstone"
glass_type = /obj/machinery/door/airlock/sandstone/glass
/obj/structure/door_assembly/door_assembly_titanium
name = "titanium airlock assembly"
- icon = 'icons/obj/doors/airlocks/shuttle/shuttle.dmi'
- base_name = "shuttle airlock"
- overlays_file = 'icons/obj/doors/airlocks/shuttle/overlays.dmi'
+ icon = /obj/machinery/door/airlock/titanium::icon
glass_type = /obj/machinery/door/airlock/titanium/glass
airlock_type = /obj/machinery/door/airlock/titanium
mineral = "titanium"
/obj/structure/door_assembly/door_assembly_wood
name = "wooden airlock assembly"
- icon = 'icons/obj/doors/airlocks/station/wood.dmi'
- base_name = "wooden airlock"
+ icon = /obj/machinery/door/airlock/wood::icon
airlock_type = /obj/machinery/door/airlock/wood
mineral = "wood"
glass_type = /obj/machinery/door/airlock/wood/glass
/obj/structure/door_assembly/door_assembly_bronze
name = "bronze airlock assembly"
- icon = 'icons/obj/doors/airlocks/clockwork/pinion_airlock.dmi'
- base_name = "bronze airlock"
+ icon = /obj/machinery/door/airlock/bronze::icon
airlock_type = /obj/machinery/door/airlock/bronze
noglass = TRUE
material_type = /obj/item/stack/sheet/bronze
@@ -259,21 +217,21 @@
/obj/structure/door_assembly/door_assembly_bronze/seethru
airlock_type = /obj/machinery/door/airlock/bronze/seethru
+/obj/structure/door_assembly/multi_tile/door_assembly_public
+ name = "large public airlock assembly"
+ base_name = "large public airlock"
+
/obj/structure/door_assembly/door_assembly_material
name = "airlock assembly"
+ icon = /obj/machinery/door/airlock/material::icon
airlock_type = /obj/machinery/door/airlock/material
glass_type = /obj/machinery/door/airlock/material/glass
- greyscale_config = /datum/greyscale_config/material_airlock
nomineral = TRUE
material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_GREYSCALE | MATERIAL_AFFECT_STATISTICS
-/obj/structure/door_assembly/multi_tile/door_assembly_public
- name = "large public airlock assembly"
- base_name = "large public airlock"
-
/obj/structure/door_assembly/multi_tile/door_assembly_tram
name = "tram door assembly"
- icon = 'icons/obj/doors/airlocks/tram/tram.dmi'
+ icon = /obj/machinery/door/airlock/tram::icon
base_name = "tram door"
overlays_file = 'icons/obj/doors/airlocks/tram/tram-overlays.dmi'
glass_type = /obj/machinery/door/airlock/tram
diff --git a/code/game/objects/structures/dresser.dm b/code/game/objects/structures/dresser.dm
index cf3864b140059..fc5126612d922 100644
--- a/code/game/objects/structures/dresser.dm
+++ b/code/game/objects/structures/dresser.dm
@@ -1,4 +1,5 @@
/obj/structure/dresser
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "dresser"
desc = "A nicely-crafted wooden dresser. It's filled with lots of undies."
icon = 'icons/obj/fluff/general.dmi'
diff --git a/code/game/objects/structures/electricchair.dm b/code/game/objects/structures/electricchair.dm
index 8d7be60a4f9ec..f6b46915c0f59 100644
--- a/code/game/objects/structures/electricchair.dm
+++ b/code/game/objects/structures/electricchair.dm
@@ -9,7 +9,7 @@
/obj/structure/chair/e_chair/Initialize(mapload)
. = ..()
var/obj/item/assembly/shock_kit/stored_kit = new(contents)
- var/image/export_to_component = image('icons/obj/chairs.dmi', loc, "echair_over")
+ var/image/export_to_component = image('icons/obj/structures/chairs.dmi', loc, "echair_over")
AddComponent(/datum/component/electrified_buckle, (SHOCK_REQUIREMENT_ITEM | SHOCK_REQUIREMENT_LIVE_CABLE | SHOCK_REQUIREMENT_SIGNAL_RECEIVED_TOGGLE), stored_kit, list(export_to_component))
/obj/structure/chair/e_chair/attackby(obj/item/W, mob/user, params)
diff --git a/code/game/objects/structures/extinguisher.dm b/code/game/objects/structures/extinguisher.dm
index e78b71f14e963..72188fe6eca1a 100644
--- a/code/game/objects/structures/extinguisher.dm
+++ b/code/game/objects/structures/extinguisher.dm
@@ -1,34 +1,31 @@
/obj/structure/extinguisher_cabinet
- name = "extinguisher cabinet"
- desc = "A small wall mounted cabinet designed to hold a fire extinguisher."
- icon = 'icons/obj/wallmounts.dmi'
- icon_state = "extinguisher_closed"
+ name = "extinguisher rack"
+ desc = "A small wall mounted rack designed to hold a fire extinguisher."
+ icon = 'icons/obj/structures/cabinet.dmi'
+ icon_state = "rack"
anchored = TRUE
density = FALSE
max_integrity = 200
integrity_failure = 0.25
var/obj/item/extinguisher/stored_extinguisher
- var/opened = FALSE
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/extinguisher_cabinet, 29)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/extinguisher_cabinet)
/obj/structure/extinguisher_cabinet/Initialize(mapload, ndir, building)
. = ..()
- if(building)
- opened = TRUE
- else
+ if(!building)
stored_extinguisher = new /obj/item/extinguisher(src)
update_appearance(UPDATE_ICON)
register_context()
find_and_hang_on_wall()
+ AddComponent(/datum/component/examine_balloon, pixel_y_offset = 36)
/obj/structure/extinguisher_cabinet/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
if(isnull(held_item))
- context[SCREENTIP_CONTEXT_RMB] = opened ? "Close" : "Open"
if(stored_extinguisher)
- context[SCREENTIP_CONTEXT_LMB] = "Take extinguisher" //Yes, this shows whether or not it's open! Extinguishers are taken immediately on LMB click when closed
+ context[SCREENTIP_CONTEXT_LMB] = "Take extinguisher"
return CONTEXTUAL_SCREENTIP_SET
if(stored_extinguisher)
@@ -37,10 +34,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/extinguisher_cabinet, 29)
if(held_item.tool_behaviour == TOOL_WRENCH)
context[SCREENTIP_CONTEXT_LMB] = "Disassemble cabinet"
return CONTEXTUAL_SCREENTIP_SET
- if(istype(held_item, /obj/item/extinguisher) && opened)
+ if(istype(held_item, /obj/item/extinguisher))
context[SCREENTIP_CONTEXT_LMB] = "Insert extinguisher"
return CONTEXTUAL_SCREENTIP_SET
-
return .
/obj/structure/extinguisher_cabinet/Destroy()
@@ -67,18 +63,18 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/extinguisher_cabinet, 29)
/obj/structure/extinguisher_cabinet/attackby(obj/item/used_item, mob/living/user, params)
if(used_item.tool_behaviour == TOOL_WRENCH && !stored_extinguisher)
- user.balloon_alert(user, "deconstructing cabinet...")
+ user.balloon_alert(user, "deconstructing rack...")
used_item.play_tool_sound(src)
if(used_item.use_tool(src, user, 60))
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
- user.balloon_alert(user, "cabinet deconstructed")
+ user.balloon_alert(user, "rack deconstructed")
deconstruct(TRUE)
return
if(iscyborg(user) || isalien(user))
return
if(istype(used_item, /obj/item/extinguisher))
- if(!stored_extinguisher && opened)
+ if(!stored_extinguisher)
if(!user.transferItemToLoc(used_item, src))
return
stored_extinguisher = used_item
@@ -86,9 +82,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/extinguisher_cabinet, 29)
update_appearance(UPDATE_ICON)
return TRUE
else
- toggle_cabinet(user)
- else if(!user.combat_mode)
- toggle_cabinet(user)
+ return
else
return ..()
@@ -102,18 +96,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/extinguisher_cabinet, 29)
if(stored_extinguisher)
user.put_in_hands(stored_extinguisher)
user.balloon_alert(user, "extinguisher removed")
- if(!opened)
- opened = 1
- playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3)
- update_appearance(UPDATE_ICON)
- else
- toggle_cabinet(user)
-
-/obj/structure/extinguisher_cabinet/attack_hand_secondary(mob/living/user)
- if(!user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS|ALLOW_RESTING))
- return ..()
- toggle_cabinet(user)
- return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/structure/extinguisher_cabinet/attack_tk(mob/user)
. = COMPONENT_CANCEL_ATTACK_CHAIN
@@ -121,54 +103,21 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/extinguisher_cabinet, 29)
stored_extinguisher.forceMove(loc)
to_chat(user, span_notice("You telekinetically remove [stored_extinguisher] from [src]."))
stored_extinguisher = null
- opened = TRUE
- playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3)
update_appearance(UPDATE_ICON)
return
- toggle_cabinet(user)
-
/obj/structure/extinguisher_cabinet/attack_paw(mob/user, list/modifiers)
return attack_hand(user, modifiers)
-/obj/structure/extinguisher_cabinet/proc/toggle_cabinet(mob/user)
- if(opened && broken)
- user.balloon_alert(user, "it's broken!")
- else
- playsound(loc, 'sound/machines/click.ogg', 15, TRUE, -3)
- opened = !opened
- update_appearance(UPDATE_ICON)
-
-/obj/structure/extinguisher_cabinet/update_icon_state()
- icon_state = "extinguisher"
-
- if(isnull(stored_extinguisher))
- icon_state += ""
- else if(istype(stored_extinguisher, /obj/item/extinguisher/mini))
- icon_state += "_mini"
- else if(istype(stored_extinguisher, /obj/item/extinguisher/advanced))
- icon_state += "_advanced"
- else if(istype(stored_extinguisher, /obj/item/extinguisher/crafted))
- icon_state += "_crafted"
- else if(istype(stored_extinguisher, /obj/item/extinguisher))
- icon_state += "_default"
-
- if(!opened)
- icon_state += "_closed"
-
- return ..()
-
/obj/structure/extinguisher_cabinet/atom_break(damage_flag)
. = ..()
if(!broken)
broken = 1
- opened = 1
if(stored_extinguisher)
stored_extinguisher.forceMove(loc)
stored_extinguisher = null
update_appearance(UPDATE_ICON)
-
/obj/structure/extinguisher_cabinet/atom_deconstruct(disassembled = TRUE)
if(disassembled)
new /obj/item/wallframe/extinguisher_cabinet(loc)
@@ -178,10 +127,14 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/extinguisher_cabinet, 29)
stored_extinguisher.forceMove(loc)
stored_extinguisher = null
+/obj/structure/extinguisher_cabinet/update_overlays()
+ . = ..()
+ if(stored_extinguisher)
+ . += stored_extinguisher.cabinet_icon_state
+
/obj/item/wallframe/extinguisher_cabinet
- name = "extinguisher cabinet frame"
+ name = "extinguisher rack frame"
desc = "Used for building wall-mounted extinguisher cabinets."
- icon = 'icons/obj/wallmounts.dmi'
- icon_state = "extinguisher_assembly"
+ icon = 'icons/obj/structures/cabinet.dmi'
+ icon_state = "rack"
result_path = /obj/structure/extinguisher_cabinet
- pixel_shift = 29
diff --git a/code/game/objects/structures/fake_stairs.dm b/code/game/objects/structures/fake_stairs.dm
index 1152a8c02dcca..57d492822789a 100644
--- a/code/game/objects/structures/fake_stairs.dm
+++ b/code/game/objects/structures/fake_stairs.dm
@@ -1,7 +1,7 @@
/// Stairs but they are FAKE and dont have any of the Z-changing behavior. DO NOT MAP THESE NEXT TO REAL STAIRS
/obj/structure/fake_stairs
name = "stairs"
- icon = 'icons/obj/stairs.dmi'
+ icon = 'icons/obj/structures/stairs.dmi'
icon_state = "stairs"
anchored = TRUE
move_resist = INFINITY
@@ -9,12 +9,12 @@
layer = ABOVE_OPEN_TURF_LAYER
plane = FLOOR_PLANE //one with the floor
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/fake_stairs, 0)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/fake_stairs)
/obj/structure/fake_stairs/wood
icon_state = "stairs_wood"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/fake_stairs/wood, 0)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/fake_stairs/wood)
/obj/structure/fake_stairs/stone
icon_state = "stairs_stone"
diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm
index 0564223a7c759..ea8922b3c31ac 100644
--- a/code/game/objects/structures/false_walls.dm
+++ b/code/game/objects/structures/false_walls.dm
@@ -1,40 +1,142 @@
/*
* False Walls
*/
+
+/// Stores a list of icon -> icon but with all fullblack pixels fully transparent
+/// I would LOVE to do this with colormatrix bullshit but alpha does NOT cap at 1 on some GPUs for some stupid reason
+GLOBAL_LIST_INIT(falsewall_alpha_icons, generate_transparent_falsewalls())
+
+/proc/generate_transparent_falsewalls()
+ var/list/icon_alphas = list()
+ for(var/obj/structure/falsewall/false_type as anything in typesof(/obj/structure/falsewall))
+ var/icon/make_transparent = initial(false_type.fake_icon)
+ if(icon_alphas[make_transparent])
+ continue
+ var/icon/alpha_icon = icon(make_transparent)
+ // I am SO sorry (makes any full black pixels transparent)
+ alpha_icon.SwapColor("#000000FF", rgb(0, 0, 0, 0))
+ icon_alphas[make_transparent] = alpha_icon
+ return icon_alphas
+
+/// Static mask we use to hide the bits of falsewalls that would be "below" the floor when opening/closing
+/obj/effect/falsewall_mask
+ icon = 'icons/effects/32x48.dmi'
+ icon_state = "white"
+ vis_flags = VIS_INHERIT_ID
+ appearance_flags = parent_type::appearance_flags | KEEP_TOGETHER | RESET_TRANSFORM
+
+/obj/effect/falsewall_mask/Initialize(mapload)
+ . = ..()
+ render_target = "*falsewall_mask"
+
+/// Duplicates our parent falsewall's rendering behavior
+/// But renders walls as overlays instead of with splitvis
+/// In viscontents to make separation of concerns easier
+/obj/effect/falsewall_floating
+ vis_flags = VIS_INHERIT_PLANE|VIS_INHERIT_ID
+ appearance_flags = parent_type::appearance_flags | KEEP_TOGETHER | RESET_ALPHA
+ /// The icon to fake ourselves as
+ var/icon/fake_icon
+ /// If darkness in our sprite is visible or not
+ /// Exists so we can make falsewalls fall "into" the floor
+ /// Potentially breaks art but the usecase is so minimal I will accept it
+ var/opaque_darkness = TRUE
+ var/static/obj/effect/falsewall_mask/trim
+
+/obj/effect/falsewall_floating/proc/set_fake_icon(icon/fake_icon)
+ if(src.fake_icon == fake_icon)
+ return
+ src.fake_icon = fake_icon
+ update_appearance()
+
+/obj/effect/falsewall_floating/proc/set_darkness_opacity(opaque_darkness)
+ if(src.opaque_darkness == opaque_darkness)
+ return
+ src.opaque_darkness = opaque_darkness
+ update_appearance()
+
+/obj/effect/falsewall_floating/set_smoothed_icon_state(new_junction)
+ . = ..()
+ update_appearance()
+
+/// Runs an animation that masks off the bottom of our sprite
+/obj/effect/falsewall_floating/proc/trim_base(delay)
+ if(!trim)
+ trim = new(src)
+ vis_contents += trim
+ add_filter("trim_mask", 1, alpha_mask_filter(y = -40, render_source = trim.render_target, flags = MASK_INVERSE))
+ transition_filter("trim_mask", alpha_mask_filter(y = -16, render_source = trim.render_target, flags = MASK_INVERSE), time = delay)
+
+/// Runs an animation that slowly reveals the bottom of our sprite
+/obj/effect/falsewall_floating/proc/untrim_base(delay)
+ if(!trim)
+ trim = new(src)
+ vis_contents += trim
+ add_filter("trim_mask", 1, alpha_mask_filter(y = -16, render_source = trim.render_target, flags = MASK_INVERSE))
+ transition_filter("trim_mask", alpha_mask_filter(y = -40, render_source = trim.render_target, flags = MASK_INVERSE), time = delay)
+
+/obj/effect/falsewall_floating/update_overlays()
+ . = ..()
+ // If we smooth north then as we open there's gonna be a weird hole left by the lack of blackness from above. this should help? compensate for that.
+ if(smoothing_junction & NORTH_JUNCTION && opaque_darkness)
+ var/mutable_appearance/black_backdrop = mutable_appearance('icons/turf/walls/wall_blackness.dmi', "wall_background")
+ black_backdrop.pixel_z = 16
+ . += black_backdrop
+
+ var/icon/working_fake_icon = opaque_darkness ? fake_icon : GLOB.falsewall_alpha_icons[fake_icon]
+ . += generate_joined_wall(working_fake_icon, smoothing_junction, draw_darkness = opaque_darkness)
+
/obj/structure/falsewall
name = "wall"
desc = "A huge chunk of metal used to separate rooms."
anchored = TRUE
icon = 'icons/turf/walls/false_walls.dmi'
- icon_state = "wall-open"
+ icon_state = "wall"
base_icon_state = "wall"
layer = LOW_OBJ_LAYER
density = TRUE
opacity = TRUE
max_integrity = 100
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_WALLS
can_be_unanchored = FALSE
can_atmos_pass = ATMOS_PASS_DENSITY
rad_insulation = RAD_MEDIUM_INSULATION
material_flags = MATERIAL_EFFECTS
+ // Complex appearance gotta use render targets
+ blocks_emissive = EMISSIVE_BLOCK_UNIQUE
+ /// The object we are using to fake being a wall
+ var/obj/effect/falsewall_floating/visuals
+ /// Our usual smoothing groups
+ var/list/usual_groups
/// The icon this falsewall is faking being. we'll switch out our icon with this when we're in fake mode
- var/fake_icon = 'icons/turf/walls/wall.dmi'
+ var/fake_icon = 'icons/turf/walls/metal_wall.dmi'
var/mineral = /obj/item/stack/sheet/iron
var/mineral_amount = 2
var/walltype = /turf/closed/wall
var/girder_type = /obj/structure/girder/displaced
var/opening = FALSE
-
/obj/structure/falsewall/Initialize(mapload)
. = ..()
+ visuals = new(src)
+ visuals.set_fake_icon(fake_icon)
+ AddElement(/datum/element/split_visibility, fake_icon, color)
var/obj/item/stack/initialized_mineral = new mineral // Okay this kinda sucks.
set_custom_materials(initialized_mineral.mats_per_unit, mineral_amount)
qdel(initialized_mineral)
+ set_opacity(TRUE) // walls cannot be transparent fuck u materials
air_update_turf(TRUE, TRUE)
+/obj/structure/falsewall/Destroy(force)
+ QDEL_NULL(visuals)
+ return ..()
+
+/obj/structure/falsewall/set_smoothed_icon_state(new_junction)
+ . = ..()
+ visuals.set_smoothed_icon_state(new_junction)
+
/obj/structure/falsewall/attack_hand(mob/user, list/modifiers)
if(opening)
return
@@ -42,47 +144,55 @@
if(.)
return
- opening = TRUE
if(!density)
var/srcturf = get_turf(src)
for(var/mob/living/obstacle in srcturf) //Stop people from using this as a shield
- opening = FALSE
return
- update_appearance()
- addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/structure/falsewall, toggle_open)), 0.5 SECONDS)
+ INVOKE_ASYNC(src, PROC_REF(toggle_open))
/obj/structure/falsewall/proc/toggle_open()
- if(!QDELETED(src))
- set_density(!density)
- set_opacity(density)
- opening = FALSE
- update_appearance()
- air_update_turf(TRUE, !density)
-
-/obj/structure/falsewall/update_icon(updates=ALL)//Calling icon_update will refresh the smoothwalls if it's closed, otherwise it will make sure the icon is correct if it's open
- . = ..()
- if(!density || !(updates & UPDATE_SMOOTHING))
- return
-
- if(opening)
- smoothing_flags = NONE
- clear_smooth_overlays()
- else
- smoothing_flags = SMOOTH_BITMASK
- QUEUE_SMOOTH(src)
-
-/obj/structure/falsewall/update_icon_state()
- if(opening)
- icon = initial(icon)
- icon_state = "[base_icon_state]-[density ? "opening" : "closing"]"
- return ..()
+ opening = TRUE
if(density)
- icon = fake_icon
- icon_state = "[base_icon_state]-[smoothing_junction]"
+ open()
else
- icon = initial(icon)
- icon_state = "[base_icon_state]-open"
- return ..()
+ close()
+ opening = FALSE
+
+/obj/structure/falsewall/proc/open()
+ vis_contents += visuals
+ RemoveElement(/datum/element/split_visibility, fake_icon, color)
+ visuals.trim_base(1 SECONDS)
+ animate(src, pixel_z = -24, time = 1 SECONDS)
+ usual_groups = smoothing_groups
+ smoothing_groups = list()
+ QUEUE_SMOOTH_NEIGHBORS(src) // Update any walls around us
+ sleep(0.2 SECONDS)
+ set_opacity(FALSE)
+ sleep(0.8 SECONDS)
+ visuals.set_darkness_opacity(FALSE)
+ set_density(FALSE)
+ air_update_turf(TRUE, TRUE)
+
+/obj/structure/falsewall/proc/close()
+ set_density(TRUE)
+ air_update_turf(TRUE, FALSE)
+ visuals.untrim_base(1 SECONDS)
+ animate(src, pixel_z = 0, time = 1 SECONDS)
+ visuals.set_darkness_opacity(TRUE)
+ sleep(0.3 SECONDS)
+ set_opacity(TRUE)
+ smoothing_groups = usual_groups
+ usual_groups = null
+ QUEUE_SMOOTH_NEIGHBORS(src)
+ sleep(0.7 SECONDS)
+ vis_contents -= visuals
+ AddElement(/datum/element/split_visibility, fake_icon, color)
+
+/obj/structure/falsewall/update_icon(updates=ALL)
+ . = ..()
+ if(!(updates & UPDATE_SMOOTHING))
+ return
+ QUEUE_SMOOTH(src)
/obj/structure/falsewall/proc/ChangeToWall(delete = 1)
var/turf/T = get_turf(src)
@@ -155,7 +265,7 @@
name = "reinforced wall"
desc = "A huge chunk of reinforced metal used to separate rooms."
fake_icon = 'icons/turf/walls/reinforced_wall.dmi'
- icon_state = "reinforced_wall-open"
+ icon_state = "reinforced_wall"
base_icon_state = "reinforced_wall"
walltype = /turf/closed/wall/r_wall
mineral = /obj/item/stack/sheet/plasteel
@@ -178,12 +288,12 @@
name = "uranium wall"
desc = "A wall with uranium plating. This is probably a bad idea."
fake_icon = 'icons/turf/walls/uranium_wall.dmi'
- icon_state = "uranium_wall-open"
+ icon_state = "uranium_wall"
base_icon_state = "uranium_wall"
mineral = /obj/item/stack/sheet/mineral/uranium
walltype = /turf/closed/wall/mineral/uranium
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_URANIUM_WALLS + SMOOTH_GROUP_WALLS
+ smoothing_groups = SMOOTH_GROUP_URANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_URANIUM_WALLS
/// Mutex to prevent infinite recursion when propagating radiation pulses
@@ -229,36 +339,36 @@
name = "gold wall"
desc = "A wall with gold plating. Swag!"
fake_icon = 'icons/turf/walls/gold_wall.dmi'
- icon_state = "gold_wall-open"
+ icon_state = "gold_wall"
base_icon_state = "gold_wall"
mineral = /obj/item/stack/sheet/mineral/gold
walltype = /turf/closed/wall/mineral/gold
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_GOLD_WALLS + SMOOTH_GROUP_WALLS
+ smoothing_groups = SMOOTH_GROUP_GOLD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_GOLD_WALLS
/obj/structure/falsewall/silver
name = "silver wall"
desc = "A wall with silver plating. Shiny."
fake_icon = 'icons/turf/walls/silver_wall.dmi'
- icon_state = "silver_wall-open"
+ icon_state = "silver_wall"
base_icon_state = "silver_wall"
mineral = /obj/item/stack/sheet/mineral/silver
walltype = /turf/closed/wall/mineral/silver
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_SILVER_WALLS + SMOOTH_GROUP_WALLS
+ smoothing_groups = SMOOTH_GROUP_SILVER_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_SILVER_WALLS
/obj/structure/falsewall/diamond
name = "diamond wall"
desc = "A wall with diamond plating. You monster."
fake_icon = 'icons/turf/walls/diamond_wall.dmi'
- icon_state = "diamond_wall-open"
+ icon_state = "diamond_wall"
base_icon_state = "diamond_wall"
mineral = /obj/item/stack/sheet/mineral/diamond
walltype = /turf/closed/wall/mineral/diamond
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_DIAMOND_WALLS + SMOOTH_GROUP_WALLS
+ smoothing_groups = SMOOTH_GROUP_DIAMOND_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_DIAMOND_WALLS
max_integrity = 800
@@ -266,24 +376,24 @@
name = "plasma wall"
desc = "A wall with plasma plating. This is definitely a bad idea."
fake_icon = 'icons/turf/walls/plasma_wall.dmi'
- icon_state = "plasma_wall-open"
+ icon_state = "plasma_wall"
base_icon_state = "plasma_wall"
mineral = /obj/item/stack/sheet/mineral/plasma
walltype = /turf/closed/wall/mineral/plasma
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_PLASMA_WALLS + SMOOTH_GROUP_WALLS
+ smoothing_groups = SMOOTH_GROUP_PLASMA_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_PLASMA_WALLS
/obj/structure/falsewall/bananium
name = "bananium wall"
desc = "A wall with bananium plating. Honk!"
fake_icon = 'icons/turf/walls/bananium_wall.dmi'
- icon_state = "bananium_wall-open"
+ icon_state = "bananium_wall"
base_icon_state = "bananium_wall"
mineral = /obj/item/stack/sheet/mineral/bananium
walltype = /turf/closed/wall/mineral/bananium
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_BANANIUM_WALLS + SMOOTH_GROUP_WALLS
+ smoothing_groups = SMOOTH_GROUP_BANANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_BANANIUM_WALLS
@@ -291,97 +401,121 @@
name = "sandstone wall"
desc = "A wall with sandstone plating. Rough."
fake_icon = 'icons/turf/walls/sandstone_wall.dmi'
- icon_state = "sandstone_wall-open"
+ icon_state = "sandstone_wall"
base_icon_state = "sandstone_wall"
mineral = /obj/item/stack/sheet/mineral/sandstone
walltype = /turf/closed/wall/mineral/sandstone
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_SANDSTONE_WALLS + SMOOTH_GROUP_WALLS
+ smoothing_groups = SMOOTH_GROUP_SANDSTONE_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_SANDSTONE_WALLS
/obj/structure/falsewall/wood
name = "wooden wall"
desc = "A wall with wooden plating. Stiff."
fake_icon = 'icons/turf/walls/wood_wall.dmi'
- icon_state = "wood_wall-open"
+ icon_state = "wood_wall"
base_icon_state = "wood_wall"
mineral = /obj/item/stack/sheet/mineral/wood
walltype = /turf/closed/wall/mineral/wood
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WOOD_WALLS + SMOOTH_GROUP_WALLS
+ smoothing_groups = SMOOTH_GROUP_WOOD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_WOOD_WALLS
/obj/structure/falsewall/bamboo
name = "bamboo wall"
desc = "A wall with bamboo finish. Zen."
fake_icon = 'icons/turf/walls/bamboo_wall.dmi'
- icon_state = "bamboo_wall-open"
+ icon_state = "bamboo_wall"
base_icon_state = "bamboo_wall"
mineral = /obj/item/stack/sheet/mineral/bamboo
walltype = /turf/closed/wall/mineral/bamboo
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_BAMBOO_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_BAMBOO_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_BAMBOO_WALLS
+/obj/structure/falsewall/meat
+ name = "meat wall"
+ desc = "A wall of somone's compacted meat."
+ fake_icon = 'icons/turf/walls/meat_wall.dmi'
+ icon_state = "meat_wall"
+ base_icon_state = "meat_wall"
+ mineral = /obj/item/stack/sheet/meat
+ walltype = /turf/closed/wall/mineral/meat
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_MEAT_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_MEAT_WALLS
+
+/obj/structure/falsewall/pizza
+ name = "pepperoni wallzza"
+ desc = "It's a delicious pepperoni wallzza!"
+ fake_icon = 'icons/turf/walls/pizza_wall.dmi'
+ icon_state = "pizza_wall"
+ base_icon_state = "pizza_wall"
+ mineral = /obj/item/stack/sheet/pizza
+ walltype = /turf/closed/wall/mineral/pizza
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_PIZZA_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_PIZZA_WALLS
+
/obj/structure/falsewall/iron
name = "rough iron wall"
desc = "A wall with rough metal plating."
fake_icon = 'icons/turf/walls/iron_wall.dmi'
- icon_state = "iron_wall-open"
+ icon_state = "iron_wall"
base_icon_state = "iron_wall"
mineral = /obj/item/stack/rods
mineral_amount = 5
walltype = /turf/closed/wall/mineral/iron
base_icon_state = "iron_wall"
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_IRON_WALLS + SMOOTH_GROUP_WALLS
+ smoothing_groups = SMOOTH_GROUP_IRON_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_IRON_WALLS
/obj/structure/falsewall/abductor
name = "alien wall"
desc = "A wall with alien alloy plating."
fake_icon = 'icons/turf/walls/abductor_wall.dmi'
- icon_state = "abductor_wall-open"
+ icon_state = "abductor_wall"
base_icon_state = "abductor_wall"
mineral = /obj/item/stack/sheet/mineral/abductor
walltype = /turf/closed/wall/mineral/abductor
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_ABDUCTOR_WALLS + SMOOTH_GROUP_WALLS
+ smoothing_groups = SMOOTH_GROUP_ABDUCTOR_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_ABDUCTOR_WALLS
/obj/structure/falsewall/titanium
name = "wall"
desc = "A light-weight titanium wall used in shuttles."
fake_icon = 'icons/turf/walls/shuttle_wall.dmi'
- icon_state = "shuttle_wall-open"
+ icon_state = "shuttle_wall"
base_icon_state = "shuttle_wall"
mineral = /obj/item/stack/sheet/mineral/titanium
walltype = /turf/closed/wall/mineral/titanium
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS
- canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_TITANIUM_WALLS
+ smoothing_groups = SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_TALL_WALLS
+ canSmoothWith = SMOOTH_GROUP_TITANIUM_WALLS
/obj/structure/falsewall/plastitanium
name = "wall"
desc = "An evil wall of plasma and titanium."
fake_icon = 'icons/turf/walls/plastitanium_wall.dmi'
- icon_state = "plastitanium_wall-open"
+ icon_state = "plastitanium_wall"
base_icon_state = "plastitanium_wall"
mineral = /obj/item/stack/sheet/mineral/plastitanium
walltype = /turf/closed/wall/mineral/plastitanium
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_WALLS
- canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_PLASTITANIUM_WALLS
+ smoothing_groups = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_TALL_WALLS
+ canSmoothWith = SMOOTH_GROUP_PLASTITANIUM_WALLS
/obj/structure/falsewall/material
name = "wall"
desc = "A huge chunk of material used to separate rooms."
fake_icon = 'icons/turf/walls/material_wall.dmi'
- icon_state = "material_wall-open"
+ icon_state = "material_wall"
base_icon_state = "material_wall"
walltype = /turf/closed/wall/material
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_MATERIAL_WALLS
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_MATERIAL_WALLS
canSmoothWith = SMOOTH_GROUP_MATERIAL_WALLS
material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
@@ -395,19 +529,7 @@
/obj/structure/falsewall/material/mat_update_desc(mat)
desc = "A huge chunk of [mat] used to separate rooms."
-/obj/structure/falsewall/material/toggle_open()
- if(!QDELETED(src))
- set_density(!density)
- var/mat_opacity = TRUE
- for(var/datum/material/mat in custom_materials)
- if(mat.alpha < 255)
- mat_opacity = FALSE
- break
- set_opacity(density && mat_opacity)
- opening = FALSE
- update_appearance()
- air_update_turf(TRUE, !density)
-
+// wallening todo: does this work??
/obj/structure/falsewall/material/ChangeToWall(delete = 1)
var/turf/current_turf = get_turf(src)
var/turf/closed/wall/material/new_wall = current_turf.place_on_top(/turf/closed/wall/material)
@@ -416,6 +538,8 @@
qdel(src)
return current_turf
+// Wallening todo: do we want this?
+/*
/obj/structure/falsewall/material/update_icon(updates)
. = ..()
for(var/datum/material/mat in custom_materials)
@@ -433,3 +557,6 @@
var/mutable_appearance/girder_underlay = mutable_appearance('icons/obj/structures.dmi', girder_icon_state, layer = LOW_OBJ_LAYER-0.01)
girder_underlay.appearance_flags = RESET_ALPHA | RESET_COLOR
underlays += girder_underlay
+*/
+
+
diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm
index 95c725868ca1a..4496f5681ef03 100644
--- a/code/game/objects/structures/fence.dm
+++ b/code/game/objects/structures/fence.dm
@@ -16,7 +16,7 @@
density = TRUE
anchored = TRUE
- icon = 'icons/obj/fence.dmi'
+ icon = 'icons/obj/structures/fence.dmi'
icon_state = "straight"
var/cuttable = TRUE
diff --git a/code/game/objects/structures/fireaxe.dm b/code/game/objects/structures/fireaxe.dm
index ab69b7bc7a41e..c154d45e345cb 100644
--- a/code/game/objects/structures/fireaxe.dm
+++ b/code/game/objects/structures/fireaxe.dm
@@ -1,7 +1,7 @@
/obj/structure/fireaxecabinet
name = "fire axe cabinet"
desc = "There is a small label that reads \"For Emergency use only\" along with details for safe use of the axe. As if."
- icon = 'icons/obj/wallmounts.dmi'
+ icon = 'icons/obj/structures/cabinet.dmi'
icon_state = "fireaxe"
anchored = TRUE
density = FALSE
@@ -21,7 +21,7 @@
/// Whether we should populate our own contents on Initialize()
var/populate_contents = TRUE
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet)
/datum/armor/structure_fireaxecabinet
melee = 50
@@ -209,15 +209,14 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet, 32)
/obj/structure/fireaxecabinet/empty
populate_contents = FALSE
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet/empty, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet/empty)
/obj/item/wallframe/fireaxecabinet
name = "fire axe cabinet"
desc = "Home to a window's greatest nightmare. Apply to wall to use."
- icon = 'icons/obj/wallmounts.dmi'
+ icon = 'icons/obj/structures/cabinet.dmi'
icon_state = "fireaxe"
result_path = /obj/structure/fireaxecabinet/empty
- pixel_shift = 32
/obj/structure/fireaxecabinet/mechremoval
name = "mech removal tool cabinet"
@@ -226,7 +225,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet/empty, 32)
item_path = /obj/item/crowbar/mechremoval
item_overlay = "crowbar"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet/mechremoval, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet/mechremoval)
/obj/structure/fireaxecabinet/mechremoval/atom_deconstruct(disassembled = TRUE)
if(held_item && loc)
@@ -236,7 +235,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet/mechremoval, 32)
/obj/structure/fireaxecabinet/mechremoval/empty
populate_contents = FALSE
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet/mechremoval/empty, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/fireaxecabinet/mechremoval/empty)
/obj/item/wallframe/fireaxecabinet/mechremoval
name = "mech removal tool cabinet"
diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm
index 49a230d6bdc48..8a557f1fb8921 100644
--- a/code/game/objects/structures/flora.dm
+++ b/code/game/objects/structures/flora.dm
@@ -2,7 +2,7 @@
name = "flora"
desc = "Some sort of plant."
resistance_flags = FLAMMABLE
- max_integrity = 150
+ max_integrity = 100
anchored = TRUE
drag_slowdown = 1.3
@@ -113,6 +113,12 @@
if(harvest(user))
after_harvest(user)
+/obj/structure/flora/run_atom_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
+ if(damage_flag == MELEE)
+ if(damage_type == BURN)
+ damage_amount *= 4
+ return ..()
+
/obj/structure/flora/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
var/use_default_sound = TRUE //Because I don't wanna do unnecessary bitflag checks in a single if statement, while also allowing for multiple sounds to be played
if(flora_flags & FLORA_HERBAL)
@@ -278,9 +284,9 @@
name = "tree"
desc = "A large tree."
density = TRUE
+ max_integrity = 150
pixel_x = -16
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
drag_slowdown = 1.5
product_types = list(/obj/item/grown/log/tree = 1)
harvest_amount_low = 6
@@ -948,7 +954,6 @@
pixel_x = -16
pixel_y = -12
layer = ABOVE_ALL_MOB_LAYER
- plane = ABOVE_GAME_PLANE
/obj/structure/flora/bush/large/style_2
icon_state = "bush2"
diff --git a/code/game/objects/structures/fluff.dm b/code/game/objects/structures/fluff.dm
index 7acfa65f62577..2f4ca5a929822 100644
--- a/code/game/objects/structures/fluff.dm
+++ b/code/game/objects/structures/fluff.dm
@@ -115,7 +115,6 @@
icon_state = "frontwalltop"
density = FALSE
layer = ABOVE_ALL_MOB_LAYER //except for the stairs tile, which should be set to OBJ_LAYER aka 3.
- plane = ABOVE_GAME_PLANE
/obj/structure/fluff/bus/passable/seat
@@ -207,12 +206,11 @@
density = TRUE
deconstructible = FALSE
layer = ABOVE_ALL_MOB_LAYER
- plane = ABOVE_GAME_PLANE
/obj/structure/fluff/beach_towel
name = "beach towel"
desc = "A towel decorated in various beach-themed designs."
- icon = 'icons/obj/railings.dmi'
+ icon = 'icons/obj/structures/railings.dmi'
icon_state = "railing"
density = FALSE
anchored = TRUE
@@ -283,7 +281,7 @@
/obj/structure/fluff/tram_rail
name = "tram rail"
desc = "Great for trams, not so great for skating."
- icon = 'icons/obj/tram/tram_rails.dmi'
+ icon = 'icons/obj/structures/tram/tram_rails.dmi'
icon_state = "rail"
layer = TRAM_RAIL_LAYER
plane = FLOOR_PLANE
diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm
index 0f20f87e0b547..7d1a715029685 100644
--- a/code/game/objects/structures/girders.dm
+++ b/code/game/objects/structures/girders.dm
@@ -1,5 +1,6 @@
/obj/structure/girder
name = "girder"
+ icon = 'icons/obj/structures/tall.dmi'
icon_state = "girder"
desc = "A large structural assembly made out of metal; It requires a layer of iron before it can be considered a wall."
anchored = TRUE
@@ -330,6 +331,7 @@
if(state != GIRDER_REINF)
return
state = GIRDER_REINF_STRUTS
+ update_icon()
return TRUE
else if(state == GIRDER_REINF_STRUTS)
@@ -338,6 +340,7 @@
if(state != GIRDER_REINF_STRUTS)
return
state = GIRDER_REINF
+ update_icon()
return TRUE
// Wirecutter behavior for girders
@@ -388,6 +391,18 @@
var/remains = pick(/obj/item/stack/rods, /obj/item/stack/sheet/iron)
new remains(loc)
+/obj/structure/girder/update_icon_state()
+ . = ..()
+ switch(state)
+ if(GIRDER_NORMAL)
+ icon_state = "girder"
+ if(GIRDER_DISPLACED)
+ icon_state = "displaced"
+ if(GIRDER_REINF)
+ icon_state = "reinforced"
+ if(GIRDER_REINF_STRUTS)
+ icon_state = "reinforced_struts"
+
/obj/structure/girder/narsie_act()
new /obj/structure/girder/cult(loc)
qdel(src)
@@ -410,6 +425,7 @@
/obj/structure/girder/tram
name = "tram girder"
desc = "Titanium framework to construct tram walls. Can be plated with titanium glass or other wall materials."
+ icon = 'icons/obj/structures/tall.dmi'
icon_state = "tram"
state = GIRDER_TRAM
obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN
@@ -422,8 +438,7 @@
/obj/structure/girder/cult
name = "runed girder"
desc = "Framework made of a strange and shockingly cold metal. It doesn't seem to have any bolts."
- icon = 'icons/obj/antags/cult/structures.dmi'
- icon_state= "cultgirder"
+ icon_state = "cultgirder"
can_displace = FALSE
/obj/structure/girder/cult/attackby(obj/item/W, mob/user, params)
@@ -477,7 +492,7 @@
return FALSE
/obj/structure/girder/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- switch(rcd_data["[RCD_DESIGN_MODE]"])
+ switch(rcd_data[RCD_DESIGN_MODE])
if(RCD_TURF)
if(the_rcd.rcd_design_path != /turf/open/floor/plating/rcd)
return FALSE
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 3bd3e00cc273b..2e570e51f3311 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -4,8 +4,8 @@
/obj/structure/grille
desc = "A flimsy framework of iron rods."
name = "grille"
- icon = 'icons/obj/structures.dmi'
- icon_state = "grille"
+ icon = 'icons/obj/structures/smooth/grille.dmi'
+ icon_state = "grille-0"
base_icon_state = "grille"
density = TRUE
anchored = TRUE
@@ -15,6 +15,9 @@
armor_type = /datum/armor/structure_grille
max_integrity = 50
integrity_failure = 0.4
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_GRILLE
+ canSmoothWith = SMOOTH_GROUP_GRILLE
var/rods_type = /obj/item/stack/rods
var/rods_amount = 2
@@ -37,17 +40,44 @@
. = ..()
update_appearance()
-/obj/structure/grille/update_appearance(updates)
- if(QDELETED(src) || broken)
+/obj/structure/grille/update_icon(updates=ALL)
+ if(QDELETED(src))
return
+ var/old_base_state = base_icon_state
+ var/ratio = atom_integrity / max_integrity
+ if(ratio <= 0.7)
+ icon = 'icons/obj/structures/smooth/grille_damaged.dmi'
+ base_icon_state = "grille_damaged"
+ else
+ icon = 'icons/obj/structures/smooth/grille.dmi'
+ base_icon_state = "grille"
+
+ if(old_base_state != base_icon_state)
+ icon_state = "[base_icon_state]-[smoothing_junction]"
+
+ var/old_smoothing_flags = smoothing_flags
+ if(broken)
+ icon = 'icons/obj/structures/smooth/tall_structure_variations.dmi'
+ icon_state = "grille-broken"
+ base_icon_state = "grille-broken"
+ smoothing_flags = NONE
+ smoothing_groups = null
+ canSmoothWith = null
+ else
+ smoothing_flags = initial(smoothing_flags)
+ smoothing_groups = initial(smoothing_groups)
+ canSmoothWith = initial(canSmoothWith)
+ SETUP_SMOOTHING()
. = ..()
- if((updates & UPDATE_SMOOTHING) && (smoothing_flags & USES_SMOOTHING))
- QUEUE_SMOOTH(src)
-/obj/structure/grille/update_icon_state()
- icon_state = "[base_icon_state][((atom_integrity / max_integrity) <= 0.5) ? "50_[rand(0, 3)]" : null]"
- return ..()
+ if(!(updates & UPDATE_SMOOTHING))
+ return
+ if(old_smoothing_flags == smoothing_flags && (smoothing_flags & USES_SMOOTHING))
+ return
+ // If our flags changed, update EVERYBODY
+ QUEUE_SMOOTH(src)
+ QUEUE_SMOOTH_NEIGHBORS(src)
/obj/structure/grille/examine(mob/user)
. = ..()
@@ -87,7 +117,7 @@
return FALSE
/obj/structure/grille/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- switch(rcd_data["[RCD_DESIGN_MODE]"])
+ switch(rcd_data[RCD_DESIGN_MODE])
if(RCD_DECONSTRUCT)
qdel(src)
return TRUE
@@ -101,18 +131,19 @@
if(!clear_tile(user))
return FALSE
- var/obj/structure/window/window_path = rcd_data["[RCD_DESIGN_PATH]"]
+ var/obj/structure/window/window_path = rcd_data[RCD_DESIGN_PATH]
if(!ispath(window_path))
CRASH("Invalid window path type in RCD: [window_path]")
+ var/window_direction = rcd_data[RCD_BUILD_DIRECTION] || user.dir
//checks if its a valid build direction
if(!initial(window_path.fulltile))
- if(!valid_build_direction(loc, user.dir, is_fulltile = FALSE))
+ if(!valid_build_direction(loc, window_direction , is_fulltile = FALSE))
balloon_alert(user, "window already here!")
return FALSE
- var/obj/structure/window/WD = new window_path(T, user.dir)
- WD.set_anchored(TRUE)
+ var/obj/structure/window/window = new window_path(T, window_direction )
+ window.set_anchored(TRUE)
return TRUE
return FALSE
@@ -296,21 +327,21 @@
/obj/structure/grille/atom_break()
. = ..()
if(!broken)
- icon_state = "brokengrille"
set_density(FALSE)
atom_integrity = 20
broken = TRUE
rods_amount = 1
var/obj/item/dropped_rods = new rods_type(drop_location(), rods_amount)
transfer_fingerprints_to(dropped_rods)
+ update_appearance()
/obj/structure/grille/proc/repair_grille()
if(broken)
- icon_state = "grille"
set_density(TRUE)
atom_integrity = max_integrity
broken = FALSE
rods_amount = 2
+ update_appearance()
return TRUE
return FALSE
@@ -366,10 +397,14 @@
return null
/obj/structure/grille/broken // Pre-broken grilles for map placement
- icon_state = "brokengrille"
+ icon = 'icons/obj/structures/smooth/tall_structure_variations.dmi'
+ icon_state = "grille-broken"
density = FALSE
broken = TRUE
rods_amount = 1
+ smoothing_flags = null
+ smoothing_groups = null
+ canSmoothWith = null
/obj/structure/grille/broken/Initialize(mapload)
. = ..()
diff --git a/code/game/objects/structures/guillotine.dm b/code/game/objects/structures/guillotine.dm
index a51e82498e2bc..8501ce40bf1d7 100644
--- a/code/game/objects/structures/guillotine.dm
+++ b/code/game/objects/structures/guillotine.dm
@@ -31,7 +31,7 @@
/obj/structure/guillotine
name = "guillotine"
desc = "A large structure used to remove the heads of traitors and treasonists."
- icon = 'icons/obj/guillotine.dmi'
+ icon = 'icons/obj/structures/guillotine.dmi'
icon_state = "guillotine_raised"
icon_preview = 'icons/obj/fluff/previews.dmi'
icon_state_preview = "guilliotine"
diff --git a/code/game/objects/structures/gym/punching_bag.dm b/code/game/objects/structures/gym/punching_bag.dm
index 59ccf6f23c2c3..65028d4038bc8 100644
--- a/code/game/objects/structures/gym/punching_bag.dm
+++ b/code/game/objects/structures/gym/punching_bag.dm
@@ -1,4 +1,5 @@
/obj/structure/punching_bag
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "punching bag"
desc = "A punching bag. Can you get to speed level 4???"
icon = 'icons/obj/fluff/gym_equipment.dmi'
diff --git a/code/game/objects/structures/gym/weight_machine.dm b/code/game/objects/structures/gym/weight_machine.dm
index 352ef65ff0050..357c4359be7e9 100644
--- a/code/game/objects/structures/gym/weight_machine.dm
+++ b/code/game/objects/structures/gym/weight_machine.dm
@@ -3,6 +3,7 @@
#define SAFE_DRUNK_LEVEL 39
/obj/structure/weightmachine
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "chest press machine"
desc = "Just looking at this thing makes you feel tired."
icon = 'icons/obj/fluff/gym_equipment.dmi'
diff --git a/code/game/objects/structures/headpike.dm b/code/game/objects/structures/headpike.dm
index fca325744554d..4d26332a275d6 100644
--- a/code/game/objects/structures/headpike.dm
+++ b/code/game/objects/structures/headpike.dm
@@ -1,7 +1,7 @@
/obj/structure/headpike
name = "spooky head on a spear"
desc = "When you really want to send a message."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/weapons/spear.dmi'
icon_state = "headpike"
density = FALSE
anchored = TRUE
diff --git a/code/game/objects/structures/holosign.dm b/code/game/objects/structures/holosign.dm
index 1d7fc470afff8..30983c5088d67 100644
--- a/code/game/objects/structures/holosign.dm
+++ b/code/game/objects/structures/holosign.dm
@@ -3,7 +3,7 @@
/obj/structure/holosign
name = "holo sign"
- icon = 'icons/effects/effects.dmi'
+ icon = 'icons/effects/holosigns.dmi'
anchored = TRUE
max_integrity = 1
armor_type = /datum/armor/structure_holosign
@@ -21,10 +21,7 @@
/obj/structure/holosign/Initialize(mapload, source_projector)
. = ..()
- var/turf/our_turf = get_turf(src)
- if(use_vis_overlay)
- alpha = 0
- SSvis_overlays.add_vis_overlay(src, icon, icon_state, ABOVE_MOB_LAYER, MUTATE_PLANE(GAME_PLANE, our_turf), dir, add_appearance_flags = RESET_ALPHA) //you see mobs under it, but you hit them like they are above it
+ create_vis_overlay()
if(source_projector)
projector = source_projector
LAZYADD(projector.signs, src)
@@ -41,6 +38,11 @@
return
attack_holosign(user, modifiers)
+/obj/structure/holosign/CanAllowThrough(atom/movable/mover, border_dir)
+ . = ..()
+ if(!. && isprojectile(mover)) // Its short enough to be shot over
+ return TRUE
+
/obj/structure/holosign/proc/attack_holosign(mob/living/user, list/modifiers)
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
user.changeNext_move(CLICK_CD_MELEE)
@@ -54,38 +56,100 @@
if(BURN)
playsound(loc, 'sound/weapons/egloves.ogg', 80, TRUE)
+/obj/structure/holosign/proc/create_vis_overlay()
+ var/turf/our_turf = get_turf(src)
+ if(use_vis_overlay)
+ alpha = 0
+ SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
+ SSvis_overlays.add_vis_overlay(src, icon, icon_state, ABOVE_MOB_LAYER, MUTATE_PLANE(GAME_PLANE, our_turf), dir, add_appearance_flags = RESET_ALPHA) //you see mobs under it, but you hit them like they are above it
+
/obj/structure/holosign/wetsign
name = "wet floor sign"
desc = "The words flicker as if they mean nothing."
- icon = 'icons/effects/effects.dmi'
icon_state = "holosign"
/obj/structure/holosign/barrier
- name = "holobarrier"
- desc = "A short holographic barrier which can only be passed by walking."
+ name = "security holobarrier"
+ desc = "A strong short security holographic barrier used for crowd control and blocking crime scenes. Can only be passed by walking."
icon_state = "holosign_sec"
+ base_icon_state = "holosign_sec"
pass_flags_self = PASSTABLE | PASSGRILLE | PASSGLASS | LETPASSTHROW
density = TRUE
max_integrity = 20
- var/allow_walk = TRUE //can we pass through it on walk intent
+ COOLDOWN_DECLARE(cooldown_open)
+ ///Can we pass through it on walk intent?
+ var/allow_walk = TRUE
+ ///Can it be temporarily opened with the holosign projector?
+ var/openable = TRUE
+ ///Is it opened?
+ var/opened = FALSE
+ ///What is the icon of opened holobarrier?
+ var/pass_icon_state = "holosign_pass"
/obj/structure/holosign/barrier/CanAllowThrough(atom/movable/mover, border_dir)
. = ..()
if(.)
return
+
+ if(opened)
+ return TRUE
+
if(iscarbon(mover))
- var/mob/living/carbon/C = mover
- if(C.stat) // Lets not prevent dragging unconscious/dead people.
+ var/mob/living/carbon/moving_carbon = mover
+ if(moving_carbon.stat) // Lets not prevent dragging unconscious/dead people.
return TRUE
- if(allow_walk && C.move_intent == MOVE_INTENT_WALK)
+ if(allow_walk && moving_carbon.move_intent == MOVE_INTENT_WALK)
return TRUE
+/obj/structure/holosign/barrier/ranged_item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ . = ..()
+ if(tool != projector)
+ return
+ if(openable)
+ open(user)
+
+/obj/structure/holosign/barrier/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ . = ..()
+ if(tool != projector)
+ return
+ qdel(src)
+
+/obj/structure/holosign/barrier/update_icon_state()
+ if(!opened)
+ icon_state = base_icon_state
+ else
+ icon_state = pass_icon_state
+
+ create_vis_overlay()
+ . = ..()
+
+/obj/structure/holosign/barrier/proc/open(user)
+ if(!openable)
+ balloon_alert(user, "unable!")
+ return
+
+ if(!COOLDOWN_FINISHED(src, cooldown_open))
+ balloon_alert(user, "on cooldown!")
+ return
+
+ if(!opened)
+ density = FALSE
+ opened = TRUE
+ playsound(src, 'sound/machines/door_open.ogg', 50, TRUE)
+ else
+ density = TRUE
+ opened = FALSE
+ playsound(src, 'sound/machines/door_close.ogg', 50, TRUE)
+
+ update_icon_state()
+ COOLDOWN_START(src, cooldown_open, 1 SECONDS)
+
/obj/structure/holosign/barrier/wetsign
name = "wet floor holobarrier"
- desc = "When it says walk it means walk."
- icon = 'icons/effects/effects.dmi'
- icon_state = "holosign"
+ desc = "When it says walk it means WALK!"
+ icon_state = "holosign_dense"
max_integrity = 1
+ openable = FALSE
/obj/structure/holosign/barrier/wetsign/CanAllowThrough(atom/movable/mover, border_dir)
. = ..()
@@ -97,7 +161,10 @@
return FALSE
/obj/structure/holosign/barrier/engineering
+ name = "engineering holobarrier"
+ desc = "A short engineering holographic barrier used for designating hazardous zones, slightly blocks radiation. Can only be passed by walking."
icon_state = "holosign_engi"
+ base_icon_state = "holosign_engi"
rad_insulation = RAD_LIGHT_INSULATION
max_integrity = 1
@@ -105,6 +172,7 @@
name = "holofirelock"
desc = "A holographic barrier resembling a firelock. Though it does not prevent solid objects from passing through, gas is kept out."
icon_state = "holo_firelock"
+ openable = FALSE
density = FALSE
anchored = TRUE
can_atmos_pass = ATMOS_PASS_NO
@@ -129,11 +197,13 @@
/obj/structure/holosign/barrier/atmos/sturdy
name = "sturdy holofirelock"
max_integrity = 150
+ openable = FALSE
/obj/structure/holosign/barrier/atmos/tram
name = "tram atmos barrier"
max_integrity = 150
icon_state = "holo_tram"
+ openable = FALSE
/obj/structure/holosign/barrier/atmos/Initialize(mapload)
. = ..()
@@ -166,9 +236,10 @@
name = "\improper PENLITE holobarrier"
desc = "A holobarrier that uses biometrics to detect human viruses. Denies passing to personnel with easily-detected, malicious viruses. Good for quarantines."
icon_state = "holo_medical"
- alpha = 125 //lazy :)
+ base_icon_state = "holo_medical"
max_integrity = 1
- var/buzzcd = 0
+ openable = FALSE
+ COOLDOWN_DECLARE(virus_detected)
/obj/structure/holosign/barrier/medical/CanAllowThrough(atom/movable/mover, border_dir)
. = ..()
@@ -183,12 +254,18 @@
/obj/structure/holosign/barrier/medical/Bumped(atom/movable/AM)
. = ..()
- icon_state = "holo_medical"
- if(ishuman(AM) && !CheckHuman(AM))
- if(buzzcd < world.time)
- playsound(get_turf(src),'sound/machines/buzz-sigh.ogg',65,TRUE,4)
- buzzcd = (world.time + 60)
- icon_state = "holo_medical-deny"
+ icon_state = base_icon_state
+ update_icon_state()
+ if(!ishuman(AM) && CheckHuman(AM))
+ return
+
+ if(!COOLDOWN_FINISHED(src, virus_detected))
+ return
+
+ playsound(get_turf(src),'sound/machines/buzz-sigh.ogg', 65, TRUE, 4)
+ COOLDOWN_START(src, virus_detected, 1 SECONDS)
+ icon_state = "holo_medical-deny"
+ update_icon_state()
/obj/structure/holosign/barrier/medical/proc/CheckHuman(mob/living/carbon/human/sickboi)
var/threat = sickboi.check_virus()
diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm
index 314539aa2b412..79acf784663c5 100644
--- a/code/game/objects/structures/ladders.dm
+++ b/code/game/objects/structures/ladders.dm
@@ -2,7 +2,7 @@
/obj/structure/ladder
name = "ladder"
desc = "A sturdy metal ladder."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/fluff/general.dmi'
icon_state = "ladder11"
anchored = TRUE
obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index cf6fe65abe274..5f1d138b90b67 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -1,7 +1,7 @@
/obj/structure/lattice
name = "lattice"
desc = "A lightweight support lattice. These hold our station together."
- icon = 'icons/obj/smooth_structures/lattice.dmi'
+ icon = 'icons/obj/structures/smooth/lattice.dmi'
icon_state = "lattice-255"
base_icon_state = "lattice"
density = FALSE
@@ -23,6 +23,7 @@
if(length(give_turf_traits))
give_turf_traits = string_list(give_turf_traits)
AddElement(/datum/element/give_turf_traits, give_turf_traits)
+ AddElement(/datum/element/footstep_override, footstep = FOOTSTEP_CATWALK)
/datum/armor/structure_lattice
melee = 50
@@ -66,8 +67,8 @@
return FALSE
/obj/structure/lattice/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- if(rcd_data["[RCD_DESIGN_MODE]"] == RCD_TURF)
- var/design_structure = rcd_data["[RCD_DESIGN_PATH]"]
+ if(rcd_data[RCD_DESIGN_MODE] == RCD_TURF)
+ var/design_structure = rcd_data[RCD_DESIGN_PATH]
if(design_structure == /turf/open/floor/plating)
var/turf/T = src.loc
if(isgroundlessturf(T))
@@ -88,7 +89,7 @@
/obj/structure/lattice/catwalk
name = "catwalk"
desc = "A catwalk for easier EVA maneuvering and cable placement."
- icon = 'icons/obj/smooth_structures/catwalk.dmi'
+ icon = 'icons/obj/structures/smooth/catwalk.dmi'
icon_state = "catwalk-0"
base_icon_state = "catwalk"
number_of_mats = 2
@@ -98,10 +99,6 @@
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, TRAIT_IMMERSE_STOPPED, TRAIT_HYPERSPACE_STOPPED)
-/obj/structure/lattice/catwalk/Initialize(mapload)
- . = ..()
- AddElement(/datum/element/footstep_override, footstep = FOOTSTEP_CATWALK)
-
/obj/structure/lattice/catwalk/deconstruction_hints(mob/user)
return span_notice("The supporting rods look like they could be cut.")
@@ -141,7 +138,7 @@
/obj/structure/lattice/lava
name = "heatproof support lattice"
desc = "A specialized support beam for building across lava. Watch your step."
- icon = 'icons/obj/smooth_structures/catwalk.dmi'
+ icon = 'icons/obj/structures/smooth/catwalk.dmi'
icon_state = "catwalk-0"
base_icon_state = "catwalk"
number_of_mats = 1
diff --git a/code/game/objects/structures/lavaland/geyser.dm b/code/game/objects/structures/lavaland/geyser.dm
index 7ef40423e0d44..48601a3ec98a9 100644
--- a/code/game/objects/structures/lavaland/geyser.dm
+++ b/code/game/objects/structures/lavaland/geyser.dm
@@ -45,7 +45,7 @@
///start making those CHHHHHEEEEEEMS. Called whenever chems are removed, it's fine because START_PROCESSING checks if we arent already processing
/obj/structure/geyser/proc/start_chemming()
- START_PROCESSING(SSplumbing, src) //It's main function is to be plumbed, so use SSplumbing
+ START_PROCESSING(SSplumbing, src) //Its main function is to be plumbed, so use SSplumbing
///We're full so stop processing
/obj/structure/geyser/proc/stop_chemming()
@@ -117,7 +117,7 @@
/obj/item/plunger
name = "plunger"
desc = "It's a plunger for plunging."
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/tools.dmi'
icon_state = "plunger"
worn_icon_state = "plunger"
@@ -185,3 +185,7 @@
layer_mode_sprite = "reinforced_plunger_layer"
custom_premium_price = PAYCHECK_CREW * 8
+
+/obj/item/plunger/cyborg/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
diff --git a/code/game/objects/structures/lavaland/ore_vent.dm b/code/game/objects/structures/lavaland/ore_vent.dm
index cb9d203e3f2c7..f9214b989b95c 100644
--- a/code/game/objects/structures/lavaland/ore_vent.dm
+++ b/code/game/objects/structures/lavaland/ore_vent.dm
@@ -39,6 +39,7 @@
MEDIUM_VENT_TYPE = 5,
SMALL_VENT_TYPE = 7,
)
+ var/wave_timer = WAVE_DURATION_SMALL
/// What string do we use to warn the player about the excavation event?
var/excavation_warning = "Are you ready to excavate this ore vent?"
@@ -159,7 +160,7 @@
* This proc is called when the ore vent is initialized, in order to determine what minerals boulders it spawns can contain.
* The materials available are determined by SSore_generation.ore_vent_minerals, which is a list of all minerals that can be contained in ore vents for a given cave generation.
* As a result, minerals use a weighted list as seen by ore_vent_minerals_lavaland, which is then copied to ore_vent_minerals.
- * Once a material is picked from the weighted list, it's removed from ore_vent_minerals, so that it can't be picked again and provided it's own internal weight used when assigning minerals to boulders spawned by this vent.
+ * Once a material is picked from the weighted list, it's removed from ore_vent_minerals, so that it can't be picked again and provided its own internal weight used when assigning minerals to boulders spawned by this vent.
* May also be called after the fact, as seen in SSore_generation's initialize, to add more minerals to an existing vent.
*
* The above applies only when spawning in at mapload, otherwise we pick randomly from ore_vent_minerals_lavaland.
@@ -219,6 +220,9 @@
node.arrive(src)
RegisterSignal(node, COMSIG_QDELETING, PROC_REF(handle_wave_conclusion))
RegisterSignal(node, COMSIG_MOVABLE_MOVED, PROC_REF(handle_wave_conclusion))
+ addtimer(CALLBACK(node, TYPE_PROC_REF(/atom, update_appearance)), wave_timer * 0.25)
+ addtimer(CALLBACK(node, TYPE_PROC_REF(/atom, update_appearance)), wave_timer * 0.5)
+ addtimer(CALLBACK(node, TYPE_PROC_REF(/atom, update_appearance)), wave_timer * 0.75)
particles = new /particles/smoke/ash()
for(var/i in 1 to 5) // Clears the surroundings of the ore vent before starting wave defense.
for(var/turf/closed/mineral/rock in oview(i))
@@ -247,11 +251,6 @@
spawn_distance = 4, \
spawn_distance_exclude = 3, \
)
- var/wave_timer = 60 SECONDS
- if(boulder_size == BOULDER_SIZE_MEDIUM)
- wave_timer = 90 SECONDS
- else if(boulder_size == BOULDER_SIZE_LARGE)
- wave_timer = 150 SECONDS
COOLDOWN_START(src, wave_cooldown, wave_timer)
addtimer(CALLBACK(src, PROC_REF(handle_wave_conclusion)), wave_timer)
icon_state = icon_state_tapped
@@ -290,6 +289,7 @@
icon_state = icon_state_tapped
update_appearance(UPDATE_ICON_STATE)
qdel(GetComponent(/datum/component/gps))
+ UnregisterSignal(node, COMSIG_QDELETING)
else
visible_message(span_danger("\the [src] creaks and groans as the mining attempt fails, and the vent closes back up."))
icon_state = initial(icon_state)
@@ -474,18 +474,22 @@
switch(string_boulder_size)
if(LARGE_VENT_TYPE)
boulder_size = BOULDER_SIZE_LARGE
+ wave_timer = WAVE_DURATION_LARGE
if(mapload)
GLOB.ore_vent_sizes["large"] += 1
if(MEDIUM_VENT_TYPE)
boulder_size = BOULDER_SIZE_MEDIUM
+ wave_timer = WAVE_DURATION_MEDIUM
if(mapload)
GLOB.ore_vent_sizes["medium"] += 1
if(SMALL_VENT_TYPE)
boulder_size = BOULDER_SIZE_SMALL
+ wave_timer = WAVE_DURATION_SMALL
if(mapload)
GLOB.ore_vent_sizes["small"] += 1
else
boulder_size = BOULDER_SIZE_SMALL //Might as well set a default value
+ wave_timer = WAVE_DURATION_SMALL
name = initial(name)
diff --git a/code/game/objects/structures/maintenance.dm b/code/game/objects/structures/maintenance.dm
index ecd6f54d2557a..4a66a46375755 100644
--- a/code/game/objects/structures/maintenance.dm
+++ b/code/game/objects/structures/maintenance.dm
@@ -2,8 +2,10 @@
as well as a location where a hidden item can somtimes be retrieved
at the cost of risking a vicious bite.**/
/obj/structure/moisture_trap
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "moisture trap"
desc = "A device installed in order to control moisture in poorly ventilated areas.\nThe stagnant water inside basin seems to produce serious biofouling issues when improperly maintained.\nThis unit in particular seems to be teeming with life!\nWho thought mother Gaia could assert herself so vigoriously in this sterile and desolate place?"
+ icon = 'icons/obj/maintenance_loot.dmi'
icon_state = "moisture_trap"
anchored = TRUE
density = FALSE
@@ -31,7 +33,7 @@ at the cost of risking a vicious bite.**/
/obj/structure/moisture_trap/Initialize(mapload)
. = ..()
- ADD_TRAIT(src, TRAIT_FISH_SAFE_STORAGE, TRAIT_GENERIC)
+ AddElement(/datum/element/fish_safe_storage)
AddElement(/datum/element/swabable, CELL_LINE_TABLE_MOIST, CELL_VIRUS_TABLE_GENERIC, rand(2,4), 20)
if(prob(40))
critter_infested = FALSE
@@ -81,7 +83,7 @@ at the cost of risking a vicious bite.**/
if(critter_infested && prob(50) && iscarbon(user))
var/mob/living/carbon/bite_victim = user
var/obj/item/bodypart/affecting = bite_victim.get_bodypart("[(user.active_hand_index % 2 == 0) ? "r" : "l" ]_arm")
- to_chat(user, span_danger("You feel a sharp pain as an unseen creature sinks it's [pick("fangs", "beak", "proboscis")] into your arm!"))
+ to_chat(user, span_danger("You feel a sharp pain as an unseen creature sinks its [pick("fangs", "beak", "proboscis")] into your arm!"))
if(affecting?.receive_damage(30))
bite_victim.update_damage_overlays()
playsound(src,'sound/weapons/bite.ogg', 70, TRUE)
@@ -248,6 +250,7 @@ at the cost of risking a vicious bite.**/
/obj/structure/steam_vent
name = "steam vent"
desc = "A device periodically filtering out moisture particles from the nearby walls and windows. It's only possible due to the moisture traps nearby."
+ icon = 'icons/obj/maintenance_loot.dmi'
icon_state = "steam_vent"
anchored = TRUE
density = FALSE
diff --git a/code/game/objects/structures/mannequin.dm b/code/game/objects/structures/mannequin.dm
index a47802273d5a0..b53de86d5eec3 100644
--- a/code/game/objects/structures/mannequin.dm
+++ b/code/game/objects/structures/mannequin.dm
@@ -15,7 +15,6 @@
pixel_y = 3
base_pixel_y = 3
layer = ABOVE_MOB_LAYER
- plane = ABOVE_GAME_PLANE
/// Which body type we use, male or female?
var/body_type
/// Material we're used of, wood or plastic?
@@ -98,15 +97,15 @@
var/datum/sprite_accessory/underwear/underwear = SSaccessories.underwear_list[underwear_name]
if(underwear)
if(body_type == FEMALE && underwear.gender == MALE)
- . += wear_female_version(underwear.icon_state, underwear.icon, BODY_LAYER, FEMALE_UNIFORM_FULL)
+ . += mutable_appearance(wear_female_version(underwear.icon_state, underwear.icon, FEMALE_UNIFORM_FULL), layer = -BODY_LAYER)
else
- . += mutable_appearance(underwear.icon, underwear.icon_state, -BODY_LAYER)
+ . += mutable_appearance(underwear.icon, underwear.icon_state, layer = -BODY_LAYER)
var/datum/sprite_accessory/undershirt/undershirt = SSaccessories.undershirt_list[undershirt_name]
if(undershirt)
if(body_type == FEMALE)
- . += wear_female_version(undershirt.icon_state, undershirt.icon, BODY_LAYER)
+ . += mutable_appearance(wear_female_version(undershirt.icon_state, undershirt.icon), layer = -BODY_LAYER)
else
- . += mutable_appearance(undershirt.icon, undershirt.icon_state, -BODY_LAYER)
+ . += mutable_appearance(undershirt.icon, undershirt.icon_state, layer = -BODY_LAYER)
var/datum/sprite_accessory/socks/socks = SSaccessories.socks_list[socks_name]
if(socks)
. += mutable_appearance(socks.icon, socks.icon_state, -BODY_LAYER)
diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm
index d67f61e9442bb..af616412916a5 100644
--- a/code/game/objects/structures/mineral_doors.dm
+++ b/code/game/objects/structures/mineral_doors.dm
@@ -41,6 +41,11 @@
set_custom_materials(initialized_mineral.mats_per_unit, sheetAmount)
qdel(initialized_mineral)
air_update_turf(TRUE, TRUE)
+ return INITIALIZE_HINT_LATELOAD
+
+/obj/structure/mineral_door/LateInitialize(mapload)
+ if(mapload)
+ auto_align()
/obj/structure/mineral_door/Destroy()
if(!door_opened)
@@ -103,9 +108,8 @@
/obj/structure/mineral_door/proc/Open()
isSwitchingStates = TRUE
playsound(src, openSound, 100, TRUE)
+ sleep(0.8 SECONDS)
set_opacity(FALSE)
- flick("[initial(icon_state)]opening",src)
- sleep(1 SECONDS)
set_density(FALSE)
door_opened = TRUE
layer = OPEN_DOOR_LAYER
@@ -124,8 +128,7 @@
return
isSwitchingStates = TRUE
playsound(src, closeSound, 100, TRUE)
- flick("[initial(icon_state)]closing",src)
- sleep(1 SECONDS)
+ sleep(0.8 SECONDS)
set_density(TRUE)
set_opacity(TRUE)
door_opened = FALSE
@@ -135,9 +138,16 @@
isSwitchingStates = FALSE
/obj/structure/mineral_door/update_icon_state()
- icon_state = "[initial(icon_state)][door_opened ? "open":""]"
+ icon_state = "[initial(icon_state)][door_opened ? "_open_top":""]"
return ..()
+/obj/structure/mineral_door/update_overlays()
+ . = ..()
+ if(!density)
+ // If we're open we layer the bit below us "above" any mobs so they can walk through
+ . += mutable_appearance(icon, "[initial(icon_state)]_open_bottom", ABOVE_MOB_LAYER, appearance_flags = KEEP_APART)
+ . += emissive_blocker(icon, "[initial(icon_state)]_open_bottom", src, ABOVE_MOB_LAYER)
+
/obj/structure/mineral_door/attackby(obj/item/I, mob/living/user)
if(pickaxe_door(user, I))
return
diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm
index 7ea2330281413..d8e73252319af 100644
--- a/code/game/objects/structures/mirror.dm
+++ b/code/game/objects/structures/mirror.dm
@@ -18,7 +18,7 @@
/obj/structure/mirror
name = "mirror"
desc = "Mirror mirror on the wall, who's the most robust of them all?"
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "mirror"
movement_type = FLOATING
density = FALSE
@@ -31,10 +31,20 @@
var/race_flags = MIRROR_MAGIC
///List of all Races that can be chosen, decided by its Initialize.
var/list/selectable_races = list()
+ ///Per-dir reflection filters
+ var/static/list/list/reflection_filters
/obj/structure/mirror/Initialize(mapload)
. = ..()
- update_choices()
+ var/static/matrix/reflection_matrix = matrix(0.75, 0, 0, 0, 0.75, 0)
+ var/datum/callback/can_reflect = CALLBACK(src, PROC_REF(can_reflect))
+ var/list/update_signals = list(COMSIG_ATOM_BREAK)
+ if (isnull(reflection_filters))
+ reflection_filters = list()
+ for (var/car_dir in GLOB.cardinals)
+ reflection_filters["[car_dir]"] = alpha_mask_filter(icon = icon('icons/obj/structures/watercloset.dmi', "mirror_mask", dir = car_dir))
+ AddComponent(/datum/component/reflection, reflection_filter = reflection_filters["[dir]"], reflection_matrix = reflection_matrix, can_reflect = can_reflect, update_signals = update_signals)
+ AddComponent(/datum/component/examine_balloon)
/obj/structure/mirror/Destroy()
mirror_options = null
@@ -45,9 +55,11 @@
for(var/i in mirror_options)
mirror_options[i] = icon('icons/hud/radial.dmi', i)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/mirror)
+
/obj/structure/mirror/Initialize(mapload)
. = ..()
- var/static/list/reflection_filter = alpha_mask_filter(icon = icon('icons/obj/watercloset.dmi', "mirror_mask"))
+ var/static/list/reflection_filter = alpha_mask_filter(icon = icon('icons/obj/structures/watercloset.dmi', "mirror_mask"))
var/static/matrix/reflection_matrix = matrix(0.75, 0, 0, 0, 0.75, 0)
var/datum/callback/can_reflect = CALLBACK(src, PROC_REF(can_reflect))
var/list/update_signals = list(COMSIG_ATOM_BREAK)
@@ -61,11 +73,9 @@
return FALSE
return TRUE
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror, 28)
-
/obj/structure/mirror/Initialize(mapload)
. = ..()
- find_and_hang_on_wall()
+ find_and_hang_on_wall(wall_layer = FLAT_ON_WALL_LAYER)
/obj/structure/mirror/broken
icon_state = "mirror_broke"
@@ -74,7 +84,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror, 28)
. = ..()
atom_break(null, mapload)
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken)
/obj/structure/mirror/attack_hand(mob/living/carbon/human/user)
. = ..()
@@ -305,14 +315,13 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
/obj/item/wallframe/mirror
name = "mirror"
desc = "An unmounted mirror. Attach it to a wall to use."
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "mirror"
custom_materials = list(
/datum/material/glass = SHEET_MATERIAL_AMOUNT,
/datum/material/silver = SHEET_MATERIAL_AMOUNT,
)
result_path = /obj/structure/mirror
- pixel_shift = 28
/obj/structure/mirror/magic
name = "magic mirror"
@@ -320,6 +329,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/mirror/broken, 28)
icon_state = "magic_mirror"
mirror_options = MAGIC_MIRROR_OPTIONS
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/mirror/magic)
+
/obj/structure/mirror/magic/Initialize(mapload)
. = ..()
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index b80cee093fc66..76a2c71848a61 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -31,7 +31,7 @@
GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants and other ghosties.
/obj/structure/bodycontainer
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/storage/storage.dmi'
icon_state = "morgue1"
density = TRUE
anchored = TRUE
@@ -594,7 +594,7 @@ GLOBAL_LIST_EMPTY(crematoriums)
/obj/structure/tray/m_tray
name = "morgue tray"
desc = "Apply corpse before closing."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/storage/storage.dmi'
icon_state = "morguet"
pass_flags_self = PASSTABLE | LETPASSTHROW
layer = /obj/structure/bodycontainer/morgue::layer - 0.03
diff --git a/code/game/objects/structures/mystery_box.dm b/code/game/objects/structures/mystery_box.dm
index e165c2e295c93..9bb51ba09cbef 100644
--- a/code/game/objects/structures/mystery_box.dm
+++ b/code/game/objects/structures/mystery_box.dm
@@ -19,7 +19,6 @@
#define MBOX_DURATION_STANDBY (2.7 SECONDS)
GLOBAL_LIST_INIT(mystery_box_guns, list(
- /obj/item/gun/energy/lasercannon,
/obj/item/gun/energy/recharge/ebow/large,
/obj/item/gun/energy/e_gun,
/obj/item/gun/energy/e_gun/nuclear,
@@ -45,6 +44,7 @@ GLOBAL_LIST_INIT(mystery_box_guns, list(
/obj/item/gun/ballistic/automatic/m90/unrestricted,
/obj/item/gun/ballistic/automatic/tommygun,
/obj/item/gun/ballistic/automatic/wt550,
+ /obj/item/gun/ballistic/automatic/smartgun,
/obj/item/gun/ballistic/rifle/sniper_rifle,
/obj/item/gun/ballistic/rifle/boltaction,
))
@@ -85,6 +85,30 @@ GLOBAL_LIST_INIT(mystery_magic, list(
/obj/item/runic_vendor_scepter,
))
+GLOBAL_LIST_INIT(mystery_fishing, list(
+ /obj/item/storage/toolbox/fishing/master,
+ /obj/item/storage/box/fish_revival_kit,
+ /obj/item/circuitboard/machine/fishing_portal_generator/emagged,
+ /obj/item/fishing_rod/telescopic/master,
+ /obj/item/bait_can/super_baits,
+ /obj/item/storage/fish_case/tiziran,
+ /obj/item/storage/fish_case/syndicate,
+ /obj/item/claymore/cutlass/old,
+ /obj/item/gun/energy/laser/retro/old,
+ /obj/item/gun/energy/laser/musket,
+ /obj/item/gun/energy/disabler/smoothbore,
+ /obj/item/gun/ballistic/rifle/boltaction/surplus,
+ /obj/item/food/rationpack,
+ /obj/item/food/canned/squid_ink,
+ /obj/item/reagent_containers/cup/glass/bottle/rum/aged,
+ /obj/item/storage/bag/money/dutchmen,
+ /obj/item/language_manual/piratespeak,
+ /obj/item/clothing/head/costume/pirate/armored,
+ /obj/item/clothing/suit/costume/pirate/armored,
+ /obj/structure/cannon/mystery_box,
+ /obj/item/stack/cannonball/trashball/four,
+ /obj/item/stack/cannonball/four,
+))
/obj/structure/mystery_box
name = "mystery box"
@@ -117,6 +141,10 @@ GLOBAL_LIST_INIT(mystery_magic, list(
var/grant_extra_mag = TRUE
/// Stores the current sound channel we're using so we can cut off our own sounds as needed. Randomized after each roll
var/current_sound_channel
+ /// How many time can it still be used?
+ var/uses_left
+ /// A list of weakrefs to mind datums of people that opened it and how many times.
+ var/list/datum/weakref/minds_that_opened_us
/obj/structure/mystery_box/Initialize(mapload)
. = ..()
@@ -126,6 +154,7 @@ GLOBAL_LIST_INIT(mystery_magic, list(
QDEL_NULL(presented_item)
if(current_sound_channel)
SSsounds.free_sound_channel(current_sound_channel)
+ minds_that_opened_us = null
return ..()
/obj/structure/mystery_box/attack_hand(mob/living/user, list/modifiers)
@@ -163,6 +192,11 @@ GLOBAL_LIST_INIT(mystery_magic, list(
current_sound_channel = SSsounds.reserve_sound_channel(src)
playsound(src, open_sound, 70, FALSE, channel = current_sound_channel, falloff_exponent = 10)
playsound(src, crate_open_sound, 80)
+ if(user.mind)
+ LAZYINITLIST(minds_that_opened_us)
+ var/datum/weakref/ref = WEAKREF(user.mind)
+ minds_that_opened_us[ref] += 1
+ uses_left--
/// The box has finished choosing, mark it as available for grabbing
/obj/structure/mystery_box/proc/present_weapon()
@@ -186,6 +220,9 @@ GLOBAL_LIST_INIT(mystery_magic, list(
box_close_timer = null
box_expire_timer = null
addtimer(CALLBACK(src, PROC_REF(ready_again)), MBOX_DURATION_STANDBY)
+ if(uses_left <= 0)
+ visible_message("[src] breaks down.")
+ deconstruct(disassembled = FALSE)
/// The cooldown between activations has finished, shake to show that
/obj/structure/mystery_box/proc/ready_again()
@@ -196,22 +233,26 @@ GLOBAL_LIST_INIT(mystery_magic, list(
/// Someone attacked the box with an empty hand, spawn the shown prize and give it to them, then close the box
/obj/structure/mystery_box/proc/grant_weapon(mob/living/user)
- var/obj/item/instantiated_weapon = new presented_item.selected_path(src)
- user.put_in_hands(instantiated_weapon)
-
- if(isgun(instantiated_weapon)) // handle pins + possibly extra ammo
- var/obj/item/gun/instantiated_gun = instantiated_weapon
- instantiated_gun.unlock()
- if(grant_extra_mag && istype(instantiated_gun, /obj/item/gun/ballistic))
- var/obj/item/gun/ballistic/instantiated_ballistic = instantiated_gun
- if(!instantiated_ballistic.internal_magazine)
- var/obj/item/ammo_box/magazine/extra_mag = new instantiated_ballistic.spawn_magazine_type(loc)
- user.put_in_hands(extra_mag)
-
+ var/atom/movable/instantiated_weapon = new presented_item.selected_path(loc)
user.visible_message(span_notice("[user] takes [presented_item] from [src]."), span_notice("You take [presented_item] from [src]."), vision_distance = COMBAT_MESSAGE_RANGE)
playsound(src, grant_sound, 70, FALSE, channel = current_sound_channel, falloff_exponent = 10)
close_box()
+ if(!isitem(instantiated_weapon))
+ return
+ user.put_in_hands(instantiated_weapon)
+
+ if(!isgun(instantiated_weapon))
+ return
+ // handle pins + possibly extra ammo
+ var/obj/item/gun/instantiated_gun = instantiated_weapon
+ instantiated_gun.unlock()
+ if(!grant_extra_mag || !istype(instantiated_gun, /obj/item/gun/ballistic))
+ return
+ var/obj/item/gun/ballistic/instantiated_ballistic = instantiated_gun
+ if(!instantiated_ballistic.internal_magazine)
+ var/obj/item/ammo_box/magazine/extra_mag = new instantiated_ballistic.spawn_magazine_type(loc)
+ user.put_in_hands(extra_mag)
/obj/structure/mystery_box/guns
desc = "A wooden crate that seems equally magical and mysterious, capable of granting the user all kinds of different pieces of gear. This one seems focused on firearms."
@@ -231,6 +272,28 @@ GLOBAL_LIST_INIT(mystery_magic, list(
/obj/structure/mystery_box/wands/generate_valid_types()
valid_types = GLOB.mystery_magic
+///One of a kind, rarely found by fishing in the ocean.
+/obj/structure/mystery_box/fishing
+ name = "treasure chest"
+ desc = "A pirate-y chest that seems equally magial and mysterious, capable of granting the user different pieces of gear."
+ icon_state = "treasure"
+ uses_left = 18
+ max_integrity = 100
+ damage_deflection = 30
+ grant_extra_mag = FALSE
+
+/obj/structure/mystery_box/handle_deconstruct(disassembled)
+ new /obj/item/stack/sheet/mineral/wood(drop_location(), 2)
+ return ..()
+
+/obj/structure/mystery_box/fishing/generate_valid_types()
+ valid_types = GLOB.mystery_fishing
+
+/obj/structure/mystery_box/fishing/activate(mob/living/user)
+ if(user.mind && minds_that_opened_us?[WEAKREF(user.mind)] >= 3)
+ to_chat(user, span_warning("[src] refuses to open to you anymore. Perhaps you should present it to someone else..."))
+ return
+ return ..()
/// This represents the item that comes out of the box and is constantly changing before the box finishes deciding. Can probably be just an /atom or /movable.
/obj/mystery_box_item
@@ -290,14 +353,14 @@ GLOBAL_LIST_INIT(mystery_magic, list(
/// animate() isn't up to the task for queueing up icon changes, so this is the proc we call with timers to update our icon
/obj/mystery_box_item/proc/update_random_icon(new_item_type)
- var/obj/item/new_item = new_item_type
- icon = initial(new_item.icon)
- icon_state = initial(new_item.icon_state)
+ var/atom/movable/new_item = new_item_type
+ icon = new_item::icon
+ icon_state = new_item::icon_state
/obj/mystery_box_item/proc/present_item()
- var/obj/item/selected_item = selected_path
+ var/atom/movable/selected_item = selected_path
add_filter("ready_outline", 2, list("type" = "outline", "color" = COLOR_VIVID_YELLOW, "size" = 0.2))
- name = initial(selected_item.name)
+ name = selected_item::name
parent_box.present_weapon()
claimable = TRUE
diff --git a/code/game/objects/structures/noticeboard.dm b/code/game/objects/structures/noticeboard.dm
index 841da89972599..9669be7364b88 100644
--- a/code/game/objects/structures/noticeboard.dm
+++ b/code/game/objects/structures/noticeboard.dm
@@ -3,7 +3,7 @@
/obj/structure/noticeboard
name = "notice board"
desc = "A board for pinning important notices upon. It is made of the finest Spanish cork."
- icon = 'icons/obj/wallmounts.dmi'
+ icon = 'icons/obj/structures/wallmounts.dmi'
icon_state = "noticeboard"
density = FALSE
anchored = TRUE
@@ -11,7 +11,7 @@
/// Current number of a pinned notices
var/notices = 0
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/noticeboard, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/noticeboard)
/obj/structure/noticeboard/Initialize(mapload)
. = ..()
@@ -26,7 +26,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/noticeboard, 32)
I.forceMove(src)
notices++
update_appearance(UPDATE_ICON)
- find_and_hang_on_wall()
+ find_and_hang_on_wall(wall_layer = FLAT_ON_WALL_LAYER)
//attaching papers!!
/obj/structure/noticeboard/attackby(obj/item/O, mob/user, params)
@@ -66,7 +66,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/noticeboard, 32)
data["items"] += list(content_data)
return data
-/obj/structure/noticeboard/ui_act(action, params)
+/obj/structure/noticeboard/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -118,17 +118,21 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/noticeboard, 32)
for(var/obj/item/content in contents)
remove_item(content)
+/obj/structure/noticeboard/update_overlays()
+ . = ..()
+ if(notices)
+ . += "notices_[notices]"
+
/obj/item/wallframe/noticeboard
name = "notice board"
desc = "Right now it's more of a clipboard. Attach to a wall to use."
- icon = 'icons/obj/wallmounts.dmi'
+ icon = 'icons/obj/structures/wallmounts.dmi'
icon_state = "noticeboard"
custom_materials = list(
/datum/material/wood = SHEET_MATERIAL_AMOUNT,
)
resistance_flags = FLAMMABLE
result_path = /obj/structure/noticeboard
- pixel_shift = 32
// Notice boards for the heads of staff (plus the qm)
diff --git a/code/game/objects/structures/pinatas.dm b/code/game/objects/structures/pinatas.dm
index 63502f12ad5e2..6483d39b1a26c 100644
--- a/code/game/objects/structures/pinatas.dm
+++ b/code/game/objects/structures/pinatas.dm
@@ -89,3 +89,28 @@
desc = "A papier-mâché corgi that contains various candy and explosives, must be set up before you can smash it."
icon_state = "pinata_syndie"
pinata_type = /obj/structure/pinata/syndie
+
+/obj/structure/pinata/donk
+ name = "donk corgi pinata"
+ desc = "A papier-mâché representation of a corgi that contains all sorts of savory treats."
+ icon_state = "pinata_donk_placed"
+ base_icon_state = "pinata_donk_placed"
+ debris = /obj/effect/decal/cleanable/wrapping/pinata/donk
+ candy_options = list(
+ /obj/item/food/donkpocket/warm,
+ /obj/item/food/donkpocket/warm/pizza,
+ /obj/item/food/donkpocket/warm/honk,
+ /obj/item/food/donkpocket/warm/berry,
+ /obj/item/food/donkpocket/warm,
+ /obj/item/food/tatortot,
+ /obj/item/gun/ballistic/automatic/pistol/toy,
+ /obj/item/hot_potato/harmless/toy,
+ /obj/item/storage/box/donkpockets,
+ /obj/item/toy/plush/donkpocket,
+ )
+
+/obj/item/pinata/donk
+ name = "\improper Donk Co. pinata assembly kit"
+ desc = "A papier-mâché corgi that contains various foodstuff and toys, must be set up before you can smash it."
+ icon_state = "pinata_donk"
+ pinata_type = /obj/structure/pinata/donk
diff --git a/code/game/objects/structures/plaques/_plaques.dm b/code/game/objects/structures/plaques/_plaques.dm
index 1277869dbf67f..cc9c556021c97 100644
--- a/code/game/objects/structures/plaques/_plaques.dm
+++ b/code/game/objects/structures/plaques/_plaques.dm
@@ -13,6 +13,8 @@
///Custom plaque structures and items both start "unengraved", once engraved with a fountain pen their text can't be altered again. Static plaques are already engraved.
var/engraved = FALSE
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque)
+
/datum/armor/structure_plaque
melee = 50
fire = 50
@@ -20,6 +22,7 @@
/obj/structure/plaque/Initialize(mapload)
. = ..()
+ find_and_hang_on_wall(wall_layer = FLAT_ON_WALL_LAYER)
register_context()
/obj/structure/plaque/add_context(atom/source, list/context, obj/item/held_item, mob/user)
@@ -103,6 +106,7 @@
engraved = TRUE //The plaque now has a name, description, and can't be altered again.
user.visible_message(span_notice("[user] engraves [src]."), \
span_notice("You engrave [src]."))
+ icon_state = "goldenplaque"
return
if(istype(I, /obj/item/pen))
if(engraved)
@@ -193,14 +197,6 @@
var/obj/structure/plaque/placed_plaque = new plaque_path(user_turf) //We place the plaque on the turf the user is standing, and pixel shift it to the target wall, as below.
//This is to mimic how signs and other wall objects are usually placed by mappers, and so they're only visible from one side of a wall.
var/dir = get_dir(user_turf, target_turf)
- if(dir & NORTH)
- placed_plaque.pixel_y = 32
- else if(dir & SOUTH)
- placed_plaque.pixel_y = -32
- if(dir & EAST)
- placed_plaque.pixel_x = 32
- else if(dir & WEST)
- placed_plaque.pixel_x = -32
user.visible_message(span_notice("[user] fastens [src] to [target_turf]."), \
span_notice("You attach [src] to [target_turf]."))
playsound(target_turf, 'sound/items/deconstruct.ogg', 50, TRUE)
diff --git a/code/game/objects/structures/plaques/static_plaques.dm b/code/game/objects/structures/plaques/static_plaques.dm
index 3ae3b66b71b66..7fade5654645f 100644
--- a/code/game/objects/structures/plaques/static_plaques.dm
+++ b/code/game/objects/structures/plaques/static_plaques.dm
@@ -2,23 +2,35 @@
/obj/structure/plaque/static_plaque
engraved = TRUE
+ icon_state = "goldenplaque"
+
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque)
/obj/structure/plaque/static_plaque/atmos
name = "\improper FEA Atmospherics Division plaque"
desc = "This plaque commemorates the fall of the Atmos FEA division. For all the charred, dizzy, and brittle men who have died in its hands."
+ icon_state = "employeeplaque"
+
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/atmos)
/obj/structure/plaque/static_plaque/thunderdome
name = "Thunderdome Plaque"
desc = "This plaque commemorates those who have fallen in glorious combat. For all the charred, dizzy, and beaten men who have died in its hands."
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/thunderdome)
+
/obj/structure/plaque/static_plaque/golden
name = "The Most Robust Men Award for Robustness"
desc = "To be Robust is not an action or a way of life, but a mental state. Only those with the force of Will strong enough to act during a crisis, saving friend from foe, are truly Robust. Stay Robust my friends."
- icon_state = "goldenplaque"
+ icon_state = "employeeplaque"
+
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden)
/obj/structure/plaque/static_plaque/golden/captain
name = "The Most Robust Captain Award for Robustness"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/captain)
+
/obj/structure/plaque/static_plaque/tram
/// The tram we have info about
var/specific_transport_id = TRAMSTATION_LINE_1
@@ -30,6 +42,10 @@
icon_state = "commission_tram"
custom_materials = list(/datum/material/titanium = SHEET_MATERIAL_AMOUNT)
layer = SIGN_LAYER
+ pixel_y = -29
+ pixel_z = 29
+
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/tram)
/obj/structure/plaque/static_plaque/tram/Initialize(mapload)
. = ..()
@@ -107,90 +123,132 @@
icon_state = "commission_nt"
layer = BELOW_OPEN_DOOR_LAYER
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission)
+
//Current stations
// Icebox Station: added May 13, 2020 (#51090)
/obj/structure/plaque/static_plaque/golden/commission/icebox
desc = "Spinward Sector Station SS-13\n'Box' Class Outpost (Revision 2.2: 'Icebox')\nCommissioned 13/05/2560\n'Cold Reliable'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/icebox)
+
// Metastation: added Mar 11, 2013 (best estimate, pre-git)
/obj/structure/plaque/static_plaque/golden/commission/meta
desc = "Spinward Sector Station SS-13\n'Meta' Class Outpost\nCommissioned 11/03/2553\n'Theseus' Station'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/meta)
+
// Deltastation: added Dec 17, 2016 (#22066)
/obj/structure/plaque/static_plaque/golden/commission/delta
desc = "Spinward Sector Station SS-13\n'Delta' Class Outpost\nCommissioned 17/12/2556\n'Efficiency Through Redundancy'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/delta)
+
// Tramstation: added Mar 11, 2021 (#56509)
/obj/structure/plaque/static_plaque/golden/commission/tram
desc = "Spinward Sector Station SS-13\n'Tram' Class Outpost\nCommissioned 11/03/2561\n'Making Moves'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/tram)
+
// Wawastation: added add date here
/obj/structure/plaque/static_plaque/golden/commission/wawa
desc = "Spinward Sector Station SS-13\n'Wawa' Class Outpost\nCommissioned 11/03/add here\n'Forever Vertical'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/wawa)
+
// North Star: added Apr 13, 2023 (#74371)
/obj/structure/plaque/static_plaque/golden/commission/northstar
desc = "Spinward Sector Ship SS-13\n'North Star' Class Vessel\nCommissioned 13/04/2563\n'New Opportunities'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/northstar)
+
// Birdshot: added Apr 29, 2023 (#74371)
/obj/structure/plaque/static_plaque/golden/commission/birdshot
desc = "Spinward Sector Station SS-13\n'Birdshot' Class Outpost\nCommissioned 29/04/2563\n'Shooting for the Stars'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/birdshot)
+
//Removed stations
// Asteroidstation: added Oct 17, 2015 (169ab09f7b52254ee505e54cdea681fab287647b), removed Jun 19, 2016 (#18661)- 8 months, 2 days
/obj/structure/plaque/static_plaque/golden/commission/asteroid
desc = "Spinward Sector Station SS-12\n'Asteroid' Class Outpost\nCommissioned 13/10/2555\nDecommissioned 19/06/2556\n'A Meteoric Success'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/asteroid)
+
// Birdboat Station: added Sep 17, 2015 (#11829), removed Feb 09, 2017 (#23754)- 1 year, 4 months, 23 days
/obj/structure/plaque/static_plaque/golden/commission/birdboat
desc = "Spinward Sector Station SS-03\n'Birdboat' Class Outpost\nCommissioned 17/09/2555\nDecommissioned 09/02/2557\n'Rocking the Boat'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/birdboat)
+
// Boxstation: added Nov 15, 2010 (pre-git), removed Jul 06, 2020 (#52017)- 9 years, 7 months, 21 days
/obj/structure/plaque/static_plaque/golden/commission/box
desc = "Spinward Sector Station SS-02\n'Box' Class Outpost\nCommissioned 15/11/2550\nDecommissioned 06/07/2560\n'Old Faithful'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/box)
+
// Pubbystation: added Oct 19, 2016 (#20925), removed Dec 10, 2020 (#54588)- 4 years, 1 month, 21 days
/obj/structure/plaque/static_plaque/golden/commission/pubby
desc = "Spinward Sector Station SS-06\n'Pubby' Class Outpost\nCommissioned 19/10/2556\nDecommissioned 10/12/2560\n'No Law But Ours'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/pubby)
+
// Cerestation: added Mar 29, 2017 (#24665), removed Aug 26th, 2017 (#30196)- 4 months, 28 days
/obj/structure/plaque/static_plaque/golden/commission/cere
desc = "Spinward Sector Station SS-10\n'Cere' Class Outpost\nCommissioned 29/03/2557\nDecommissioned 26/08/2557\n'Take a Hike'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/cere)
+
// Discstation: added Sep 21, 2015 (#11923), removed Jan 31, 2016 (#15069)- 4 months, 10 days
/obj/structure/plaque/static_plaque/golden/commission/disc
desc = "Spinward Sector Station SS-05\n'Disc' Class Outpost\nCommissioned 21/09/2555\nDecommissioned 31/01/2556\n'Sleep Tight'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/disc)
+
// Donutstation: added Dec 16, 2018 (#41099), removed Apr 28, 2020 (#50730)- 1 year, 4 months, 12 days
/obj/structure/plaque/static_plaque/golden/commission/donut
desc = "Spinward Sector Station SS-11\n'Donut' Class Outpost\nCommissioned 16/12/2558\nDecommissioned 28/04/2560\n'Hail the Lord'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/donut)
+
// Dreamstation: added Oct 06, 2015 (#12154), removed Dec 22, 2016 (#22305)- 1 year, 2 months, 16 days
/obj/structure/plaque/static_plaque/golden/commission/dream
desc = "Spinward Sector Station SS-04\n'Dream' Class Outpost\nCommissioned 06/10/2555\nDecommissioned 22/12/2556\n'Aiming High'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/dream)
+
// Efficiencystation: added Jan 28, 2016 (46f64266cfb8b40e35faa8a4d9a2d3aeec689943), removed Dec 20, 2016 (#22306)- 10 months, 22 days
/obj/structure/plaque/static_plaque/golden/commission/efficiency
desc = "Spinward Sector Station SS-07\n'Efficiency' Class Outpost\nCommissioned 28/01/2556\nDecommissioned 20/12/2556\n'Work Smarter, Not Harder'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/efficiency)
+
// Kilostation: added Nov 13, 2019 (#46968), removed
/obj/structure/plaque/static_plaque/golden/commission/kilo
desc = "Spinward Sector Station SS-13\n'Kilo' Class Outpost\nCommissioned 13/11/2559\nDecommissioned \n'Forever Different'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/kilo)
+
// Ministation: added Jan 29, 2014 (7a76e9456b782e6626bf81e27a912d8232c76b18), removed Dec 27, 2016 (#22453)- 2 years, 10 months, 28 days
/obj/structure/plaque/static_plaque/golden/commission/mini
desc = "Spinward Sector Station SS-08\n'Mini' Class Outpost\nCommissioned 29/01/2554\nDecommissioned 27/12/2556\n'The Littlest Station'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/mini)
+
// Omegastation: added Dec 27, 2016 (#22453), removed Sep 20, 2018 (#40352)- 1 year, 8 months, 24 days
/obj/structure/plaque/static_plaque/golden/commission/omega
desc = "Spinward Sector Station SS-09\n'Omega' Class Outpost\nCommissioned 27/12/2556\nDecommissioned 20/09/2558\n'Tiny Take Two'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/omega)
+
// Uterusstation: added Sep 03, 2011 (bbd6db9ce2d6341892b89a620593fc8877f5a817), removed Jun 21, 2012 (72d72f7ce522c2d2ad4863f44ee9f5054413c489)- 9 months, 18 days
/obj/structure/plaque/static_plaque/golden/commission/uterus
desc = "Spinward Sector Station SS-01\n'Uterus' Class Outpost\nCommissioned 03/09/2551\nDecommissioned 21/06/2552\n'Humanity's Vanguard'"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/uterus)
+
// Other Stations
// Space Station 13, Developer Class Outpost, Station Commissioned 30.12.2322, For the Glory of the Workers of the Third Soviet Union
@@ -199,6 +257,8 @@
desc = "космическая-станция-13\nфорпост класса разработчика\nстанция сдана 30.12.2322\nво славу тружеников третьего советского союза"
icon_state = "commission_commie"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/plaque/static_plaque/golden/commission/ks13)
+
//These are plaques that aren't made of metal, so we'll just consider them signs. Those are made of plastic (default) or wood, not gold.
//See: code>game>objects>structures>signs>_signs.dm
@@ -207,26 +267,40 @@
desc = "Next to the extremely long list of names and job titles, there is a drawing of a little child. The child appears to be disabled. Beneath the image, someone has scratched the word \"PACKETS\"."
icon_state = "kiddieplaque"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/plaques/kiddie)
+
/obj/structure/sign/plaques/kiddie/devils_tooth
name = "\improper Devil's Tooth Plaque"
desc = "A plaque commemorating the fallen souls who had to die tunneling out this segment of the frozen ice planet that surrounds it. It's named \"Devil's Tooth\" because those who laid down their life here surely thought they were in hell."
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/plaques/kiddie/devils_tooth)
+
/obj/structure/sign/plaques/kiddie/badger
name = "\improper Remembrance Plaque"
desc = "A plaque commemorating the fallen, may they rest in peace, forever asleep amongst the stars. Someone has drawn a picture of a crying badger at the bottom."
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/plaques/kiddie/badger)
+
/obj/structure/sign/plaques/kiddie/library
name = "\improper Library Rules Sign"
desc = "A long list of rules to be followed when in the library, extolling the virtues of being quiet at all times and threatening those who would dare eat hot food inside."
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/plaques/kiddie/library)
+
/obj/structure/sign/plaques/kiddie/perfect_man
name = "\improper 'Perfect Man' sign"
desc = "A guide to the exhibit, explaining how recent developments in mindshield implant and cloning technologies by Nanotrasen Corporation have led to the development and the effective immortality of the 'perfect man', the loyal Nanotrasen Employee."
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/plaques/kiddie/perfect_man)
+
/obj/structure/sign/plaques/kiddie/perfect_drone
name = "\improper 'Perfect Drone' sign"
desc = "A guide to the drone shell dispenser, detailing the constructive and destructive applications of modern repair drones, as well as the development of the incorruptible cyborg servants of tomorrow, available today."
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/plaques/kiddie/perfect_drone)
+
/obj/structure/sign/plaques/kiddie/gameoflife
name = "\improper Conway's The Game Of Life plaque"
desc = "A plaque detailing the historical significance of The Game Of Life in the field of computer science, and that the mural underfoot is a representation of the game in action."
+
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/plaques/kiddie/gameoflife)
diff --git a/code/game/objects/structures/plasticflaps.dm b/code/game/objects/structures/plasticflaps.dm
index a5ed048153c41..e7c753f2fbde4 100644
--- a/code/game/objects/structures/plasticflaps.dm
+++ b/code/game/objects/structures/plasticflaps.dm
@@ -2,7 +2,7 @@
name = "airtight plastic flaps"
desc = "Heavy duty, airtight, plastic flaps. Definitely can't get past those. No way."
gender = PLURAL
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/structures/tall.dmi'
icon_state = "plasticflaps"
armor_type = /datum/armor/structure_plasticflaps
density = FALSE
@@ -24,20 +24,24 @@
/obj/structure/plasticflaps/Initialize(mapload)
. = ..()
- alpha = 0
- gen_overlay()
+ // Render targeting big icons shifts em down, lets counteract
+ pixel_z = 16
+ AddElement(/datum/element/render_over_keep_hitbox)
+ // We need to shift overlays drawn to use down to counteract the counteraction. I hate byond
+ AddComponent(/datum/component/vis_block, "standard", "standard", parent_z_shift = -16)
air_update_turf(TRUE, TRUE)
+ if(mapload)
+ return INITIALIZE_HINT_LATELOAD
-/obj/structure/plasticflaps/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
- if(same_z_layer)
- return ..()
- SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
- gen_overlay()
- return ..()
+/obj/structure/plasticflaps/LateInitialize(mapload)
+ if(mapload)
+ auto_align()
-/obj/structure/plasticflaps/proc/gen_overlay()
- var/turf/our_turf = get_turf(src)
- SSvis_overlays.add_vis_overlay(src, icon, icon_state, ABOVE_MOB_LAYER, MUTATE_PLANE(GAME_PLANE, our_turf), dir, add_appearance_flags = RESET_ALPHA) //you see mobs under it, but you hit them like they are above it
+/obj/structure/plasticflaps/Destroy()
+ var/atom/oldloc = loc
+ . = ..()
+ if (oldloc)
+ oldloc.air_update_turf(TRUE, FALSE)
/obj/structure/plasticflaps/examine(mob/user)
. = ..()
@@ -124,12 +128,5 @@
if(living_mover.body_position == STANDING_UP && living_mover.mob_size != MOB_SIZE_TINY && !(HAS_TRAIT(living_mover, TRAIT_VENTCRAWLER_ALWAYS) || HAS_TRAIT(living_mover, TRAIT_VENTCRAWLER_NUDE)))
return FALSE //If you're not laying down, or a small creature, or a ventcrawler, then no pass.
-
/obj/structure/plasticflaps/atom_deconstruct(disassembled = TRUE)
new /obj/item/stack/sheet/plastic/five(loc)
-
-/obj/structure/plasticflaps/Destroy()
- var/atom/oldloc = loc
- . = ..()
- if (oldloc)
- oldloc.air_update_turf(TRUE, FALSE)
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
index c3d9b115f01de..897135cdd7669 100644
--- a/code/game/objects/structures/railings.dm
+++ b/code/game/objects/structures/railings.dm
@@ -1,7 +1,7 @@
/obj/structure/railing
name = "railing"
desc = "Basic railing meant to protect idiots like you from falling."
- icon = 'icons/obj/railings.dmi'
+ icon = 'icons/obj/structures/railings.dmi'
icon_state = "railing"
flags_1 = ON_BORDER_1
obj_flags = CAN_BE_HIT | BLOCKS_CONSTRUCTION_DIR
@@ -25,17 +25,29 @@
energy = 100
bomb = 10
+/obj/structure/railing/unbreakable
+ resistance_flags = INDESTRUCTIBLE
+
/obj/structure/railing/corner //aesthetic corner sharp edges hurt oof ouch
icon_state = "railing_corner"
density = FALSE
climbable = FALSE
+/obj/structure/railing/corner/unbreakable
+ resistance_flags = INDESTRUCTIBLE
+
/obj/structure/railing/corner/end //end of a segment of railing without making a loop
icon_state = "railing_end"
+/obj/structure/railing/corner/end/unbreakable
+ resistance_flags = INDESTRUCTIBLE
+
/obj/structure/railing/corner/end/flip //same as above but flipped around
icon_state = "railing_end_flip"
+/obj/structure/railing/corner/end/flip/unbreakable
+ resistance_flags = INDESTRUCTIBLE
+
/obj/structure/railing/Initialize(mapload)
. = ..()
if(climbable)
@@ -61,6 +73,11 @@
AddElement(/datum/element/contextual_screentip_tools, tool_behaviors)
AddComponent(/datum/component/simple_rotation, ROTATION_NEEDS_ROOM)
+ update_appearance()
+
+/obj/structure/railing/update_appearance(updates)
+ . = ..()
+ update_layering()
/obj/structure/railing/examine(mob/user)
. = ..()
@@ -119,6 +136,15 @@
return TRUE
return ..()
+/obj/structure/railing/proc/update_layering()
+ // If we're on a north edge, render as if we were "higher" then we are
+ if(dir & NORTH)
+ pixel_y = 32
+ pixel_z = -32
+ else
+ pixel_y = 0
+ pixel_z = 0
+
/obj/structure/railing/proc/on_exit(datum/source, atom/movable/leaving, direction)
SIGNAL_HANDLER
@@ -151,10 +177,11 @@
/obj/structure/railing/wooden_fence
name = "wooden fence"
desc = "wooden fence meant to keep animals in."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/structures/railings.dmi'
icon_state = "wooden_railing"
item_deconstruct = /obj/item/stack/sheet/mineral/wood
layer = ABOVE_MOB_LAYER
+ plane = GAME_PLANE
/obj/structure/railing/wooden_fence/Initialize(mapload)
. = ..()
@@ -167,13 +194,12 @@
/obj/structure/railing/wooden_fence/proc/adjust_dir_layer(direction)
layer = (direction & NORTH) ? MOB_LAYER : initial(layer)
- plane = (direction & NORTH) ? GAME_PLANE : initial(plane)
/obj/structure/railing/corner/end/wooden_fence
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/structures/railings.dmi'
icon_state = "wooden_railing_corner"
/obj/structure/railing/corner/end/flip/wooden_fence
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/structures/railings.dmi'
icon_state = "wooden_railing_corner_flipped"
diff --git a/code/game/objects/structures/reflector.dm b/code/game/objects/structures/reflector.dm
index e27f5fcf42b40..aed8693c2b81d 100644
--- a/code/game/objects/structures/reflector.dm
+++ b/code/game/objects/structures/reflector.dm
@@ -1,6 +1,6 @@
/obj/structure/reflector
name = "reflector base"
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/machines/engine/reflector.dmi'
icon_state = "reflector_map"
desc = "A base for reflector assemblies."
anchored = FALSE
@@ -311,7 +311,7 @@
return data
-/obj/structure/reflector/ui_act(action, params)
+/obj/structure/reflector/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm
index bcc9d1386955b..3fff77faf89e8 100644
--- a/code/game/objects/structures/safe.dm
+++ b/code/game/objects/structures/safe.dm
@@ -13,7 +13,7 @@ FLOOR SAFES
/obj/structure/safe
name = "safe"
desc = "A huge chunk of metal with a dial embedded in it. Fine print on the dial reads \"Scarborough Arms - 2 tumbler safe, guaranteed thermite resistant, explosion resistant, and assistant resistant.\""
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/storage/storage.dmi'
icon_state = "safe"
anchored = TRUE
density = TRUE
@@ -129,7 +129,7 @@ FLOOR SAFES
return data
-/obj/structure/safe/ui_act(action, params)
+/obj/structure/safe/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/secure_safe.dm b/code/game/objects/structures/secure_safe.dm
index 9e1bf9b5adef9..e91e27da058f9 100644
--- a/code/game/objects/structures/secure_safe.dm
+++ b/code/game/objects/structures/secure_safe.dm
@@ -5,7 +5,6 @@
icon_state = "wall_safe"
base_icon_state = "wall_safe"
result_path = /obj/structure/secure_safe
- pixel_shift = 32
/obj/item/wallframe/secure_safe/Initialize(mapload)
. = ..()
@@ -34,7 +33,7 @@
anchored = TRUE
density = FALSE
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/secure_safe, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/secure_safe)
/obj/structure/secure_safe/Initialize(mapload)
. = ..()
@@ -55,9 +54,33 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/secure_safe, 32)
new /obj/item/paper(src)
new /obj/item/pen(src)
+/obj/structure/secure_safe/update_icon()
+ ..()
+ if(!atom_storage)
+ return
+ if(atom_storage.locked)
+ icon_state = "[initial(icon_state)]_locked"
+ else
+ icon_state = "[initial(icon_state)]_open"
+
+/obj/structure/secure_safe/update_overlays()
+ . = ..()
+ if(!atom_storage)
+ return
+ if(atom_storage.locked) //No door if we're locked.
+ return
+ var/mutable_appearance/door_overlay = mutable_appearance(icon, "[initial(icon_state)]_door") // Wallening todo: This needs attention for wall safes.
+ if(dir == SOUTH)
+ door_overlay.pixel_y = -1
+ else if(dir == WEST)
+ door_overlay.pixel_y = -6
+ . += door_overlay
+
/obj/structure/secure_safe/hos
name = "head of security's safe"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/secure_safe/hos)
+
/**
* This safe is meant to be damn robust. To break in, you're supposed to get creative, or use acid or an explosion.
*
@@ -73,7 +96,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/secure_safe, 32)
It is made out of the same material as the station's Black Box and is designed to resist all conventional weaponry. \
There appears to be a small amount of surface corrosion. It doesn't look like it could withstand much of an explosion.\
Due to the expensive material, it was made incredibly small to cut corners, leaving only enough room to fit something as slim as an ID card."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/storage/storage.dmi'
icon_state = "spare_safe"
base_icon_state = "spare_safe"
armor_type = /datum/armor/safe_caps_spare
diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm
index 350adcb11f1ab..bc204d773084f 100644
--- a/code/game/objects/structures/shower.dm
+++ b/code/game/objects/structures/shower.dm
@@ -4,7 +4,7 @@
#define SHOWER_NORMAL_TEMP 300
#define SHOWER_BOILING "boiling"
#define SHOWER_BOILING_TEMP 400
-/// The volume of it's internal reagents the shower applies to everything it sprays.
+/// The volume of its internal reagents the shower applies to everything it sprays.
#define SHOWER_SPRAY_VOLUME 5
/// How much the volume of the shower's spay reagents are amplified by when it sprays something.
#define SHOWER_EXPOSURE_MULTIPLIER 2 // Showers effectively double exposed reagents
@@ -29,7 +29,7 @@ GLOBAL_LIST_INIT(shower_mode_descriptions, list(
/obj/machinery/shower
name = "shower"
desc = "The HS-452. Installed in the 2550s by the Nanotrasen Hygiene Division, now with 2560 lead compliance! Passively replenishes itself with water when not in use."
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "shower"
density = FALSE
layer = ABOVE_WINDOW_LAYER
@@ -49,16 +49,14 @@ GLOBAL_LIST_INIT(shower_mode_descriptions, list(
var/reagent_capacity = 200
///How many units the shower refills every second.
var/refill_rate = 0.5
- ///Does the shower have a water recycler to recollect it's water supply?
+ ///Does the shower have a water recycler to recollect its water supply?
var/has_water_reclaimer = TRUE
///Which mode the shower is operating in.
var/mode = SHOWER_MODE_UNTIL_EMPTY
///The cooldown for SHOWER_MODE_TIMED mode.
COOLDOWN_DECLARE(timed_cooldown)
- ///How far to shift the sprite when placing.
- var/pixel_shift = 16
-MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16))
+SHOWER_DIRECTIONAL_HELPERS(/obj/machinery/shower)
/obj/machinery/shower/Initialize(mapload, ndir = 0, has_water_reclaimer = null)
. = ..()
@@ -69,29 +67,16 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16))
if(has_water_reclaimer != null)
src.has_water_reclaimer = has_water_reclaimer
- switch(dir)
- if(NORTH)
- pixel_x = 0
- pixel_y = -pixel_shift
- if(SOUTH)
- pixel_x = 0
- pixel_y = pixel_shift
- if(EAST)
- pixel_x = -pixel_shift
- pixel_y = 0
- if(WEST)
- pixel_x = pixel_shift
- pixel_y = 0
-
create_reagents(reagent_capacity)
if(src.has_water_reclaimer)
reagents.add_reagent(reagent_id, reagent_capacity)
soundloop = new(src, FALSE)
- AddComponent(/datum/component/plumbing/simple_demand, extend_pipe_to_edge = TRUE)
+ AddComponent(/datum/component/plumbing/inverted_simple_demand, extend_pipe_to_edge = TRUE, invert_demand = TRUE)
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
+ find_and_hang_on_wall()
/obj/machinery/shower/examine(mob/user)
. = ..()
@@ -188,21 +173,23 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16))
deconstruct()
return TRUE
+/obj/machinery/shower/setDir(newdir)
+ . = ..()
+ update_appearance(UPDATE_OVERLAYS)
+
/obj/machinery/shower/update_overlays()
. = ..()
if(!actually_on)
return
- var/mutable_appearance/water_falling = mutable_appearance('icons/obj/watercloset.dmi', "water", ABOVE_MOB_LAYER)
+ var/mutable_appearance/water_falling = mutable_appearance('icons/obj/structures/watercloset.dmi', "water", ABOVE_MOB_LAYER)
water_falling.color = mix_color_from_reagents(reagents.reagent_list)
switch(dir)
- if(NORTH)
- water_falling.pixel_y += pixel_shift
if(SOUTH)
- water_falling.pixel_y -= pixel_shift
+ water_falling.pixel_z -= 24
if(EAST)
- water_falling.pixel_x += pixel_shift
+ water_falling.pixel_w += 16
if(WEST)
- water_falling.pixel_x -= pixel_shift
+ water_falling.pixel_w -= 16
. += water_falling
/obj/machinery/shower/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
@@ -333,7 +320,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16))
/obj/structure/showerframe
name = "shower frame"
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "shower_frame"
desc = "A shower frame, that needs a water recycler to finish construction."
anchored = FALSE
@@ -369,10 +356,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16))
/obj/effect/mist
name = "mist"
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "mist"
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
diff --git a/code/game/objects/structures/signs/_signs.dm b/code/game/objects/structures/signs/_signs.dm
index 9268cb9c059ce..2d84aada386d0 100644
--- a/code/game/objects/structures/signs/_signs.dm
+++ b/code/game/objects/structures/signs/_signs.dm
@@ -22,11 +22,18 @@
fire = 50
acid = 50
+/obj/structure/sign/blank //This subtype is necessary for now because some other things (posters, picture frames, paintings) inheret from the parent type.
+ icon_state = "backing"
+ name = "sign backing"
+ desc = "A plastic sign backing, use a pen to change the decal. It can be detached from the wall with a wrench."
+ is_editable = TRUE
+ sign_change_name = "Blank Sign"
+
/obj/structure/sign/Initialize(mapload)
. = ..()
register_context()
knock_down_callback = CALLBACK(src, PROC_REF(knock_down))
- find_and_hang_on_wall(custom_drop_callback = knock_down_callback)
+ find_and_hang_on_wall(custom_drop_callback = knock_down_callback, wall_layer = FLAT_ON_WALL_LAYER)
/obj/structure/sign/Destroy()
. = ..()
@@ -217,14 +224,6 @@
var/obj/structure/sign/placed_sign = new sign_path(user_turf) //We place the sign on the turf the user is standing, and pixel shift it to the target wall, as below.
//This is to mimic how signs and other wall objects are usually placed by mappers, and so they're only visible from one side of a wall.
var/dir = get_dir(user_turf, target_turf)
- if(dir & NORTH)
- placed_sign.pixel_y = 32
- else if(dir & SOUTH)
- placed_sign.pixel_y = -32
- if(dir & EAST)
- placed_sign.pixel_x = 32
- else if(dir & WEST)
- placed_sign.pixel_x = -32
user.visible_message(span_notice("[user] fastens [src] to [target_turf]."), \
span_notice("You attach the sign to [target_turf]."))
playsound(target_turf, 'sound/items/deconstruct.ogg', 50, TRUE)
diff --git a/code/game/objects/structures/signs/signs_departments.dm b/code/game/objects/structures/signs/signs_departments.dm
index 532cbcfc426ad..f6967195662e0 100644
--- a/code/game/objects/structures/signs/signs_departments.dm
+++ b/code/game/objects/structures/signs/signs_departments.dm
@@ -1,7 +1,30 @@
+
//departmental signs
/obj/structure/sign/departments
+ icon = 'icons/obj/structures/departmental_signs.dmi'
is_editable = TRUE
+ var/emissive_type
+
+/obj/structure/sign/departments/Initialize(mapload)
+ . = ..()
+ if (!emissive_type)
+ return
+ var/area/cur_area = get_area(src)
+ if (!isnull(cur_area))
+ RegisterSignal(cur_area, COMSIG_AREA_POWER_CHANGE, PROC_REF(power_check))
+
+/obj/structure/sign/departments/proc/power_check()
+ SIGNAL_HANDLER
+ update_appearance()
+
+/obj/structure/sign/departments/update_overlays()
+ . = ..()
+ if (!emissive_type)
+ return
+ var/area/cur_area = get_area(src)
+ if (!isnull(cur_area) && cur_area.power_light)
+ . += emissive_appearance(icon, emissive_type, src)
///////MEDBAY
@@ -11,14 +34,14 @@
desc = "A sign labelling an area of the medical department."
icon_state = "med"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/med, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/med)
/obj/structure/sign/departments/med_alt
name = "\improper Medbay sign"
sign_change_name = "Department - Medbay Alt"
icon_state = "medbay"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/med_alt, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/med_alt)
/obj/structure/sign/departments/medbay
name = "\improper Medbay sign"
@@ -26,14 +49,15 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/med_alt, 32)
desc = "The intergalactic symbol of medical institutions. You'll probably get help here."
icon_state = "bluecross"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/medbay, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/medbay)
/obj/structure/sign/departments/medbay/alt
name = "\improper Medbay sign"
sign_change_name = "Generic Medical Alt"
- icon_state = "bluecross2"
+ icon_state = "department_med"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/medbay/alt, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/medbay/alt)
/obj/structure/sign/departments/exam_room
name = "\improper Exam Room sign"
@@ -41,37 +65,40 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/medbay/alt, 32)
desc = "A guidance sign which reads 'Exam Room'."
icon_state = "examroom"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/exam_room, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/exam_room)
/obj/structure/sign/departments/chemistry
name = "\improper Chemistry sign"
sign_change_name = "Department - Medbay: Chemistry"
desc = "A sign labelling an area containing chemical equipment."
- icon_state = "chemistry1"
+ icon_state = "department_chem"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/chemistry, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/chemistry)
/obj/structure/sign/departments/chemistry/alt
sign_change_name = "Department - Medbay: Chemistry Alt"
icon_state = "chemistry2"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/chemistry/alt, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/chemistry/alt)
/obj/structure/sign/departments/chemistry/pharmacy
name = "\improper Pharmacy sign"
sign_change_name = "Department - Medbay: Pharmacy"
desc = "A sign labelling an area containing pharmacy equipment."
- icon_state = "pharmacy"
+ icon_state = "department_chem"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/chemistry/pharmacy, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/chemistry/pharmacy)
/obj/structure/sign/departments/psychology
name = "\improper Psychology sign"
sign_change_name = "Department - Medbay: Psychology"
desc = "A sign labelling an area where the Psychologist works, they can probably help you get your head straight."
- icon_state = "psychology"
+ icon_state = "department_psych"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/psychology, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/psychology)
/obj/structure/sign/departments/virology
name = "\improper Virology sign"
@@ -79,7 +106,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/psychology, 32)
desc = "A sign labelling an area where the virologist's laboratory is located."
icon_state = "pharmacy"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/virology, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/virology)
/obj/structure/sign/departments/morgue
name = "\improper Morgue sign"
@@ -87,7 +114,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/virology, 32)
desc = "A sign labelling an area where the station stores its ever-piling bodies."
icon_state = "morgue"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/morgue, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/morgue)
///////ENGINEERING
@@ -95,9 +122,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/morgue, 32)
name = "\improper Engineering sign"
sign_change_name = "Department - Engineering"
desc = "A sign labelling an area where engineers work."
- icon_state = "engine"
+ icon_state = "department_engi"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/engineering, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/engineering)
///////SCIENCE
@@ -105,45 +133,49 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/engineering, 32)
name = "\improper Science sign"
sign_change_name = "Department - Science"
desc = "A sign labelling an area where research and science is performed."
- icon_state = "science1"
+ icon_state = "department_sci"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/science, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/science)
/obj/structure/sign/departments/science/alt
sign_change_name = "Department - Science Alt"
icon_state = "science2"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/science/alt, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/science/alt)
+
/obj/structure/sign/departments/xenobio
name = "\improper Xenobiology sign"
sign_change_name = "Department - Science: Xenobiology"
desc = "A sign labelling an area where xenobiological entities are researched."
- icon_state = "xenobio1"
+ icon_state = "department_xeno"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/xenobio, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/xenobio)
+// Wallening todo: we need a new sprite for this
/obj/structure/sign/departments/xenobio/alt
sign_change_name = "Department - Science: Xenobiology Alt"
icon_state = "xenobio2"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/xenobio/alt, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/xenobio/alt)
+// Wallening todo: new sprite for htis, clear it out of signs.dmi
/obj/structure/sign/departments/genetics
name = "\improper Genetics sign"
sign_change_name = "Department - Science: Genetics"
desc = "A sign labelling an area where the field of genetics is researched."
icon_state = "gene"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/genetics, 32)
-
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/genetics)
/obj/structure/sign/departments/rndserver
name ="\improper R&D Server sign"
sign_change_name = "Department - Science: R&D Server"
desc = "A sign labelling an area where scientific data is stored."
icon_state = "rndserver"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/rndserver, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/rndserver)
///////SERVICE
@@ -151,27 +183,28 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/rndserver, 32)
name = "\improper Botany sign"
sign_change_name = "Department - Botany (Flower)"
desc = "A sign labelling an area as a place where plants are grown."
- icon_state = "hydro1"
+ icon_state = "department_hydro"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/botany, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/botany)
/obj/structure/sign/departments/botany/alt1
sign_change_name = "Department - Botany (Tray)"
icon_state = "hydro2"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/botany/alt1, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/botany/alt1)
/obj/structure/sign/departments/botany/alt2
sign_change_name = "Department - Botany (Watering Can)"
icon_state = "hydro3"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/botany/alt2, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/botany/alt2)
/obj/structure/sign/departments/botany/botany/alt3
sign_change_name = "Department - Botany (Tray) Alt"
icon_state = "botany"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/botany/alt3, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/botany/alt3)
/obj/structure/sign/departments/custodian
name = "\improper Janitor sign"
@@ -179,15 +212,16 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/botany/alt3, 32)
desc = "A sign labelling an area where the janitor works."
icon_state = "custodian"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/custodian, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/custodian)
/obj/structure/sign/departments/holy
name = "\improper Chapel sign"
sign_change_name = "Department - Chapel"
desc = "A sign labelling a religious area."
- icon_state = "holy"
+ icon_state = "department_chapel"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/holy, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/holy)
/obj/structure/sign/departments/holy_alt
name = "\improper Chapel sign"
@@ -195,15 +229,16 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/holy, 32)
desc = "A sign labelling a religious area."
icon_state = "chapel"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/holy, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/holy)
/obj/structure/sign/departments/lawyer
name = "\improper Legal Department sign"
sign_change_name = "Department - Legal"
desc = "A sign labelling an area where the Lawyers work, apply here for arrivals shuttle whiplash settlement."
- icon_state = "lawyer"
+ icon_state = "department_lawyer"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/lawyer, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/lawyer)
///////SUPPLY
@@ -211,9 +246,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/lawyer, 32)
name = "\improper Cargo sign"
sign_change_name = "Department - Cargo"
desc = "A sign labelling an area where cargo ships dock."
- icon_state = "cargo"
+ icon_state = "department_cargo"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/cargo, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/cargo)
/obj/structure/sign/departments/exodrone
name = "\improper Exodrone sign"
@@ -221,7 +257,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/cargo, 32)
desc = "A sign labelling an area where exodrones are used."
icon_state = "exodrone"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/exodrone, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/exodrone)
///////SECURITY
@@ -229,9 +265,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/exodrone, 32)
name = "\improper Security sign"
sign_change_name = "Department - Security"
desc = "A sign labelling an area where the law is law."
- icon_state = "security"
+ icon_state = "department_sec"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/security, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/security)
////MISC LOCATIONS
@@ -239,9 +276,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/security, 32)
name = "\improper Restroom sign"
sign_change_name = "Location - Restroom"
desc = "A sign labelling a restroom."
- icon_state = "restroom"
+ icon_state = "department_wc"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/restroom, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/restroom)
/obj/structure/sign/departments/maint
name = "\improper Maintenance Tunnel sign"
@@ -249,7 +287,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/restroom, 32)
desc = "A sign labelling an area where the departments of the station are linked together."
icon_state = "mait1"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/maint, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/maint)
/obj/structure/sign/departments/maint/alt
name = "\improper Maintenance Tunnel sign"
@@ -257,15 +295,38 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/maint, 32)
desc = "A sign labelling an area where the departments of the station are linked together."
icon_state = "mait2"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/maint/alt, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/maint/alt)
/obj/structure/sign/departments/evac
name = "\improper Evacuation sign"
sign_change_name = "Location - Evacuation"
desc = "A sign labelling an area where evacuation procedures take place."
- icon_state = "evac"
+ icon_state = "department_evac"
+ emissive_type = "department_evac_e"
+ is_editable = TRUE
+ ///This var detemines which arrow overlay to use.
+ var/arrow_direction_state = "evac_overlay_f"
+
+/obj/structure/sign/departments/evac/Initialize(mapload)
+ . = ..()
+ add_overlay(arrow_direction_state)
+
+/obj/structure/sign/departments/evac/fore
+ arrow_direction_state = "evac_overlay_f"
+
+/obj/structure/sign/departments/evac/aft
+ arrow_direction_state = "evac_overlay_a"
+
+/obj/structure/sign/departments/evac/starboard
+ arrow_direction_state = "evac_overlay_s"
+
+/obj/structure/sign/departments/evac/port
+ arrow_direction_state = "evac_overlay_p"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/evac, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/evac/fore)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/evac/aft)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/evac/starboard)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/evac/port)
/obj/structure/sign/departments/drop
name = "\improper Drop Pods sign"
@@ -273,29 +334,31 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/evac, 32)
desc = "A sign labelling an area where drop pod loading procedures take place."
icon_state = "drop"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/drop, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/drop)
/obj/structure/sign/departments/court
name = "\improper Courtroom sign"
sign_change_name = "Location - Courtroom"
desc = "A sign labelling the courtroom, where the ever sacred Space Law is upheld."
- icon_state = "court"
+ icon_state = "department_law"
+ emissive_type = "department_e"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/court, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/court)
+// Wallening todo: here down, too
/obj/structure/sign/departments/telecomms
name = "\improper Telecommunications sign"
sign_change_name = "Location - Telecommunications"
desc = "A sign labelling an area where the station's radio and NTnet servers are stored."
icon_state = "telecomms"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/telecomms, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/telecomms)
/obj/structure/sign/departments/telecomms/alt
icon_state = "telecomms2"
sign_change_name = "Location - Telecommunications Alt"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/telecomms/alt, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/telecomms/alt)
/obj/structure/sign/departments/aiupload
name = "\improper AI Upload sign"
@@ -303,7 +366,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/telecomms/alt, 32)
desc = "A sign labelling an area where laws are uploaded to the station's AI and cyborgs."
icon_state = "aiupload"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/aiupload, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/aiupload)
/obj/structure/sign/departments/aisat
name = "\improper AI Satellite sign"
@@ -311,7 +374,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/aiupload, 32)
desc = "A sign labelling the AI's heavily-fortified satellite."
icon_state = "aisat"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/aisat, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/aisat)
/obj/structure/sign/departments/vault
name = "\improper Vault sign"
@@ -319,4 +382,4 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/aisat, 32)
desc = "A sign labelling a saferoom where the station's resources and self-destruct are secured."
icon_state = "vault"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/vault, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/departments/vault)
diff --git a/code/game/objects/structures/signs/signs_flags.dm b/code/game/objects/structures/signs/signs_flags.dm
index 64a9d7225bbac..33a6496d32b97 100644
--- a/code/game/objects/structures/signs/signs_flags.dm
+++ b/code/game/objects/structures/signs/signs_flags.dm
@@ -9,39 +9,39 @@
desc = "The official corporate flag of Nanotrasen. Mostly flown as a ceremonial piece, or to mark land on a new frontier."
icon_state = "flag_nt"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/flag/nanotrasen, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/flag/nanotrasen)
/obj/structure/sign/flag/ssc
name = "flag of the Spinward Stellar Coalition"
desc = "The flag of the Independent Coalition of the Spinward Sector. The colours represent panslavism, and the three stars represent the three central systems of the SSC."
icon_state = "flag_ssc"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/flag/ssc, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/flag/ssc)
/obj/structure/sign/flag/terragov
name = "flag of TerraGov"
desc = "The flag of TerraGov. It's a symbol of humanity no matter where they go, or how much they wish it wasn't."
icon_state = "flag_terragov"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/flag/terragov, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/flag/terragov)
/obj/structure/sign/flag/tizira
name = "flag of the Tiziran Empire"
desc = "The flag of the Great Empire of Tizira. Depending on who you ask, it represents strength or being stuck in the past."
icon_state = "flag_tizira"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/flag/tizira, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/flag/tizira)
/obj/structure/sign/flag/mothic
name = "flag of the Grand Nomad Fleet"
desc = "The flag of the Mothic Grand Nomad Fleet. A classic naval ensign, its use has superceded the old national flag which can be seen in its canton."
icon_state = "flag_mothic"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/flag/mothic, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/flag/mothic)
/obj/structure/sign/flag/mars
name = "flag of the Martian Republic"
desc = "The flag of Mars. Originally a revolutionary flag during the Martian Rebellions, it has since been adopted as the official flag of the planet, as a reminder of how Mars fought for representation and democracy."
icon_state = "flag_mars"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/flag/mars, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/flag/mars)
diff --git a/code/game/objects/structures/signs/signs_interactive.dm b/code/game/objects/structures/signs/signs_interactive.dm
index 1e407034f4a71..b5f14362289b4 100644
--- a/code/game/objects/structures/signs/signs_interactive.dm
+++ b/code/game/objects/structures/signs/signs_interactive.dm
@@ -3,7 +3,7 @@
desc = "It's your run-of-the-mill wall clock showing both the local Coalition Standard Time and the galactic Treaty Coordinated Time. Perfect for staring at instead of working."
icon_state = "clock"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/clock, 32)
+_WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/clock, 35, 0, -8, 24, -24, 16)
/obj/structure/sign/clock/examine(mob/user)
. = ..()
@@ -15,7 +15,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/clock, 32)
desc = "It's an old-school wall calendar. Sure, it might be obsolete with modern technology, but it's still hard to imagine an office without one."
icon_state = "calendar"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/calendar, 32)
+_WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/calendar, 35, 0, -8, 24, -24, 16)
/obj/structure/sign/calendar/examine(mob/user)
. = ..()
diff --git a/code/game/objects/structures/signs/signs_maps.dm b/code/game/objects/structures/signs/signs_maps.dm
index 2ed5cfa934161..dd4e81e076f2c 100644
--- a/code/game/objects/structures/signs/signs_maps.dm
+++ b/code/game/objects/structures/signs/signs_maps.dm
@@ -1,96 +1,271 @@
-//map and direction signs
+// //map and direction signs
/obj/structure/sign/map
name = "station map"
desc = "A navigational chart of the station."
max_integrity = 500
+/// Cerestation Map
+/obj/structure/sign/map/cerestation
+ icon_state = "map-CS"
+
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/map/cerestation)
+
+/// Pubbystation Map
+/obj/structure/sign/map/Pubbystation
+ icon_state = "map-pubby"
+
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/map/Pubbystation)
+
+/// Boxstation Map
/obj/structure/sign/map/left
icon_state = "map-left"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/map/left)
+
/obj/structure/sign/map/right
icon_state = "map-right"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/map/right)
+
+/// Metastation Map
+/obj/structure/sign/map/left/metastation
+ icon_state = "map-left-MS"
+
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/map/left/metastation)
+
+/obj/structure/sign/map/right/metastation
+ icon_state = "map-right-MS"
+
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/map/right/metastation)
+
+/obj/structure/sign/directions
+ icon = 'icons/obj/structures/directional_signs.dmi'
+ /// What direction is the arrow on the sign pointing?
+ var/sign_arrow_direction = null
+ /// If this sign has a support on the left or right, which side? null if niether
+ var/support_side = null
+
+/obj/structure/sign/directions/Initialize(mapload)
+ . = ..()
+ update_appearance()
+
+/obj/structure/sign/directions/update_icon_state()
+ . = ..()
+ if(support_side)
+ icon_state = "[initial(icon_state)]_[support_side]"
+ else
+ icon_state = "[initial(icon_state)]"
+
+/obj/structure/sign/directions/update_overlays()
+ . = ..()
+ if(sign_arrow_direction)
+ . += "arrow_[sign_arrow_direction]"
+
/obj/structure/sign/directions/science
name = "science department sign"
desc = "A direction sign, pointing out which way the Science department is."
icon_state = "direction_sci"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/science, 32)
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/science)
+
+/obj/structure/sign/directions/science/right
+ support_side = SUPPORT_RIGHT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/science/right)
+/obj/structure/sign/directions/science/left
+ support_side = SUPPORT_LEFT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/science/left)
/obj/structure/sign/directions/engineering
name = "engineering department sign"
desc = "A direction sign, pointing out which way the Engineering department is."
icon_state = "direction_eng"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/engineering, 32)
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/engineering)
+
+/obj/structure/sign/directions/engineering/right
+ support_side = SUPPORT_RIGHT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/engineering/right)
+/obj/structure/sign/directions/engineering/left
+ support_side = SUPPORT_LEFT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/engineering/left)
/obj/structure/sign/directions/security
name = "security department sign"
desc = "A direction sign, pointing out which way the Security department is."
icon_state = "direction_sec"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/security, 32)
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/security)
+
+/obj/structure/sign/directions/security/right
+ support_side = SUPPORT_RIGHT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/security/right)
+/obj/structure/sign/directions/security/left
+ support_side = SUPPORT_LEFT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/security/left)
/obj/structure/sign/directions/medical
name = "medbay sign"
desc = "A direction sign, pointing out which way the Medbay is."
icon_state = "direction_med"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/medical, 32)
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/medical)
+
+/obj/structure/sign/directions/medical/right
+ support_side = SUPPORT_RIGHT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/medical/right)
+/obj/structure/sign/directions/medical/left
+ support_side = SUPPORT_LEFT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/medical/left)
/obj/structure/sign/directions/evac
name = "evacuation sign"
desc = "A direction sign, pointing out which way the escape shuttle dock is."
icon_state = "direction_evac"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/evac, 32)
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/evac)
+
+/obj/structure/sign/directions/evac/right
+ support_side = SUPPORT_RIGHT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/evac/right)
+/obj/structure/sign/directions/evac/left
+ support_side = SUPPORT_LEFT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/evac/left)
/obj/structure/sign/directions/supply
name = "cargo sign"
desc = "A direction sign, pointing out which way the Cargo Bay is."
icon_state = "direction_supply"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/supply, 32)
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/supply)
+
+/obj/structure/sign/directions/supply/right
+ support_side = SUPPORT_RIGHT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/supply/right)
+/obj/structure/sign/directions/supply/left
+ support_side = SUPPORT_LEFT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/supply/left)
/obj/structure/sign/directions/command
name = "command department sign"
desc = "A direction sign, pointing out which way the Command department is."
icon_state = "direction_bridge"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/command, 32)
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/command)
+
+/obj/structure/sign/directions/command/right
+ support_side = SUPPORT_RIGHT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/command/right)
+/obj/structure/sign/directions/command/left
+ support_side = SUPPORT_LEFT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/command/left)
/obj/structure/sign/directions/vault
name = "vault sign"
desc = "A direction sign, pointing out which way the station's Vault is."
icon_state = "direction_vault"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/vault, 32)
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/vault)
+
+/obj/structure/sign/directions/vault/right
+ support_side = SUPPORT_RIGHT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/vault/right)
+/obj/structure/sign/directions/vault/left
+ support_side = SUPPORT_LEFT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/vault/left)
/obj/structure/sign/directions/upload
name = "upload sign"
desc = "A direction sign, pointing out which way the station's AI Upload is."
icon_state = "direction_upload"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/upload, 32)
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/upload)
+
+/obj/structure/sign/directions/upload/right
+ support_side = SUPPORT_RIGHT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/upload/right)
+/obj/structure/sign/directions/upload/left
+ support_side = SUPPORT_LEFT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/upload/left)
/obj/structure/sign/directions/dorms
name = "dormitories sign"
desc = "A direction sign, pointing out which way the dormitories are."
icon_state = "direction_dorms"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/dorms, 32)
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/dorms)
+
+/obj/structure/sign/directions/dorms/right
+ support_side = SUPPORT_RIGHT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/dorms/right)
+/obj/structure/sign/directions/dorms/left
+ support_side = SUPPORT_LEFT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/dorms/left)
/obj/structure/sign/directions/lavaland
name = "lava sign"
desc = "A direction sign, pointing out which way the hot stuff is."
icon_state = "direction_lavaland"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/lavaland, 32)
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/lavaland)
+
+/obj/structure/sign/directions/lavaland/right
+ support_side = SUPPORT_RIGHT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/lavaland/right)
+/obj/structure/sign/directions/lavaland/left
+ support_side = SUPPORT_LEFT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/lavaland/left)
/obj/structure/sign/directions/arrival
name = "arrivals sign"
desc = "A direction sign, pointing out which way the arrivals shuttle dock is."
icon_state = "direction_arrival"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/arrival, 32)
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/arrival)
+
+/obj/structure/sign/directions/arrival/right
+ support_side = SUPPORT_RIGHT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/arrival/right)
+/obj/structure/sign/directions/arrival/left
+ support_side = SUPPORT_LEFT
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/arrival/left)
+
+/obj/structure/sign/directions/doornum
+ name = "room number sign"
+ desc = "A sign that states the labeled room's number."
+ icon_state = "direction_doornum"
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/doornum)
+
+/obj/structure/sign/directions/doornum/right
+ icon_state = "direction_doornum_right"
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/doornum/right)
+/obj/structure/sign/directions/doornum/left
+ icon_state = "direction_doornum_left"
+
+DIRECTIONAL_SIGNS_DIRECTIONAL_HELPERS(/obj/structure/sign/directions/doornum/left)
diff --git a/code/game/objects/structures/signs/signs_misc.dm b/code/game/objects/structures/signs/signs_misc.dm
index 0b0348977df77..12b60baf139cd 100644
--- a/code/game/objects/structures/signs/signs_misc.dm
+++ b/code/game/objects/structures/signs/signs_misc.dm
@@ -26,4 +26,4 @@
icon = 'icons/obj/machines/barsigns.dmi'
desc = "85cr for a iced lactose-free caramel frappe?! Who buys that?!"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/xenobio_guide, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/xenobio_guide)
diff --git a/code/game/objects/structures/signs/signs_warning.dm b/code/game/objects/structures/signs/signs_warning.dm
index db44c75669d8a..0c86c626095cf 100644
--- a/code/game/objects/structures/signs/signs_warning.dm
+++ b/code/game/objects/structures/signs/signs_warning.dm
@@ -10,21 +10,21 @@
icon_state = "securearea"
is_editable = TRUE
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning)
/obj/structure/sign/warning/secure_area
name = "\improper SECURE AREA sign"
sign_change_name = "Warning - Secure Area"
desc = "A warning sign which reads 'SECURE AREA'."
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/secure_area, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/secure_area)
/obj/structure/sign/warning/docking
name = "\improper KEEP CLEAR: DOCKING AREA sign"
sign_change_name = "Warning - Docking Area"
desc = "A warning sign which reads 'KEEP CLEAR OF DOCKING AREA'."
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/docking, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/docking)
/obj/structure/sign/warning/biohazard
name = "\improper BIOHAZARD sign"
@@ -32,7 +32,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/docking, 32)
desc = "A warning sign which reads 'BIOHAZARD'."
icon_state = "bio"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/biohazard, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/biohazard)
/obj/structure/sign/warning/electric_shock
name = "\improper HIGH VOLTAGE sign"
@@ -40,7 +40,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/biohazard, 32)
desc = "A warning sign which reads 'HIGH VOLTAGE'."
icon_state = "shock"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/electric_shock, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/electric_shock)
/obj/structure/sign/warning/vacuum
name = "\improper HARD VACUUM AHEAD sign"
@@ -48,7 +48,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/electric_shock, 32)
desc = "A warning sign which reads 'HARD VACUUM AHEAD'."
icon_state = "space"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/vacuum, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/vacuum)
/obj/structure/sign/warning/vacuum/external
name = "\improper EXTERNAL AIRLOCK sign"
@@ -56,7 +56,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/vacuum, 32)
desc = "A warning sign which reads 'EXTERNAL AIRLOCK'."
layer = MOB_LAYER
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/vacuum/external, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/vacuum/external)
/obj/structure/sign/warning/deathsposal
name = "\improper DISPOSAL: LEADS TO SPACE sign"
@@ -64,7 +64,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/vacuum/external, 32)
desc = "A warning sign which reads 'DISPOSAL: LEADS TO SPACE'."
icon_state = "deathsposal"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/deathsposal, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/deathsposal)
/obj/structure/sign/warning/bodysposal
name = "\improper DISPOSAL: LEADS TO MORGUE sign"
@@ -72,7 +72,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/deathsposal, 32)
desc = "A warning sign which reads 'DISPOSAL: LEADS TO MORGUE'."
icon_state = "bodysposal"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/bodysposal, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/bodysposal)
/obj/structure/sign/warning/fire
name = "\improper DANGER: FIRE sign"
@@ -81,7 +81,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/bodysposal, 32)
icon_state = "fire"
resistance_flags = FIRE_PROOF
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/fire, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/fire)
/obj/structure/sign/warning/no_smoking
name = "\improper NO SMOKING sign"
@@ -90,7 +90,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/fire, 32)
icon_state = "nosmoking2"
resistance_flags = FLAMMABLE
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/no_smoking, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/no_smoking)
/obj/structure/sign/warning/no_smoking/circle
name = "\improper NO SMOKING sign"
@@ -98,7 +98,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/no_smoking, 32)
desc = "A warning sign which reads 'NO SMOKING'."
icon_state = "nosmoking"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/no_smoking/circle, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/no_smoking/circle)
/obj/structure/sign/warning/yes_smoking/circle
name = "\improper YES SMOKING sign"
@@ -106,7 +106,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/no_smoking/circle, 32)
desc = "A warning sign which reads 'YES SMOKING'."
icon_state = "yessmoking"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/yes_smoking/circle, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/yes_smoking/circle)
/obj/structure/sign/warning/radiation
name = "\improper HAZARDOUS RADIATION sign"
@@ -114,14 +114,14 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/yes_smoking/circle, 32)
desc = "A warning sign alerting the user of potential radiation hazards."
icon_state = "radiation"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/radiation, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/radiation)
/obj/structure/sign/warning/radiation/rad_area
name = "\improper RADIOACTIVE AREA sign"
sign_change_name = "Warning - Radioactive Area"
desc = "A warning sign which reads 'RADIOACTIVE AREA'."
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/radiation/rad_area, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/radiation/rad_area)
/obj/structure/sign/warning/xeno_mining
name = "\improper DANGEROUS ALIEN LIFE sign"
@@ -130,7 +130,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/radiation/rad_area, 32)
icon = 'icons/obj/signs.dmi'
icon_state = "xeno_warning"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/xeno_mining, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/xeno_mining)
/obj/structure/sign/warning/engine_safety
name = "\improper ENGINEERING SAFETY sign"
@@ -138,7 +138,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/xeno_mining, 32)
desc = "A sign detailing the various safety protocols when working on-site to ensure a safe shift."
icon_state = "safety"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/engine_safety, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/engine_safety)
/obj/structure/sign/warning/explosives
name = "\improper HIGH EXPLOSIVES sign"
@@ -146,7 +146,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/engine_safety, 32)
desc = "A warning sign which reads 'HIGH EXPLOSIVES'."
icon_state = "explosives"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/explosives, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/explosives)
/obj/structure/sign/warning/explosives/alt
name = "\improper HIGH EXPLOSIVES sign"
@@ -154,7 +154,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/explosives, 32)
desc = "A warning sign which reads 'HIGH EXPLOSIVES'."
icon_state = "explosives2"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/explosives/alt, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/explosives/alt)
/obj/structure/sign/warning/test_chamber
name = "\improper TESTING AREA sign"
@@ -162,7 +162,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/explosives/alt, 32)
desc = "A sign that warns of high-power testing equipment in the area."
icon_state = "testchamber"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/test_chamber, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/test_chamber)
/obj/structure/sign/warning/firing_range
name = "\improper FIRING RANGE sign"
@@ -170,7 +170,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/test_chamber, 32)
desc = "A sign reminding you to remain behind the firing line, and to wear ear protection."
icon_state = "firingrange"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/firing_range, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/firing_range)
/obj/structure/sign/warning/cold_temp
name = "\improper FREEZING AIR sign"
@@ -178,7 +178,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/firing_range, 32)
desc = "A sign that warns of extremely cold air in the vicinity."
icon_state = "cold"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/cold_temp, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/cold_temp)
/obj/structure/sign/warning/hot_temp
name = "\improper SUPERHEATED AIR sign"
@@ -186,7 +186,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/cold_temp, 32)
desc = "A sign that warns of extremely hot air in the vicinity."
icon_state = "heat"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/hot_temp, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/hot_temp)
/obj/structure/sign/warning/gas_mask
name = "\improper CONTAMINATED AIR sign"
@@ -194,7 +194,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/hot_temp, 32)
desc = "A sign that warns of dangerous particulates or gasses in the air, instructing you to wear internals."
icon_state = "gasmask"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/gas_mask, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/gas_mask)
/obj/structure/sign/warning/chem_diamond
name = "\improper REACTIVE CHEMICALS sign"
@@ -202,7 +202,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/gas_mask, 32)
desc = "A sign that warns of potentially reactive chemicals nearby, be they explosive, flammable, or acidic."
icon_state = "chemdiamond"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/chem_diamond, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/chem_diamond)
/obj/structure/sign/warning/doors
name = "\improper BLAST DOORS sign"
@@ -220,7 +220,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/doors, 32)
desc = "A warning sign which reads 'ESCAPE PODS'."
icon_state = "pods"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/pods, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/pods)
/obj/structure/sign/warning/rad_shelter
name = "\improper RADSTORM SHELTER sign"
@@ -228,4 +228,4 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/pods, 32)
desc = "A warning sign which reads 'RADSTORM SHELTER'."
icon_state = "radshelter"
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/rad_shelter, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/warning/rad_shelter)
diff --git a/code/game/objects/structures/spirit_board.dm b/code/game/objects/structures/spirit_board.dm
index e8882251237fd..c10eded8bdd3b 100644
--- a/code/game/objects/structures/spirit_board.dm
+++ b/code/game/objects/structures/spirit_board.dm
@@ -1,7 +1,7 @@
/obj/structure/spirit_board
name = "spirit board"
desc = "A wooden board with letters etched into it, used in seances."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/fluff/general.dmi'
icon_state = "spirit_board"
resistance_flags = FLAMMABLE
density = TRUE
diff --git a/code/game/objects/structures/stairs.dm b/code/game/objects/structures/stairs.dm
index a26354211dc27..99a2f8971f859 100644
--- a/code/game/objects/structures/stairs.dm
+++ b/code/game/objects/structures/stairs.dm
@@ -8,7 +8,7 @@
/obj/structure/stairs
name = "stairs"
- icon = 'icons/obj/stairs.dmi'
+ icon = 'icons/obj/structures/stairs.dmi'
icon_state = "stairs"
anchored = TRUE
move_resist = INFINITY
@@ -162,7 +162,7 @@
/obj/structure/stairs_frame
name = "stairs frame"
desc = "Everything you need to call something a staircase, aside from the stuff you actually step on."
- icon = 'icons/obj/stairs.dmi'
+ icon = 'icons/obj/structures/stairs.dmi'
icon_state = "stairs_frame"
density = FALSE
anchored = FALSE
diff --git a/code/game/objects/structures/table_frames.dm b/code/game/objects/structures/table_frames.dm
index cb45eb18e75eb..556276319c6dc 100644
--- a/code/game/objects/structures/table_frames.dm
+++ b/code/game/objects/structures/table_frames.dm
@@ -12,7 +12,7 @@
/obj/structure/table_frame
name = "table frame"
desc = "Four metal legs with four framing rods for a table. You could easily pass through this."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/structures/smooth/table_singles.dmi'
icon_state = "table_frame"
density = FALSE
anchored = FALSE
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 546b6ad796827..98f4d500f088c 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -13,9 +13,12 @@
*/
/obj/structure/table
+ // Shift tables down to avoid layering headaches
+ SET_BASE_PIXEL_NOMAP(0, -8)
+ SET_BASE_VISUAL_PIXEL(0, 8)
name = "table"
desc = "A square piece of iron standing on four metal legs. It can not move."
- icon = 'icons/obj/smooth_structures/table.dmi'
+ icon = 'icons/obj/structures/smooth/table.dmi'
icon_state = "table-0"
base_icon_state = "table"
density = TRUE
@@ -37,6 +40,10 @@
var/buildstackamount = 1
var/framestackamount = 2
var/deconstruction_ready = TRUE
+ var/bottom_placable_y = 9
+ var/top_placable_y = 37
+ var/bottom_placable_x = 4
+ var/top_placable_x = 28
/obj/structure/table/Initialize(mapload, _buildstack)
. = ..()
@@ -58,6 +65,7 @@
///Adds the element used to make the object climbable, and also the one that shift the mob buckled to it up.
/obj/structure/table/proc/make_climbable()
AddElement(/datum/element/climbable)
+ AddComponent(/datum/component/climb_walkable)
AddElement(/datum/element/elevation, pixel_shift = 12)
/obj/structure/table/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
@@ -142,15 +150,6 @@
/obj/structure/table/attack_tk(mob/user)
return
-/obj/structure/table/CanAllowThrough(atom/movable/mover, border_dir)
- . = ..()
- if(.)
- return
- if(mover.throwing)
- return TRUE
- if(locate(/obj/structure/table) in get_turf(mover))
- return TRUE
-
/obj/structure/table/CanAStarPass(to_dir, datum/can_pass_info/pass_info)
if(!density)
return TRUE
@@ -228,75 +227,103 @@
if(istype(tool, /obj/item/construction/rcd))
return NONE
+ var/deck_act_value = NONE
if(istype(tool, /obj/item/toy/cards/deck))
- var/obj/item/toy/cards/deck/dealer_deck = tool
- if(HAS_TRAIT(dealer_deck, TRAIT_WIELDED)) // deal a card faceup on the table
- var/obj/item/toy/singlecard/card = dealer_deck.draw(user)
- if(card)
- card.Flip()
- attackby(card, user, list2params(modifiers))
- return ITEM_INTERACT_SUCCESS
+ deck_act_value = deck_act(user, tool, modifiers, TRUE)
+ // Continue to placing if we don't do anything else
+ if(deck_act_value != NONE)
+ return deck_act_value
+
+ if(!user.combat_mode)
+ return table_place_act(user, tool, modifiers)
- return item_interaction(user, tool, modifiers)
+ return NONE
/obj/structure/table/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ . = NONE
if(istype(tool, /obj/item/storage/bag/tray))
- var/obj/item/storage/bag/tray/tray = tool
- if(tray.contents.len > 0) // If the tray isn't empty
- for(var/obj/item/thing in tray.contents)
- AfterPutItemOnTable(thing, user)
- tool.atom_storage.remove_all(drop_location())
- user.visible_message(span_notice("[user] empties [tool] on [src]."))
- return ITEM_INTERACT_SUCCESS
- // If the tray IS empty, continue on (tray will be placed on the table like other items)
+ . = tray_act(user, tool)
+ else if(istype(tool, /obj/item/toy/cards/deck))
+ . = deck_act(user, tool, modifiers, FALSE)
+ else if(istype(tool, /obj/item/riding_offhand))
+ . = riding_offhand_act(user, tool)
- if(istype(tool, /obj/item/toy/cards/deck))
- var/obj/item/toy/cards/deck/dealer_deck = tool
- if(HAS_TRAIT(dealer_deck, TRAIT_WIELDED)) // deal a card facedown on the table
- var/obj/item/toy/singlecard/card = dealer_deck.draw(user)
- if(card)
- attackby(card, user, list2params(modifiers))
- return ITEM_INTERACT_SUCCESS
-
- if(istype(tool, /obj/item/riding_offhand))
- var/obj/item/riding_offhand/riding_item = tool
- var/mob/living/carried_mob = riding_item.rider
- if(carried_mob == user) //Piggyback user.
- return NONE
- if(user.combat_mode)
- user.unbuckle_mob(carried_mob)
- tablelimbsmash(user, carried_mob)
- else
- var/tableplace_delay = 3.5 SECONDS
- var/skills_space = ""
- if(HAS_TRAIT(user, TRAIT_QUICKER_CARRY))
- tableplace_delay = 2 SECONDS
- skills_space = " expertly"
- else if(HAS_TRAIT(user, TRAIT_QUICK_CARRY))
- tableplace_delay = 2.75 SECONDS
- skills_space = " quickly"
-
- var/obj/item/organ/internal/cyberimp/chest/spine/potential_spine = user.get_organ_slot(ORGAN_SLOT_SPINE)
- if(istype(potential_spine))
- tableplace_delay *= potential_spine.athletics_boost_multiplier
-
- carried_mob.visible_message(span_notice("[user] begins to[skills_space] place [carried_mob] onto [src]..."),
- span_userdanger("[user] begins to[skills_space] place [carried_mob] onto [src]..."))
- if(do_after(user, tableplace_delay, target = carried_mob))
- user.unbuckle_mob(carried_mob)
- tableplace(user, carried_mob)
- return ITEM_INTERACT_SUCCESS
+ // Continue to placing if we don't do anything else
+ if(. != NONE)
+ return .
- // Where putting things on tables is handled.
- if(!user.combat_mode && !(tool.item_flags & ABSTRACT) && user.transferItemToLoc(tool, drop_location(), silent = FALSE))
- //Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf)
- tool.pixel_x = clamp(text2num(LAZYACCESS(modifiers, ICON_X)) - 16, -(world.icon_size/2), world.icon_size/2)
- tool.pixel_y = clamp(text2num(LAZYACCESS(modifiers, ICON_Y)) - 16, -(world.icon_size/2), world.icon_size/2)
- AfterPutItemOnTable(tool, user)
- return ITEM_INTERACT_SUCCESS
+ if(!user.combat_mode)
+ return table_place_act(user, tool, modifiers)
return NONE
+/obj/structure/table/proc/tray_act(mob/living/user, obj/item/storage/bag/tray/used_tray)
+ if(used_tray.contents.len <= 0)
+ return NONE // If the tray IS empty, continue on (tray will be placed on the table like other items)
+
+ for(var/obj/item/thing in used_tray.contents)
+ AfterPutItemOnTable(thing, user)
+ used_tray.atom_storage.remove_all(drop_location())
+ user.visible_message(span_notice("[user] empties [used_tray] on [src]."))
+ return ITEM_INTERACT_SUCCESS
+
+/obj/structure/table/proc/deck_act(mob/living/user, obj/item/toy/cards/deck/dealer_deck, list/modifiers, flip)
+ if(!HAS_TRAIT(dealer_deck, TRAIT_WIELDED))
+ return NONE
+
+ var/obj/item/toy/singlecard/card = dealer_deck.draw(user)
+ if(isnull(card))
+ return ITEM_INTERACT_BLOCKING
+ if(flip)
+ card.Flip()
+ return table_place_act(user, card, modifiers)
+
+/obj/structure/table/proc/riding_offhand_act(mob/living/user, obj/item/riding_offhand/riding_item)
+ var/mob/living/carried_mob = riding_item.rider
+ if(carried_mob == user) //Piggyback user.
+ return NONE
+
+ if(user.combat_mode)
+ user.unbuckle_mob(carried_mob)
+ tablelimbsmash(user, carried_mob)
+ return ITEM_INTERACT_SUCCESS
+
+ var/tableplace_delay = 3.5 SECONDS
+ var/skills_space = ""
+ if(HAS_TRAIT(user, TRAIT_QUICKER_CARRY))
+ tableplace_delay = 2 SECONDS
+ skills_space = " expertly"
+ else if(HAS_TRAIT(user, TRAIT_QUICK_CARRY))
+ tableplace_delay = 2.75 SECONDS
+ skills_space = " quickly"
+
+ var/obj/item/organ/internal/cyberimp/chest/spine/potential_spine = user.get_organ_slot(ORGAN_SLOT_SPINE)
+ if(istype(potential_spine))
+ tableplace_delay *= potential_spine.athletics_boost_multiplier
+
+ carried_mob.visible_message(span_notice("[user] begins to[skills_space] place [carried_mob] onto [src]..."),
+ span_userdanger("[user] begins to[skills_space] place [carried_mob] onto [src]..."))
+ if(!do_after(user, tableplace_delay, target = carried_mob))
+ return ITEM_INTERACT_BLOCKING
+ user.unbuckle_mob(carried_mob)
+ tableplace(user, carried_mob)
+ return ITEM_INTERACT_SUCCESS
+
+// Where putting things on tables is handled.
+/obj/structure/table/proc/table_place_act(mob/living/user, obj/item/tool, list/modifiers)
+ if(tool.item_flags & ABSTRACT)
+ return NONE
+ if(!user.transferItemToLoc(tool, drop_location(), silent = FALSE))
+ return ITEM_INTERACT_BLOCKING
+ // Items are centered by default, but we move them if click ICON_X and ICON_Y are available
+ if(LAZYACCESS(modifiers, ICON_X) && LAZYACCESS(modifiers, ICON_Y))
+ //Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf)
+ // +- 8 to bound it to a typical item hitbox (16x16) instead of the assumed max of 32x32
+ tool.pixel_x = clamp(text2num(LAZYACCESS(modifiers, ICON_X)) - 16, bottom_placable_x - 8, top_placable_x + 8)
+ tool.pixel_y = clamp(text2num(LAZYACCESS(modifiers, ICON_Y)) - 16, bottom_placable_y - 8, top_placable_y + 8)
+ AfterPutItemOnTable(tool, user)
+ return ITEM_INTERACT_SUCCESS
+
/obj/structure/table/proc/AfterPutItemOnTable(obj/item/thing, mob/living/user)
return
@@ -319,16 +346,33 @@
return FALSE
/obj/structure/table/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- if(rcd_data["[RCD_DESIGN_MODE]"] == RCD_DECONSTRUCT)
+ if(rcd_data[RCD_DESIGN_MODE] == RCD_DECONSTRUCT)
qdel(src)
return TRUE
return FALSE
+/obj/structure/table/set_smoothed_icon_state(new_junction)
+ . = ..()
+ bottom_placable_x = initial(bottom_placable_x)
+ bottom_placable_y = initial(bottom_placable_y)
+ top_placable_x = initial(top_placable_x)
+ top_placable_y = initial(top_placable_y)
+ // Allow free movement if we smooth in a direction, while handling bounding
+ if(new_junction & NORTH)
+ top_placable_y = 32 + 8
+ if(new_junction & SOUTH)
+ bottom_placable_y = 0 - 8
+ if(new_junction & EAST)
+ top_placable_x = 32 + 8
+ if(new_junction & WEST)
+ bottom_placable_x = 0 - 8
+
/obj/structure/table/proc/table_living(datum/source, mob/living/shover, mob/living/target, shove_flags, obj/item/weapon)
SIGNAL_HANDLER
if((shove_flags & SHOVE_KNOCKDOWN_BLOCKED) || !(shove_flags & SHOVE_BLOCKED))
return
target.Knockdown(SHOVE_KNOCKDOWN_TABLE)
+ target.apply_status_effect(/datum/status_effect/next_shove_stuns)
target.visible_message(span_danger("[shover.name] shoves [target.name] onto \the [src]!"),
span_userdanger("You're shoved onto \the [src] by [shover.name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, shover)
to_chat(shover, span_danger("You shove [target.name] onto \the [src]!"))
@@ -337,7 +381,7 @@
return COMSIG_LIVING_SHOVE_HANDLED
/obj/structure/table/greyscale
- icon = 'icons/obj/smooth_structures/table_greyscale.dmi'
+ icon = 'icons/obj/structures/smooth/table_greyscale.dmi'
icon_state = "table_greyscale-0"
base_icon_state = "table_greyscale"
material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
@@ -353,14 +397,21 @@
///Table on wheels
/obj/structure/table/rolling
+ SET_BASE_PIXEL(0, 0)
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "Rolling table"
desc = "An NT brand \"Rolly poly\" rolling table. It can and will move."
anchored = FALSE
smoothing_flags = NONE
smoothing_groups = null
canSmoothWith = null
- icon = 'icons/obj/smooth_structures/rollingtable.dmi'
+ icon = 'icons/obj/structures/smooth/rollingtable.dmi'
icon_state = "rollingtable"
+ // this one's 32x32 so it uses different clickable bounds
+ bottom_placable_y = 12
+ top_placable_y = 29
+ bottom_placable_x = 4
+ top_placable_x = 28
/// Lazylist of the items that we have on our surface.
var/list/attached_items = null
@@ -414,7 +465,7 @@
/obj/structure/table/glass
name = "glass table"
desc = "What did I say about leaning on the glass tables? Now you need surgery."
- icon = 'icons/obj/smooth_structures/glass_table.dmi'
+ icon = 'icons/obj/structures/smooth/glass_table.dmi'
icon_state = "glass_table-0"
base_icon_state = "glass_table"
custom_materials = list(/datum/material/glass =SHEET_MATERIAL_AMOUNT)
@@ -486,7 +537,7 @@
/obj/structure/table/glass/plasmaglass
name = "plasma glass table"
desc = "Someone thought this was a good idea."
- icon = 'icons/obj/smooth_structures/plasmaglass_table.dmi'
+ icon = 'icons/obj/structures/smooth/plasmaglass_table.dmi'
icon_state = "plasmaglass_table-0"
base_icon_state = "plasmaglass_table"
custom_materials = list(/datum/material/alloy/plasmaglass =SHEET_MATERIAL_AMOUNT)
@@ -501,7 +552,7 @@
/obj/structure/table/wood
name = "wooden table"
desc = "Do not apply fire to this. Rumour says it burns easily."
- icon = 'icons/obj/smooth_structures/wood_table.dmi'
+ icon = 'icons/obj/structures/smooth/wood_table.dmi'
icon_state = "wood_table-0"
base_icon_state = "wood_table"
frame = /obj/structure/table_frame/wood
@@ -519,7 +570,7 @@
/obj/structure/table/wood/poker //No specialties, Just a mapping object.
name = "gambling table"
desc = "A seedy table for seedy dealings in seedy places."
- icon = 'icons/obj/smooth_structures/poker_table.dmi'
+ icon = 'icons/obj/structures/smooth/poker_table.dmi'
icon_state = "poker_table-0"
base_icon_state = "poker_table"
buildstack = /obj/item/stack/tile/carpet
@@ -530,7 +581,7 @@
/obj/structure/table/wood/fancy
name = "fancy table"
desc = "A standard metal table frame covered with an amazingly fancy, patterned cloth."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/structures/smooth/table_singles.dmi'
icon_state = "fancy_table"
base_icon_state = "fancy_table"
frame = /obj/structure/table_frame
@@ -538,7 +589,7 @@
buildstack = /obj/item/stack/tile/carpet
smoothing_groups = SMOOTH_GROUP_FANCY_WOOD_TABLES //Don't smooth with SMOOTH_GROUP_TABLES or SMOOTH_GROUP_WOOD_TABLES
canSmoothWith = SMOOTH_GROUP_FANCY_WOOD_TABLES
- var/smooth_icon = 'icons/obj/smooth_structures/fancy_table.dmi' // see Initialize()
+ var/smooth_icon = 'icons/obj/structures/smooth/fancy_table.dmi' // see Initialize()
/obj/structure/table/wood/fancy/Initialize(mapload)
. = ..()
@@ -552,55 +603,55 @@
icon_state = "fancy_table_black"
base_icon_state = "fancy_table_black"
buildstack = /obj/item/stack/tile/carpet/black
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_black.dmi'
+ smooth_icon = 'icons/obj/structures/smooth/fancy_table_black.dmi'
/obj/structure/table/wood/fancy/blue
icon_state = "fancy_table_blue"
base_icon_state = "fancy_table_blue"
buildstack = /obj/item/stack/tile/carpet/blue
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_blue.dmi'
+ smooth_icon = 'icons/obj/structures/smooth/fancy_table_blue.dmi'
/obj/structure/table/wood/fancy/cyan
icon_state = "fancy_table_cyan"
base_icon_state = "fancy_table_cyan"
buildstack = /obj/item/stack/tile/carpet/cyan
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_cyan.dmi'
+ smooth_icon = 'icons/obj/structures/smooth/fancy_table_cyan.dmi'
/obj/structure/table/wood/fancy/green
icon_state = "fancy_table_green"
base_icon_state = "fancy_table_green"
buildstack = /obj/item/stack/tile/carpet/green
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_green.dmi'
+ smooth_icon = 'icons/obj/structures/smooth/fancy_table_green.dmi'
/obj/structure/table/wood/fancy/orange
icon_state = "fancy_table_orange"
base_icon_state = "fancy_table_orange"
buildstack = /obj/item/stack/tile/carpet/orange
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_orange.dmi'
+ smooth_icon = 'icons/obj/structures/smooth/fancy_table_orange.dmi'
/obj/structure/table/wood/fancy/purple
icon_state = "fancy_table_purple"
base_icon_state = "fancy_table_purple"
buildstack = /obj/item/stack/tile/carpet/purple
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_purple.dmi'
+ smooth_icon = 'icons/obj/structures/smooth/fancy_table_purple.dmi'
/obj/structure/table/wood/fancy/red
icon_state = "fancy_table_red"
base_icon_state = "fancy_table_red"
buildstack = /obj/item/stack/tile/carpet/red
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_red.dmi'
+ smooth_icon = 'icons/obj/structures/smooth/fancy_table_red.dmi'
/obj/structure/table/wood/fancy/royalblack
icon_state = "fancy_table_royalblack"
base_icon_state = "fancy_table_royalblack"
buildstack = /obj/item/stack/tile/carpet/royalblack
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_royalblack.dmi'
+ smooth_icon = 'icons/obj/structures/smooth/fancy_table_royalblack.dmi'
/obj/structure/table/wood/fancy/royalblue
icon_state = "fancy_table_royalblue"
base_icon_state = "fancy_table_royalblue"
buildstack = /obj/item/stack/tile/carpet/royalblue
- smooth_icon = 'icons/obj/smooth_structures/fancy_table_royalblue.dmi'
+ smooth_icon = 'icons/obj/structures/smooth/fancy_table_royalblue.dmi'
/*
* Reinforced tables
@@ -608,7 +659,7 @@
/obj/structure/table/reinforced
name = "reinforced table"
desc = "A reinforced version of the four legged table."
- icon = 'icons/obj/smooth_structures/reinforced_table.dmi'
+ icon = 'icons/obj/structures/smooth/reinforced_table.dmi'
icon_state = "reinforced_table-0"
base_icon_state = "reinforced_table"
deconstruction_ready = FALSE
@@ -669,7 +720,7 @@
/obj/structure/table/bronze
name = "bronze table"
desc = "A solid table made out of bronze."
- icon = 'icons/obj/smooth_structures/brass_table.dmi'
+ icon = 'icons/obj/structures/smooth/brass_table.dmi'
icon_state = "brass_table-0"
base_icon_state = "brass_table"
resistance_flags = FIRE_PROOF | ACID_PROOF
@@ -684,7 +735,7 @@
/obj/structure/table/reinforced/rglass
name = "reinforced glass table"
desc = "A reinforced version of the glass table."
- icon = 'icons/obj/smooth_structures/rglass_table.dmi'
+ icon = 'icons/obj/structures/smooth/rglass_table.dmi'
icon_state = "rglass_table-0"
base_icon_state = "rglass_table"
custom_materials = list(/datum/material/glass =SHEET_MATERIAL_AMOUNT, /datum/material/iron =SHEET_MATERIAL_AMOUNT)
@@ -694,7 +745,7 @@
/obj/structure/table/reinforced/plasmarglass
name = "reinforced plasma glass table"
desc = "A reinforced version of the plasma glass table."
- icon = 'icons/obj/smooth_structures/rplasmaglass_table.dmi'
+ icon = 'icons/obj/structures/smooth/rplasmaglass_table.dmi'
icon_state = "rplasmaglass_table-0"
base_icon_state = "rplasmaglass_table"
custom_materials = list(/datum/material/alloy/plasmaglass =SHEET_MATERIAL_AMOUNT, /datum/material/iron =SHEET_MATERIAL_AMOUNT)
@@ -703,7 +754,7 @@
/obj/structure/table/reinforced/titaniumglass
name = "titanium glass table"
desc = "A titanium reinforced glass table, with a fresh coat of NT white paint."
- icon = 'icons/obj/smooth_structures/titaniumglass_table.dmi'
+ icon = 'icons/obj/structures/smooth/titaniumglass_table.dmi'
icon_state = "titaniumglass_table-0"
base_icon_state = "titaniumglass_table"
custom_materials = list(/datum/material/alloy/titaniumglass =SHEET_MATERIAL_AMOUNT)
@@ -713,7 +764,7 @@
/obj/structure/table/reinforced/plastitaniumglass
name = "plastitanium glass table"
desc = "A table made of titanium reinforced silica-plasma composite. About as durable as it sounds."
- icon = 'icons/obj/smooth_structures/plastitaniumglass_table.dmi'
+ icon = 'icons/obj/structures/smooth/plastitaniumglass_table.dmi'
icon_state = "plastitaniumglass_table-0"
base_icon_state = "plastitaniumglass_table"
custom_materials = list(/datum/material/alloy/plastitaniumglass =SHEET_MATERIAL_AMOUNT)
@@ -725,6 +776,7 @@
*/
/obj/structure/table/optable
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "operating table"
desc = "Used for advanced medical procedures."
icon = 'icons/obj/medical/surgery_table.dmi'
@@ -766,16 +818,6 @@
pushed_mob.set_resting(TRUE, TRUE)
visible_message(span_notice("[user] lays [pushed_mob] on [src]."))
-///Align the mob with the table when buckled.
-/obj/structure/table/optable/post_buckle_mob(mob/living/buckled)
- . = ..()
- buckled.pixel_y += 6
-
-///Disalign the mob with the table when unbuckled.
-/obj/structure/table/optable/post_unbuckle_mob(mob/living/buckled)
- . = ..()
- buckled.pixel_y -= 6
-
/// Any mob that enters our tile will be marked as a potential patient. They will be turned into a patient if they lie down.
/obj/structure/table/optable/proc/mark_patient(datum/source, mob/living/carbon/potential_patient)
SIGNAL_HANDLER
@@ -818,12 +860,12 @@
/obj/structure/rack
name = "rack"
desc = "Different from the Middle Ages version."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/fluff/general.dmi'
icon_state = "rack"
layer = TABLE_LAYER
density = TRUE
anchored = TRUE
- pass_flags_self = LETPASSTHROW //You can throw objects over this, despite it's density.
+ pass_flags_self = LETPASSTHROW //You can throw objects over this, despite its density.
max_integrity = 20
/obj/structure/rack/skeletal
@@ -912,7 +954,7 @@
/obj/item/rack_parts
name = "rack parts"
desc = "Parts of a rack."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/fluff/general.dmi'
icon_state = "rack_parts"
inhand_icon_state = "rack_parts"
obj_flags = CONDUCTS_ELECTRICITY
diff --git a/code/game/objects/structures/tall_stairs.dm b/code/game/objects/structures/tall_stairs.dm
new file mode 100644
index 0000000000000..1a1cd6f5e64a5
--- /dev/null
+++ b/code/game/objects/structures/tall_stairs.dm
@@ -0,0 +1,143 @@
+/obj/structure/tall_stairs
+ name = "stairs"
+ icon = 'icons/obj/structures/tall.dmi'
+ icon_state = "stairs_start"
+ anchored = TRUE
+ move_resist = INFINITY
+
+ /// Our paired stairs, used for tracking destruction
+ var/obj/structure/tall_stairs/paired
+ /// Should we destroy our paired stairs?
+ var/destroy_paired = TRUE
+
+/obj/structure/tall_stairs/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/climb_walkable)
+ locate_pair()
+
+/obj/structure/tall_stairs/proc/locate_pair()
+ return
+
+/obj/structure/tall_stairs/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
+ . = ..()
+ if (!isnull(paired))
+ paired.paired = null
+ paired = null
+ locate_pair()
+
+/obj/structure/tall_stairs/Destroy(force)
+ . = ..()
+ if (!QDELETED(paired) && destroy_paired)
+ paired.destroy_paired = FALSE
+ QDEL_NULL(paired)
+
+/obj/structure/tall_stairs/start
+ icon_state = "stairs_start"
+
+/obj/structure/tall_stairs/start/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/elevation, pixel_shift = 8)
+
+/obj/structure/tall_stairs/start/locate_pair()
+ if (paired)
+ return
+ paired = locate(/obj/structure/tall_stairs/end) in get_step(src, dir)
+ if (paired)
+ paired.paired = src
+
+/obj/structure/tall_stairs/end
+ icon_state = "stairs_end"
+
+/obj/structure/tall_stairs/end/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/elevation, pixel_shift = 20)
+ var/static/list/loc_connections = list(
+ COMSIG_ATOM_EXIT = PROC_REF(on_exit),
+ )
+ AddElement(/datum/element/connect_loc, loc_connections)
+
+/obj/structure/tall_stairs/end/intercept_zImpact(list/falling_movables, levels = 1)
+ . = ..()
+ if (levels > 1)
+ return
+ for(var/mob/living/fallen_mob in falling_movables)
+ if (fallen_mob.get_timed_status_effect_duration(/datum/status_effect/staggered) || HAS_TRAIT(fallen_mob, TRAIT_WADDLING) && prob(5))
+ addtimer(CALLBACK(src, PROC_REF(flop), fallen_mob), 1)
+
+ . |= FALL_INTERCEPTED | FALL_NO_MESSAGE | FALL_RETAIN_PULL
+
+/obj/structure/tall_stairs/end/proc/flop(mob/living/fallen_mob)
+ var/turf/target_turf = get_step(get_step(src, REVERSE_DIR(dir)), REVERSE_DIR(dir))
+ fallen_mob.throw_at(target_turf, 2, 1, spin = TRUE)
+ fallen_mob.visible_message(span_warning("[fallen_mob] falls down [src]!"), span_userdanger("You painfully fall down [src]!"))
+ fallen_mob.Knockdown(5 SECONDS)
+ fallen_mob.apply_damage(5, BRUTE)
+
+/obj/structure/tall_stairs/end/proc/on_exit(datum/source, atom/movable/leaving, direction)
+ SIGNAL_HANDLER
+
+ if(leaving == src)
+ return
+
+ if(isobserver(leaving))
+ return
+
+ if (direction & dir)
+ leaving.set_currently_z_moving(CURRENTLY_Z_ASCENDING)
+ INVOKE_ASYNC(src, PROC_REF(stair_ascend), leaving)
+ leaving.Bump(src)
+ return COMPONENT_ATOM_BLOCK_EXIT
+
+ if (!isliving(leaving) || (locate(/obj/structure/tall_stairs) in get_step(src, direction)))
+ return
+
+ var/mob/living/loser = leaving
+ var/graceful_landing = FALSE
+ if(!loser.incapacitated(IGNORE_RESTRAINTS))
+ var/obj/item/organ/external/wings/gliders = loser.get_organ_by_type(/obj/item/organ/external/wings)
+ if(HAS_TRAIT(loser, TRAIT_FREERUNNING) || gliders?.can_soften_fall())
+ graceful_landing = HAS_TRAIT(loser, TRAIT_CATLIKE_GRACE)
+
+ if(graceful_landing)
+ loser.add_movespeed_modifier(/datum/movespeed_modifier/landed_on_feet)
+ addtimer(CALLBACK(loser, TYPE_PROC_REF(/mob, remove_movespeed_modifier), /datum/movespeed_modifier/landed_on_feet), 2 SECONDS)
+ new /obj/effect/temp_visual/mook_dust/small(get_step(src, direction))
+ else
+ loser.Knockdown(3 SECONDS)
+
+ loser.visible_message(span_warning("[loser] falls from [src]!"), span_warning("You fall from [src]!"))
+
+/obj/structure/tall_stairs/end/proc/stair_ascend(atom/movable/climber)
+ var/turf/checking = get_step_multiz(get_turf(src), UP)
+ if(!istype(checking))
+ return
+ // I'm only interested in if the pass is unobstructed, not if the mob will actually make it
+ if(!climber.can_z_move(UP, get_turf(src), checking, z_move_flags = ZMOVE_ALLOW_BUCKLED))
+ return
+ var/turf/target = get_step_multiz(get_turf(src), (dir|UP))
+ if(istype(target) && !climber.can_z_move(DOWN, target, z_move_flags = ZMOVE_FALL_FLAGS)) //Don't throw them into a tile that will just dump them back down.
+ climber.zMove(target = target, z_move_flags = ZMOVE_STAIRS_FLAGS)
+ /// Moves anything that's being dragged by src or anything buckled to it to the stairs turf.
+ climber.pulling?.move_from_pull(climber, loc, climber.glide_size)
+ for(var/mob/living/buckled as anything in climber.buckled_mobs)
+ buckled.pulling?.move_from_pull(buckled, loc, buckled.glide_size)
+
+/obj/structure/tall_stairs/end/locate_pair()
+ if (paired)
+ return
+ paired = locate(/obj/structure/tall_stairs/start) in get_step(src, REVERSE_DIR(dir))
+ if (paired)
+ paired.paired = src
+
+/obj/structure/tall_stairs/end/CanAllowThrough(atom/movable/mover, border_dir)
+ if (isnull(locate(/obj/structure/tall_stairs) in get_turf(mover)))
+ return FALSE
+ return ..()
+
+/obj/structure/tall_stairs/small
+ icon_state = "stairs_small"
+ destroy_paired = FALSE
+
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/tall_stairs/start)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/tall_stairs/end)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/tall_stairs/small)
diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm
index 2d16ea30a69e4..79217f2373eb2 100644
--- a/code/game/objects/structures/tank_dispenser.dm
+++ b/code/game/objects/structures/tank_dispenser.dm
@@ -1,9 +1,10 @@
#define TANK_DISPENSER_CAPACITY 10
/obj/structure/tank_dispenser
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "tank dispenser"
desc = "A simple yet bulky storage device for gas tanks."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/canisters.dmi'
icon_state = "dispenser"
density = TRUE
anchored = TRUE
diff --git a/code/game/objects/structures/tank_holder.dm b/code/game/objects/structures/tank_holder.dm
index 34284b1f1dd0d..c71eac0188fb2 100644
--- a/code/game/objects/structures/tank_holder.dm
+++ b/code/game/objects/structures/tank_holder.dm
@@ -1,5 +1,6 @@
///?
/obj/structure/tank_holder
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "tank holder"
desc = "A metallic frame that can hold tanks and extinguishers."
icon = 'icons/obj/canisters.dmi'
diff --git a/code/game/objects/structures/toiletbong.dm b/code/game/objects/structures/toiletbong.dm
index 0ea21e9ff8480..a348835af38b5 100644
--- a/code/game/objects/structures/toiletbong.dm
+++ b/code/game/objects/structures/toiletbong.dm
@@ -1,7 +1,7 @@
/obj/structure/toiletbong
name = "toilet bong"
desc = "A repurposed toilet with re-arranged piping and an attached flamethrower. Why would anyone build this?"
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "toiletbong"
base_icon_state = "toiletbong"
density = FALSE
@@ -15,10 +15,10 @@
AddComponent(/datum/component/simple_rotation, post_rotation = CALLBACK(src, PROC_REF(post_rotation)))
create_storage(max_total_storage = 100, max_slots = 12, canhold = /obj/item/food)
atom_storage.attack_hand_interact = FALSE
- atom_storage.rustle_sound = FALSE
+ atom_storage.do_rustle = FALSE
atom_storage.animated = FALSE
- weed_overlay = mutable_appearance('icons/obj/watercloset.dmi', "[base_icon_state]_overlay")
+ weed_overlay = mutable_appearance('icons/obj/structures/watercloset.dmi', "[base_icon_state]_overlay")
START_PROCESSING(SSobj, src)
/obj/structure/toiletbong/update_overlays()
diff --git a/code/game/objects/structures/training_machine.dm b/code/game/objects/structures/training_machine.dm
index bed4c4805cca6..c1963ea38a3ef 100644
--- a/code/game/objects/structures/training_machine.dm
+++ b/code/game/objects/structures/training_machine.dm
@@ -79,7 +79,7 @@
*
* Will not respond if moving and emagged, so once you set it to go it can't be stopped!
*/
-/obj/structure/training_machine/ui_act(action, params)
+/obj/structure/training_machine/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/game/objects/structures/water_structures/sink.dm b/code/game/objects/structures/water_structures/sink.dm
index 24cb2e806965f..059dbac499390 100644
--- a/code/game/objects/structures/water_structures/sink.dm
+++ b/code/game/objects/structures/water_structures/sink.dm
@@ -1,6 +1,6 @@
/obj/structure/sink
name = "sink"
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "sink"
desc = "A sink used for washing one's hands and face. Passively reclaims water over time."
anchored = TRUE
@@ -14,14 +14,12 @@
var/buildstacktype = /obj/item/stack/sheet/iron
///Number of sheets of material to drop when broken or deconstructed.
var/buildstackamount = 1
- ///Does the sink have a water recycler to recollect it's water supply?
+ ///Does the sink have a water recycler to recollect its water supply?
var/has_water_reclaimer = TRUE
///Units of water to reclaim per second
var/reclaim_rate = 0.5
- ///Amount of shift the pixel for placement
- var/pixel_shift = 14
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink, (-14))
+SINK_DIRECTIONAL_HELPERS(/obj/structure/sink)
/obj/structure/sink/Initialize(mapload, ndir = 0, has_water_reclaimer = null)
. = ..()
@@ -32,24 +30,11 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink, (-14))
if(has_water_reclaimer != null)
src.has_water_reclaimer = has_water_reclaimer
- switch(dir)
- if(NORTH)
- pixel_x = 0
- pixel_y = -pixel_shift
- if(SOUTH)
- pixel_x = 0
- pixel_y = pixel_shift
- if(EAST)
- pixel_x = -pixel_shift
- pixel_y = 0
- if(WEST)
- pixel_x = pixel_shift
- pixel_y = 0
-
create_reagents(100, NO_REACT)
if(src.has_water_reclaimer)
reagents.add_reagent(dispensedreagent, 100)
- AddComponent(/datum/component/plumbing/simple_demand, extend_pipe_to_edge = TRUE)
+ AddComponent(/datum/component/plumbing/inverted_simple_demand, extend_pipe_to_edge = TRUE, invert_demand = TRUE)
+ find_and_hang_on_wall()
/obj/structure/sink/examine(mob/user)
. = ..()
@@ -246,9 +231,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink, (-14))
name = "kitchen sink"
icon_state = "sink_alt"
pixel_z = 4
- pixel_shift = 16
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink/kitchen, (-16))
+SINK_DIRECTIONAL_HELPERS(/obj/structure/sink/kitchen)
/obj/structure/sink/gasstation
name = "plasma fuel station"
@@ -264,7 +248,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/sink/kitchen, (-16))
/obj/structure/sinkframe
name = "sink frame"
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "sink_frame"
desc = "A sink frame, that needs a water recycler to finish construction."
anchored = FALSE
diff --git a/code/game/objects/structures/water_structures/toilet.dm b/code/game/objects/structures/water_structures/toilet.dm
index 7a64404a238a9..615134a745871 100644
--- a/code/game/objects/structures/water_structures/toilet.dm
+++ b/code/game/objects/structures/water_structures/toilet.dm
@@ -1,7 +1,8 @@
/obj/structure/toilet
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "toilet"
desc = "The HT-451, a torque rotation-based, waste disposal unit for small matter. This one seems remarkably clean."
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "toilet00" //The first number represents if the toilet lid is up, the second is if the cistern is open.
base_icon_state = "toilet"
density = FALSE
@@ -36,6 +37,7 @@
update_appearance(UPDATE_ICON)
if(mapload && SSmapping.level_trait(z, ZTRAIT_STATION))
AddElement(/datum/element/lazy_fishing_spot, /datum/fish_source/toilet)
+ AddElement(/datum/element/fish_safe_storage)
register_context()
/obj/structure/toilet/add_context(atom/source, list/context, obj/item/held_item, mob/user)
diff --git a/code/game/objects/structures/water_structures/urinal.dm b/code/game/objects/structures/water_structures/urinal.dm
index 3b34e2cc0e5b8..2fae415892e9d 100644
--- a/code/game/objects/structures/water_structures/urinal.dm
+++ b/code/game/objects/structures/water_structures/urinal.dm
@@ -1,7 +1,7 @@
/obj/structure/urinal
name = "urinal"
desc = "The HU-452, an experimental urinal. Comes complete with experimental urinal cake."
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "urinal"
density = FALSE
anchored = TRUE
@@ -10,13 +10,13 @@
/// What's in the urinal
var/obj/item/hidden_item
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/urinal, 32)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/urinal)
/obj/structure/urinal/Initialize(mapload)
. = ..()
if(mapload)
hidden_item = new /obj/item/food/urinalcake(src)
- find_and_hang_on_wall()
+ find_and_hang_on_wall(wall_layer = FLAT_ON_WALL_LAYER)
/obj/structure/urinal/Exited(atom/movable/gone, direction)
. = ..()
@@ -92,15 +92,14 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/urinal, 32)
/obj/item/wallframe/urinal
name = "urinal frame"
desc = "An unmounted urinal. Attach it to a wall to use."
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "urinal"
result_path = /obj/structure/urinal
- pixel_shift = 32
/obj/item/food/urinalcake
name = "urinal cake"
desc = "The noble urinal cake, protecting the station's pipes from the station's pee. Do not eat."
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/maintenance_loot.dmi'
icon_state = "urinalcake"
w_class = WEIGHT_CLASS_TINY
food_reagents = list(
diff --git a/code/game/objects/structures/water_structures/water_source.dm b/code/game/objects/structures/water_structures/water_source.dm
index b7ad26a65ea7e..69d4056b8d0e5 100644
--- a/code/game/objects/structures/water_structures/water_source.dm
+++ b/code/game/objects/structures/water_structures/water_source.dm
@@ -1,7 +1,7 @@
//Water source, use the type water_source for unlimited water sources like classic sinks.
/obj/structure/water_source
name = "Water Source"
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/structures/watercloset.dmi'
icon_state = "sink"
desc = "A sink used for washing one's hands and face. This one seems to be infinite!"
anchored = TRUE
@@ -133,10 +133,20 @@
/obj/structure/water_source/puddle //splishy splashy ^_^
name = "puddle"
desc = "A puddle used for washing one's hands and face."
+ icon = 'icons/obj/mining_zones/terrain.dmi'
icon_state = "puddle"
base_icon_state = "puddle"
resistance_flags = UNACIDABLE
+/obj/structure/water_source/puddle/Initialize(mapload)
+ . = ..()
+ register_context()
+
+/obj/structure/water_source/puddle/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = ..()
+ if(isnull(held_item))
+ context[SCREENTIP_CONTEXT_RMB] = "Scoop Tadpoles"
+
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/structure/water_source/puddle/attack_hand(mob/user, list/modifiers)
icon_state = "[base_icon_state]-splash"
@@ -147,3 +157,20 @@
icon_state = "[base_icon_state]-splash"
. = ..()
icon_state = base_icon_state
+
+/obj/structure/water_source/puddle/attack_hand_secondary(mob/living/carbon/human/user, list/modifiers)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+ return
+ if(DOING_INTERACTION_WITH_TARGET(user, src))
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ icon_state = "[base_icon_state]-splash"
+ balloon_alert(user, "scooping tadpoles...")
+ if(do_after(user, 5 SECONDS, src))
+ playsound(loc, 'sound/effects/slosh.ogg', 15, TRUE)
+ balloon_alert(user, "got a tadpole")
+ var/obj/item/fish/tadpole/tadpole = new(loc)
+ tadpole.randomize_size_and_weight()
+ user.put_in_hands(tadpole)
+ icon_state = base_icon_state
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm
index 4c22cbf01b29d..c369fb3fc8e81 100644
--- a/code/game/objects/structures/windoor_assembly.dm
+++ b/code/game/objects/structures/windoor_assembly.dm
@@ -20,7 +20,9 @@
//Vars to help with the icon's name
///Does the windoor open to the left or right?
var/facing = "l"
- ///Whether or not this creates a secure windoor
+ /// Whether this can make a secure windoor at all
+ var/can_secure = TRUE
+ /// Whether or not this creates a secure windoor
var/secure = FALSE
/**
* Windoor (window door) assembly -Nodrak
@@ -160,7 +162,7 @@
name = "windoor assembly"
//Adding plasteel makes the assembly a secure windoor assembly. Step 2 (optional) complete.
- else if(istype(W, /obj/item/stack/sheet/plasteel) && !secure)
+ else if(istype(W, /obj/item/stack/sheet/plasteel) && can_secure && !secure)
var/obj/item/stack/sheet/plasteel/P = W
if(P.get_amount() < 2)
to_chat(user, span_warning("You need more plasteel to do this!"))
@@ -301,22 +303,23 @@
/obj/structure/windoor_assembly/proc/finish_door()
var/obj/machinery/door/window/windoor
+
+ var/created_type = get_created_type()
+ windoor = new created_type(loc)
+
if(secure)
- windoor = new /obj/machinery/door/window/brigdoor(loc)
if(facing == "l")
- windoor.icon_state = "leftsecureopen"
- windoor.base_state = "leftsecure"
+ windoor.icon_state = "left_secure_open"
+ windoor.base_state = "left_secure"
else
- windoor.icon_state = "rightsecureopen"
- windoor.base_state = "rightsecure"
-
+ windoor.icon_state = "right_secure_open"
+ windoor.base_state = "right_secure"
else
- windoor = new /obj/machinery/door/window(loc)
if(facing == "l")
- windoor.icon_state = "leftopen"
+ windoor.icon_state = "left_open"
windoor.base_state = "left"
else
- windoor.icon_state = "rightopen"
+ windoor.icon_state = "right_open"
windoor.base_state = "right"
windoor.setDir(dir)
@@ -347,6 +350,9 @@
qdel(src)
+/// Returns the typepath of windoor to make
+/obj/structure/windoor_assembly/proc/get_created_type()
+ return secure ? /obj/machinery/door/window/brigdoor : /obj/machinery/door/window
//Flips the windoor assembly, determines whather the door opens to the left or the right
/obj/structure/windoor_assembly/verb/flip()
@@ -370,3 +376,11 @@
update_appearance()
return
+
+/obj/structure/windoor_assembly/half
+ icon = 'icons/obj/doors/windoor_half.dmi'
+ name = "short windoor Assembly"
+ can_secure = FALSE
+
+/obj/structure/windoor_assembly/half/get_created_type()
+ return /obj/machinery/door/window/half
diff --git a/code/game/objects/structures/window_frames.dm b/code/game/objects/structures/window_frames.dm
new file mode 100644
index 0000000000000..48dce958c6778
--- /dev/null
+++ b/code/game/objects/structures/window_frames.dm
@@ -0,0 +1,589 @@
+/obj/structure/window_frame
+ name = "window frame"
+ desc = "A frame section to place a window on top."
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_normal.dmi'
+ icon_state = "window_frame_normal-0"
+ base_icon_state = "window_frame_normal"
+ smoothing_flags = SMOOTH_BITMASK|SMOOTH_OBJ
+ smoothing_groups = SMOOTH_GROUP_WINDOW_FRAMES
+ canSmoothWith = SMOOTH_GROUP_WINDOW_FRAMES
+ pass_flags_self = PASSTABLE | LETPASSTHROW | PASSGRILLE | PASSWINDOW
+ opacity = FALSE
+ density = TRUE
+ rad_insulation = null
+ armor_type = /datum/armor/window_frame
+ max_integrity = 50
+ anchored = TRUE
+
+ ///whether we currently have a grille
+ var/has_grille = FALSE
+ ///whether we spawn a window structure with us on mapload
+ var/start_with_window = FALSE
+ ///Icon used by grilles for this window frame
+ var/grille_icon = 'icons/obj/structures/smooth/window_grille.dmi'
+
+ var/grille_black_icon = 'icons/obj/structures/smooth/window_grille_black.dmi'
+ ///Icon state used by grilles for this window frame.
+ var/grille_icon_state = "window_grille"
+
+ var/frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_normal.dmi'
+
+ ///whether or not this window is reinforced and thus doesnt use the default attackby() behavior
+ var/is_reinforced = FALSE
+
+ ///typepath. creates a corresponding window for this frame.
+ ///is either a material sheet typepath (eg /obj/item/stack/sheet/glass) or a fulltile window typepath (eg /obj/structure/window/fulltile)
+ var/window_type = /obj/item/stack/sheet/glass
+
+ var/sheet_type = /obj/item/stack/sheet/iron
+ var/sheet_amount = 2
+
+ /// Whether or not we're disappearing but dramatically
+ var/dramatically_disappearing = FALSE
+
+/datum/armor/window_frame
+ melee = 50
+ bullet = 70
+ laser = 70
+ energy = 100
+ bomb = 10
+ bio = 100
+ fire = 0
+ acid = 0
+
+/obj/structure/window_frame/Initialize(mapload)
+ . = ..()
+
+ update_appearance()
+ AddComponent(/datum/component/climb_walkable)
+ AddElement(/datum/element/climbable, on_try_climb_procpath = TYPE_PROC_REF(/obj/structure/window_frame, on_try_climb))
+
+ if(mapload && start_with_window)
+ create_structure_window(window_type)
+
+ var/static/list/loc_connections = list(
+ COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
+ )
+ AddElement(/datum/element/connect_loc, loc_connections)
+
+///helper proc to check if we already have a window
+/obj/structure/window_frame/proc/has_window()
+ SHOULD_BE_PURE(TRUE)
+
+ for(var/obj/structure/window/window in loc)
+ if(window.fulltile)
+ return TRUE
+
+ return FALSE
+
+///Called by the climbable element if you try climb up. Better hope you're well protected against shocks! XD
+/obj/structure/window_frame/proc/on_try_climb(mob/climber)
+ try_shock(climber, 100)
+
+///Gives the user a shock if they get unlucky (Based on shock chance)
+/obj/structure/window_frame/proc/try_shock(mob/user, shock_chance)
+ var/turf/my_turf = get_turf(src)
+ var/obj/structure/cable/underlaying_cable = my_turf.get_cable_node()
+ if(!has_grille) // no grille? dont shock.
+ return FALSE
+ if(!underlaying_cable)
+ return FALSE
+ if(!prob(shock_chance))
+ return FALSE
+ if(!in_range(src, user))//To prevent TK and mech users from getting shocked
+ return FALSE
+ if(electrocute_mob(user, underlaying_cable, src, 1, TRUE))
+ var/datum/effect_system/spark_spread/spark_effect = new /datum/effect_system/spark_spread
+ spark_effect.set_up(3, 1, src)
+ spark_effect.start()
+ return TRUE
+ return FALSE
+
+/obj/structure/window_frame/proc/on_entered(datum/source, atom/movable/AM)
+ SIGNAL_HANDLER
+ if(!isliving(AM))
+ return
+ var/mob/living/potential_victim = AM
+ if(potential_victim.movement_type & MOVETYPES_NOT_TOUCHING_GROUND)
+ return
+ try_shock(potential_victim, 100)
+
+
+/obj/structure/window_frame/attack_animal(mob/user, list/modifiers)
+ . = ..()
+ if(!.)
+ return
+ if(!try_shock(user, 70) && !QDELETED(src)) //Last hit still shocks but shouldn't deal damage to the grille
+ take_damage(rand(5,10), BRUTE, MELEE, 1)
+
+/obj/structure/window_frame/attack_hulk(mob/living/carbon/human/user)
+ if(try_shock(user, 70))
+ return
+ . = ..()
+
+/obj/structure/window_frame/attack_hand(mob/living/user, list/modifiers)
+ . = ..()
+ if(.)
+ return
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.do_attack_animation(src, ATTACK_EFFECT_KICK)
+ user.visible_message(span_warning("[user] hits [src]."), null, null, COMBAT_MESSAGE_RANGE)
+ log_combat(user, src, "hit")
+ if(!try_shock(user, 70))
+ take_damage(rand(5,10), BRUTE, MELEE, 1)
+
+/obj/structure/window_frame/attack_alien(mob/living/user, list/modifiers)
+ user.do_attack_animation(src)
+ user.changeNext_move(CLICK_CD_MELEE)
+ user.visible_message(span_warning("[user] mangles [src]."), null, null, COMBAT_MESSAGE_RANGE)
+ if(!try_shock(user, 70))
+ take_damage(20, BRUTE, MELEE, 1)
+
+/obj/structure/window_frame/wirecutter_act(mob/living/user, obj/item/tool)
+ add_fingerprint(user)
+ if(try_shock(user, 100))
+ return
+ if(!has_grille)
+ return
+ if(!tool.use_tool(src, user, 0, volume = 50))
+ return
+ tool.play_tool_sound(src, 100)
+ balloon_alert(user, "grille cut!")
+ has_grille = FALSE
+ update_appearance()
+ return ITEM_INTERACT_SUCCESS
+
+
+/obj/structure/window_frame/welder_act_secondary(mob/living/user, obj/item/tool)
+ . = ..()
+
+ add_fingerprint(user)
+
+ if(!tool.tool_start_check(user, amount = 0))
+ return ITEM_INTERACT_BLOCKING
+ balloon_alert(user, "cutting...")
+ if(!tool.use_tool(src, user, 70, volume = 50))
+ return ITEM_INTERACT_BLOCKING
+
+ balloon_alert(user, "deconstructed")
+ deconstruct(TRUE)
+
+ return ITEM_INTERACT_SUCCESS
+
+/obj/structure/window_frame/welder_act(mob/living/user, obj/item/tool)
+ . = ..()
+ add_fingerprint(user)
+
+ if(atom_integrity >= max_integrity)
+ to_chat(user, span_warning("[src] is already in good condition!"))
+ return ITEM_INTERACT_BLOCKING
+ if(!tool.tool_start_check(user, amount = 0))
+ return ITEM_INTERACT_BLOCKING
+
+ balloon_alert(user, "repairing...")
+ if(!tool.use_tool(src, user, 40, volume = 50))
+ return ITEM_INTERACT_BLOCKING
+
+ atom_integrity = max_integrity
+ balloon_alert(user, "repaired!")
+ update_appearance()
+ return ITEM_INTERACT_SUCCESS
+
+///creates a window from the typepath given from window_type, which is either a glass sheet typepath or a /obj/structure/window subtype
+/obj/structure/window_frame/proc/create_structure_window(window_material_type)
+ var/obj/structure/window/our_window
+
+ if(ispath(window_material_type, /obj/structure/window))
+ our_window = new window_material_type(loc)
+ if(!our_window.fulltile)
+ stack_trace("Window frames can't use non fulltile windows!")
+
+ //window_material_type isnt a window typepath, so check if its a material typepath
+ if(ispath(window_material_type, /obj/item/stack/sheet/glass))
+ our_window = new /obj/structure/window/fulltile(loc)
+
+ if(ispath(window_material_type, /obj/item/stack/sheet/rglass))
+ our_window = new /obj/structure/window/reinforced/fulltile(loc)
+
+ if(ispath(window_material_type, /obj/item/stack/sheet/plasmaglass))
+ our_window = new /obj/structure/window/plasma/fulltile(loc)
+
+ if(ispath(window_material_type, /obj/item/stack/sheet/plasmarglass))
+ our_window = new /obj/structure/window/reinforced/plasma/fulltile(loc)
+
+ if(ispath(window_material_type, /obj/item/stack/sheet/titaniumglass))
+ our_window = new /obj/structure/window/reinforced/shuttle(loc)
+
+ if(ispath(window_material_type, /obj/item/stack/sheet/plastitaniumglass))
+ our_window = new /obj/structure/window/reinforced/plasma/plastitanium(loc)
+
+ if(ispath(window_material_type, /obj/item/stack/sheet/paperframes))
+ our_window = new /obj/structure/window/paperframe(loc)
+
+ our_window.update_appearance()
+ return our_window
+
+/obj/structure/window_frame/attackby(obj/item/attacking_item, mob/living/user, params)
+ add_fingerprint(user)
+ if(isstack(attacking_item))
+ var/obj/item/stack/adding_stack = attacking_item
+ var/stack_name = "[adding_stack]" // in case the stack gets deleted after use()
+
+ if(is_glass_sheet(adding_stack) && !(has_window()) && adding_stack.use(sheet_amount))
+ to_chat(user, "You start to add [stack_name] to [src].")
+ if(!do_after(user, 2 SECONDS, src))
+ return
+
+ to_chat(user, "You add [stack_name] to [src].")
+ var/obj/structure/window/our_window = create_structure_window(adding_stack.type)
+ our_window.state = WINDOW_OUT_OF_FRAME
+ our_window.set_anchored(FALSE)
+
+ else if(istype(adding_stack, /obj/item/stack/rods) && !has_grille && adding_stack.use(sheet_amount))
+ has_grille = TRUE
+ to_chat(user, "You add [stack_name] to [src]")
+ update_appearance()
+
+ else if((attacking_item.obj_flags & CONDUCTS_ELECTRICITY) && try_shock(user, 70))
+ return
+
+ return ..()
+
+/obj/structure/window_frame/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = NONE)
+ switch(damage_type)
+ if(BRUTE)
+ if(damage_amount)
+ playsound(src, 'sound/effects/grillehit.ogg', 80, TRUE)
+ else
+ playsound(src, 'sound/weapons/tap.ogg', 50, TRUE)
+ if(BURN)
+ playsound(src, 'sound/items/welder.ogg', 80, TRUE)
+
+/obj/structure/window_frame/attack_paw(mob/user, list/modifiers)
+ return attack_hand(user, modifiers)
+
+/obj/structure/window_frame/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
+ switch(the_rcd.mode)
+ if(RCD_DECONSTRUCT)
+ return list("mode" = RCD_DECONSTRUCT, "delay" = 20, "cost" = 5)
+ if(RCD_WINDOWGRILLE)
+ var/cost = 0
+ var/delay = 0
+
+ if(!has_grille)
+ return rcd_result_with_memory(
+ list("delay" = 2 SECONDS, "cost" = 2),
+ get_turf(src), RCD_MEMORY_WINDOWGRILLE
+ )
+
+ var/obj/structure/window/window_path = the_rcd.rcd_design_path
+ if(!ispath(window_path))
+ stack_trace("invalid window path passed to rcd_vals: [window_path]")
+ return FALSE
+
+ if(initial(window_path.fulltile))
+ cost = 8
+ delay = 3 SECONDS
+ else
+ cost = 4
+ delay = 2 SECONDS
+
+ if(initial(window_path.reinf))
+ cost *= 1.5
+ delay *= 1.5
+
+ if(!cost)
+ return FALSE
+
+ return rcd_result_with_memory(
+ list("delay" = delay, "cost" = cost),
+ get_turf(src), RCD_MEMORY_WINDOWGRILLE
+ )
+
+ return FALSE
+
+/obj/structure/window_frame/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
+ switch(rcd_data["[RCD_DESIGN_MODE]"])
+ if(RCD_DECONSTRUCT)
+ var/turf/home = get_turf(src)
+ // No thing to display on if we get deleted
+ home.balloon_alert(user, "deconstructed!")
+ qdel(src)
+ return TRUE
+ if(RCD_WINDOWGRILLE)
+ if(!isturf(loc))
+ return FALSE
+
+ if(!has_grille)
+ balloon_alert(user, "grill added!")
+ has_grille = TRUE
+ update_appearance()
+ return TRUE
+
+ var/obj/structure/window/window_path = rcd_data["[RCD_DESIGN_PATH]"]
+ if(!ispath(window_path))
+ CRASH("Invalid window path type in RCD: [window_path]")
+
+ if(!initial(window_path.fulltile))
+ if(!valid_build_direction(loc, user.dir, is_fulltile = FALSE))
+ balloon_alert(user, "window already here!")
+ return FALSE
+
+ var/obj/structure/window/window = new window_path(loc, user.dir)
+ window.set_anchored(TRUE)
+ return TRUE
+
+ return FALSE
+
+/obj/structure/window_frame/examine(mob/user)
+ . = ..()
+ if(has_window() && has_grille)
+ . += "The window is fully constructed."
+ else if(has_window())
+ . += "The window set into the frame has no reinforcement."
+ else if(has_grille)
+ . += "The window frame only has a grille set into it."
+ else
+ . += "The window frame is empty"
+
+///delightfully devilous seymour
+/obj/structure/window_frame/set_smoothed_icon_state(new_junction)
+ . = ..()
+ update_icon()
+
+/// if this frame has a grill, creates both the overlay for the grill (that goes over cables) and the black overlay beneath it (goes over us, but not cables)
+/obj/structure/window_frame/proc/create_grill_overlays(list/return_list)
+ if(!has_grille || !return_list)
+ return
+
+ return_list += mutable_appearance(grille_black_icon, "[grille_icon_state]_black-[smoothing_junction]")
+ return_list += mutable_appearance(grille_icon, "[grille_icon_state]-[smoothing_junction]")
+
+/// if this frame has a valid frame icon, creates it. this is what obscures the cable if it goes through the frame
+/obj/structure/window_frame/proc/create_frame_overlay(list/return_list)
+ if(!frame_icon || !return_list)
+ return
+ return_list += mutable_appearance(frame_icon, "[base_icon_state]-[smoothing_junction]", appearance_flags = KEEP_APART)
+
+/obj/structure/window_frame/update_overlays()
+ . = ..()
+ create_grill_overlays(.)
+ create_frame_overlay(.)
+
+/obj/structure/window_frame/proc/temporary_shatter(time_to_go = 0 SECONDS, time_to_return = 4 SECONDS)
+ if(dramatically_disappearing)
+ return
+
+ //dissapear in 1 second
+ dramatically_disappearing = TRUE
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/movable, moveToNullspace)), time_to_go) //woosh
+
+ // come back in 1 + 4 seconds
+ addtimer(VARSET_CALLBACK(src, atom_integrity, atom_integrity), time_to_go + time_to_return) //set the health back (icon is updated on move)
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/movable, forceMove), loc), time_to_go + time_to_return) //we back boys
+ addtimer(VARSET_CALLBACK(src, dramatically_disappearing, FALSE), time_to_go + time_to_return) //also set the var back
+
+/// Do some very specific checks to see if we *would* get shocked. Returns TRUE if it's shocked
+/obj/structure/window_frame/proc/is_shocked()
+ var/turf/turf = get_turf(src)
+ var/obj/structure/cable/cable = turf.get_cable_node()
+ var/list/powernet_info = get_powernet_info_from_source(cable)
+
+ if(!powernet_info)
+ return FALSE
+
+ var/datum/powernet/powernet = powernet_info["powernet"]
+ return !!powernet.get_electrocute_damage()
+
+/obj/structure/window_frame/grille
+ has_grille = TRUE
+
+/obj/structure/window_frame/grille_and_window
+ has_grille = TRUE
+ start_with_window = TRUE
+
+/obj/structure/window_frame/reinforced
+ name = "reinforced window frame"
+ window_type = /obj/item/stack/sheet/rglass
+ armor_type = /datum/armor/window_frame_reinforced
+ max_integrity = 150
+ damage_deflection = 11
+
+/datum/armor/window_frame_reinforced
+ melee = 80
+ bomb = 25
+ bio = 100
+ fire = 80
+ acid = 100
+
+/obj/structure/window_frame/reinforced/grille_and_window
+ has_grille = TRUE
+ start_with_window = TRUE
+
+/obj/structure/window_frame/reinforced/damaged
+ var/integrity_min_factor = 0.2
+ var/integrity_max_factor = 0.8
+
+/obj/structure/window_frame/reinforced/damaged/Initialize(mapload)
+ . = ..()
+ var/obj/structure/window/our_window = locate() in get_turf(src)
+ if(!our_window)
+ return
+
+ our_window.update_integrity(rand(max_integrity * integrity_min_factor, max_integrity * integrity_max_factor))
+
+/obj/structure/window_frame/reinforced/damaged/grille_and_window
+ has_grille = TRUE
+ start_with_window = TRUE
+
+/obj/structure/window_frame/titanium
+ name = "shuttle window frame"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_shuttle.dmi'
+ icon_state = "window_frame_shuttle-0"
+ base_icon_state = "window_frame_shuttle"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_shuttle.dmi'
+ sheet_type = /obj/item/stack/sheet/mineral/titanium
+ window_type = /obj/item/stack/sheet/titaniumglass
+ custom_materials = list(/datum/material/titanium = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/titanium/grille_and_window
+ has_grille = TRUE
+ start_with_window = TRUE
+
+/obj/structure/window_frame/titanium/grille
+ has_grille = TRUE
+
+/obj/structure/window_frame/plastitanium
+ name = "plastitanium window frame"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_plastitanium.dmi'
+ icon_state = "window_frame_plastitanium-0"
+ base_icon_state = "window_frame_plastitanium"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_plastitanium.dmi'
+ sheet_type = /obj/item/stack/sheet/mineral/plastitanium
+ window_type = /obj/item/stack/sheet/plastitaniumglass
+ custom_materials = list(/datum/material/alloy/plastitanium = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/plastitanium/grille_and_window
+ has_grille = TRUE
+ start_with_window = TRUE
+
+/obj/structure/window_frame/wood
+ name = "wooden platform"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_wood.dmi'
+ icon_state = "window_frame_wood-0"
+ base_icon_state = "window_frame_wood"
+ frame_icon = null //no walls above the center
+ sheet_type = /obj/item/stack/sheet/mineral/wood
+ custom_materials = list(/datum/material/wood = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/uranium
+ name = "uranium window frame"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_uranium.dmi'
+ icon_state = "window_frame_uranium-0"
+ base_icon_state = "window_frame_uranium"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_uranium.dmi'
+ sheet_type = /obj/item/stack/sheet/mineral/uranium
+ custom_materials = list(/datum/material/uranium = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/iron
+ name = "rough iron window frame"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_iron.dmi'
+ icon_state = "window_frame_iron-0"
+ base_icon_state = "window_frame_iron"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_iron.dmi'
+ sheet_type = /obj/item/stack/sheet/iron
+ custom_materials = list(/datum/material/iron = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/silver
+ name = "silver window frame"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_silver.dmi'
+ icon_state = "window_frame_silver-0"
+ base_icon_state = "window_frame_silver"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_silver.dmi'
+ sheet_type = /obj/item/stack/sheet/mineral/silver
+ custom_materials = list(/datum/material/silver = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/gold
+ name = "gold window frame"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_gold.dmi'
+ icon_state = "window_frame_gold-0"
+ base_icon_state = "window_frame_gold"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_gold.dmi'
+ sheet_type = /obj/item/stack/sheet/mineral/gold
+ custom_materials = list(/datum/material/gold = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/bronze
+ name = "clockwork window mount"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_bronze.dmi'
+ icon_state = "window_frame_bronze-0"
+ base_icon_state = "window_frame_bronze"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_bronze.dmi'
+ sheet_type = /obj/item/stack/sheet/bronze
+ custom_materials = list(/datum/material/bronze = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/cult
+ name = "rune-scarred window frame"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_cult.dmi'
+ icon_state = "window_frame_cult-0"
+ base_icon_state = "window_frame_cult"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_cult.dmi'
+ sheet_type = /obj/item/stack/sheet/runed_metal
+ custom_materials = list(/datum/material/runedmetal = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/hotel
+ name = "hotel window frame"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_hotel.dmi'
+ icon_state = "window_frame_hotel-0"
+ base_icon_state = "window_frame_hotel"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_hotel.dmi'
+ sheet_type = /obj/item/stack/sheet/mineral/wood
+ custom_materials = list(/datum/material/wood = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/material
+ name = "material window frame"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_material.dmi'
+ icon_state = "window_frame_material-0"
+ base_icon_state = "window_frame_material"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_material.dmi'
+ material_flags = MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
+
+/obj/structure/window_frame/rusty
+ name = "rusty window frame"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_rusty.dmi'
+ icon_state = "window_frame_rusty-0"
+ base_icon_state = "window_frame_rusty"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_rusty.dmi'
+ sheet_type = /obj/item/stack/sheet/iron
+ custom_materials = list(/datum/material/iron = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/sandstone
+ name = "sandstone plinth"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_sandstone.dmi'
+ icon_state = "window_frame_sandstone-0"
+ base_icon_state = "window_frame_sandstone"
+ frame_icon = null //no walls above center
+ sheet_type = /obj/item/stack/sheet/mineral/sandstone
+ custom_materials = list(/datum/material/sandstone = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/bamboo
+ name = "bamboo platform"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_bamboo.dmi'
+ icon_state = "window_frame_bamboo-0"
+ base_icon_state = "window_frame_bamboo"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_bamboo.dmi'
+ sheet_type = /obj/item/stack/sheet/mineral/bamboo
+ custom_materials = list(/datum/material/bamboo = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/paperframe
+ name = "japanese window frame"
+ icon = 'icons/obj/structures/smooth/window_frames/window_frame_paperframe.dmi'
+ icon_state = "window_frame_paperframe-0"
+ base_icon_state = "window_frame_paperframe"
+ frame_icon = 'icons/obj/structures/smooth/window_frames/frame_faces/window_frame_paperframe.dmi'
+ sheet_type = /obj/item/stack/sheet/paperframes
+ window_type = /obj/item/stack/sheet/paperframes
+ custom_materials = list(/datum/material/paper = WINDOW_FRAME_BASE_MATERIAL_AMOUNT)
+
+/obj/structure/window_frame/paperframe/grille_and_window
+ has_grille = TRUE
+ start_with_window = TRUE
diff --git a/code/game/objects/structures/windows/fulltile_windows.dm b/code/game/objects/structures/windows/fulltile_windows.dm
new file mode 100644
index 0000000000000..7dfb1a0d49ef8
--- /dev/null
+++ b/code/game/objects/structures/windows/fulltile_windows.dm
@@ -0,0 +1,275 @@
+/obj/structure/window/fulltile
+ icon = 'icons/obj/structures/smooth/windows/normal_window.dmi'
+ icon_state = "0-lower"
+ pixel_y = WINDOW_OFF_FRAME_Y_OFFSET
+ max_integrity = 50
+ fulltile = TRUE
+ flags_1 = PREVENT_CLICK_UNDER_1
+ smoothing_flags = SMOOTH_BITMASK | SMOOTH_OBJ
+ smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE
+ canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
+ glass_amount = 2
+ rcd_spritesheet_override = "full tile window"
+
+/obj/structure/window/fulltile/unanchored
+ anchored = FALSE
+
+/obj/structure/window/plasma/fulltile
+ icon = 'icons/obj/structures/smooth/windows/plasma_window.dmi'
+ icon_state = "0-lower"
+ pixel_y = WINDOW_OFF_FRAME_Y_OFFSET
+ max_integrity = 300
+ fulltile = TRUE
+ flags_1 = PREVENT_CLICK_UNDER_1
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE
+ canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
+ glass_amount = 2
+
+/obj/structure/window/plasma/fulltile/unanchored
+ anchored = FALSE
+
+/obj/structure/window/reinforced/plasma/fulltile
+ icon = 'icons/obj/structures/smooth/windows/plasma_reinforced_window.dmi'
+ icon_state = "0-lower"
+ pixel_y = WINDOW_OFF_FRAME_Y_OFFSET
+ state = RWINDOW_SECURE
+ max_integrity = 1000
+ fulltile = TRUE
+ flags_1 = PREVENT_CLICK_UNDER_1
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE
+ canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
+ glass_amount = 2
+
+/obj/structure/window/reinforced/plasma/fulltile/unanchored
+ anchored = FALSE
+ state = WINDOW_OUT_OF_FRAME
+
+/obj/structure/window/reinforced/fulltile
+ icon = 'icons/obj/structures/smooth/windows/reinforced_window.dmi'
+ icon_state = "0-lower"
+ pixel_y = WINDOW_OFF_FRAME_Y_OFFSET
+ max_integrity = 150
+ fulltile = TRUE
+ flags_1 = PREVENT_CLICK_UNDER_1
+ state = RWINDOW_SECURE
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE
+ canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
+ glass_amount = 2
+ rcd_spritesheet_override = "reinforced full tile window"
+
+/obj/structure/window/reinforced/fulltile/unanchored
+ anchored = FALSE
+ state = WINDOW_OUT_OF_FRAME
+
+/obj/structure/window/reinforced/fulltile/damaged
+ var/integrity_min_factor = 0.2
+ var/integrity_max_factor = 0.8
+
+/obj/structure/window/reinforced/fulltile/damaged/Initialize(mapload)
+ . = ..()
+ atom_integrity = rand(max_integrity * integrity_min_factor, max_integrity * integrity_max_factor)
+
+/obj/structure/window/reinforced/tinted/fulltile
+ icon = 'icons/obj/structures/smooth/windows/tinted_window.dmi'
+ icon_state = "0-lower"
+ pixel_y = WINDOW_OFF_FRAME_Y_OFFSET
+ fulltile = TRUE
+ flags_1 = PREVENT_CLICK_UNDER_1
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE
+ canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
+ glass_amount = 2
+
+/obj/structure/window/reinforced/fulltile/ice
+ icon = 'icons/obj/structures/smooth/windows/frosted_window.dmi'
+ max_integrity = 150
+ glass_amount = 2
+
+//there is a sub shuttle window in survival_pod.dm for mining pods
+/obj/structure/window/reinforced/shuttle//this is called reinforced because it is reinforced w/titanium
+ name = "shuttle window"
+ desc = "A reinforced, air-locked pod window."
+ icon = 'icons/obj/structures/smooth/windows/titanium_window.dmi'
+ icon_state = "0-lower"
+ pixel_y = WINDOW_OFF_FRAME_Y_OFFSET
+ max_integrity = 150
+ wtype = "shuttle"
+ reinf = TRUE
+ fulltile = TRUE
+ flags_1 = PREVENT_CLICK_UNDER_1
+ reinf = TRUE
+ heat_resistance = 1600
+ armor_type = /datum/armor/reinforced_shuttle
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE
+ canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE
+ explosion_block = 3
+ glass_type = /obj/item/stack/sheet/titaniumglass
+ glass_amount = 2
+ receive_ricochet_chance_mod = 1.2
+
+/datum/armor/reinforced_shuttle
+ melee = 90
+ bomb = 50
+ fire = 80
+ acid = 100
+
+/obj/structure/window/reinforced/shuttle/narsie_act()
+ add_atom_colour("#3C3434", FIXED_COLOUR_PRIORITY)
+
+/obj/structure/window/reinforced/shuttle/tinted
+ opacity = TRUE
+
+/obj/structure/window/reinforced/shuttle/unanchored
+ anchored = FALSE
+
+/obj/structure/window/reinforced/shuttle/indestructible
+ name = "hardened shuttle window"
+ obj_flags = CAN_BE_HIT | BLOCKS_CONSTRUCTION
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+
+/obj/structure/window/reinforced/shuttle/indestructible/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
+ return FALSE
+
+/obj/structure/window/reinforced/plasma/plastitanium
+ name = "plastitanium window"
+ desc = "A durable looking window made of an alloy of of plasma and titanium."
+ icon = 'icons/obj/structures/smooth/windows/plastitanium_window.dmi'
+ icon_state = "0-lower"
+ pixel_y = WINDOW_OFF_FRAME_Y_OFFSET
+ max_integrity = 1200
+ wtype = "shuttle"
+ fulltile = TRUE
+ flags_1 = PREVENT_CLICK_UNDER_1
+ heat_resistance = 1600
+ armor_type = /datum/armor/plasma_plastitanium
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM
+ canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM
+ explosion_block = 3
+ damage_deflection = 21 //The same as reinforced plasma windows.3
+ glass_type = /obj/item/stack/sheet/plastitaniumglass
+ glass_amount = 2
+ rad_insulation = RAD_HEAVY_INSULATION
+
+/datum/armor/plasma_plastitanium
+ melee = 95
+ bomb = 50
+ fire = 80
+ acid = 100
+
+/obj/structure/window/reinforced/plasma/plastitanium/unanchored
+ anchored = FALSE
+ state = WINDOW_OUT_OF_FRAME
+
+/obj/structure/window/paperframe
+ name = "paper frame"
+ desc = "A fragile separator made of thin wood and paper."
+ icon = 'icons/obj/structures/smooth/windows/paper_window.dmi'
+ icon_state = null
+ opacity = TRUE
+ max_integrity = 15
+ fulltile = TRUE
+ flags_1 = PREVENT_CLICK_UNDER_1
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_PAPERFRAME
+ canSmoothWith = SMOOTH_GROUP_PAPERFRAME
+ glass_amount = 2
+ glass_type = /obj/item/stack/sheet/paperframes
+ heat_resistance = 233
+ decon_speed = 10
+ can_atmos_pass = ATMOS_PASS_YES
+ resistance_flags = FLAMMABLE
+ armor_type = /datum/armor/none
+ knock_sound = "pageturn"
+ bash_sound = 'sound/weapons/slashmiss.ogg'
+ break_sound = 'sound/items/poster_ripped.ogg'
+ hit_sound = 'sound/weapons/slashmiss.ogg'
+
+/obj/structure/window/paperframe/Initialize(mapload)
+ . = ..()
+ update_appearance()
+
+/obj/structure/window/paperframe/examine(mob/user)
+ . = ..()
+ if(atom_integrity < max_integrity)
+ . += span_info("It looks a bit damaged, you may be able to fix it with some paper.")
+
+/obj/structure/window/paperframe/spawn_debris(location)
+ . = list(new /obj/item/stack/sheet/mineral/wood(location))
+ for (var/i in 1 to rand(1,4))
+ . += new /obj/item/paper/natural(location)
+
+/obj/structure/window/paperframe/attack_hand(mob/living/user, list/modifiers)
+ . = ..()
+ if(.)
+ return
+ if(user.combat_mode)
+ take_damage(4, BRUTE, MELEE, 0)
+ if(!QDELETED(src))
+ update_appearance()
+
+/obj/structure/window/paperframe/update_appearance(updates)
+ . = ..()
+ set_opacity(atom_integrity >= max_integrity)
+
+/obj/structure/window/paperframe/update_icon(updates=ALL)
+ if(atom_integrity >= max_integrity)
+ icon = 'icons/obj/structures/smooth/windows/paper_window.dmi'
+ else
+ icon = 'icons/obj/structures/smooth/windows/paper_window_torn.dmi'
+ . = ..()
+ if((updates & UPDATE_SMOOTHING) && (smoothing_flags & USES_SMOOTHING))
+ QUEUE_SMOOTH(src)
+
+/obj/structure/window/paperframe/attackby(obj/item/W, mob/living/user)
+ if(W.get_temperature())
+ fire_act(W.get_temperature())
+ return
+ if(user.combat_mode)
+ return ..()
+ if(istype(W, /obj/item/paper) && atom_integrity < max_integrity)
+ user.visible_message(span_notice("[user] starts to patch the holes in \the [src]."))
+ if(do_after(user, 20, target = src))
+ atom_integrity = min(atom_integrity+4,max_integrity)
+ qdel(W)
+ user.visible_message(span_notice("[user] patches some of the holes in \the [src]."))
+ if(atom_integrity == max_integrity)
+ update_appearance()
+ return
+ ..()
+ update_appearance()
+
+
+/obj/structure/window/bronze
+ name = "brass window"
+ desc = "A paper-thin pane of translucent yet reinforced brass. Nevermind, this is just weak bronze!"
+ icon = 'icons/obj/structures/smooth/structure_variations.dmi'
+ icon_state = "clockwork_window-single"
+ glass_type = /obj/item/stack/sheet/bronze
+
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/bronze/spawner)
+
+/obj/structure/window/bronze/unanchored
+ anchored = FALSE
+
+/obj/structure/window/bronze/fulltile
+ icon = 'icons/obj/structures/smooth/clockwork_window.dmi'
+ pixel_y = WINDOW_OFF_FRAME_Y_OFFSET
+ icon_state = "clockwork_window-0"
+ base_icon_state = "clockwork_window"
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE_BRONZE + SMOOTH_GROUP_WINDOW_FULLTILE
+ canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_BRONZE
+ fulltile = TRUE
+ flags_1 = PREVENT_CLICK_UNDER_1
+ obj_flags = CAN_BE_HIT
+ max_integrity = 50
+ glass_amount = 2
+
+/obj/structure/window/bronze/fulltile/unanchored
+ anchored = FALSE
+
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/windows/window.dm
similarity index 62%
rename from code/game/objects/structures/window.dm
rename to code/game/objects/structures/windows/window.dm
index 6c41b2f8b157d..7d2a024820001 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/windows/window.dm
@@ -1,15 +1,16 @@
/obj/structure/window
name = "window"
desc = "A directional window."
+ icon = 'icons/obj/structures/smooth/windows/normal_thindow.dmi'
icon_state = "window"
density = TRUE
layer = ABOVE_OBJ_LAYER //Just above doors
+ can_be_unanchored = TRUE
pressure_resistance = 4*ONE_ATMOSPHERE
anchored = TRUE //initially is 0 for tile smoothing
flags_1 = ON_BORDER_1
obj_flags = CAN_BE_HIT | BLOCKS_CONSTRUCTION_DIR | IGNORE_DENSITY
max_integrity = 50
- can_be_unanchored = TRUE
resistance_flags = ACID_PROOF
armor_type = /datum/armor/structure_window
can_atmos_pass = ATMOS_PASS_PROC
@@ -18,6 +19,8 @@
set_dir_on_move = FALSE
flags_ricochet = RICOCHET_HARD
receive_ricochet_chance_mod = 0.5
+ canSmoothWith = SMOOTH_GROUP_THINDOWS
+ smoothing_groups = SMOOTH_GROUP_THINDOWS
var/state = WINDOW_OUT_OF_FRAME
var/reinf = FALSE
var/heat_resistance = 800
@@ -25,6 +28,7 @@
var/wtype = "glass"
var/fulltile = FALSE
var/glass_type = /obj/item/stack/sheet/glass
+ var/shard_type = /obj/item/shard
var/glass_amount = 1
var/real_explosion_block //ignore this, just use explosion_block
var/break_sound = SFX_SHATTER
@@ -35,6 +39,10 @@
var/bloodied = FALSE
///Datum that the shard and debris type is pulled from for when the glass is broken.
var/datum/material/glass_material_datum = /datum/material/glass
+ /// Whether or not we're disappearing but dramatically
+ var/dramatically_disappearing = FALSE
+ /// If we added a leaning component to ourselves
+ var/added_leaning = FALSE
/datum/armor/structure_window
melee = 50
@@ -43,6 +51,8 @@
/obj/structure/window/Initialize(mapload, direct)
AddElement(/datum/element/blocks_explosives)
+ if(!fulltile)
+ blocks_emissive = EMISSIVE_BLOCK_NONE
. = ..()
if(direct)
setDir(direct)
@@ -55,10 +65,16 @@
air_update_turf(TRUE, TRUE)
if(fulltile)
- setDir()
+ setDir(direct)
obj_flags &= ~BLOCKS_CONSTRUCTION_DIR
obj_flags &= ~IGNORE_DENSITY
+ update_icon_state()
AddElement(/datum/element/can_barricade)
+ AddComponent(/datum/component/window_smoothing)
+ else
+ smoothing_flags = SMOOTH_BITMASK|SMOOTH_BORDER_OBJECT|SMOOTH_OBJ
+ setDir(dir)
+ AddElement(/datum/element/render_over_keep_hitbox, BELOW_OBJ_LAYER, /* use_position_layering = */ TRUE)
//windows only block while reinforced and fulltile
if(!reinf || !fulltile)
@@ -76,6 +92,35 @@
if (flags_1 & ON_BORDER_1)
AddElement(/datum/element/connect_loc, loc_connections)
+/obj/structure/window/mouse_drop_receive(atom/dropping, mob/user, params)
+ . = ..()
+ if (added_leaning)
+ return
+ /// For performance reasons and to cut down on init times we are "lazy-loading" the leaning component when someone drags their sprite onto us, and then calling dragging code again to trigger the component
+ AddComponent(/datum/component/leanable, 11, same_turf = (flags_1 & ON_BORDER_1), lean_check = CALLBACK(src, PROC_REF(lean_check)))
+ added_leaning = TRUE
+ dropping.base_mouse_drop_handler(src, null, null, params)
+
+/obj/structure/window/proc/lean_check(mob/living/leaner, list/modifiers)
+ if (!(flags_1 & ON_BORDER_1))
+ return TRUE
+
+ if (leaner.loc == loc)
+ return dir == REVERSE_DIR(leaner.dir)
+
+ return get_dir(src, leaner) == dir && leaner.dir == dir
+
+/obj/structure/window/setDir(newdir)
+ . = ..()
+ if(fulltile)
+ return
+ // Needed because render targets seem to shift larger then 32x32 icons down constantly. No idea why
+ pixel_y = 0
+ pixel_z = 16
+ if(smoothing_flags & SMOOTH_BORDER_OBJECT)
+ QUEUE_SMOOTH_NEIGHBORS(src)
+ QUEUE_SMOOTH(src)
+
/obj/structure/window/examine(mob/user)
. = ..()
@@ -90,13 +135,18 @@
else
. += span_notice("The window is unscrewed from the floor, and could be deconstructed by wrenching.")
+/obj/structure/window/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
+ . = ..()
+ if(fulltile)
+ update_icon_state()
+
/obj/structure/window/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
if(the_rcd.mode == RCD_DECONSTRUCT)
return list("delay" = 2 SECONDS, "cost" = 5)
return FALSE
/obj/structure/window/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- if(rcd_data["[RCD_DESIGN_MODE]"] == RCD_DECONSTRUCT)
+ if(rcd_data[RCD_DESIGN_MODE] == RCD_DECONSTRUCT)
qdel(src)
return TRUE
return FALSE
@@ -260,6 +310,7 @@
if(tool.use_tool(src, user, 10 SECONDS, volume = 75, extra_checks = CALLBACK(src, PROC_REF(check_state_and_anchored), state, anchored)))
state = WINDOW_OUT_OF_FRAME
to_chat(user, span_notice("You pry the window out of the frame."))
+ set_anchored(FALSE)
if(WINDOW_OUT_OF_FRAME)
to_chat(user, span_notice("You begin to lever the window back into the frame..."))
if(tool.use_tool(src, user, 5 SECONDS, volume = 75, extra_checks = CALLBACK(src, PROC_REF(check_state_and_anchored), state, anchored)))
@@ -279,7 +330,7 @@
/obj/structure/window/set_anchored(anchorvalue)
- ..()
+ . = ..()
air_update_turf(TRUE, anchorvalue)
update_nearby_icons()
@@ -359,6 +410,8 @@
else
set_opacity(initial(opacity))
+
+
/obj/structure/window/wash(clean_types)
. = ..()
if(!(clean_types & CLEAN_SCRUB))
@@ -396,20 +449,223 @@
if(smoothing_flags & USES_SMOOTHING)
QUEUE_SMOOTH_NEIGHBORS(src)
-//merges adjacent full-tile windows into one
+/obj/structure/window/update_icon_state()
+ . = ..()
+ if(fulltile)
+ if(locate(/obj/structure/window_frame) in loc)
+ pixel_y = WINDOW_ON_FRAME_Y_OFFSET
+ else
+ pixel_y = WINDOW_OFF_FRAME_Y_OFFSET
+ else
+ switch(dir)
+ if(NORTH)
+ icon_state = "body-t"
+ if(SOUTH)
+ icon_state = "body-b"
+ if(EAST)
+ icon_state = "body-r"
+ if(WEST)
+ icon_state = "body-l"
+
+//merges adjacent windows, handle cracking for fulltiles
/obj/structure/window/update_overlays(updates=ALL)
. = ..()
- if(QDELETED(src) || !fulltile)
+ if(QDELETED(src))
return
if((updates & UPDATE_SMOOTHING) && (smoothing_flags & USES_SMOOTHING))
QUEUE_SMOOTH(src)
- var/ratio = atom_integrity / max_integrity
- ratio = CEILING(ratio*4, 1) * 25
- if(ratio > 75)
- return
- . += mutable_appearance('icons/obj/structures.dmi', "damage[ratio]", -(layer+0.1))
+ if(fulltile)
+ var/ratio = atom_integrity / max_integrity
+ ratio = CEILING(ratio*4, 1)
+ var/broken_icon = null
+ switch(ratio)
+ if(3)
+ broken_icon = 'icons/obj/structures/smooth/windows/window_broken_light.dmi'
+ if(2)
+ broken_icon = 'icons/obj/structures/smooth/windows/window_broken_medium.dmi'
+ if(1)
+ broken_icon = 'icons/obj/structures/smooth/windows/window_broken_heavy.dmi'
+
+ if(broken_icon)
+ . += mutable_appearance(broken_icon, "[smoothing_junction]", -(layer+0.1))
+ return .
+
+ var/list/states_to_apply = list()
+ var/handled_junctions = NONE
+ if(smoothing_junction & NORTHEAST_JUNCTION && smoothing_junction & SOUTHEAST_JUNCTION && smoothing_junction & EAST_JUNCTION)
+ handled_junctions |= NORTHEAST_JUNCTION | SOUTHEAST_JUNCTION | EAST_JUNCTION
+ switch(dir)
+ if(NORTH)
+ states_to_apply += "quad-tr"
+ if(SOUTH)
+ states_to_apply += "quad-br"
+ if(smoothing_junction & NORTHWEST_JUNCTION && smoothing_junction & SOUTHWEST_JUNCTION && smoothing_junction & WEST_JUNCTION)
+ handled_junctions |= NORTHWEST_JUNCTION | SOUTHWEST_JUNCTION | WEST_JUNCTION
+ switch(dir)
+ if(NORTH)
+ states_to_apply += "quad-tl"
+ if(SOUTH)
+ states_to_apply += "quad-bl"
+ if(smoothing_junction & SOUTHWEST_JUNCTION && smoothing_junction & SOUTHEAST_JUNCTION && smoothing_junction & SOUTH_JUNCTION)
+ handled_junctions |= SOUTHWEST_JUNCTION | SOUTHEAST_JUNCTION | SOUTH_JUNCTION
+ if(smoothing_junction & NORTHWEST_JUNCTION && smoothing_junction & NORTHEAST_JUNCTION && smoothing_junction & NORTH_JUNCTION)
+ handled_junctions |= NORTHWEST_JUNCTION | NORTHEAST_JUNCTION | NORTH_JUNCTION
+
+ if(smoothing_junction & NORTHWEST_JUNCTION && smoothing_junction & WEST_JUNCTION && !(handled_junctions & (NORTHWEST_JUNCTION|WEST_JUNCTION)))
+ switch(dir)
+ if(SOUTH)
+ handled_junctions |= NORTHWEST_JUNCTION | WEST_JUNCTION
+ states_to_apply += "up-triple-bl"
+ if(smoothing_junction & NORTHEAST_JUNCTION && smoothing_junction & EAST_JUNCTION && !(handled_junctions & (NORTHEAST_JUNCTION|EAST_JUNCTION)))
+ switch(dir)
+ if(SOUTH)
+ handled_junctions |= NORTHEAST_JUNCTION | EAST_JUNCTION
+ states_to_apply += "up-triple-br"
+ if(smoothing_junction & SOUTHWEST_JUNCTION && smoothing_junction & WEST_JUNCTION && !(handled_junctions & (SOUTHWEST_JUNCTION|WEST_JUNCTION)))
+ switch(dir)
+ if(NORTH)
+ handled_junctions |= SOUTHWEST_JUNCTION | WEST_JUNCTION
+ states_to_apply += "down-triple-tl"
+ if(smoothing_junction & SOUTHEAST_JUNCTION && smoothing_junction & EAST_JUNCTION && !(handled_junctions & (SOUTHEAST_JUNCTION|EAST_JUNCTION)))
+ switch(dir)
+ if(NORTH)
+ handled_junctions |= SOUTHEAST_JUNCTION | EAST_JUNCTION
+ states_to_apply += "down-triple-tr"
+
+ if(smoothing_junction & SOUTHEAST_JUNCTION && smoothing_junction & SOUTH_JUNCTION && !(handled_junctions & (SOUTHEAST_JUNCTION|SOUTH_JUNCTION)))
+ switch(dir)
+ if(WEST)
+ handled_junctions |= SOUTHEAST_JUNCTION | SOUTH_JUNCTION | EAST_JUNCTION
+ states_to_apply += "right-triple-bl"
+ if(smoothing_junction & SOUTHWEST_JUNCTION && smoothing_junction & SOUTH_JUNCTION && !(handled_junctions & (SOUTHWEST_JUNCTION|SOUTH_JUNCTION)))
+ switch(dir)
+ if(EAST)
+ handled_junctions |= SOUTHWEST_JUNCTION | SOUTH_JUNCTION | WEST_JUNCTION
+ states_to_apply += "left-triple-br"
+
+ if(smoothing_junction & NORTHEAST_JUNCTION && smoothing_junction & NORTH_JUNCTION && !(handled_junctions & (NORTHEAST_JUNCTION|NORTH_JUNCTION)))
+ switch(dir)
+ if(WEST)
+ handled_junctions |= NORTHEAST_JUNCTION | NORTH_JUNCTION | EAST_JUNCTION
+ states_to_apply += "right-triple-tl"
+ if(smoothing_junction & NORTHWEST_JUNCTION && smoothing_junction & NORTH_JUNCTION && !(handled_junctions & (NORTHWEST_JUNCTION|NORTH_JUNCTION)))
+ switch(dir)
+ if(EAST)
+ handled_junctions |= NORTHWEST_JUNCTION | NORTH_JUNCTION | WEST_JUNCTION
+ states_to_apply += "left-triple-tr"
+
+ // These cases exist JUST to eat diagonal smooths for NORTH/SOUTH windows
+ if(smoothing_junction & SOUTHWEST_JUNCTION && smoothing_junction & NORTHWEST_JUNCTION)
+ switch(dir)
+ if(NORTH, SOUTH)
+ handled_junctions |= SOUTHWEST_JUNCTION | NORTHWEST_JUNCTION | WEST_JUNCTION | NORTH_JUNCTION | SOUTH_JUNCTION
+ if(smoothing_junction & SOUTHEAST_JUNCTION && smoothing_junction & NORTHEAST_JUNCTION)
+ switch(dir)
+ if(NORTH, SOUTH)
+ handled_junctions |= SOUTHEAST_JUNCTION | NORTHEAST_JUNCTION | EAST_JUNCTION | NORTH_JUNCTION | SOUTH_JUNCTION
+
+ // filter out everything on the tile opposing us
+ handled_junctions |= dir_to_all_junctions(dir)
+
+ if(smoothing_junction & NORTHWEST_JUNCTION && !(handled_junctions & NORTHWEST_JUNCTION))
+ handled_junctions |= NORTH_JUNCTION | WEST_JUNCTION
+ // Only gonna define dirs that allow a body to exist sanely
+ // So South is acceptable
+ // Also want to avoid double application
+ // hhhh
+ switch(dir)
+ if(SOUTH)
+ states_to_apply += "up-right-corner-bl"
+ if(smoothing_junction & NORTHEAST_JUNCTION && !(handled_junctions & NORTHEAST_JUNCTION))
+ handled_junctions |= NORTH_JUNCTION | EAST_JUNCTION
+ switch(dir)
+ if(SOUTH)
+ states_to_apply += "up-left-corner-br"
+ if(smoothing_junction & SOUTHWEST_JUNCTION && !(handled_junctions & SOUTHWEST_JUNCTION))
+ handled_junctions |= SOUTH_JUNCTION | WEST_JUNCTION
+ switch(dir)
+ if(NORTH)
+ states_to_apply += "down-right-corner-tl"
+ if(smoothing_junction & SOUTHEAST_JUNCTION && !(handled_junctions & SOUTHEAST_JUNCTION))
+ handled_junctions |= SOUTH_JUNCTION | EAST_JUNCTION
+ switch(dir)
+ if(NORTH)
+ states_to_apply += "down-left-corner-tr"
+
+ if(!(handled_junctions & WEST_JUNCTION))
+ if(smoothing_junction & WEST_JUNCTION)
+ switch(dir)
+ if(NORTH)
+ states_to_apply += "horizontal-cont-tl"
+ if(SOUTH)
+ states_to_apply += "horizontal-cont-bl"
+ else
+ switch(dir)
+ if(NORTH)
+ states_to_apply += "horizontal-edge-tl"
+ if(SOUTH)
+ states_to_apply += "horizontal-edge-bl"
+
+ if(!(handled_junctions & EAST_JUNCTION))
+ if(smoothing_junction & EAST_JUNCTION)
+ switch(dir)
+ if(NORTH)
+ states_to_apply += "horizontal-cont-tr"
+ if(SOUTH)
+ states_to_apply += "horizontal-cont-br"
+ else
+ switch(dir)
+ if(NORTH)
+ states_to_apply += "horizontal-edge-tr"
+ if(SOUTH)
+ states_to_apply += "horizontal-edge-br"
+
+ if(!(handled_junctions & SOUTH_JUNCTION))
+ if(smoothing_junction & SOUTH_JUNCTION)
+ switch(dir)
+ if(EAST)
+ states_to_apply += "vertical-cont-br"
+ if(WEST)
+ states_to_apply += "vertical-cont-bl"
+ else
+ switch(dir)
+ if(EAST)
+ states_to_apply += "vertical-edge-br"
+ if(WEST)
+ states_to_apply += "vertical-edge-bl"
+
+ if(!(handled_junctions & NORTH_JUNCTION))
+ if(smoothing_junction & NORTH_JUNCTION)
+ switch(dir)
+ if(EAST)
+ states_to_apply += "vertical-cont-tr"
+ if(WEST)
+ states_to_apply += "vertical-cont-tl"
+ else
+ switch(dir)
+ if(EAST)
+ states_to_apply += "vertical-edge-tr"
+ if(WEST)
+ states_to_apply += "vertical-edge-tl"
+
+ for(var/window_state in states_to_apply)
+ . += mutable_appearance(icon, window_state)
+
+ // We can't use typical emissive blocking because of the pixel offset, remove when that's fixed please
+ var/list/states_to_block = states_to_apply + icon_state
+ for(var/blocked_state in states_to_block)
+ // Cancels out the pixel offset we apply to the parent
+ // (Which is needed because render_target is bugged)
+ var/mutable_appearance/blocker = emissive_blocker(icon, blocked_state, offset_spokesman = src)
+ blocker.pixel_z = -pixel_z
+ . += blocker
+
+/obj/structure/window/set_smoothed_icon_state(new_junction)
+ if(fulltile)
+ return ..()
+ smoothing_junction = new_junction
/obj/structure/window/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
return exposed_temperature > T0C + heat_resistance
@@ -428,17 +684,61 @@
return TRUE
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/spawner)
+
+/obj/structure/window/proc/temporary_shatter(time_to_go = 1 SECONDS, time_to_return = 4 SECONDS, take_grill = TRUE)
+ if(dramatically_disappearing)
+ return
+
+ // do a cute breaking animation
+ var/static/time_interval = 2 DECISECONDS //per how many steps should we do damage?
+ for(var/damage_step in 1 to (floor(time_to_go / time_interval) - 1)) //10 ds / 2 ds = 5 damage steps, minus 1 so we dont actually break it
+ // slowly drain our total health for the illusion of shattering
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, take_damage), floor(atom_integrity / (time_to_go / time_interval))), time_interval * damage_step)
+
+ //dissapear in 1 second
+ dramatically_disappearing = TRUE
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), loc, break_sound, 70, TRUE), time_to_go) //SHATTER SOUND
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/movable, moveToNullspace)), time_to_go) //woosh
+
+ // come back in 1 + 4 seconds
+ addtimer(VARSET_CALLBACK(src, atom_integrity, atom_integrity), time_to_go + time_to_return) //set the health back (icon is updated on move)
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/movable, forceMove), loc), time_to_go + time_to_return) //we back boys
+ addtimer(VARSET_CALLBACK(src, dramatically_disappearing, FALSE), time_to_go + time_to_return) //also set the var back
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), get_turf(src), 'sound/effects/glass_reverse.ogg', 70, TRUE), time_to_go + time_to_return)
+
+ var/obj/structure/window_frame/frame = take_grill ? (locate(/obj/structure/window_frame) in loc) : null
+ if(frame)
+ frame.temporary_shatter(time_to_go, time_to_return)
+
+/obj/structure/window/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
+ . = ..()
+
+ if(loc)
+ update_nearby_icons()
+
MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/spawner, 0)
/obj/structure/window/unanchored
anchored = FALSE
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/unanchored/spawner, 0)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/unanchored/spawner)
+
+/obj/structure/window/half
+ can_atmos_pass = ATMOS_PASS_YES
+ icon = 'icons/obj/structures/smooth/windows/half_thindow.dmi'
+
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/half)
+
+/obj/structure/window/half/unanchored
+ anchored = FALSE
+
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/half/unanchored)
/obj/structure/window/reinforced
name = "reinforced window"
desc = "A window that is reinforced with metal rods."
- icon_state = "rwindow"
+ icon = 'icons/obj/structures/smooth/windows/reinforced_thindow.dmi'
reinf = TRUE
heat_resistance = 1600
armor_type = /datum/armor/window_reinforced
@@ -560,13 +860,25 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/unanchored/spawner, 0)
if(RWINDOW_BARS_CUT)
. += span_notice("The main pane can be easily moved out of the way to reveal some bolts holding the frame in.")
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/spawner, 0)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/reinforced/spawner)
/obj/structure/window/reinforced/unanchored
anchored = FALSE
state = WINDOW_OUT_OF_FRAME
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/unanchored/spawner, 0)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/reinforced/unanchored/spawner)
+
+/obj/structure/window/reinforced/half
+ can_atmos_pass = ATMOS_PASS_YES
+ icon = 'icons/obj/structures/smooth/windows/reinforced_half_thindow.dmi'
+
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/reinforced/half)
+
+/obj/structure/window/reinforced/half/unanchored
+ anchored = FALSE
+ state = WINDOW_OUT_OF_FRAME
+
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/reinforced/half/unanchored)
// You can't rust glass! So only reinforced glass can be impacted.
/obj/structure/window/reinforced/rust_heretic_act()
@@ -579,7 +891,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/unanchored/spawner,
/obj/structure/window/plasma
name = "plasma window"
desc = "A window made out of a plasma-silicate alloy. It looks insanely tough to break and burn through."
- icon_state = "plasmawindow"
+ icon = 'icons/obj/structures/smooth/windows/plasma_thindow.dmi'
reinf = FALSE
heat_resistance = 25000
armor_type = /datum/armor/window_plasma
@@ -600,7 +912,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/unanchored/spawner,
. = ..()
RemoveElement(/datum/element/atmos_sensitive)
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/plasma/spawner, 0)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/plasma/spawner)
/obj/structure/window/plasma/unanchored
anchored = FALSE
@@ -608,7 +920,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/plasma/spawner, 0)
/obj/structure/window/reinforced/plasma
name = "reinforced plasma window"
desc = "A window made out of a plasma-silicate alloy and a rod matrix. It looks hopelessly tough to break and is most likely nigh fireproof."
- icon_state = "plasmarwindow"
+ icon = 'icons/obj/structures/smooth/windows/plasma_reinforced_thindow.dmi'
reinf = TRUE
heat_resistance = 50000
armor_type = /datum/armor/reinforced_plasma
@@ -629,7 +941,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/plasma/spawner, 0)
/obj/structure/window/reinforced/plasma/block_superconductivity()
return TRUE
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/plasma/spawner, 0)
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/reinforced/plasma/spawner)
/obj/structure/window/reinforced/plasma/unanchored
anchored = FALSE
@@ -637,319 +949,17 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/plasma/spawner, 0)
/obj/structure/window/reinforced/tinted
name = "tinted window"
- icon_state = "twindow"
-
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/tinted/spawner, 0)
-
-/obj/structure/window/reinforced/tinted/frosted
- name = "frosted window"
- icon_state = "fwindow"
-
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/reinforced/tinted/frosted/spawner, 0)
-
-/* Full Tile Windows (more atom_integrity) */
-
-/obj/structure/window/fulltile
- name = "full tile window"
- desc = "A full tile window."
- icon = 'icons/obj/smooth_structures/window.dmi'
- icon_state = "window-0"
- base_icon_state = "window"
- max_integrity = 100
- fulltile = TRUE
- flags_1 = PREVENT_CLICK_UNDER_1
- obj_flags = CAN_BE_HIT
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE
- canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
- glass_amount = 2
-
-/obj/structure/window/fulltile/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
- if(the_rcd.mode == RCD_DECONSTRUCT)
- return list("delay" = 2.5 SECONDS, "cost" = 10)
- return FALSE
+ icon = 'icons/obj/structures/smooth/windows/tinted_thindow.dmi'
-/obj/structure/window/fulltile/unanchored
- anchored = FALSE
-
-/obj/structure/window/plasma/fulltile
- icon = 'icons/obj/smooth_structures/plasma_window.dmi'
- icon_state = "plasma_window-0"
- base_icon_state = "plasma_window"
- max_integrity = 400
- fulltile = TRUE
- flags_1 = PREVENT_CLICK_UNDER_1
- obj_flags = CAN_BE_HIT
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE
- canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
- glass_amount = 2
-
-/obj/structure/window/plasma/fulltile/unanchored
- anchored = FALSE
-
-/obj/structure/window/reinforced/plasma/fulltile
- icon = 'icons/obj/smooth_structures/rplasma_window.dmi'
- icon_state = "rplasma_window-0"
- base_icon_state = "rplasma_window"
- state = RWINDOW_SECURE
- max_integrity = 1000
- fulltile = TRUE
- flags_1 = PREVENT_CLICK_UNDER_1
- obj_flags = CAN_BE_HIT
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE
- canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
- glass_amount = 2
-
-/obj/structure/window/reinforced/plasma/fulltile/unanchored
- anchored = FALSE
- state = WINDOW_OUT_OF_FRAME
-
-/obj/structure/window/reinforced/fulltile
- name = "full tile reinforced window"
- desc = "A full tile window that is reinforced with metal rods."
- icon = 'icons/obj/smooth_structures/reinforced_window.dmi'
- icon_state = "reinforced_window-0"
- base_icon_state = "reinforced_window"
- max_integrity = 150
- fulltile = TRUE
- flags_1 = PREVENT_CLICK_UNDER_1
- obj_flags = CAN_BE_HIT
- state = RWINDOW_SECURE
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE
- canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
- glass_amount = 2
-
-/obj/structure/window/reinforced/fulltile/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
- if(the_rcd.mode == RCD_DECONSTRUCT)
- return list("mode" = RCD_DECONSTRUCT, "delay" = 4 SECONDS, "cost" = 20)
- return FALSE
-
-/obj/structure/window/reinforced/fulltile/unanchored
- anchored = FALSE
- state = WINDOW_OUT_OF_FRAME
-
-/obj/structure/window/reinforced/tinted/fulltile
- icon = 'icons/obj/smooth_structures/tinted_window.dmi'
- icon_state = "tinted_window-0"
- base_icon_state = "tinted_window"
- fulltile = TRUE
- flags_1 = PREVENT_CLICK_UNDER_1
- obj_flags = CAN_BE_HIT
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE
- canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
- glass_amount = 2
- // Not on the parent because directional opacity does NOT WORK
- opacity = TRUE
-
-/obj/structure/window/reinforced/fulltile/ice
- icon = 'icons/obj/smooth_structures/rice_window.dmi'
- icon_state = "rice_window-0"
- base_icon_state = "rice_window"
- max_integrity = 150
- glass_amount = 2
-
-//there is a sub shuttle window in survival_pod.dm for mining pods
-/obj/structure/window/reinforced/shuttle//this is called reinforced because it is reinforced w/titanium
- name = "shuttle window"
- desc = "A reinforced, air-locked pod window."
- icon = 'icons/obj/smooth_structures/shuttle_window.dmi'
- icon_state = "shuttle_window-0"
- base_icon_state = "shuttle_window"
- max_integrity = 150
- wtype = "shuttle"
- reinf = TRUE
- fulltile = TRUE
- flags_1 = PREVENT_CLICK_UNDER_1
- obj_flags = CAN_BE_HIT
- reinf = TRUE
- heat_resistance = 1600
- armor_type = /datum/armor/reinforced_shuttle
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE
- canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE
- explosion_block = 3
- glass_type = /obj/item/stack/sheet/titaniumglass
- glass_amount = 2
- receive_ricochet_chance_mod = 1.2
- rad_insulation = RAD_MEDIUM_INSULATION
- glass_material_datum = /datum/material/alloy/titaniumglass
-
-/datum/armor/reinforced_shuttle
- melee = 90
- bomb = 50
- fire = 80
- acid = 100
-
-/obj/structure/window/reinforced/shuttle/narsie_act()
- add_atom_colour("#3C3434", FIXED_COLOUR_PRIORITY)
-
-/obj/structure/window/reinforced/shuttle/tinted
- opacity = TRUE
-
-/obj/structure/window/reinforced/shuttle/unanchored
- anchored = FALSE
- state = WINDOW_OUT_OF_FRAME
-
-/obj/structure/window/reinforced/shuttle/indestructible
- name = "hardened shuttle window"
- flags_1 = PREVENT_CLICK_UNDER_1
- resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
-
-/obj/structure/window/reinforced/shuttle/indestructible/welder_act(mob/living/user, obj/item/tool)
- return NONE
-
-/obj/structure/window/reinforced/shuttle/indestructible/screwdriver_act(mob/living/user, obj/item/tool)
- return NONE
-
-/obj/structure/window/reinforced/shuttle/indestructible/wrench_act(mob/living/user, obj/item/tool)
- return NONE
-
-/obj/structure/window/reinforced/shuttle/indestructible/crowbar_act(mob/living/user, obj/item/tool)
- return NONE
-
-/obj/structure/window/reinforced/plasma/plastitanium
- name = "plastitanium window"
- desc = "A durable looking window made of an alloy of of plasma and titanium."
- icon = 'icons/obj/smooth_structures/plastitanium_window.dmi'
- icon_state = "plastitanium_window-0"
- base_icon_state = "plastitanium_window"
- max_integrity = 1200
- wtype = "shuttle"
- fulltile = TRUE
- flags_1 = PREVENT_CLICK_UNDER_1
- obj_flags = CAN_BE_HIT
- heat_resistance = 1600
- armor_type = /datum/armor/plasma_plastitanium
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM
- canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM
- explosion_block = 3
- damage_deflection = 21 //The same as reinforced plasma windows.3
- glass_type = /obj/item/stack/sheet/plastitaniumglass
- glass_amount = 2
- rad_insulation = RAD_EXTREME_INSULATION
- glass_material_datum = /datum/material/alloy/plastitaniumglass
-
-/datum/armor/plasma_plastitanium
- melee = 95
- bomb = 50
- fire = 80
- acid = 100
-
-/obj/structure/window/reinforced/plasma/plastitanium/unanchored
- anchored = FALSE
- state = WINDOW_OUT_OF_FRAME
-
-/obj/structure/window/paperframe
- name = "paper frame"
- desc = "A fragile separator made of thin wood and paper."
- icon = 'icons/obj/smooth_structures/paperframes.dmi'
- icon_state = "paperframes-0"
- base_icon_state = "paperframes"
- opacity = TRUE
- max_integrity = 15
- fulltile = TRUE
- flags_1 = PREVENT_CLICK_UNDER_1
- obj_flags = CAN_BE_HIT
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_PAPERFRAME
- canSmoothWith = SMOOTH_GROUP_PAPERFRAME
- glass_amount = 2
- glass_type = /obj/item/stack/sheet/paperframes
- heat_resistance = 233
- decon_speed = 10
- can_atmos_pass = ATMOS_PASS_YES
- resistance_flags = FLAMMABLE
- armor_type = /datum/armor/none
- knock_sound = SFX_PAGE_TURN
- bash_sound = 'sound/weapons/slashmiss.ogg'
- break_sound = 'sound/items/poster_ripped.ogg'
- hit_sound = 'sound/weapons/slashmiss.ogg'
- var/static/mutable_appearance/torn = mutable_appearance('icons/obj/smooth_structures/structure_variations.dmi',icon_state = "paper-torn", layer = ABOVE_OBJ_LAYER - 0.1)
- var/static/mutable_appearance/paper = mutable_appearance('icons/obj/smooth_structures/structure_variations.dmi',icon_state = "paper-whole", layer = ABOVE_OBJ_LAYER - 0.1)
-
-/obj/structure/window/paperframe/Initialize(mapload)
+/obj/structure/window/reinforced/tinted/Initialize(mapload, direct)
. = ..()
- update_appearance()
-
-/obj/structure/window/paperframe/examine(mob/user)
- . = ..()
- if(atom_integrity < max_integrity)
- . += span_info("It looks a bit damaged, you may be able to fix it with some paper.")
-
-/obj/structure/window/paperframe/spawn_debris(location)
- . = list(new /obj/item/stack/sheet/mineral/wood(location))
- for (var/i in 1 to rand(1,4))
- . += new /obj/item/paper/natural(location)
-
-/obj/structure/window/paperframe/attack_hand(mob/living/user, list/modifiers)
- . = ..()
- if(.)
- return
- if(user.combat_mode)
- take_damage(4, BRUTE, MELEE, 0)
- if(!QDELETED(src))
- update_appearance()
-
-/obj/structure/window/paperframe/update_appearance(updates)
- . = ..()
- set_opacity(atom_integrity >= max_integrity)
-
-/obj/structure/window/paperframe/update_icon(updates=ALL)
- . = ..()
- if((updates & UPDATE_SMOOTHING) && (smoothing_flags & USES_SMOOTHING))
- QUEUE_SMOOTH(src)
-
-/obj/structure/window/paperframe/update_overlays()
- . = ..()
- . += (atom_integrity < max_integrity) ? torn : paper
-
-/obj/structure/window/paperframe/attackby(obj/item/W, mob/living/user)
- if(W.get_temperature())
- fire_act(W.get_temperature())
- return
- if(user.combat_mode)
- return ..()
- if(istype(W, /obj/item/paper) && atom_integrity < max_integrity)
- user.visible_message(span_notice("[user] starts to patch the holes in \the [src]."))
- if(do_after(user, 2 SECONDS, target = src))
- atom_integrity = min(atom_integrity+4,max_integrity)
- qdel(W)
- user.visible_message(span_notice("[user] patches some of the holes in \the [src]."))
- if(atom_integrity == max_integrity)
- update_appearance()
- return
- ..()
- update_appearance()
-
-/obj/structure/window/bronze
- name = "brass window"
- desc = "A paper-thin pane of translucent yet reinforced brass. Nevermind, this is just weak bronze!"
- icon = 'icons/obj/smooth_structures/structure_variations.dmi'
- icon_state = "clockwork_window-single"
- glass_type = /obj/item/stack/sheet/bronze
-
-MAPPING_DIRECTIONAL_HELPERS(/obj/structure/window/bronze/spawner, 0)
+ if(fulltile)
+ set_opacity(TRUE)
-/obj/structure/window/bronze/unanchored
- anchored = FALSE
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/reinforced/tinted/spawner)
-/obj/structure/window/bronze/fulltile
- icon = 'icons/obj/smooth_structures/clockwork_window.dmi'
- icon_state = "clockwork_window-0"
- base_icon_state = "clockwork_window"
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE_BRONZE + SMOOTH_GROUP_WINDOW_FULLTILE
- canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_BRONZE
- fulltile = TRUE
- flags_1 = PREVENT_CLICK_UNDER_1
- obj_flags = CAN_BE_HIT
- max_integrity = 50
- glass_amount = 2
+/obj/structure/window/reinforced/tinted/frosted
+ name = "frosted window"
+ icon = 'icons/obj/structures/smooth/windows/frosted_thindow.dmi'
-/obj/structure/window/bronze/fulltile/unanchored
- anchored = FALSE
+MAPPING_DIRECTIONAL_HELPERS_EMPTY(/obj/structure/window/reinforced/tinted/frosted/spawner)
diff --git a/code/game/shuttle_engines.dm b/code/game/shuttle_engines.dm
index 8ddae94231d31..94cab53ea2fa5 100644
--- a/code/game/shuttle_engines.dm
+++ b/code/game/shuttle_engines.dm
@@ -7,9 +7,8 @@
/obj/machinery/power/shuttle_engine
name = "engine"
desc = "A bluespace engine used to make shuttles move."
- icon = 'icons/turf/shuttle.dmi'
+ icon = 'icons/turf/engines.dmi'
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
- smoothing_groups = SMOOTH_GROUP_SHUTTLE_PARTS
armor_type = /datum/armor/power_shuttle_engine
can_atmos_pass = ATMOS_PASS_DENSITY
max_integrity = 500
@@ -150,6 +149,7 @@
/obj/machinery/power/shuttle_engine/heater
name = "engine heater"
desc = "Directs energy into compressed particles in order to power engines."
+ icon = 'icons/turf/shuttle.dmi'
icon_state = "heater"
circuit = /obj/item/circuitboard/machine/engine/heater
engine_power = 0 // todo make these into 2x1 parts
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 7c9784bcc7f61..0181bc3d0419f 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -472,4 +472,27 @@
'sound/creatures/monkey/monkey_screech_6.ogg',
'sound/creatures/monkey/monkey_screech_7.ogg',
)
+ if(SFX_TOOL_SWITCH)
+ soundin = 'sound/items/handling/tool_switch.ogg'
+ if(SFX_KEYBOARD_CLICKS)
+ soundin = pick(
+ 'sound/machines/computer/keyboard_clicks_1.ogg',
+ 'sound/machines/computer/keyboard_clicks_2.ogg',
+ 'sound/machines/computer/keyboard_clicks_3.ogg',
+ 'sound/machines/computer/keyboard_clicks_4.ogg',
+ 'sound/machines/computer/keyboard_clicks_5.ogg',
+ 'sound/machines/computer/keyboard_clicks_6.ogg',
+ 'sound/machines/computer/keyboard_clicks_7.ogg',
+ )
+ if(SFX_STONE_DROP)
+ soundin = pick(
+ 'sound/items/stones/stone_drop1.ogg',
+ 'sound/items/stones/stone_drop2.ogg',
+ 'sound/items/stones/stone_drop3.ogg',
+ )
+ if(SFX_STONE_PICKUP)
+ soundin = pick(
+ 'sound/items/stones/stone_pick_up1.ogg',
+ 'sound/items/stones/stone_pick_up2.ogg',
+ )
return soundin
diff --git a/code/game/turfs/closed/_closed.dm b/code/game/turfs/closed/_closed.dm
index 8ccaabc46c0af..888b85d84692b 100644
--- a/code/game/turfs/closed/_closed.dm
+++ b/code/game/turfs/closed/_closed.dm
@@ -1,6 +1,6 @@
/turf/closed
+ plane = GAME_PLANE
layer = CLOSED_TURF_LAYER
- plane = WALL_PLANE
turf_flags = IS_SOLID
opacity = TRUE
density = TRUE
@@ -8,6 +8,20 @@
init_air = FALSE
rad_insulation = RAD_MEDIUM_INSULATION
pass_flags_self = PASSCLOSEDTURF
+ var/use_splitvis = TRUE
+
+/turf/closed/Initialize(mapload)
+ . = ..()
+ if(use_splitvis)
+ // Micro-op to avoid needing to hash a bunch of nulls
+ if(color)
+ AddElement(/datum/element/split_visibility, icon, color)
+ else
+ AddElement(/datum/element/split_visibility, icon)
+ else
+ // We draw a copy to the wall plane so we can use it to mask byond darkness, that's all
+ add_overlay(mutable_appearance('icons/turf/walls/wall_blackness.dmi', "wall_background", UNDER_WALL_LAYER, src, WALL_PLANE))
+
/turf/closed/AfterChange()
. = ..()
diff --git a/code/game/turfs/closed/indestructible.dm b/code/game/turfs/closed/indestructible.dm
index 4b5e51b8f3b72..78b402e25ac47 100644
--- a/code/game/turfs/closed/indestructible.dm
+++ b/code/game/turfs/closed/indestructible.dm
@@ -1,8 +1,12 @@
/turf/closed/indestructible
name = "wall"
desc = "Effectively impervious to conventional methods of destruction."
- icon = 'icons/turf/walls.dmi'
+ icon = 'icons/turf/walls/metal_wall.dmi'
+ icon_state = "0-2"
explosive_resistance = 50
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_WALLS
rust_resistance = RUST_RESISTANCE_ABSOLUTE
/turf/closed/indestructible/TerraformTurf(path, new_baseturf, flags, defer_change = FALSE, ignore_air = FALSE)
@@ -28,34 +32,25 @@
name = "strange shuttle wall"
icon = 'icons/turf/shuttleold.dmi'
icon_state = "block"
+ use_splitvis = FALSE
+ smoothing_flags = NONE
+ smoothing_groups = null
+ canSmoothWith = null
/turf/closed/indestructible/weeb
name = "paper wall"
desc = "Reinforced paper walling. Someone really doesn't want you to leave."
- icon = 'icons/obj/smooth_structures/paperframes.dmi'
- icon_state = "paperframes-0"
- base_icon_state = "paperframes"
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_PAPERFRAME
- canSmoothWith = SMOOTH_GROUP_PAPERFRAME
- var/static/mutable_appearance/indestructible_paper = mutable_appearance('icons/obj/smooth_structures/structure_variations.dmi',icon_state = "paper-whole", layer = CLOSED_TURF_LAYER - 0.1)
-
-/turf/closed/indestructible/weeb/Initialize(mapload)
- . = ..()
- update_appearance()
-
-/turf/closed/indestructible/weeb/update_overlays()
- . = ..()
- . += indestructible_paper
+ icon = 'icons/turf/walls/paperframe_wall.dmi'
+ smoothing_groups = SMOOTH_GROUP_PAPERFRAME + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_PAPERFRAME + SMOOTH_GROUP_CLOSED_TURFS
/turf/closed/indestructible/sandstone
name = "sandstone wall"
desc = "A wall with sandstone plating. Rough."
icon = 'icons/turf/walls/sandstone_wall.dmi'
- icon_state = "sandstone_wall-0"
- base_icon_state = "sandstone_wall"
baseturfs = /turf/closed/indestructible/sandstone
- smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS
/turf/closed/indestructible/oldshuttle/corner
icon_state = "corner"
@@ -63,10 +58,15 @@
/turf/closed/indestructible/splashscreen
name = "Space Station 13"
desc = null
+ baseturfs = /turf/cordon
icon = 'icons/blanks/blank_title.png'
icon_state = ""
pixel_x = -64
plane = SPLASHSCREEN_PLANE
+ use_splitvis = FALSE
+ smoothing_flags = NONE
+ smoothing_groups = null
+ canSmoothWith = null
bullet_bounce_sound = null
INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen)
@@ -99,74 +99,48 @@ INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen)
desc = pick(strings(SPLASH_FILE, "splashes"))
return ..()
-/turf/closed/indestructible/start_area
- name = null
- desc = null
- mouse_opacity = MOUSE_OPACITY_TRANSPARENT
-
/turf/closed/indestructible/reinforced
name = "reinforced wall"
desc = "A huge chunk of reinforced metal used to separate rooms. Effectively impervious to conventional methods of destruction."
icon = 'icons/turf/walls/reinforced_wall.dmi'
- icon_state = "reinforced_wall-0"
- base_icon_state = "reinforced_wall"
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
- canSmoothWith = SMOOTH_GROUP_WALLS
/turf/closed/indestructible/reinforced/titanium
name = "reinforced titanium imitation wall"
desc = "A huge chunk of reinforced metal used to separate rooms. Naturally, to cut down on costs, this is just a really good paint job to resemble titanium. Effectively impervious to conventional methods of destruction."
icon = 'icons/turf/walls/shuttle_wall.dmi'
- icon_state = "shuttle_wall-0"
base_icon_state = "shuttle_wall"
/turf/closed/indestructible/reinforced/titanium/nodiagonal
- icon_state = "shuttle_wall-15"
+ icon_state = "12-2"
smoothing_flags = SMOOTH_BITMASK
/turf/closed/indestructible/riveted
- icon = 'icons/turf/walls/riveted.dmi'
- icon_state = "riveted-0"
- base_icon_state = "riveted"
- smoothing_flags = SMOOTH_BITMASK
+ icon = 'icons/turf/walls/riveted_wall.dmi'
smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS
/turf/closed/indestructible/syndicate
icon = 'icons/turf/walls/plastitanium_wall.dmi'
- icon_state = "plastitanium_wall-0"
- base_icon_state = "plastitanium_wall"
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_SYNDICATE_WALLS
- canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_SYNDICATE_WALLS
+ smoothing_groups = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_SYNDICATE_WALLS
+ canSmoothWith = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_SYNDICATE_WALLS
/turf/closed/indestructible/riveted/uranium
icon = 'icons/turf/walls/uranium_wall.dmi'
- icon_state = "uranium_wall-0"
- base_icon_state = "uranium_wall"
- smoothing_flags = SMOOTH_BITMASK
/turf/closed/indestructible/riveted/plastinum
name = "plastinum wall"
desc = "A luxurious wall made out of a plasma-platinum alloy. Effectively impervious to conventional methods of destruction."
icon = 'icons/turf/walls/plastinum_wall.dmi'
- icon_state = "plastinum_wall-0"
- base_icon_state = "plastinum_wall"
smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
- smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_PLASTINUM_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_PLASTINUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_PLASTINUM_WALLS
+// Wallening todo: remove
/turf/closed/indestructible/riveted/plastinum/nodiagonal
- icon_state = "map-shuttle_nd"
- smoothing_flags = SMOOTH_BITMASK
/turf/closed/indestructible/wood
icon = 'icons/turf/walls/wood_wall.dmi'
- icon_state = "wood_wall-0"
- base_icon_state = "wood_wall"
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WOOD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_WOOD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_WOOD_WALLS
@@ -174,119 +148,157 @@ INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen)
name = "alien wall"
desc = "A wall with alien alloy plating."
icon = 'icons/turf/walls/abductor_wall.dmi'
- icon_state = "abductor_wall-0"
- base_icon_state = "abductor_wall"
smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
- smoothing_groups = SMOOTH_GROUP_ABDUCTOR_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_ABDUCTOR_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_ABDUCTOR_WALLS
+/turf/closed/indestructible/alien/nodiagonal
+ icon_state = "12-2"
+ smoothing_flags = SMOOTH_BITMASK
/turf/closed/indestructible/cult
name = "runed metal wall"
desc = "A cold metal wall engraved with indecipherable symbols. Studying them causes your head to pound. Effectively impervious to conventional methods of destruction."
icon = 'icons/turf/walls/cult_wall.dmi'
- icon_state = "cult_wall-0"
- base_icon_state = "cult_wall"
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_WALLS
/turf/closed/indestructible/abductor
- icon_state = "alien1"
+ name = "alien wall"
+ icon = 'icons/turf/walls/abductor_wall.dmi'
+ smoothing_groups = SMOOTH_GROUP_ABDUCTOR_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_ABDUCTOR_WALLS
/turf/closed/indestructible/opshuttle
- icon_state = "wall3"
-
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS
/turf/closed/indestructible/fakeglass
name = "window"
- icon = MAP_SWITCH('icons/obj/smooth_structures/reinforced_window.dmi', 'icons/obj/smooth_structures/structure_variations.dmi')
- icon_state = MAP_SWITCH("reinforced_window-0", "fake_window")
- base_icon_state = "reinforced_window"
+ icon = MAP_SWITCH('icons/obj/structures/smooth/windows/reinforced_window.dmi', 'icons/obj/structures/smooth/structure_variations.dmi')
+ icon_state = MAP_SWITCH("0-lower", "plastitanium_window-0")
+ layer = ABOVE_OBJ_LAYER
opacity = FALSE
- smoothing_flags = SMOOTH_BITMASK
+ use_splitvis = FALSE
smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE
canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE
/turf/closed/indestructible/fakeglass/Initialize(mapload)
. = ..()
- underlays += mutable_appearance('icons/obj/structures.dmi', "grille", layer - 0.01) //add a grille underlay
- underlays += mutable_appearance('icons/turf/floors.dmi', "plating", layer - 0.02) //add the plating underlay, below the grille
+ AddComponent(/datum/component/window_smoothing, /turf/closed/indestructible/fakeglass)
+ underlays += mutable_appearance('icons/turf/floors.dmi', "plating", LOW_FLOOR_LAYER, offset_spokesman = src, plane = FLOOR_PLANE)
+
+/turf/closed/indestructible/fakeglass/smooth_icon()
+ . = ..()
+ update_appearance(~UPDATE_SMOOTHING)
+
+/turf/closed/indestructible/fakeglass/update_overlays()
+ . = ..()
+ . += mutable_appearance('icons/obj/structures/smooth/window_grille_black.dmi', "window_grille_black-[smoothing_junction]", BELOW_OBJ_LAYER)
+ . += mutable_appearance('icons/obj/structures/smooth/window_grille.dmi', "window_grille-[smoothing_junction]", BELOW_OBJ_LAYER)
+ . += mutable_appearance('icons/obj/structures/smooth/window_frames/frame_faces/window_frame_normal.dmi', "window_frame_normal-[smoothing_junction]", BELOW_OBJ_LAYER, appearance_flags = KEEP_APART)
/turf/closed/indestructible/opsglass
name = "window"
- icon = 'icons/obj/smooth_structures/plastitanium_window.dmi'
- icon_state = "plastitanium_window-0"
- base_icon_state = "plastitanium_window"
+ icon = MAP_SWITCH('icons/obj/structures/smooth/windows/plastitanium_window.dmi', 'icons/obj/structures_spawners.dmi')
+ icon_state = MAP_SWITCH("0-lower", "plastitaniumwindow_spawner")
+ layer = ABOVE_OBJ_LAYER
opacity = FALSE
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM
+ use_splitvis = FALSE
+ smoothing_groups = SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM
canSmoothWith = SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM
/turf/closed/indestructible/opsglass/Initialize(mapload)
. = ..()
- icon_state = null
- underlays += mutable_appearance('icons/obj/structures.dmi', "grille", layer - 0.01)
- underlays += mutable_appearance('icons/turf/floors.dmi', "plating", layer - 0.02)
+ AddComponent(/datum/component/window_smoothing, /turf/closed/indestructible/opsglass)
+ underlays += mutable_appearance('icons/turf/floors.dmi', "plating", LOW_FLOOR_LAYER, offset_spokesman = src, plane = FLOOR_PLANE)
+
+/turf/closed/indestructible/opsglass/smooth_icon()
+ . = ..()
+ update_appearance(~UPDATE_SMOOTHING)
+
+/turf/closed/indestructible/opsglass/update_overlays()
+ . = ..()
+ . += mutable_appearance('icons/obj/structures/smooth/window_grille_black.dmi', "window_grille_black-[smoothing_junction]", BELOW_OBJ_LAYER)
+ . += mutable_appearance('icons/obj/structures/smooth/window_grille.dmi', "window_grille-[smoothing_junction]", BELOW_OBJ_LAYER)
+ . += mutable_appearance('icons/obj/structures/smooth/window_frames/frame_faces/window_frame_plastitanium.dmi', "window_frame_plastitanium-[smoothing_junction]", BELOW_OBJ_LAYER, appearance_flags = KEEP_APART)
/turf/closed/indestructible/fakedoor
name = "airlock"
- icon = 'icons/obj/doors/airlocks/centcom/centcom.dmi'
+ icon = 'icons/obj/doors/airlocks/tall/centcom.dmi'
icon_state = "fake_door"
+ use_splitvis = FALSE
+ smoothing_flags = NONE
+ canSmoothWith = null
+ smoothing_groups = null
+ /// What kind of turf should be visually represented under this door?
+ var/turf/floor_to_copy = /turf/open/floor/plating
+
+/turf/closed/indestructible/fakedoor/Initialize(mapload)
+ . = ..()
+ underlays += mutable_appearance(initial(floor_to_copy.icon), initial(floor_to_copy.icon_state), initial(floor_to_copy.layer), offset_spokesman = src, plane = FLOOR_PLANE)
/turf/closed/indestructible/fakedoor/maintenance
- icon = 'icons/obj/doors/airlocks/hatch/maintenance.dmi'
+ icon = 'icons/obj/doors/airlocks/tall/hatch/maintenance.dmi'
+ icon_state = "closed" // Should probably be given it's own fake_door state too
/turf/closed/indestructible/fakedoor/glass_airlock
- icon = 'icons/obj/doors/airlocks/external/external.dmi'
+ icon = 'icons/obj/doors/airlocks/tall/external/external.dmi'
+ icon_state = "closed" // Ditto
opacity = FALSE
+/// WALLENING TODO: Needs additional work; the base airlock it's meant to mimic is a GAGS-Generated sprite.
/turf/closed/indestructible/fakedoor/engineering
icon = 'icons/obj/doors/airlocks/station/engineering.dmi'
+ icon_state = "closed"
/turf/closed/indestructible/rock
name = "dense rock"
desc = "An extremely densely-packed rock, most mining tools or explosives would never get through this."
- icon = 'icons/turf/mining.dmi'
- icon_state = "rock"
+ icon = 'icons/turf/walls/rock_wall2.dmi'
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS
/turf/closed/indestructible/rock/snow
name = "mountainside"
desc = "An extremely densely-packed rock, sheeted over with centuries worth of ice and snow."
- icon = 'icons/turf/walls.dmi'
- icon_state = "snowrock"
+ icon = MAP_SWITCH('icons/turf/walls/mountain_wall.dmi', 'icons/turf/mining.dmi')
+#ifdef MAP_EDITOR
+ icon_state = "mountainrock"
+#endif
bullet_sizzle = TRUE
bullet_bounce_sound = null
/turf/closed/indestructible/rock/snow/ice
+ icon = 'icons/turf/walls/icerock_wall.dmi'
name = "iced rock"
+ icon_state = null
desc = "Extremely densely-packed sheets of ice and rock, forged over the years of the harsh cold."
- icon = 'icons/turf/walls.dmi'
- icon_state = "icerock"
-
-/turf/closed/indestructible/rock/snow/ice/ore
- icon = 'icons/turf/walls/icerock_wall.dmi'
- icon_state = "icerock_wall-0"
- base_icon_state = "icerock_wall"
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS
- pixel_x = -4
- pixel_y = -4
+// Is it ok that this looks the same as its parent? why does it exist?
+// Something is wrong here huh
+// rock/snow used to have a unique state, do we need to put it back?
+// also we need a sprite for /rock, I think? need to dig more
+// wallening todo, replace with /turf/closed/indestructible/rock/snow/ice, it is essentially redundant since we didn't make a unique state for that one state in mining.dmi
+/turf/closed/indestructible/rock/snow/ice/ore
/turf/closed/indestructible/paper
name = "thick paper wall"
desc = "A wall layered with impenetrable sheets of paper."
- icon = 'icons/turf/walls.dmi'
- icon_state = "paperwall"
+ icon = 'icons/turf/walls/paper_wall.dmi'
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS
/turf/closed/indestructible/necropolis
name = "necropolis wall"
desc = "A seemingly impenetrable wall."
- icon = 'icons/turf/walls.dmi'
- icon_state = "necro"
+ icon = 'icons/turf/walls/necro_wall.dmi'
explosive_resistance = 50
baseturfs = /turf/closed/indestructible/necropolis
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS
/turf/closed/indestructible/necropolis/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
underlay_appearance.icon = 'icons/turf/floors.dmi'
@@ -297,10 +309,7 @@ INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen)
name = "impervious iron wall"
desc = "A wall with tough iron plating."
icon = 'icons/turf/walls/iron_wall.dmi'
- icon_state = "iron_wall-0"
- base_icon_state = "iron_wall"
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_IRON_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_IRON_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_IRON_WALLS
opacity = FALSE
@@ -308,14 +317,12 @@ INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen)
name = "necropolis wall"
desc = "A thick, seemingly indestructible stone wall."
icon = 'icons/turf/walls/boss_wall.dmi'
- icon_state = "boss_wall-0"
- base_icon_state = "boss_wall"
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_BOSS_WALLS
- canSmoothWith = SMOOTH_GROUP_BOSS_WALLS
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_BOSS_WALLS
+ canSmoothWith = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_BOSS_WALLS
explosive_resistance = 50
baseturfs = /turf/closed/indestructible/riveted/boss
+/// To avoid confusion; this is used as a bombable tile. It's justified; I checked.
/turf/closed/indestructible/riveted/boss/wasteland
baseturfs = /turf/open/misc/asteroid/basalt/wasteland
@@ -327,31 +334,28 @@ INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen)
underlay_appearance.icon_state = "basalt"
return TRUE
+
/turf/closed/indestructible/riveted/hierophant
name = "wall"
desc = "A wall made out of a strange metal. The squares on it pulse in a predictable pattern."
icon = 'icons/turf/walls/hierophant_wall.dmi'
- icon_state = "wall"
- smoothing_flags = SMOOTH_CORNERS
- smoothing_groups = SMOOTH_GROUP_HIERO_WALL
+ smoothing_groups = SMOOTH_GROUP_HIERO_WALL + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_HIERO_WALL
/turf/closed/indestructible/resin
name = "resin wall"
- icon = 'icons/obj/smooth_structures/alien/resin_wall.dmi'
- icon_state = "resin_wall-0"
- base_icon_state = "resin_wall"
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_ALIEN_WALLS + SMOOTH_GROUP_ALIEN_RESIN
+ icon = 'icons/obj/structures/smooth/alien/resin_wall_1.dmi'
+ smoothing_groups = SMOOTH_GROUP_ALIEN_WALLS + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_ALIEN_WALLS
/turf/closed/indestructible/resin/membrane
name = "resin membrane"
- icon = 'icons/obj/smooth_structures/alien/resin_membrane.dmi'
+ icon = 'icons/obj/structures/smooth/alien/resin_membrane.dmi'
icon_state = "resin_membrane-0"
base_icon_state = "resin_membrane"
opacity = FALSE
- smoothing_groups = SMOOTH_GROUP_ALIEN_WALLS + SMOOTH_GROUP_ALIEN_RESIN
+ use_splitvis = FALSE
+ smoothing_groups = SMOOTH_GROUP_ALIEN_WALLS
canSmoothWith = SMOOTH_GROUP_ALIEN_WALLS
/turf/closed/indestructible/resin/membrane/Initialize(mapload)
@@ -360,9 +364,14 @@ INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen)
/turf/closed/indestructible/grille
name = "grille"
- icon = 'icons/obj/structures.dmi'
- icon_state = "grille"
+ desc = "A flimsy framework of iron rods."
+ icon = 'icons/obj/structures/smooth/grille.dmi'
+ icon_state = "grille-0"
base_icon_state = "grille"
+ use_splitvis = FALSE
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_GRILLE
+ canSmoothWith = SMOOTH_GROUP_GRILLE
/turf/closed/indestructible/grille/Initialize(mapload)
. = ..()
@@ -372,8 +381,5 @@ INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen)
name = "dense meat wall"
desc = "A huge chunk of dense, packed meat. Effectively impervious to conventional methods of destruction."
icon = 'icons/turf/walls/meat_wall.dmi'
- icon_state = "meat_wall-0"
- base_icon_state = "meat_wall"
- smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WALLS
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_WALLS
diff --git a/code/game/turfs/closed/minerals.dm b/code/game/turfs/closed/minerals.dm
index 3ec5ee06e581f..782b6101f6ca5 100644
--- a/code/game/turfs/closed/minerals.dm
+++ b/code/game/turfs/closed/minerals.dm
@@ -4,8 +4,10 @@
/turf/closed/mineral //wall piece
name = "rock"
- icon = MAP_SWITCH('icons/turf/smoothrocks.dmi', 'icons/turf/mining.dmi')
+ icon = MAP_SWITCH('icons/turf/walls/rock_wall.dmi', 'icons/turf/mining.dmi')
+#ifdef MAP_EDITOR
icon_state = "rock"
+#endif
smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_MINERAL_WALLS
canSmoothWith = SMOOTH_GROUP_MINERAL_WALLS
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
@@ -13,16 +15,7 @@
initial_gas_mix = AIRLESS_ATMOS
opacity = TRUE
density = TRUE
- // We're a BIG wall, larger then 32x32, so we need to be on the game plane
- // Otherwise we'll draw under shit in weird ways
- plane = GAME_PLANE
layer = EDGED_TURF_LAYER
- base_icon_state = "smoothrocks"
-
- // This is static
- // Done like this to avoid needing to make it dynamic and save cpu time
- // 4 to the left, 4 down
- transform = MAP_SWITCH(TRANSLATE_MATRIX(-4, -4), matrix())
temperature = TCMB
var/turf/open/floor/plating/turf_type = /turf/open/misc/asteroid/airless
@@ -43,18 +36,6 @@
/// How long it takes to mine this turf without tools, if it's weak.
var/hand_mine_speed = 15 SECONDS
-
-/turf/closed/mineral/Initialize(mapload)
- . = ..()
- // Mineral turfs are big, so they need to be on the game plane at a high layer
- // But they're also turfs, so we need to cut them out from the light mask plane
- // So we draw them as if they were on the game plane, and then overlay a copy onto
- // The wall plane (so emissives/light masks behave)
- // I am so sorry
- var/static/mutable_appearance/wall_overlay = mutable_appearance('icons/turf/mining.dmi', "rock", appearance_flags = RESET_TRANSFORM)
- wall_overlay.plane = MUTATE_PLANE(WALL_PLANE, src)
- overlays += wall_overlay
-
// Inlined version of the bump click element. way faster this way, the element's nice but it's too much overhead
/turf/closed/mineral/Bumped(atom/movable/bumped_atom)
. = ..()
@@ -276,7 +257,7 @@
/turf/closed/mineral/random
/// What are the base odds that this turf spawns a mineral in the wall on initialize?
var/mineralChance = 13
- /// Does this mineral determine it's random chance and mineral contents based on proximity to a vent? Overrides mineralChance and mineralAmt.
+ /// Does this mineral determine its random chance and mineral contents based on proximity to a vent? Overrides mineralChance and mineralAmt.
var/proximity_based = FALSE
/// Returns a list of the chances for minerals to spawn.
@@ -423,8 +404,9 @@
/turf/closed/mineral/random/snow
name = "snowy mountainside"
icon = MAP_SWITCH('icons/turf/walls/mountain_wall.dmi', 'icons/turf/mining.dmi')
+#ifdef MAP_EDITOR
icon_state = "mountainrock"
- base_icon_state = "mountain_wall"
+#endif
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS
defer_change = TRUE
@@ -438,9 +420,8 @@
. = ..()
if(mineralType)
icon = 'icons/turf/walls/icerock_wall.dmi'
- icon_state = "icerock_wall-0"
- base_icon_state = "icerock_wall"
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
+ QUEUE_SMOOTH(src)
/turf/closed/mineral/random/snow/mineral_chances()
return list(
@@ -519,8 +500,9 @@
/turf/closed/mineral/random/labormineral/ice
name = "snowy mountainside"
icon = MAP_SWITCH('icons/turf/walls/mountain_wall.dmi', 'icons/turf/mining.dmi')
+#ifdef MAP_EDITOR
icon_state = "mountainrock"
- base_icon_state = "mountain_wall"
+#endif
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS
defer_change = TRUE
@@ -539,9 +521,8 @@
. = ..()
if(mineralType)
icon = 'icons/turf/walls/icerock_wall.dmi'
- icon_state = "icerock_wall-0"
- base_icon_state = "icerock_wall"
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
+ QUEUE_SMOOTH(src)
/turf/closed/mineral/iron
mineralType = /obj/item/stack/ore/iron
@@ -554,9 +535,10 @@
defer_change = TRUE
/turf/closed/mineral/iron/ice
- icon_state = "icerock_iron"
icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi')
- base_icon_state = "icerock_wall"
+#ifdef MAP_EDITOR
+ icon_state = "icerock_iron"
+#endif
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
turf_type = /turf/open/misc/asteroid/snow/ice
baseturfs = /turf/open/misc/asteroid/snow/ice
@@ -584,9 +566,10 @@
defer_change = TRUE
/turf/closed/mineral/diamond/ice
- icon_state = "icerock_iron"
icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi')
- base_icon_state = "icerock_wall"
+#ifdef MAP_EDITOR
+ icon_state = "icerock_iron"
+#endif
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
turf_type = /turf/open/misc/asteroid/snow/ice
baseturfs = /turf/open/misc/asteroid/snow/ice
@@ -639,9 +622,10 @@
defer_change = TRUE
/turf/closed/mineral/plasma/ice
- icon_state = "icerock_plasma"
icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi')
- base_icon_state = "icerock_wall"
+#ifdef MAP_EDITOR
+ icon_state = "icerock_plasma"
+#endif
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
turf_type = /turf/open/misc/asteroid/snow/ice
baseturfs = /turf/open/misc/asteroid/snow/ice
@@ -682,10 +666,10 @@
/turf/closed/mineral/ash_rock //wall piece
name = "rock"
- icon = 'icons/turf/mining.dmi'
- icon = MAP_SWITCH('icons/turf/walls/rock_wall.dmi', 'icons/turf/mining.dmi')
+ icon = MAP_SWITCH('icons/turf/walls/rock_wall2.dmi', 'icons/turf/mining.dmi')
+#ifdef MAP_EDITOR
icon_state = "rock2"
- base_icon_state = "rock_wall"
+#endif
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS
baseturfs = /turf/open/misc/ashplanet/wateryrock
@@ -697,8 +681,9 @@
/turf/closed/mineral/snowmountain
name = "snowy mountainside"
icon = MAP_SWITCH('icons/turf/walls/mountain_wall.dmi', 'icons/turf/mining.dmi')
+#ifdef MAP_EDITOR
icon_state = "mountainrock"
- base_icon_state = "mountain_wall"
+#endif
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
canSmoothWith = SMOOTH_GROUP_CLOSED_TURFS
baseturfs = /turf/open/misc/asteroid/snow
@@ -720,8 +705,9 @@
/turf/closed/mineral/snowmountain/cavern
name = "ice cavern rock"
icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi')
+#ifdef MAP_EDITOR
icon_state = "icerock"
- base_icon_state = "icerock_wall"
+#endif
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
baseturfs = /turf/open/misc/asteroid/snow/ice
turf_type = /turf/open/misc/asteroid/snow/ice
@@ -741,14 +727,14 @@
/turf/closed/mineral/asteroid
name = "iron rock"
+ icon = MAP_SWITCH('icons/turf/walls/red_rock_wall.dmi', 'icons/turf/mining.dmi')
+#ifdef MAP_EDITOR
icon_state = "redrock"
- icon = MAP_SWITCH('icons/turf/walls/red_wall.dmi', 'icons/turf/mining.dmi')
- base_icon_state = "red_wall"
+#endif
/turf/closed/mineral/random/stationside/asteroid
name = "iron rock"
- icon = MAP_SWITCH('icons/turf/walls/red_wall.dmi', 'icons/turf/mining.dmi')
- base_icon_state = "red_wall"
+ icon = MAP_SWITCH('icons/turf/walls/red_rock_wall.dmi', 'icons/turf/mining.dmi')
/turf/closed/mineral/random/stationside/asteroid/porus
name = "porous iron rock"
@@ -764,7 +750,9 @@
/turf/closed/mineral/gibtonite
mineralAmt = 1
- MAP_SWITCH(, icon_state = "rock_Gibtonite_inactive")
+#ifdef MAP_EDITOR
+ icon_state = "rock_Gibtonite_inactive"
+#endif
scan_state = "rock_Gibtonite"
var/det_time = 8 //Countdown till explosion, but also rewards the player for how close you were to detonation when you defuse it
var/stage = GIBTONITE_UNSTRUCK //How far into the lifecycle of gibtonite we are
@@ -789,6 +777,8 @@
/turf/closed/mineral/gibtonite/proc/explosive_reaction(mob/user = null)
if(stage == GIBTONITE_UNSTRUCK)
+ // Wallening todo: the layer of this is also weird. change if you change mineral walls
+ // Oh and we need to figure out this sprite
activated_overlay = mutable_appearance('icons/turf/smoothrocks_overlays.dmi', "rock_Gibtonite_inactive", ON_EDGED_TURF_LAYER) //shows in gaps between pulses if there are any
add_overlay(activated_overlay)
name = "gibtonite deposit"
@@ -868,9 +858,10 @@
defer_change = TRUE
/turf/closed/mineral/gibtonite/ice
- MAP_SWITCH(, icon_state = "icerock_Gibtonite_inactive")
icon = MAP_SWITCH('icons/turf/walls/icerock_wall.dmi', 'icons/turf/mining.dmi')
- base_icon_state = "icerock_wall"
+#ifdef MAP_EDITOR
+ icon_state = "icerock_Gibtonite_inactive"
+#endif
smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
turf_type = /turf/open/misc/asteroid/snow/ice
baseturfs = /turf/open/misc/asteroid/snow/ice
@@ -889,9 +880,8 @@
baseturfs = /turf/open/misc/asteroid/basalt/lava_land_surface
initial_gas_mix = LAVALAND_DEFAULT_ATMOS
defer_change = 1
- icon = MAP_SWITCH('icons/turf/walls/rock_wall.dmi', 'icons/turf/mining.dmi')
- base_icon_state = "rock_wall"
- smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
+ icon = MAP_SWITCH('icons/turf/walls/rock_wall2.dmi', 'icons/turf/mining.dmi')
+ smoothing_flags = SMOOTH_BITMASK
/turf/closed/mineral/strong/attackby(obj/item/I, mob/user, params)
if(!ishuman(user))
diff --git a/code/game/turfs/closed/wall/material_walls.dm b/code/game/turfs/closed/wall/material_walls.dm
index 5f16a68584f3e..004fe241cf699 100644
--- a/code/game/turfs/closed/wall/material_walls.dm
+++ b/code/game/turfs/closed/wall/material_walls.dm
@@ -2,10 +2,8 @@
name = "wall"
desc = "A huge chunk of material used to separate rooms."
icon = 'icons/turf/walls/material_wall.dmi'
- icon_state = "material_wall-0"
- base_icon_state = "material_wall"
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_MATERIAL_WALLS
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_MATERIAL_WALLS
canSmoothWith = SMOOTH_GROUP_MATERIAL_WALLS
rcd_memory = null
material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
diff --git a/code/game/turfs/closed/wall/mineral_walls.dm b/code/game/turfs/closed/wall/mineral_walls.dm
index 347e9af8f0d24..b6760a116ccdf 100644
--- a/code/game/turfs/closed/wall/mineral_walls.dm
+++ b/code/game/turfs/closed/wall/mineral_walls.dm
@@ -1,9 +1,7 @@
/turf/closed/wall/mineral
name = "mineral wall"
desc = "This shouldn't exist"
- icon_state = ""
smoothing_flags = SMOOTH_BITMASK
- canSmoothWith = null
rcd_memory = null
material_flags = MATERIAL_EFFECTS
rust_resistance = RUST_RESISTANCE_BASIC
@@ -12,12 +10,10 @@
name = "gold wall"
desc = "A wall with gold plating. Swag!"
icon = 'icons/turf/walls/gold_wall.dmi'
- icon_state = "gold_wall-0"
- base_icon_state = "gold_wall"
sheet_type = /obj/item/stack/sheet/mineral/gold
hardness = 65 //gold is soft
explosive_resistance = 0 //gold is a soft metal you dingus.
- smoothing_groups = SMOOTH_GROUP_GOLD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_GOLD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_GOLD_WALLS
custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT*2)
rust_resistance = RUST_RESISTANCE_BASIC
@@ -26,12 +22,10 @@
name = "silver wall"
desc = "A wall with silver plating. Shiny!"
icon = 'icons/turf/walls/silver_wall.dmi'
- icon_state = "silver_wall-0"
- base_icon_state = "silver_wall"
sheet_type = /obj/item/stack/sheet/mineral/silver
hardness = 65 //silver is also soft according to moh's scale
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_SILVER_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_SILVER_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_SILVER_WALLS
custom_materials = list(/datum/material/silver = SHEET_MATERIAL_AMOUNT*2)
@@ -39,14 +33,12 @@
name = "diamond wall"
desc = "A wall with diamond plating. You monster."
icon = 'icons/turf/walls/diamond_wall.dmi'
- icon_state = "diamond_wall-0"
- base_icon_state = "diamond_wall"
sheet_type = /obj/item/stack/sheet/mineral/diamond
hardness = 5 //diamond is very hard
slicing_duration = 200 //diamond wall takes twice as much time to slice
explosive_resistance = 3
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_DIAMOND_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_DIAMOND_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_DIAMOND_WALLS
custom_materials = list(/datum/material/diamond = SHEET_MATERIAL_AMOUNT*2)
rust_resistance = RUST_RESISTANCE_REINFORCED
@@ -58,12 +50,10 @@
name = "bananium wall"
desc = "A wall with bananium plating. Honk!"
icon = 'icons/turf/walls/bananium_wall.dmi'
- icon_state = "bananium_wall-0"
- base_icon_state = "bananium_wall"
sheet_type = /obj/item/stack/sheet/mineral/bananium
hardness = 70 //it's banana
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_BANANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_BANANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_BANANIUM_WALLS
custom_materials = list(/datum/material/bananium = SHEET_MATERIAL_AMOUNT*2)
rust_resistance = RUST_RESISTANCE_BASIC
@@ -72,13 +62,11 @@
name = "sandstone wall"
desc = "A wall with sandstone plating. Rough."
icon = 'icons/turf/walls/sandstone_wall.dmi'
- icon_state = "sandstone_wall-0"
- base_icon_state = "sandstone_wall"
sheet_type = /obj/item/stack/sheet/mineral/sandstone
hardness = 50 //moh says this is apparently 6-7 on it's scale
explosive_resistance = 0
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_SANDSTONE_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_SANDSTONE_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_SANDSTONE_WALLS
custom_materials = list(/datum/material/sandstone = SHEET_MATERIAL_AMOUNT*2)
rust_resistance = RUST_RESISTANCE_BASIC
@@ -88,12 +76,10 @@
name = "uranium wall"
desc = "A wall with uranium plating. This is probably a bad idea."
icon = 'icons/turf/walls/uranium_wall.dmi'
- icon_state = "uranium_wall-0"
- base_icon_state = "uranium_wall"
sheet_type = /obj/item/stack/sheet/mineral/uranium
hardness = 40 //uranium is a 6 on moh's scale
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_URANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_URANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_URANIUM_WALLS
custom_materials = list(/datum/material/uranium = SHEET_MATERIAL_AMOUNT*2)
rust_resistance = RUST_RESISTANCE_REINFORCED
@@ -145,13 +131,11 @@
name = "plasma wall"
desc = "A wall with plasma plating. This is definitely a bad idea."
icon = 'icons/turf/walls/plasma_wall.dmi'
- icon_state = "plasma_wall-0"
- base_icon_state = "plasma_wall"
sheet_type = /obj/item/stack/sheet/mineral/plasma
hardness = 70 // I'll tentatively compare it to Bismuth
thermal_conductivity = 0.04
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_PLASMA_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_PLASMA_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_PLASMA_WALLS
custom_materials = list(/datum/material/plasma = SHEET_MATERIAL_AMOUNT*2)
rust_resistance = RUST_RESISTANCE_BASIC
@@ -160,14 +144,12 @@
name = "wooden wall"
desc = "A wall with wooden plating. Stiff."
icon = 'icons/turf/walls/wood_wall.dmi'
- icon_state = "wood_wall-0"
- base_icon_state = "wood_wall"
sheet_type = /obj/item/stack/sheet/mineral/wood
hardness = 80
turf_flags = IS_SOLID
explosive_resistance = 0
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WOOD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_WOOD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_WOOD_WALLS
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT*2)
rust_resistance = RUST_RESISTANCE_BASIC
@@ -194,10 +176,8 @@
name = "bamboo wall"
desc = "A wall with a bamboo finish."
icon = 'icons/turf/walls/bamboo_wall.dmi'
- icon_state = "bamboo_wall-0"
- base_icon_state = "bamboo_wall"
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_BAMBOO_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_BAMBOO_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_BAMBOO_WALLS
sheet_type = /obj/item/stack/sheet/mineral/bamboo
hardness = 80 //it's not a mineral...
@@ -207,13 +187,11 @@
name = "rough iron wall"
desc = "A wall with rough iron plating."
icon = 'icons/turf/walls/iron_wall.dmi'
- icon_state = "iron_wall-0"
- base_icon_state = "iron_wall"
sheet_type = /obj/item/stack/rods
hardness = 60
sheet_amount = 5
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_IRON_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_IRON_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_IRON_WALLS
custom_materials = list(/datum/material/iron =SHEET_MATERIAL_AMOUNT * 2.5)
rust_resistance = RUST_RESISTANCE_BASIC
@@ -222,14 +200,13 @@
name = "packed snow wall"
desc = "A wall made of densely packed snow blocks."
icon = 'icons/turf/walls/snow_wall.dmi'
- icon_state = "snow_wall-0"
- base_icon_state = "snow_wall"
smoothing_flags = SMOOTH_BITMASK
hardness = 80
explosive_resistance = 0
slicing_duration = 30
sheet_type = /obj/item/stack/sheet/mineral/snow
- canSmoothWith = null
+ smoothing_groups = SMOOTH_GROUP_SNOW_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_SNOW_WALLS
girder_type = null
bullet_sizzle = TRUE
bullet_bounce_sound = null
@@ -243,34 +220,54 @@
name = "alien wall"
desc = "A wall with alien alloy plating."
icon = 'icons/turf/walls/abductor_wall.dmi'
- icon_state = "abductor_wall-0"
- base_icon_state = "abductor_wall"
sheet_type = /obj/item/stack/sheet/mineral/abductor
hardness = 10
slicing_duration = 200 //alien wall takes twice as much time to slice
explosive_resistance = 3
smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
- smoothing_groups = SMOOTH_GROUP_ABDUCTOR_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_ABDUCTOR_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_ABDUCTOR_WALLS
custom_materials = list(/datum/material/alloy/alien = SHEET_MATERIAL_AMOUNT*2)
rust_resistance = RUST_RESISTANCE_ORGANIC
+/turf/closed/wall/mineral/meat
+ name = "meat wall"
+ desc = "A wall of somone's compacted meat."
+ icon = 'icons/turf/walls/meat_wall.dmi'
+ sheet_type = /obj/item/stack/sheet/meat
+ hardness = 50
+ explosive_resistance = 0
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_MEAT_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_MEAT_WALLS
+ custom_materials = list(/datum/material/meat = 4000)
+
+/turf/closed/wall/mineral/pizza
+ name = "pepperoni wallzza"
+ desc = "It's a delicious pepperoni wallzza!"
+ icon = 'icons/turf/walls/pizza_wall.dmi'
+ sheet_type = /obj/item/stack/sheet/pizza
+ hardness = 70 // idk what you were expecting
+ explosive_resistance = 0
+ smoothing_groups = SMOOTH_GROUP_PIZZA_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_PIZZA_WALLS
+ custom_materials = list(/datum/material/pizza = SHEET_MATERIAL_AMOUNT*2)
+ rust_resistance = RUST_RESISTANCE_ORGANIC
+
/////////////////////Titanium walls/////////////////////
/turf/closed/wall/mineral/titanium //has to use this path due to how building walls works
name = "wall"
desc = "A light-weight titanium wall used in shuttles."
icon = 'icons/turf/walls/shuttle_wall.dmi'
- icon_state = "shuttle_wall-0"
- base_icon_state = "shuttle_wall"
explosive_resistance = 3
flags_1 = CAN_BE_DIRTY_1
flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD
sheet_type = /obj/item/stack/sheet/mineral/titanium
hardness = 40 //6 on moh's scale
- smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
- smoothing_groups = SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
- canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_TITANIUM_WALLS
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_TALL_WALLS
+ canSmoothWith = SMOOTH_GROUP_TITANIUM_WALLS
custom_materials = list(/datum/material/titanium = SHEET_MATERIAL_AMOUNT*2)
rust_resistance = RUST_RESISTANCE_TITANIUM
@@ -281,13 +278,9 @@
return ..()
/turf/closed/wall/mineral/titanium/nodiagonal
- icon = 'icons/turf/walls/shuttle_wall.dmi'
- icon_state = "map-shuttle_nd"
- base_icon_state = "shuttle_wall"
smoothing_flags = SMOOTH_BITMASK
/turf/closed/wall/mineral/titanium/overspace
- icon_state = "map-overspace"
smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
fixed_underlay = list("space" = TRUE)
@@ -302,22 +295,18 @@
/turf/closed/wall/mineral/titanium/survival
name = "pod wall"
desc = "An easily-compressable wall used for temporary shelter."
- icon = 'icons/turf/walls/survival_pod_walls.dmi'
- icon_state = "survival_pod_walls-0"
- base_icon_state = "survival_pod_walls"
+ icon = 'icons/turf/walls/survival_pod_wall.dmi'
smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
- canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_WINDOW_FULLTILE + SMOOTH_GROUP_TITANIUM_WALLS
+ canSmoothWith = SMOOTH_GROUP_TITANIUM_WALLS
rust_resistance = RUST_RESISTANCE_TITANIUM
/turf/closed/wall/mineral/titanium/survival/nodiagonal
- icon = 'icons/turf/walls/survival_pod_walls.dmi'
- icon_state = "survival_pod_walls-0"
- base_icon_state = "survival_pod_walls"
+ icon = 'icons/turf/walls/survival_pod_wall.dmi'
smoothing_flags = SMOOTH_BITMASK
rust_resistance = RUST_RESISTANCE_TITANIUM
/turf/closed/wall/mineral/titanium/survival/pod
- smoothing_groups = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + SMOOTH_GROUP_TITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD + SMOOTH_GROUP_TALL_WALLS
canSmoothWith = SMOOTH_GROUP_SURVIVAL_TITANIUM_POD
rust_resistance = RUST_RESISTANCE_TITANIUM
@@ -333,14 +322,12 @@
name = "wall"
desc = "A durable wall made of an alloy of plasma and titanium."
icon = 'icons/turf/walls/plastitanium_wall.dmi'
- icon_state = "plastitanium_wall-0"
- base_icon_state = "plastitanium_wall"
explosive_resistance = 4
sheet_type = /obj/item/stack/sheet/mineral/plastitanium
hardness = 25 //upgrade on titanium
smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
- smoothing_groups = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
- canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_SYNDICATE_WALLS
+ smoothing_groups = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_SYNDICATE_WALLS
+ canSmoothWith = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_SYNDICATE_WALLS
custom_materials = list(/datum/material/alloy/plastitanium = SHEET_MATERIAL_AMOUNT*2)
rust_resistance = RUST_RESISTANCE_TITANIUM
@@ -352,14 +339,10 @@
/turf/closed/wall/mineral/plastitanium/nodiagonal
- icon = 'icons/turf/walls/plastitanium_wall.dmi'
- icon_state = "map-shuttle_nd"
- base_icon_state = "plastitanium_wall"
smoothing_flags = SMOOTH_BITMASK
rust_resistance = RUST_RESISTANCE_TITANIUM
/turf/closed/wall/mineral/plastitanium/overspace
- icon_state = "map-overspace"
smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
fixed_underlay = list("space" = TRUE)
rust_resistance = RUST_RESISTANCE_TITANIUM
diff --git a/code/game/turfs/closed/wall/misc_walls.dm b/code/game/turfs/closed/wall/misc_walls.dm
index be4b6ca7ad295..c7c0508695e59 100644
--- a/code/game/turfs/closed/wall/misc_walls.dm
+++ b/code/game/turfs/closed/wall/misc_walls.dm
@@ -2,11 +2,9 @@
name = "runed metal wall"
desc = "A cold metal wall engraved with indecipherable symbols. Studying them causes your head to pound."
icon = 'icons/turf/walls/cult_wall.dmi'
- icon_state = "cult_wall-0"
- base_icon_state = "cult_wall"
turf_flags = IS_SOLID
- smoothing_flags = SMOOTH_BITMASK
- canSmoothWith = null
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_BOSS_WALLS
+ canSmoothWith = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_BOSS_WALLS
sheet_type = /obj/item/stack/sheet/runed_metal
sheet_amount = 1
girder_type = /obj/structure/girder/cult
@@ -30,19 +28,20 @@
new /obj/effect/temp_visual/cult/turf(get_turf(src))
/turf/closed/wall/ice
- icon = 'icons/turf/walls/icedmetal_wall.dmi'
- icon_state = "icedmetal_wall-0"
- base_icon_state = "icedmetal_wall"
+ icon = 'icons/turf/walls/iced_metal_wall.dmi'
desc = "A wall covered in a thick sheet of ice."
turf_flags = IS_SOLID
- smoothing_flags = SMOOTH_BITMASK
- canSmoothWith = null
rcd_memory = null
hardness = 35
slicing_duration = 150 //welding through the ice+metal
bullet_sizzle = TRUE
/turf/closed/wall/rust
+ name = "rusted wall"
+ desc = "A rusted metal wall."
+ icon = 'icons/turf/walls/rusty_wall.dmi'
+ smoothing_flags = SMOOTH_BITMASK
+ hardness = 45
//SDMM supports colors, this is simply for easier mapping
//and should be removed on initialize
color = MAP_SWITCH(null, COLOR_ORANGE_BROWN)
@@ -51,34 +50,48 @@
. = ..()
AddElement(/datum/element/rust)
+/turf/closed/wall/heretic_rust
+ color = MAP_SWITCH(null, COLOR_GREEN_GRAY)
+
+/turf/closed/wall/heretic_rust/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/rust/heretic)
+
/turf/closed/wall/r_wall/rust
- //SDMM supports colors, this is simply for easier mapping
- //and should be removed on initialize
- color = MAP_SWITCH(null, COLOR_ORANGE_BROWN)
- base_decon_state = "rusty_r_wall"
+ name = "rusted reinforced wall"
+ desc = "A huge chunk of rusted reinforced metal."
+ icon = 'icons/turf/walls/rusty_reinforced_wall.dmi'
+ smoothing_flags = SMOOTH_BITMASK
+ hardness = 15
/turf/closed/wall/r_wall/rust/Initialize(mapload)
. = ..()
AddElement(/datum/element/rust)
+/turf/closed/wall/r_wall/heretic_rust
+ color = MAP_SWITCH(null, COLOR_GREEN_GRAY)
+ icon = 'icons/turf/walls/rusty_reinforced_wall.dmi'
+
+/turf/closed/wall/r_wall/heretic_rust/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/rust/heretic)
+
/turf/closed/wall/mineral/bronze
name = "clockwork wall"
desc = "A huge chunk of bronze, decorated like gears and cogs."
icon = 'icons/turf/walls/clockwork_wall.dmi'
- icon_state = "clockwork_wall-0"
- base_icon_state = "clockwork_wall"
turf_flags = IS_SOLID
smoothing_flags = SMOOTH_BITMASK
sheet_type = /obj/item/stack/sheet/bronze
sheet_amount = 2
girder_type = /obj/structure/girder/bronze
+ smoothing_groups = SMOOTH_GROUP_CLOCK_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ canSmoothWith = SMOOTH_GROUP_CLOCK_WALLS
/turf/closed/wall/rock
name = "reinforced rock"
desc = "It has metal struts that need to be welded away before it can be mined."
- icon = 'icons/turf/walls/reinforced_rock.dmi'
- icon_state = "porous_rock-0"
- base_icon_state = "porous_rock"
+ icon = 'icons/turf/walls/reinforced_red_rock_wall.dmi'
turf_flags = NO_RUST
sheet_amount = 1
hardness = 50
@@ -99,6 +112,14 @@
smoothing_flags = NONE
canSmoothWith = null
smoothing_groups = null
+ use_splitvis = FALSE
+
+/turf/closed/wall/fake_hierophant
+ name = "vibrant wall"
+ desc = "A wall made out of a strange metal. The squares on it pulse in a predictable pattern."
+ icon = 'icons/turf/walls/hierophant_wall.dmi'
+ smoothing_groups = SMOOTH_GROUP_HIERO_WALL + SMOOTH_GROUP_TALL_WALLS
+ canSmoothWith = SMOOTH_GROUP_HIERO_WALL
/turf/closed/wall/material/meat
name = "living wall"
diff --git a/code/game/turfs/closed/wall/reinf_walls.dm b/code/game/turfs/closed/wall/reinf_walls.dm
index e94a31eeafef4..aadff1b4c8682 100644
--- a/code/game/turfs/closed/wall/reinf_walls.dm
+++ b/code/game/turfs/closed/wall/reinf_walls.dm
@@ -2,8 +2,6 @@
name = "reinforced wall"
desc = "A huge chunk of reinforced metal used to separate rooms."
icon = 'icons/turf/walls/reinforced_wall.dmi'
- icon_state = "reinforced_wall-0"
- base_icon_state = "reinforced_wall"
opacity = TRUE
density = TRUE
turf_flags = IS_SOLID
@@ -18,8 +16,15 @@
heat_capacity = 312500 //a little over 5 cm thick , 312500 for 1 m by 2.5 m by 0.25 m plasteel wall. also indicates the temperature at wich the wall will melt (currently only able to melt with H/E pipes)
///Dismantled state, related to deconstruction.
var/d_state = INTACT
- ///Base icon state to use for deconstruction
- var/base_decon_state = "r_wall"
+ ///List of icons for deconstruction steps, indexed by d_state
+ var/static/list/decon_icons = list(
+ SUPPORT_LINES = 'icons/turf/walls/reinforced_wall_decon1.dmi',
+ COVER = 'icons/turf/walls/reinforced_wall_decon2.dmi',
+ CUT_COVER = 'icons/turf/walls/reinforced_wall_decon3.dmi',
+ ANCHOR_BOLTS = 'icons/turf/walls/reinforced_wall_decon4.dmi',
+ SUPPORT_RODS = 'icons/turf/walls/reinforced_wall_decon5.dmi',
+ SHEATH = 'icons/turf/walls/reinforced_wall_decon6.dmi',
+ )
/turf/closed/wall/r_wall/deconstruction_hints(mob/user)
switch(d_state)
@@ -45,14 +50,13 @@
/turf/closed/wall/r_wall/hulk_recoil(obj/item/bodypart/arm, mob/living/carbon/human/hulkman, damage = 41)
return ..()
-/turf/closed/wall/r_wall/try_decon(obj/item/W, mob/user, turf/T)
+/turf/closed/wall/r_wall/try_decon(obj/item/W, mob/user)
//DECONSTRUCTION
switch(d_state)
if(INTACT)
if(W.tool_behaviour == TOOL_WIRECUTTER)
W.play_tool_sound(src, 100)
- d_state = SUPPORT_LINES
- update_appearance()
+ decon_change(SUPPORT_LINES)
to_chat(user, span_notice("You cut the outer grille."))
return TRUE
@@ -62,15 +66,13 @@
if(W.use_tool(src, user, 40, volume=100))
if(!istype(src, /turf/closed/wall/r_wall) || d_state != SUPPORT_LINES)
return TRUE
- d_state = COVER
- update_appearance()
+ decon_change(COVER)
to_chat(user, span_notice("You unsecure the support lines."))
return TRUE
else if(W.tool_behaviour == TOOL_WIRECUTTER)
W.play_tool_sound(src, 100)
- d_state = INTACT
- update_appearance()
+ decon_change(INTACT)
to_chat(user, span_notice("You repair the outer grille."))
return TRUE
@@ -82,8 +84,7 @@
if(W.use_tool(src, user, 60, volume=100))
if(!istype(src, /turf/closed/wall/r_wall) || d_state != COVER)
return TRUE
- d_state = CUT_COVER
- update_appearance()
+ decon_change(CUT_COVER)
to_chat(user, span_notice("You press firmly on the cover, dislodging it."))
return TRUE
@@ -92,8 +93,7 @@
if(W.use_tool(src, user, 40, volume=100))
if(!istype(src, /turf/closed/wall/r_wall) || d_state != COVER)
return TRUE
- d_state = SUPPORT_LINES
- update_appearance()
+ decon_change(SUPPORT_LINES)
to_chat(user, span_notice("The support lines have been secured."))
return TRUE
@@ -103,8 +103,7 @@
if(W.use_tool(src, user, 100, volume=100))
if(!istype(src, /turf/closed/wall/r_wall) || d_state != CUT_COVER)
return TRUE
- d_state = ANCHOR_BOLTS
- update_appearance()
+ decon_change(ANCHOR_BOLTS)
to_chat(user, span_notice("You pry off the cover."))
return TRUE
@@ -115,8 +114,7 @@
if(W.use_tool(src, user, 60, volume=100))
if(!istype(src, /turf/closed/wall/r_wall) || d_state != CUT_COVER)
return TRUE
- d_state = COVER
- update_appearance()
+ decon_change(COVER)
to_chat(user, span_notice("The metal cover has been welded securely to the frame."))
return TRUE
@@ -126,8 +124,7 @@
if(W.use_tool(src, user, 40, volume=100))
if(!istype(src, /turf/closed/wall/r_wall) || d_state != ANCHOR_BOLTS)
return TRUE
- d_state = SUPPORT_RODS
- update_appearance()
+ decon_change(SUPPORT_RODS)
to_chat(user, span_notice("You remove the bolts anchoring the support rods."))
return TRUE
@@ -136,8 +133,7 @@
if(W.use_tool(src, user, 20, volume=100))
if(!istype(src, /turf/closed/wall/r_wall) || d_state != ANCHOR_BOLTS)
return TRUE
- d_state = CUT_COVER
- update_appearance()
+ decon_change(CUT_COVER)
to_chat(user, span_notice("The metal cover has been pried back into place."))
return TRUE
@@ -149,8 +145,7 @@
if(W.use_tool(src, user, 100, volume=100))
if(!istype(src, /turf/closed/wall/r_wall) || d_state != SUPPORT_RODS)
return TRUE
- d_state = SHEATH
- update_appearance()
+ decon_change(SHEATH)
to_chat(user, span_notice("You slice through the support rods."))
return TRUE
@@ -160,8 +155,7 @@
if(W.use_tool(src, user, 40))
if(!istype(src, /turf/closed/wall/r_wall) || d_state != SUPPORT_RODS)
return TRUE
- d_state = ANCHOR_BOLTS
- update_appearance()
+ decon_change(ANCHOR_BOLTS)
to_chat(user, span_notice("You tighten the bolts anchoring the support rods."))
return TRUE
@@ -182,32 +176,24 @@
if(W.use_tool(src, user, 100, volume=100))
if(!istype(src, /turf/closed/wall/r_wall) || d_state != SHEATH)
return TRUE
- d_state = SUPPORT_RODS
- update_appearance()
+ decon_change(SUPPORT_RODS)
to_chat(user, span_notice("You weld the support rods back together."))
return TRUE
return FALSE
-/turf/closed/wall/r_wall/update_icon(updates=ALL)
- . = ..()
- if(d_state != INTACT)
- smoothing_flags = NONE
+/turf/closed/wall/r_wall/proc/decon_change(new_state)
+ if (d_state == new_state)
return
- if (!(updates & UPDATE_SMOOTHING))
- return
- smoothing_flags = SMOOTH_BITMASK
- QUEUE_SMOOTH_NEIGHBORS(src)
- QUEUE_SMOOTH(src)
-
-// We don't react to smoothing changing here because this else exists only to "revert" intact changes
-/turf/closed/wall/r_wall/update_icon_state()
- if(d_state != INTACT)
- icon = 'icons/turf/walls/reinforced_states.dmi'
- icon_state = "[base_decon_state]-[d_state]"
+ if(color)
+ RemoveElement(/datum/element/split_visibility, icon, color)
else
- icon = 'icons/turf/walls/reinforced_wall.dmi'
- icon_state = "[base_icon_state]-[smoothing_junction]"
- return ..()
+ RemoveElement(/datum/element/split_visibility, icon)
+ d_state = new_state
+ icon = (d_state != INTACT ? decon_icons[d_state] : initial(icon))
+ if(color)
+ AddElement(/datum/element/split_visibility, icon, color)
+ else
+ AddElement(/datum/element/split_visibility, icon)
/turf/closed/wall/r_wall/wall_singularity_pull(current_size)
if(current_size >= STAGE_FIVE)
@@ -220,7 +206,7 @@
/turf/closed/wall/r_wall/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- if(the_rcd.canRturf || rcd_data["[RCD_DESIGN_MODE]"] == RCD_WALLFRAME)
+ if(the_rcd.canRturf || rcd_data[RCD_DESIGN_MODE] == RCD_WALLFRAME)
return ..()
/turf/closed/wall/r_wall/rust_turf()
@@ -229,31 +215,28 @@
return
return ..()
+
/turf/closed/wall/r_wall/syndicate
name = "hull"
desc = "The armored hull of an ominous looking ship."
icon = 'icons/turf/walls/plastitanium_wall.dmi'
- icon_state = "plastitanium_wall-0"
- base_icon_state = "plastitanium_wall"
explosive_resistance = 20
sheet_type = /obj/item/stack/sheet/mineral/plastitanium
hardness = 25 //plastitanium
turf_flags = IS_SOLID
smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
- smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_SYNDICATE_WALLS
- canSmoothWith = SMOOTH_GROUP_SHUTTLE_PARTS + SMOOTH_GROUP_AIRLOCK + SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_SYNDICATE_WALLS
+ smoothing_groups = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_SYNDICATE_WALLS
+ canSmoothWith = SMOOTH_GROUP_PLASTITANIUM_WALLS + SMOOTH_GROUP_SYNDICATE_WALLS
rust_resistance = RUST_RESISTANCE_TITANIUM
+
/turf/closed/wall/r_wall/syndicate/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
return FALSE
/turf/closed/wall/r_wall/syndicate/nodiagonal
icon = 'icons/turf/walls/plastitanium_wall.dmi'
- icon_state = "map-shuttle_nd"
- base_icon_state = "plastitanium_wall"
smoothing_flags = SMOOTH_BITMASK
/turf/closed/wall/r_wall/syndicate/overspace
- icon_state = "map-overspace"
smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
fixed_underlay = list("space" = TRUE)
diff --git a/code/game/turfs/closed/walls.dm b/code/game/turfs/closed/walls.dm
index ed31138bb19f8..27b320a1a0cf2 100644
--- a/code/game/turfs/closed/walls.dm
+++ b/code/game/turfs/closed/walls.dm
@@ -1,11 +1,8 @@
-#define LEANING_OFFSET 11
-
/turf/closed/wall
name = "wall"
desc = "A huge chunk of iron used to separate rooms."
- icon = 'icons/turf/walls/wall.dmi'
- icon_state = "wall-0"
- base_icon_state = "wall"
+ icon = 'icons/turf/walls/metal_wall.dmi'
+ icon_state = "0-2"
explosive_resistance = 1
rust_resistance = RUST_RESISTANCE_BASIC
@@ -17,7 +14,7 @@
flags_ricochet = RICOCHET_HARD
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_WALLS
rcd_memory = RCD_MEMORY_WALL
@@ -31,67 +28,11 @@
var/girder_type = /obj/structure/girder
/// A turf that will replace this turf when this turf is destroyed
var/decon_type
+ /// If we added a leaning component to ourselves
+ var/added_leaning = FALSE
var/list/dent_decals
-/turf/closed/wall/mouse_drop_receive(atom/dropping, mob/user, params)
- if(dropping != user)
- return
- if(!iscarbon(dropping) && !iscyborg(dropping))
- return
- var/mob/living/leaner = dropping
- if(leaner.incapacitated(IGNORE_RESTRAINTS) || leaner.stat != CONSCIOUS || HAS_TRAIT(leaner, TRAIT_NO_TRANSFORM))
- return
- if(!leaner.density || leaner.pulledby || leaner.buckled || !(leaner.mobility_flags & MOBILITY_STAND))
- return
- if(HAS_TRAIT_FROM(leaner, TRAIT_UNDENSE, LEANING_TRAIT))
- return
- var/turf/checked_turf = get_step(leaner, REVERSE_DIR(leaner.dir))
- if(checked_turf != src)
- return
- leaner.start_leaning(src)
-
-/mob/living/proc/start_leaning(turf/closed/wall/wall)
- var/new_y = base_pixel_y + pixel_y
- var/new_x = base_pixel_x + pixel_x
- switch(dir)
- if(SOUTH)
- new_y += LEANING_OFFSET
- if(NORTH)
- new_y -= LEANING_OFFSET
- if(WEST)
- new_x += LEANING_OFFSET
- if(EAST)
- new_x -= LEANING_OFFSET
-
- animate(src, 0.2 SECONDS, pixel_x = new_x, pixel_y = new_y)
- add_traits(list(TRAIT_UNDENSE, TRAIT_EXPANDED_FOV), LEANING_TRAIT)
- visible_message(
- span_notice("[src] leans against [wall]."),
- span_notice("You lean against [wall]."),
- )
- RegisterSignals(src, list(
- COMSIG_MOB_CLIENT_PRE_MOVE,
- COMSIG_LIVING_DISARM_HIT,
- COMSIG_LIVING_GET_PULLED,
- COMSIG_MOVABLE_TELEPORTING,
- COMSIG_ATOM_DIR_CHANGE,
- ), PROC_REF(stop_leaning))
- update_fov()
-
-/mob/living/proc/stop_leaning()
- SIGNAL_HANDLER
- UnregisterSignal(src, list(
- COMSIG_MOB_CLIENT_PRE_MOVE,
- COMSIG_LIVING_DISARM_HIT,
- COMSIG_LIVING_GET_PULLED,
- COMSIG_MOVABLE_TELEPORTING,
- COMSIG_ATOM_DIR_CHANGE,
- ))
- animate(src, 0.2 SECONDS, pixel_x = base_pixel_x, pixel_y = base_pixel_y)
- remove_traits(list(TRAIT_UNDENSE, TRAIT_EXPANDED_FOV), LEANING_TRAIT)
- update_fov()
-
/turf/closed/wall/Initialize(mapload)
. = ..()
if(!can_engrave)
@@ -108,6 +49,15 @@
fixed_underlay = string_assoc_list(fixed_underlay)
underlays += underlay_appearance
+/turf/closed/wall/mouse_drop_receive(atom/dropping, mob/user, params)
+ . = ..()
+ if (added_leaning)
+ return
+ /// For performance reasons and to cut down on init times we are "lazy-loading" the leaning component when someone drags their sprite onto us, and then calling dragging code again to trigger the component
+ AddComponent(/datum/component/leanable, 11)
+ added_leaning = TRUE
+ dropping.base_mouse_drop_handler(src, null, null, params)
+
/turf/closed/wall/atom_destruction(damage_flag)
. = ..()
dismantle_wall(TRUE, FALSE)
@@ -157,6 +107,9 @@
if(girder_type)
new /obj/item/stack/sheet/iron(src)
+/turf/attacked_by(obj/item/attacking_item, mob/living/user)
+ return
+
/turf/closed/wall/ex_act(severity, target)
if(target == src)
dismantle_wall(1,1)
@@ -252,7 +205,7 @@
//the istype cascade has been spread among various procs for easy overriding
if(try_clean(W, user) || try_wallmount(W, user) || try_decon(W, user))
- return
+ return TRUE
return ..()
@@ -302,6 +255,9 @@
return FALSE
+/turf/closed/wall/proc/try_damage(obj/item/attacking_item, mob/user, turf/user_turf)
+ return //by default walls dont work like this
+
/turf/closed/wall/singularity_pull(S, current_size)
..()
wall_singularity_pull(current_size)
@@ -335,9 +291,9 @@
return FALSE
/turf/closed/wall/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- switch(rcd_data["[RCD_DESIGN_MODE]"])
+ switch(rcd_data[RCD_DESIGN_MODE])
if(RCD_WALLFRAME)
- var/obj/item/wallframe/wallmount = rcd_data["[RCD_DESIGN_PATH]"]
+ var/obj/item/wallframe/wallmount = rcd_data[RCD_DESIGN_PATH]
var/obj/item/wallframe/new_wallmount = new wallmount(user.drop_location())
return try_wallmount(new_wallmount, user, src)
if(RCD_DECONSTRUCT)
@@ -384,5 +340,3 @@
/turf/closed/wall/Exited(atom/movable/gone, direction)
. = ..()
SEND_SIGNAL(gone, COMSIG_LIVING_WALL_EXITED, src)
-
-#undef LEANING_OFFSET
diff --git a/code/game/turfs/open/_open.dm b/code/game/turfs/open/_open.dm
index 147e123c8af28..6b92681825a1e 100644
--- a/code/game/turfs/open/_open.dm
+++ b/code/game/turfs/open/_open.dm
@@ -225,13 +225,20 @@
/turf/open/indestructible/hierophant
icon = 'icons/turf/floors/hierophant_floor.dmi'
+ icon_state = "hierophant_floor-255"
+ base_icon_state = "hierophant_floor"
planetary_atmos = TRUE
initial_gas_mix = LAVALAND_DEFAULT_ATMOS
baseturfs = /turf/open/indestructible/hierophant
- smoothing_flags = SMOOTH_CORNERS
+ smoothing_flags = SMOOTH_BITMASK
+ smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_HIEROPHANT
+ canSmoothWith = SMOOTH_GROUP_HIEROPHANT
tiled_dirt = FALSE
/turf/open/indestructible/hierophant/two
+ icon = 'icons/turf/floors/hierophant_floor_alt.dmi'
+ icon_state = "hierophant_floor_alt-255"
+ base_icon_state = "hierophant_floor_alt"
/turf/open/indestructible/hierophant/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
return FALSE
diff --git a/code/game/turfs/open/asteroid.dm b/code/game/turfs/open/asteroid.dm
index cf28cdc3a4631..d0234b826a219 100644
--- a/code/game/turfs/open/asteroid.dm
+++ b/code/game/turfs/open/asteroid.dm
@@ -151,7 +151,7 @@ GLOBAL_LIST_EMPTY(dug_up_basalt)
/turf/open/misc/asteroid/basalt/refill_dug()
. = ..()
GLOB.dug_up_basalt -= src
- set_basalt_light(src)
+ set_basalt_light()
/turf/open/misc/asteroid/basalt/lava //lava underneath
baseturfs = /turf/open/lava/smooth
@@ -162,14 +162,14 @@ GLOBAL_LIST_EMPTY(dug_up_basalt)
/turf/open/misc/asteroid/basalt/Initialize(mapload)
. = ..()
- set_basalt_light(src)
+ set_basalt_light()
-/proc/set_basalt_light(turf/open/floor/B)
- switch(B.icon_state)
+/turf/open/misc/asteroid/basalt/proc/set_basalt_light()
+ switch(icon_state)
if("basalt1", "basalt2", "basalt3")
- B.set_light(2, 0.6, LIGHT_COLOR_LAVA) //more light
+ set_light(BASALT_LIGHT_RANGE_BRIGHT, BASALT_LIGHT_POWER, LIGHT_COLOR_LAVA) //more light
if("basalt5", "basalt9")
- B.set_light(1.4, 0.6, LIGHT_COLOR_LAVA) //barely anything!
+ set_light(BASALT_LIGHT_RANGE_DIM, BASALT_LIGHT_POWER, LIGHT_COLOR_LAVA) //barely anything!
///////Surface. The surface is warm, but survivable without a suit. Internals are required. The floors break to chasms, which drop you into the underground.
diff --git a/code/game/turfs/open/basalt.dm b/code/game/turfs/open/basalt.dm
index e52a6053a6efe..e1a423d3c47f6 100644
--- a/code/game/turfs/open/basalt.dm
+++ b/code/game/turfs/open/basalt.dm
@@ -11,7 +11,11 @@
AddElement(/datum/element/diggable, /obj/item/stack/ore/glass/basalt, 2)
if(prob(15))
icon_state = "basalt[rand(0, 12)]"
- set_basalt_light(src)
+ switch(icon_state)
+ if("basalt1", "basalt2", "basalt3")
+ set_light(BASALT_LIGHT_RANGE_BRIGHT, BASALT_LIGHT_POWER, LIGHT_COLOR_LAVA)
+ if("basalt5", "basalt9")
+ set_light(BASALT_LIGHT_RANGE_DIM, BASALT_LIGHT_POWER, LIGHT_COLOR_LAVA)
/turf/open/misc/basalt/safe
planetary_atmos = FALSE
diff --git a/code/game/turfs/open/chasm.dm b/code/game/turfs/open/chasm.dm
index 504e876d536ce..1fad08dc4ece2 100644
--- a/code/game/turfs/open/chasm.dm
+++ b/code/game/turfs/open/chasm.dm
@@ -42,7 +42,7 @@
return FALSE
/turf/open/chasm/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- if(rcd_data["[RCD_DESIGN_MODE]"] == RCD_TURF && rcd_data["[RCD_DESIGN_PATH]"] == /turf/open/floor/plating/rcd)
+ if(rcd_data[RCD_DESIGN_MODE] == RCD_TURF && rcd_data[RCD_DESIGN_PATH] == /turf/open/floor/plating/rcd)
place_on_top(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
diff --git a/code/game/turfs/open/cliff.dm b/code/game/turfs/open/cliff.dm
index 1417e527e643d..1496cb16ec1d0 100644
--- a/code/game/turfs/open/cliff.dm
+++ b/code/game/turfs/open/cliff.dm
@@ -82,7 +82,7 @@
// We can walk infront of the bottom cliff turf, so check that here
if(!iscliffturf(get_step(src, fall_direction)) && !(get_dir(arrived, src) & fall_direction))
return FALSE
-
+
// gravity
// marked in UNLINT due to a spacemandmm bug: https://github.com/SpaceManiac/SpacemanDMM/issues/382 (REMOVE ONCE FIXED!)
if(UNLINT(!arrived.has_gravity(src)))
@@ -125,15 +125,7 @@
smoothing_groups = SMOOTH_GROUP_TURF_OPEN_CLIFF
canSmoothWith = SMOOTH_GROUP_TURF_OPEN_CLIFF
layer = EDGED_TURF_LAYER
- plane = WALL_PLANE
-
- // This is static
- // Done like this to avoid needing to make it dynamic and save cpu time
- // 4 to the left, 4 down
- transform = MAP_SWITCH(TRANSLATE_MATRIX(-4, -4), matrix())
-
- undertile_pixel_x = 4
- undertile_pixel_y = 4
+ plane = GAME_PLANE
initial_gas_mix = ICEMOON_DEFAULT_ATMOS
planetary_atmos = TRUE
diff --git a/code/game/turfs/open/floor.dm b/code/game/turfs/open/floor.dm
index 84b9ac26e9b8d..1ecf54f7963b4 100644
--- a/code/game/turfs/open/floor.dm
+++ b/code/game/turfs/open/floor.dm
@@ -264,9 +264,10 @@
/// if you are updating this make to to update /turf/open/misc/rcd_act() too
/turf/open/floor/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- switch(rcd_data["[RCD_DESIGN_MODE]"])
+ var/selected_direction = rcd_data[RCD_BUILD_DIRECTION] || user.dir
+ switch(rcd_data[RCD_DESIGN_MODE])
if(RCD_TURF)
- if(rcd_data["[RCD_DESIGN_PATH]"] != /turf/open/floor/plating/rcd)
+ if(rcd_data[RCD_DESIGN_PATH] != /turf/open/floor/plating/rcd)
return FALSE
var/obj/structure/girder/girder = locate() in src
@@ -277,30 +278,28 @@
return TRUE
if(RCD_WINDOWGRILLE)
//check if we are building a window
- var/obj/structure/window/window_path = rcd_data["[RCD_DESIGN_PATH]"]
+ var/obj/structure/window/window_path = rcd_data[RCD_DESIGN_PATH]
if(!ispath(window_path))
CRASH("Invalid window path type in RCD: [window_path]")
//allow directional windows to be built without grills
if(!initial(window_path.fulltile))
- if(!valid_build_direction(src, user.dir, is_fulltile = FALSE))
+ if(!valid_build_direction(src, selected_direction, is_fulltile = FALSE))
balloon_alert(user, "window already here!")
return FALSE
- var/obj/structure/window/WD = new window_path(src, user.dir)
+ var/obj/structure/window/WD = new window_path(src, selected_direction)
WD.set_anchored(TRUE)
return TRUE
- //build grills to deal with full tile windows
- if(locate(/obj/structure/grille) in src)
+ if(locate(/obj/structure/window_frame) in src)
return FALSE
- var/obj/structure/grille/new_grille = new(src)
- new_grille.set_anchored(TRUE)
+ new /obj/structure/window_frame(src)
return TRUE
if(RCD_AIRLOCK)
- var/obj/machinery/door/airlock_type = rcd_data["[RCD_DESIGN_PATH]"]
+ var/obj/machinery/door/airlock_type = rcd_data[RCD_DESIGN_PATH]
if(ispath(airlock_type, /obj/machinery/door/window))
- if(!valid_build_direction(src, user.dir, is_fulltile = FALSE))
+ if(!valid_build_direction(src, selected_direction, is_fulltile = FALSE))
balloon_alert(user, "there's already a windoor!")
return FALSE
for(var/obj/machinery/door/door in src)
@@ -309,7 +308,7 @@
balloon_alert(user, "there's already a door!")
return FALSE
//create the assembly and let it finish itself
- var/obj/structure/windoor_assembly/assembly = new (src, user.dir)
+ var/obj/structure/windoor_assembly/assembly = new (src, selected_direction)
assembly.secure = ispath(airlock_type, /obj/machinery/door/window/brigdoor)
assembly.electronics = the_rcd.airlock_electronics.create_copy(assembly)
assembly.finish_door()
@@ -328,10 +327,12 @@
else
assembly.airlock_type = airlock_type
assembly.electronics = the_rcd.airlock_electronics.create_copy(assembly)
- assembly.finish_door()
+ var/atom/new_door = assembly.finish_door()
+ new_door?.setDir(selected_direction)
return TRUE
+
if(RCD_STRUCTURE)
- var/atom/movable/design_type = rcd_data["[RCD_DESIGN_PATH]"]
+ var/atom/movable/design_type = rcd_data[RCD_DESIGN_PATH]
//map absolute types to basic subtypes
var/atom/movable/locate_type = design_type
@@ -352,7 +353,7 @@
/obj/structure/bed,
)
if(is_path_in_list(locate_type, dir_types))
- design.setDir(user.dir)
+ design.setDir(selected_direction)
return TRUE
if(RCD_DECONSTRUCT)
if(rcd_proof)
diff --git a/code/game/turfs/open/floor/fancy_floor.dm b/code/game/turfs/open/floor/fancy_floor.dm
index e672b7f7294fa..bc87c25201b54 100644
--- a/code/game/turfs/open/floor/fancy_floor.dm
+++ b/code/game/turfs/open/floor/fancy_floor.dm
@@ -242,7 +242,11 @@
AddElement(/datum/element/diggable, /obj/item/stack/ore/glass/basalt, 2, worm_chance = 0)
if(prob(15))
icon_state = "basalt[rand(0, 12)]"
- set_basalt_light(src)
+ switch(icon_state)
+ if("basalt1", "basalt2", "basalt3")
+ set_light(BASALT_LIGHT_RANGE_BRIGHT, BASALT_LIGHT_POWER, LIGHT_COLOR_LAVA)
+ if("basalt5", "basalt9")
+ set_light(BASALT_LIGHT_RANGE_DIM, BASALT_LIGHT_POWER, LIGHT_COLOR_LAVA)
/turf/open/floor/carpet
name = "carpet"
@@ -487,8 +491,8 @@
/turf/open/floor/carpet/neon/Initialize(mapload)
. = ..()
- AddElement(/datum/element/decal, neon_icon || icon, neon_icon_state || base_icon_state, dir, null, null, alpha, neon_color, smoothing_junction)
- AddElement(/datum/element/decal, neon_icon || icon, neon_icon_state || base_icon_state, dir, EMISSIVE_PLANE, null, emissive_alpha, GLOB.emissive_color, smoothing_junction)
+ AddElement(/datum/element/decal, neon_icon || icon, neon_icon_state || base_icon_state, dir, null, null, alpha, neon_color, null, null, smoothing_junction)
+ AddElement(/datum/element/decal, neon_icon || icon, neon_icon_state || base_icon_state, dir, EMISSIVE_PLANE, null, emissive_alpha, GLOB.emissive_color, null, null, smoothing_junction)
/turf/open/floor/carpet/neon/simple
name = "simple neon carpet"
diff --git a/code/game/turfs/open/floor/mineral_floor.dm b/code/game/turfs/open/floor/mineral_floor.dm
index e8be1331378ab..a8cf2a6f37d07 100644
--- a/code/game/turfs/open/floor/mineral_floor.dm
+++ b/code/game/turfs/open/floor/mineral_floor.dm
@@ -97,6 +97,10 @@
/turf/open/floor/mineral/titanium/blue/airless
initial_gas_mix = AIRLESS_ATMOS
+/turf/open/floor/mineral/titanium/blue/lavaland_atmos
+ initial_gas_mix = LAVALAND_DEFAULT_ATMOS
+ planetary_atmos = TRUE
+
/turf/open/floor/mineral/titanium/white
icon_state = "titanium_white"
floor_tile = /obj/item/stack/tile/mineral/titanium/white
diff --git a/code/game/turfs/open/floor/misc_floor.dm b/code/game/turfs/open/floor/misc_floor.dm
index 7c4428c4823ec..8f8d5fbf1b08e 100644
--- a/code/game/turfs/open/floor/misc_floor.dm
+++ b/code/game/turfs/open/floor/misc_floor.dm
@@ -17,12 +17,17 @@
/turf/open/floor/circuit/Initialize(mapload)
SSmapping.nuke_tiles += src
RegisterSignal(loc, COMSIG_AREA_POWER_CHANGE, PROC_REF(handle_powerchange))
- handle_powerchange(loc)
+ var/area/cur_area = get_area(src)
+ if (!isnull(cur_area))
+ handle_powerchange(cur_area, TRUE)
. = ..()
/turf/open/floor/circuit/Destroy()
SSmapping.nuke_tiles -= src
UnregisterSignal(loc, COMSIG_AREA_POWER_CHANGE)
+ var/area/cur_area = get_area(src)
+ if(on && !isnull(cur_area))
+ cur_area.removeStaticPower(CIRCUIT_FLOOR_POWERUSE, AREA_USAGE_STATIC_LIGHT)
return ..()
/turf/open/floor/circuit/update_appearance(updates)
@@ -47,7 +52,7 @@
handle_powerchange(new_area)
/// Enables/disables our lighting based off our source area
-/turf/open/floor/circuit/proc/handle_powerchange(area/source)
+/turf/open/floor/circuit/proc/handle_powerchange(area/source, mapload = FALSE)
SIGNAL_HANDLER
var/old_on = on
if(always_off)
@@ -59,7 +64,7 @@
if(on)
source.addStaticPower(CIRCUIT_FLOOR_POWERUSE, AREA_USAGE_STATIC_LIGHT)
- else
+ else if (!mapload)
source.removeStaticPower(CIRCUIT_FLOOR_POWERUSE, AREA_USAGE_STATIC_LIGHT)
update_appearance()
@@ -253,6 +258,14 @@
AddElement(/datum/element/rust)
color = null
+/turf/open/floor/plating/heretic_rust
+ color = COLOR_GREEN_GRAY
+
+/turf/open/floor/plating/heretic_rust/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/rust/heretic)
+ color = null
+
/turf/open/floor/plating/plasma
initial_gas_mix = ATMOS_TANK_PLASMA
diff --git a/code/game/turfs/open/floor/plating.dm b/code/game/turfs/open/floor/plating.dm
index 6e4834773c325..6139440db0c34 100644
--- a/code/game/turfs/open/floor/plating.dm
+++ b/code/game/turfs/open/floor/plating.dm
@@ -166,7 +166,7 @@
return list("delay" = 0, "cost" = 1)
/turf/open/floor/plating/foam/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- if(rcd_data["[RCD_DESIGN_MODE]"] == RCD_TURF && rcd_data["[RCD_DESIGN_PATH]"] == /turf/open/floor/plating/rcd)
+ if(rcd_data[RCD_DESIGN_MODE] == RCD_TURF && rcd_data[RCD_DESIGN_PATH] == /turf/open/floor/plating/rcd)
ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
diff --git a/code/game/turfs/open/ice.dm b/code/game/turfs/open/ice.dm
index 481dcb6b84732..dbfff2efc8f91 100644
--- a/code/game/turfs/open/ice.dm
+++ b/code/game/turfs/open/ice.dm
@@ -15,10 +15,21 @@
clawfootstep = FOOTSTEP_HARD_CLAW
heavyfootstep = FOOTSTEP_GENERIC_HEAVY
rust_resistance = RUST_RESISTANCE_ORGANIC
+ var/can_make_hole = TRUE
+ var/static/list/tool_screentips = list(
+ TOOL_SHOVEL = list(
+ SCREENTIP_CONTEXT_LMB = "Dig fishing hole",
+ ),
+ TOOL_MINING = list(
+ SCREENTIP_CONTEXT_LMB = "Dig fishing hole",
+ ),
+ )
/turf/open/misc/ice/Initialize(mapload)
. = ..()
MakeSlippery(TURF_WET_PERMAFROST, INFINITY, 0, INFINITY, TRUE, FALSE)
+ if(can_make_hole)
+ AddElement(/datum/element/contextual_screentip_tools, tool_screentips)
/turf/open/misc/ice/break_tile()
return
@@ -26,6 +37,28 @@
/turf/open/misc/ice/burn_tile()
return
+/turf/open/misc/ice/examine(mob/user)
+ . = ..()
+ if(can_make_hole)
+ . += span_info("You could use a [EXAMINE_HINT("shovel")] or a [EXAMINE_HINT("pick")] to dig a fishing hole here.")
+
+/turf/open/misc/ice/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!can_make_hole)
+ return NONE
+ if(tool.tool_behaviour != TOOL_SHOVEL && tool.tool_behaviour != TOOL_MINING)
+ return NONE
+ balloon_alert(user, "digging...")
+ playsound(src, 'sound/effects/shovel_dig.ogg', 50, TRUE)
+ if(!do_after(user, 5 SECONDS, src))
+ return NONE
+ balloon_alert(user, "dug hole")
+ AddComponent(/datum/component/fishing_spot, GLOB.preset_fish_sources[/datum/fish_source/ice_fishing])
+ add_overlay(mutable_appearance('icons/turf/overlays.dmi', "ice_hole"))
+ can_make_hole = FALSE
+ RemoveElement(/datum/element/contextual_screentip_tools, tool_screentips)
+ flags_1 &= ~HAS_CONTEXTUAL_SCREENTIPS_1
+ return ITEM_INTERACT_SUCCESS
+
/turf/open/misc/ice/smooth
icon_state = "ice_turf-255"
base_icon_state = "ice_turf"
@@ -40,11 +73,13 @@
/turf/open/misc/ice/icemoon/no_planet_atmos
planetary_atmos = FALSE
+ can_make_hole = FALSE
/turf/open/misc/ice/temperate
baseturfs = /turf/open/misc/ice/temperate
desc = "Somehow, it is not melting under these conditions. Must be some very thick ice. Just as slippery too."
initial_gas_mix = COLD_ATMOS //it works with /turf/open/misc/asteroid/snow/temperatre
+ can_make_hole = FALSE
//For when you want real, genuine ice in your kitchen's cold room.
/turf/open/misc/ice/coldroom
diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm
index 23e2b6b38db84..0cc6978cc5242 100644
--- a/code/game/turfs/open/lava.dm
+++ b/code/game/turfs/open/lava.dm
@@ -165,7 +165,7 @@
return FALSE
/turf/open/lava/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- if(rcd_data["[RCD_DESIGN_MODE]"] == RCD_TURF && rcd_data["[RCD_DESIGN_PATH]"] == /turf/open/floor/plating/rcd)
+ if(rcd_data[RCD_DESIGN_MODE] == RCD_TURF && rcd_data[RCD_DESIGN_PATH] == /turf/open/floor/plating/rcd)
place_on_top(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
diff --git a/code/game/turfs/open/misc.dm b/code/game/turfs/open/misc.dm
index 02de7489786f9..da045c1b6f2f7 100644
--- a/code/game/turfs/open/misc.dm
+++ b/code/game/turfs/open/misc.dm
@@ -84,8 +84,8 @@
return FALSE
/turf/open/misc/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- if(rcd_data["[RCD_DESIGN_MODE]"] == RCD_TURF)
- if(rcd_data["[RCD_DESIGN_PATH]"] != /turf/open/floor/plating/rcd)
+ if(rcd_data[RCD_DESIGN_MODE] == RCD_TURF)
+ if(rcd_data[RCD_DESIGN_PATH] != /turf/open/floor/plating/rcd)
return FALSE
place_on_top(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
diff --git a/code/game/turfs/open/openspace.dm b/code/game/turfs/open/openspace.dm
index 1af42be3071cd..70748fb9d8557 100644
--- a/code/game/turfs/open/openspace.dm
+++ b/code/game/turfs/open/openspace.dm
@@ -145,7 +145,7 @@
return FALSE
/turf/open/openspace/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- if(rcd_data["[RCD_DESIGN_MODE]"] == RCD_TURF && rcd_data["[RCD_DESIGN_PATH]"] == /turf/open/floor/plating/rcd)
+ if(rcd_data[RCD_DESIGN_MODE] == RCD_TURF && rcd_data[RCD_DESIGN_PATH] == /turf/open/floor/plating/rcd)
place_on_top(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
diff --git a/code/game/turfs/open/planet.dm b/code/game/turfs/open/planet.dm
index 893942fc49c52..e0fe7e842b941 100644
--- a/code/game/turfs/open/planet.dm
+++ b/code/game/turfs/open/planet.dm
@@ -78,6 +78,9 @@
/turf/open/misc/grass/jungle/lavaland
initial_gas_mix = LAVALAND_DEFAULT_ATMOS
+/turf/open/misc/grass/jungle/station
+ baseturfs = /turf/open/misc/dirt/station
+
/turf/closed/mineral/random/jungle
baseturfs = /turf/open/misc/dirt/dark/jungle
diff --git a/code/game/turfs/open/sand.dm b/code/game/turfs/open/sand.dm
index c863e28231d35..fb10d297e18ef 100644
--- a/code/game/turfs/open/sand.dm
+++ b/code/game/turfs/open/sand.dm
@@ -10,6 +10,10 @@
heavyfootstep = FOOTSTEP_GENERIC_HEAVY
rust_resistance = RUST_RESISTANCE_ORGANIC
+/turf/open/misc/beach/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/lazy_fishing_spot, /datum/fish_source/sand)
+
/turf/open/misc/beach/ex_act(severity, target)
return FALSE
diff --git a/code/game/turfs/open/space/space.dm b/code/game/turfs/open/space/space.dm
index 28f7cfde8aceb..9da22b6337b8a 100644
--- a/code/game/turfs/open/space/space.dm
+++ b/code/game/turfs/open/space/space.dm
@@ -229,10 +229,10 @@ GLOBAL_LIST_EMPTY(starlight)
/turf/open/space/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
if(the_rcd.mode == RCD_TURF)
- if(rcd_data["[RCD_DESIGN_PATH]"] == /turf/open/floor/plating/rcd)
+ if(rcd_data[RCD_DESIGN_PATH] == /turf/open/floor/plating/rcd)
place_on_top(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
- else if(rcd_data["[RCD_DESIGN_PATH]"] == /obj/structure/lattice/catwalk)
+ else if(rcd_data[RCD_DESIGN_PATH] == /obj/structure/lattice/catwalk)
var/obj/structure/lattice/lattice = locate(/obj/structure/lattice, src)
if(lattice)
qdel(lattice)
diff --git a/code/game/turfs/open/water.dm b/code/game/turfs/open/water.dm
index 5dcfa85961d20..20c6215209a13 100644
--- a/code/game/turfs/open/water.dm
+++ b/code/game/turfs/open/water.dm
@@ -21,7 +21,7 @@
var/immerse_overlay_color = "#5AAA88"
/// Fishing element for this specific water tile
- var/datum/fish_source/fishing_datum = /datum/fish_source/portal
+ var/datum/fish_source/fishing_datum = /datum/fish_source/river
/turf/open/water/Initialize(mapload)
. = ..()
@@ -42,3 +42,7 @@
baseturfs = /turf/open/water/beach
immerse_overlay_color = "#7799AA"
fishing_datum = /datum/fish_source/ocean/beach
+
+/turf/open/water/lavaland_atmos
+ initial_gas_mix = LAVALAND_DEFAULT_ATMOS
+ planetary_atmos = TRUE
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 10165f869174f..f95008d793c7e 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -3,6 +3,7 @@ GLOBAL_LIST_EMPTY(station_turfs)
/// Any floor or wall. What makes up the station and the rest of the map.
/turf
icon = 'icons/turf/floors.dmi'
+ datum_flags = DF_STATIC_OBJECT
vis_flags = VIS_INHERIT_ID // Important for interaction with and visualization of openspace.
luminosity = 1
light_height = LIGHTING_HEIGHT_FLOOR
@@ -52,6 +53,8 @@ GLOBAL_LIST_EMPTY(station_turfs)
var/tiled_dirt = FALSE // use smooth tiled dirt decal
+ /// The prefix to use (from lighting_object.dmi) as our lighting underlay
+ var/lighting_state = "lighting"
///Icon-smoothing variable to map a diagonal wall corner with a fixed underlay.
var/list/fixed_underlay = null
@@ -769,6 +772,12 @@ GLOBAL_LIST_EMPTY(station_turfs)
inherent_explosive_resistance = explosion_block
explosive_resistance += get_explosive_block()
+/turf/proc/set_lighting_state(new_state)
+ lighting_state = new_state
+ if (lighting_object && !lighting_object.needs_update)
+ lighting_object.needs_update = TRUE
+ SSlighting.objects_queue += lighting_object
+
/// Returns whether it is safe for an atom to move across this turf
/turf/proc/can_cross_safely(atom/movable/crossing)
return TRUE
diff --git a/code/game/world.dm b/code/game/world.dm
index 9e57dbba343c5..72082c735d71c 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -1,11 +1,13 @@
#define RESTART_COUNTER_PATH "data/round_counter.txt"
-
+/// Load byond-tracy. If USE_BYOND_TRACY is defined, then this is ignored and byond-tracy is always loaded.
+#define USE_TRACY_PARAMETER "tracy"
/// Force the log directory to be something specific in the data/logs folder
#define OVERRIDE_LOG_DIRECTORY_PARAMETER "log-directory"
/// Prevent the master controller from starting automatically
#define NO_INIT_PARAMETER "no-init"
GLOBAL_VAR(restart_counter)
+GLOBAL_VAR(tracy_log)
/**
* WORLD INITIALIZATION
@@ -67,10 +69,12 @@ GLOBAL_VAR(restart_counter)
#ifdef USE_BYOND_TRACY
#warn USE_BYOND_TRACY is enabled
if(!tracy_initialized)
- init_byond_tracy()
+#else
+ if(!tracy_initialized && (USE_TRACY_PARAMETER in params))
+#endif
+ GLOB.tracy_log = init_byond_tracy()
Genesis(tracy_initialized = TRUE)
return
-#endif
Profile(PROFILE_RESTART)
Profile(PROFILE_RESTART, type = "sendmaps")
@@ -217,6 +221,9 @@ GLOBAL_VAR(restart_counter)
logger.init_logging()
+ if(GLOB.tracy_log)
+ rustg_file_write("[GLOB.tracy_log]", "[GLOB.log_directory]/tracy.loc")
+
var/latest_changelog = file("[global.config.directory]/../html/changelogs/archive/" + time2text(world.timeofday, "YYYY-MM") + ".yml")
GLOB.changelog_hash = fexists(latest_changelog) ? md5(latest_changelog) : 0 //for telling if the changelog has changed recently
@@ -339,7 +346,6 @@ GLOBAL_VAR(restart_counter)
#endif
/world/proc/auxcleanup()
- AUXTOOLS_FULL_SHUTDOWN(AUXLUA)
var/debug_server = world.GetConfig("env", "AUXTOOLS_DEBUG_DLL")
if (debug_server)
call_ext(debug_server, "auxtools_shutdown")()
@@ -472,6 +478,7 @@ GLOBAL_VAR(restart_counter)
/world/proc/on_tickrate_change()
SStimer?.reset_buckets()
+ DREAMLUAU_SET_EXECUTION_LIMIT_MILLIS(tick_lag * 100)
/world/proc/init_byond_tracy()
var/library
@@ -485,7 +492,9 @@ GLOBAL_VAR(restart_counter)
CRASH("Unsupported platform: [system_type]")
var/init_result = call_ext(library, "init")("block")
- if (init_result != "0")
+ if(length(init_result) != 0 && init_result[1] == ".") // if first character is ., then it returned the output filename
+ return init_result
+ else if(init_result != "0")
CRASH("Error initializing byond-tracy: [init_result]")
/world/proc/init_debugger()
@@ -500,4 +509,5 @@ GLOBAL_VAR(restart_counter)
#undef NO_INIT_PARAMETER
#undef OVERRIDE_LOG_DIRECTORY_PARAMETER
+#undef USE_TRACY_PARAMETER
#undef RESTART_COUNTER_PATH
diff --git a/code/modules/NTNet/relays.dm b/code/modules/NTNet/relays.dm
index a2db78a175b4d..f36a89e72e1ff 100644
--- a/code/modules/NTNet/relays.dm
+++ b/code/modules/NTNet/relays.dm
@@ -113,7 +113,7 @@
data["dos_crashed"] = dos_failure
return data
-/obj/machinery/ntnet_relay/ui_act(action, params)
+/obj/machinery/ntnet_relay/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index fa1b832ecc31a..52c9c65e5b917 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -1,6 +1,6 @@
//Blocks an attempt to connect before even creating our client datum thing.
-//How many new ckey matches before we revert the stickyban to it's roundstart state
+//How many new ckey matches before we revert the stickyban to its roundstart state
//These are exclusive, so once it goes over one of these numbers, it reverts the ban
#define STICKYBAN_MAX_MATCHES 15
#define STICKYBAN_MAX_EXISTING_USER_MATCHES 3 //ie, users who were connected before the ban triggered
diff --git a/code/modules/admin/admin_fax_panel.dm b/code/modules/admin/admin_fax_panel.dm
index 8874b6f38eb5a..cc88f1949f2e1 100644
--- a/code/modules/admin/admin_fax_panel.dm
+++ b/code/modules/admin/admin_fax_panel.dm
@@ -20,6 +20,8 @@ ADMIN_VERB(fax_panel, R_ADMIN, "Fax Panel", "View and respond to faxes sent to C
/datum/fax_panel_interface/New()
//Get all faxes, and save them to our list.
for(var/obj/machinery/fax/fax as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/fax))
+ if(istype(fax, /obj/machinery/fax/admin))
+ continue
available_faxes += WEAKREF(fax)
//Get all stamps
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 0f7d7a3f39b30..68096367ef80b 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -484,12 +484,14 @@ ADMIN_VERB(populate_world, R_DEBUG, "Populate World", "Populate the world with t
testing("Spawned test mob at [get_area_name(tile, TRUE)] ([tile.x],[tile.y],[tile.z])")
ADMIN_VERB(toggle_ai_interact, R_ADMIN, "Toggle Admin AI Interact", "Allows you to interact with most machines as an AI would as a ghost.", ADMIN_CATEGORY_GAME)
- user.AI_Interact = !user.AI_Interact
- if(user.mob && isAdminGhostAI(user.mob))
- user.mob.has_unlimited_silicon_privilege = user.AI_Interact
+ var/doesnt_have_silicon_access = !HAS_TRAIT_FROM(user, TRAIT_AI_ACCESS, ADMIN_TRAIT)
+ if(doesnt_have_silicon_access)
+ ADD_TRAIT(user, TRAIT_AI_ACCESS, ADMIN_TRAIT)
+ else
+ REMOVE_TRAIT(user, TRAIT_AI_ACCESS, ADMIN_TRAIT)
- log_admin("[key_name(user)] has [user.AI_Interact ? "activated" : "deactivated"] Admin AI Interact")
- message_admins("[key_name_admin(user)] has [user.AI_Interact ? "activated" : "deactivated"] their AI interaction")
+ log_admin("[key_name(user)] has [doesnt_have_silicon_access ? "activated" : "deactivated"] Admin AI Interact")
+ message_admins("[key_name_admin(user)] has [doesnt_have_silicon_access ? "activated" : "deactivated"] their AI interaction")
ADMIN_VERB(debug_statpanel, R_DEBUG, "Debug Stat Panel", "Toggles local debug of the stat panel", ADMIN_CATEGORY_DEBUG)
user.stat_panel.send_message("create_debug")
diff --git a/code/modules/admin/antag_panel.dm b/code/modules/admin/antag_panel.dm
index 877bebffe9509..ca7b45649d159 100644
--- a/code/modules/admin/antag_panel.dm
+++ b/code/modules/admin/antag_panel.dm
@@ -80,6 +80,8 @@ GLOBAL_VAR(antag_prototypes)
result += "No body!"
if(current && HAS_TRAIT(current, TRAIT_MINDSHIELD))
result += "Mindshielded"
+ if(current && HAS_MIND_TRAIT(current, TRAIT_UNCONVERTABLE))
+ result += "Unconvertable"
//Move these to mob
if(iscyborg(current))
var/mob/living/silicon/robot/robot = current
diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm
index 7a56d63da2309..f619bd8aaba87 100644
--- a/code/modules/admin/fun_balloon.dm
+++ b/code/modules/admin/fun_balloon.dm
@@ -64,7 +64,7 @@
return UI_INTERACTIVE
return ..()
-/obj/effect/fun_balloon/sentience/ui_act(action, list/params)
+/obj/effect/fun_balloon/sentience/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/admin/greyscale_modify_menu.dm b/code/modules/admin/greyscale_modify_menu.dm
index f96ecb7c590b6..0bc1ec01f5d4f 100644
--- a/code/modules/admin/greyscale_modify_menu.dm
+++ b/code/modules/admin/greyscale_modify_menu.dm
@@ -120,7 +120,7 @@
data["sprites"] = sprite_data
return data
-/datum/greyscale_modify_menu/ui_act(action, params)
+/datum/greyscale_modify_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/admin/outfit_manager.dm b/code/modules/admin/outfit_manager.dm
index f3ef7d2685c22..c5f14db8ae3e8 100644
--- a/code/modules/admin/outfit_manager.dm
+++ b/code/modules/admin/outfit_manager.dm
@@ -7,9 +7,6 @@ ADMIN_VERB(outfit_manager, R_DEBUG|R_ADMIN, "Outfit Manager", "View and edit out
/datum/outfit_manager/ui_state(mob/user)
return GLOB.admin_state
-/datum/outfit_manager/ui_close(mob/user)
- qdel(src)
-
/datum/outfit_manager/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
diff --git a/code/modules/admin/skill_panel.dm b/code/modules/admin/skill_panel.dm
index ec80768276dce..b1ba4d10910a0 100644
--- a/code/modules/admin/skill_panel.dm
+++ b/code/modules/admin/skill_panel.dm
@@ -35,7 +35,7 @@
var/exp_percent = exp / SKILL_EXP_LIST[SKILL_LEVEL_LEGENDARY]
.["skills"] += list(list("playername" = targetmind.current, "path" = type, "name" = S.name, "desc" = S.desc, "lvlnum" = lvl_num, "lvl" = lvl_name, "exp" = exp, "exp_prog" = xp_req_to_level - xp_prog_to_level, "exp_req" = xp_req_to_level, "exp_percent" = exp_percent, "max_exp" = SKILL_EXP_LIST[length(SKILL_EXP_LIST)]))
-/datum/skill_panel/ui_act(action, params)
+/datum/skill_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/admin/smites/supply_pod_quick.dm b/code/modules/admin/smites/supply_pod_quick.dm
index 6012e84ccac43..f222857860e39 100644
--- a/code/modules/admin/smites/supply_pod_quick.dm
+++ b/code/modules/admin/smites/supply_pod_quick.dm
@@ -37,7 +37,7 @@
podspawn(list(
"target" = get_turf(target),
"path" = /obj/structure/closet/supplypod/centcompod,
- "style" = STYLE_CENTCOM,
+ "style" = /datum/pod_style/centcom,
"spawn" = target_path,
"damage" = SUPPLY_POD_QUICK_DAMAGE,
"explosionSize" = list(0, 0, 0, SUPPLY_POD_QUICK_FIRE_RANGE),
diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm
index 9a713588abd6d..74955324dffd4 100644
--- a/code/modules/admin/sql_ban_system.dm
+++ b/code/modules/admin/sql_ban_system.dm
@@ -396,6 +396,7 @@
ROLE_SYNDICATE,
ROLE_TRAITOR,
ROLE_WIZARD,
+ ROLE_VOIDWALKER,
),
)
for(var/department in long_job_lists)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 13f5b3544b2a0..6a2666eef9bed 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1722,6 +1722,17 @@
if(!paper_to_show)
return
paper_to_show.ui_interact(usr)
+
+ else if (href_list["print_fax"])
+ if(!check_rights(R_ADMIN))
+ return
+
+ for(var/obj/machinery/fax/admin/FAX as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/fax/admin))
+ if(FAX.fax_id != href_list["destination"])
+ continue
+ FAX.receive(locate(href_list["print_fax"]), href_list["sender_name"])
+
+
else if(href_list["play_internet"])
if(!check_rights(R_SOUND))
return
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
index bc74347475ae9..1305e5a660d6e 100644
--- a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
@@ -51,6 +51,9 @@
/proc/_get_step(Ref, Dir)
return get_step(Ref, Dir)
+/proc/_hascall(object, procname)
+ return hascall(object, procname)
+
/proc/_hearers(Depth = world.view, Center = usr)
return hearers(Depth, Center)
diff --git a/code/modules/admin/verbs/admin_newscaster.dm b/code/modules/admin/verbs/admin_newscaster.dm
index 0439cfa8811ac..4cb7b5c3344e2 100644
--- a/code/modules/admin/verbs/admin_newscaster.dm
+++ b/code/modules/admin/verbs/admin_newscaster.dm
@@ -134,7 +134,7 @@ ADMIN_VERB(access_news_network, R_ADMIN, "Access Newscaster Network", "Allows yo
data["wanted"] = wanted_info
return data
-/datum/newspanel/ui_act(action, params)
+/datum/newspanel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/admin/verbs/adminevents.dm b/code/modules/admin/verbs/adminevents.dm
index 4bf32dedb41c1..214cf653d4e1f 100644
--- a/code/modules/admin/verbs/adminevents.dm
+++ b/code/modules/admin/verbs/adminevents.dm
@@ -218,9 +218,15 @@ ADMIN_VERB(hostile_environment, R_ADMIN, "Hostile Environment", "Disable the shu
SSshuttle.hostile_environments.Cut()
SSshuttle.checkHostileEnvironment()
-ADMIN_VERB(toggle_nuke, R_DEBUG|R_ADMIN, "Toggle Nuke", "Arm or disarm a nuke.", ADMIN_CATEGORY_EVENTS, obj/machinery/nuclearbomb/nuke in world)
+ADMIN_VERB(toggle_nuke, R_DEBUG|R_ADMIN, "Toggle Nuke", "Arm or disarm a nuke.", ADMIN_CATEGORY_EVENTS)
+ var/list/nukes = list()
+ for (var/obj/machinery/nuclearbomb/bomb in world)
+ nukes += bomb
+ var/obj/machinery/nuclearbomb/nuke = tgui_input_list(user, "", "Toggle Nuke", nukes)
+ if (isnull(nuke))
+ return
if(!nuke.timing)
- var/newtime = input(user, "Set activation timer.", "Activate Nuke", "[nuke.timer_set]") as num|null
+ var/newtime = tgui_input_number(user, "Set activation timer.", "Activate Nuke", nuke.timer_set)
if(!newtime)
return
nuke.timer_set = newtime
diff --git a/code/modules/admin/verbs/adminfun.dm b/code/modules/admin/verbs/adminfun.dm
index 8bc7a611b35d4..c7acd1cba4624 100644
--- a/code/modules/admin/verbs/adminfun.dm
+++ b/code/modules/admin/verbs/adminfun.dm
@@ -154,7 +154,7 @@ ADMIN_VERB(polymorph_all, R_ADMIN, "Polymorph All", "Applies the effects of the
message_admins("Mass polymorph started by [who_did_it] is complete.")
ADMIN_VERB_AND_CONTEXT_MENU(admin_smite, R_ADMIN|R_FUN, "Smite", "Smite a player with divine power.", ADMIN_CATEGORY_FUN, mob/living/target in world)
- var/punishment = input(user, "Choose a punishment", "DIVINE SMITING") as null|anything in GLOB.smites
+ var/punishment = tgui_input_list(user, "Choose a punishment", "DIVINE SMITING", GLOB.smites)
if(QDELETED(target) || !punishment)
return
diff --git a/code/modules/admin/verbs/admingame.dm b/code/modules/admin/verbs/admingame.dm
index 0e1e6809daae0..57311446961ed 100644
--- a/code/modules/admin/verbs/admingame.dm
+++ b/code/modules/admin/verbs/admingame.dm
@@ -361,7 +361,7 @@ ADMIN_VERB(combo_hud, R_ADMIN, "Toggle Combo HUD", "Toggles the Admin Combo HUD.
combo_hud_enabled = TRUE
- for (var/hudtype in list(DATA_HUD_SECURITY_ADVANCED, DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED))
+ for (var/hudtype in list(DATA_HUD_SECURITY_ADVANCED, DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC, DATA_HUD_BOT_PATH))
var/datum/atom_hud/atom_hud = GLOB.huds[hudtype]
atom_hud.show_to(mob)
@@ -377,7 +377,7 @@ ADMIN_VERB(combo_hud, R_ADMIN, "Toggle Combo HUD", "Toggles the Admin Combo HUD.
combo_hud_enabled = FALSE
- for (var/hudtype in list(DATA_HUD_SECURITY_ADVANCED, DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED))
+ for (var/hudtype in list(DATA_HUD_SECURITY_ADVANCED, DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC, DATA_HUD_BOT_PATH))
var/datum/atom_hud/atom_hud = GLOB.huds[hudtype]
atom_hud.hide_from(mob)
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index 0e8053c7ab06e..8540ff99e5733 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -246,7 +246,7 @@ ADMIN_VERB(cmd_admin_pm_panel, R_NONE, "Admin PM", "Show a list of clients to PM
request = "[request] an Administrator."
else
request = "[request] [recipient_print_key]."
- //get message text, limit it's length.and clean/escape html
+ //get message text, limit its length.and clean/escape html
msg = input(src,"Message:", request) as message|null
msg = trim(msg)
diff --git a/code/modules/admin/verbs/borgpanel.dm b/code/modules/admin/verbs/borgpanel.dm
index ffeb1cce2731c..97f690f2a5aad 100644
--- a/code/modules/admin/verbs/borgpanel.dm
+++ b/code/modules/admin/verbs/borgpanel.dm
@@ -64,7 +64,7 @@ ADMIN_VERB(borg_panel, R_ADMIN, "Show Borg Panel", ADMIN_VERB_NO_DESCRIPTION, AD
.["ais"] += list(list("name" = ai.name, "ref" = REF(ai), "connected" = (borg.connected_ai == ai)))
-/datum/borgpanel/ui_act(action, params)
+/datum/borgpanel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -153,15 +153,15 @@ ADMIN_VERB(borg_panel, R_ADMIN, "Show Borg Panel", ADMIN_VERB_NO_DESCRIPTION, AD
if (!borg.radio.keyslot) // There's no encryption key. This shouldn't happen but we can cope
borg.radio.channels -= channel
if (channel == RADIO_CHANNEL_SYNDICATE)
- borg.radio.syndie = FALSE
+ borg.radio.special_channels &= ~RADIO_SPECIAL_SYNDIE
else if (channel == "CentCom")
- borg.radio.independent = FALSE
+ borg.radio.special_channels &= ~RADIO_SPECIAL_CENTCOM
else
borg.radio.keyslot.channels -= channel
if (channel == RADIO_CHANNEL_SYNDICATE)
- borg.radio.keyslot.syndie = FALSE
+ borg.radio.keyslot.special_channels &= ~RADIO_SPECIAL_SYNDIE
else if (channel == "CentCom")
- borg.radio.keyslot.independent = FALSE
+ borg.radio.keyslot.special_channels &= ~RADIO_SPECIAL_CENTCOM
message_admins("[key_name_admin(user)] removed the [channel] radio channel from [ADMIN_LOOKUPFLW(borg)].")
log_silicon("[key_name(user)] removed the [channel] radio channel from [key_name(borg)].")
else // We're adding a channel
@@ -169,9 +169,9 @@ ADMIN_VERB(borg_panel, R_ADMIN, "Show Borg Panel", ADMIN_VERB_NO_DESCRIPTION, AD
borg.radio.keyslot = new()
borg.radio.keyslot.channels[channel] = 1
if (channel == RADIO_CHANNEL_SYNDICATE)
- borg.radio.keyslot.syndie = TRUE
+ borg.radio.keyslot.special_channels |= RADIO_SPECIAL_SYNDIE
else if (channel == "CentCom")
- borg.radio.keyslot.independent = TRUE
+ borg.radio.keyslot.special_channels |= RADIO_SPECIAL_CENTCOM
message_admins("[key_name_admin(user)] added the [channel] radio channel to [ADMIN_LOOKUPFLW(borg)].")
log_silicon("[key_name(user)] added the [channel] radio channel to [key_name(borg)].")
borg.radio.recalculateChannels()
diff --git a/code/modules/admin/verbs/ert.dm b/code/modules/admin/verbs/ert.dm
index 2d1ba075a4795..441a5d0dd56fe 100644
--- a/code/modules/admin/verbs/ert.dm
+++ b/code/modules/admin/verbs/ert.dm
@@ -90,6 +90,7 @@
"random_names" = list("desc" = "Randomize names", "type" = "boolean", "value" = "[(ertemplate.random_names ? "Yes" : "No")]"),
"spawn_admin" = list("desc" = "Spawn yourself as briefing officer", "type" = "boolean", "value" = "[(ertemplate.spawn_admin ? "Yes" : "No")]"),
"use_custom_shuttle" = list("desc" = "Use the ERT's custom shuttle (if it has one)", "type" = "boolean", "value" = "[(ertemplate.use_custom_shuttle ? "Yes" : "No")]"),
+ "mob_type" = list("desc" = "Base Species", "callback" = CALLBACK(src, PROC_REF(makeERTTemplateModified)), "type" = "datum", "path" = "/mob/living/carbon/human", "subtypesonly" = TRUE, "value" = ertemplate.mob_type),
)
)
@@ -117,6 +118,7 @@
ertemplate.random_names = prefs["random_names"]["value"] == "Yes"
ertemplate.spawn_admin = prefs["spawn_admin"]["value"] == "Yes"
ertemplate.use_custom_shuttle = prefs["use_custom_shuttle"]["value"] == "Yes"
+ ertemplate.mob_type = prefs["mob_type"]["value"]
var/list/spawnpoints = GLOB.emergencyresponseteamspawn
var/index = 0
@@ -222,11 +224,15 @@
continue
//Spawn the body
- var/mob/living/carbon/human/ert_operative = new ertemplate.mobtype(spawnloc)
- chosen_candidate.client.prefs.safe_transfer_prefs_to(ert_operative, is_antag = TRUE)
+ var/mob/living/carbon/human/ert_operative
+ if(ertemplate.mob_type)
+ ert_operative = new ertemplate.mob_type(spawnloc)
+ else
+ ert_operative = new /mob/living/carbon/human(spawnloc)
+ chosen_candidate.client.prefs.safe_transfer_prefs_to(ert_operative, is_antag = TRUE)
ert_operative.key = chosen_candidate.key
- if(ertemplate.enforce_human || !(ert_operative.dna.species.changesource_flags & ERT_SPAWN)) // Don't want any exploding plasmemes
+ if(ertemplate.enforce_human || !(ert_operative.dna.species.changesource_flags & ERT_SPAWN))
ert_operative.set_species(/datum/species/human)
//Give antag datum
diff --git a/code/modules/admin/verbs/ghost_pool_protection.dm b/code/modules/admin/verbs/ghost_pool_protection.dm
index 439f4a37b897d..ed31d124a7de0 100644
--- a/code/modules/admin/verbs/ghost_pool_protection.dm
+++ b/code/modules/admin/verbs/ghost_pool_protection.dm
@@ -50,7 +50,7 @@ ADMIN_VERB(ghost_pool_protection, R_ADMIN, "Ghost Pool Protection", "Choose whic
data["minigames"] = (new_role_flags & GHOSTROLE_MINIGAME)
return data
-/datum/ghost_pool_menu/ui_act(action, params)
+/datum/ghost_pool_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/admin/verbs/lua/README.md b/code/modules/admin/verbs/lua/README.md
index 707184d4d772b..9b9bfbe05f93f 100644
--- a/code/modules/admin/verbs/lua/README.md
+++ b/code/modules/admin/verbs/lua/README.md
@@ -1,150 +1,225 @@
-# Auxlua
+# Objects
----
+Datums, lists, typepaths, static appearances, and some other objects are represented in Luau as userdata. Certain operations can be performed on these types of objects.
-## Datums
+## Common metamethods
-DM datums are treated as lua userdata, and can be stored in fields. Due to fundamental limitations in lua, userdata is inherently truthy. Since datum userdata can correspond to a deleted datum, which would evaluate to `null` in DM, the function [`datum:is_null()`](#datumisnull) is provided to offer a truthiness test consistent with DM.
+The following metamethods are defined for all objects.
-Keep in mind that BYOND can't see that a datum is referenced in a lua field, and will garbage collect it if it is not referenced anywhere in DM.
+### \_\_tostring(): string
-### datum:get_var(var)
+Returns the string representation of the object. This uses BYOND's internal string conversion function.
-Equivalent to DM's `datum.var`
+### \_\_eq(other: any): boolean
-### datum:set_var(var, value)
+Compare the equality of two objects. While passing the same object into luau twice will return two references to the same userdata, some DM projects may override the equality operator using an `__operator==` proc definition.
-Equivalent to DM's `datum.var = value`
+## Datum-like Objects
-### datum:call_proc(procName, ...)
+Datum-like objects include datums themselves, clients (if they have not been redefined to be children of `/datum`), static appearances, and the world.
-Equivalent to DM's `datum.procName(...)`
+### \_\_index(index: string): any
-### datum:is_null()
+Access the member specified by `index`.
-This function is used to evaluate the truthiness of a DM var. The lua statement `if datum:is_null() then` is equivalent to the DM statement `if(datum)`.
+If `index` is a valid var for the object, the index operation will return that var's value.
+If the var getting wrapper proc is set, the operation will instead call that proc with the arguments `(object, index)`.
-### datum.vars
+For objects other than static appearances, if `index` is a valid proc for the object, the operation will return a wrapper for that proc that can be invoked using call syntax (e.g. `object:proc(...arguments)`). If the object proc calling wrapper is set, calling the returned function will instead call the wrapper proc with the arguments `(object, proc, {...arguments})`. Note that vars will be shadowed by procs with the same name. To work around this, use the `dm.get_var` function.
-Returns a userdatum that allows you to access and modifiy the vars of a DM datum by index. `datum.vars.foo` is equivalent to `datum:get_var("foo")`, while `datum.vars.foo = bar` is equivalent to `datum:set_var("foo", bar)`
+### \_\_newindex(index: string, value: any): ()
----
+Set the var specified by `index` to `value`, if that var exists on the object.
+
+If the var setting wrapper proc is set, the operation will instead call that proc with the arguments `(object, index, value)`.
## Lists
-In order to allow lists to be modified in-place across the DM-to-lua language barrier, lists are treated as userdata. Whenever running code that expects a DM value, auxlua will attempt to convert tables into lists.
+Lists are syntactically similar to tables, with one crucial difference.
+Unlike tables, numeric indices must be non-zero integers within the bounds of the list.
+
+### \_\_index(index: any): any
+
+Read the list at `index`. This works both for numeric indices and assoc keys.
+Vars lists cannot be directly read this way if the var getting wrapper proc is set.
+
+### \_\_newindex(index: any, value: any): any
+
+Write `value` to the list at `index`. This works both for writing numeric indices and assoc keys.
+Vars lists cannot be directly written this way if the var setting wrapper proc is set.
+
+### \_\_len(): integer
+
+Returns the length of the list, similarly to the `length` builtin in DM.
+
+### Iteration
+
+Lists support Luau's generalized iteration. Iteration this way returns pairs of numeric indices and list values.
+For example, the statement `for _, v in L do` is logically equivalent to the DM statement `for(var/v in L)`.
+
+# Global Fields and Modules
+
+In addition to the full extent of Luau's standard library modules, some extra functions and modules have been added.
+
+## Global-Level Fields
+
+### sleep(): ()
+
+Yields the active thread, without worrying about passing data into or out of the state.
+
+Threads yielded this way are placed at the end of a queue. Call the `awaken` hook function from DM to execute the thread at the front of the queue.
+
+### loadstring(code: string): function
+
+Luau does not inherently include the `loadstring` function common to a number of other versions of lua. This is an effective reimplementation of `loadstring`.
+
+### print(...any): ()
+
+Calls the print wrapper with the passed in arguments.
+Raises an error if no print wrapper is set, as that means there is nothing to print with.
+
+### \_state_id: integer
+
+The handle to the underlying luau state in the dreamluau binary.
+
+## \_exec
+
+The `_exec` module includes volatile fields related to the current execution context.
+
+### \_next_yield_index: integer
+
+When yielding a thread with `coroutine.yield`, it will be inserted into an internal table at the first open integer index.
+This field corresponds to that first open integer index.
+
+### \_limit: integer?
+
+If set, the execution limit, rounded to the nearest millisecond.
+
+### \_time: integer
+
+The length of successive time luau code has been executed, including recursive calls to DM and back into luau, rounded to the nearest millisecond.
+
+## dm
-List references are subject to the same limitations as datum userdata, but you are less likely to encounter these limitations for regular lists.
+The `dm` module includes fields and functions for basic interaction with DM.
-Some lists (`vars`, `contents`, `overlays`, `underlays`, `vis_contents`, and `vis_locs`) are inherently attached to datums, and as such, their corresponding userdata contains a weak reference to the containing datum. Use [`list:is_null`](#listisnull) to validate these types of lists.
+### world: userdata
-### list.len
+A static reference to the DM `world`.
-Equivalent to DM's `list.len`
+### global_vars: userdata
-### list:get(index)
+A static reference that functions like the DM keyword `global`. This can be indexed to read/write global vars.
-Equivalent to DM's `list[index]`
+### global_procs: table
-### list:set(index, value)
+A table that can be indexed by string for functions that wrap global procs.
-Equivalent to DM's `list[index] = value`
+Due to BYOND limitations, attempting to index an invalid proc returns a function logically equivalent to a no-op.
-### list:add(value)
+### get_var(object: userdata, var: string): function
-Equivalent to DM's `list.Add(value)`
+Reads the var `var` on `object`. This function can be used to get vars that are shadowed by procs declared with the same name.
-### list:remove(value)
+### new(path: string, ...any): userdata
-Equivalent to DM's `list.Remove(value)`
+Creates an instance of the object specified by `path`, with `...` as its arguments.
+If the "new" wrapper is set, that proc will be called instead, with the arguments `(path, {...})`.
-### list:to_table()
+### is_valid_ref(ref: any): boolean
-Converts a DM list into a lua table.
+Returns true if the value passed in corresponds to a valid reference-counted DM object.
-### list:of_type(type_path)
+### usr: userdata?
-Will extract only values of type `type_path`.
+Corresponds to the DM var `usr`.
-### list:is_null()
+## list
-A similar truthiness test to [`datum:is_null()`](#datumisnull). This function only has the possibility of returning `false` for lists that are inherently attached to a datum (`vars`, `contents`, `overlays`, `underlays`, `vis_contents`, and `vis_locs`).
+The `list` module contains wrappers for the builtin list procs, along with several other utility functions for working with lists.
-### list.entries
+### add(list: userdata, ...any): ()
-Returns a userdatum that allows you to access and modifiy the entries of the list by index. `list.entries.foo` is equivalent to `list:get("foo")`, while `list.entries.foo = bar` is equivalent to `list:set("foo", bar)`
+Logically equivalent to the DM statement `list.Add(...)`.
----
+### copy(list: userdata, start?: integer, end?: integer): userdata
-## The dm table
+Logically equivalent to the DM statement `list.Copy(start, end)`.
-The `dm` table consists of the basic hooks into the DM language.
+### cut(list: userdata, start?: integer, end?: integer): userdata
-### dm.state_id
+Logically equivalent to the DM statement `list.Cut(start, end)`.
-The address of the lua state in memory. This is a copy of the internal value used by auxlua to locate the lua state in a global hash map. `state_id` is a registry value that is indirectly obtained using the `dm` table's `__index` metamethod.
+### find(list: userdata, item: any, start?: integer, end?: integer): integer
-### dm.global_proc(proc, ...)
-Calls the global proc `/proc/[proc]` with `...` as its arguments.
+Logically equivalent to the DM statement `list.Find(item, start, end)`.
-### dm.world
-A reference to DM's `world`, in the form of datum userdata. This reference is always valid, since `world` always exists.
+### insert(list: userdata, index: integer, ...any): integer
-Due to limitations inherent in the wrapper functions used on tgstation, `world:set_var` and `world:call_proc` will raise an error.
+Logically equivalent to the DM statement `list.Insert(item, ...)`.
-### dm.global_vars
-A reference to DM's `global`, in the form of datum userdata. Subject to the same limitations as `dm.world`
+### join(list: userdata, glue: string, start?: integer, end?: integer): string
-### dm.usr
-A weak reference to DM's `usr`. As a rule of thumb, this is a reference to the mob of the client who triggered the chain of procs leading to the execution of Lua code. The following is a list of what `usr` is for the most common ways of executing Lua code:
-- For resumes and awakens, which are generally executed by the MC, `usr` is (most likely) null.
-- `SS13.wait` queues a resume, which gets executed by the MC. Therefore, `usr` is null after `SS13.wait` finishes.
-- For chunk loads, `usr` is generally the current mob of the admin that loaded that chunk.
-- For function calls done from the Lua editor, `usr` is the current mob of the admin calling the function.
-- `SS13.register_signal` creates a `/datum/callback` that gets executed by the `SEND_SIGNAL` macro for the corresponding signal. As such, `usr` is the mob that triggered the chain of procs leading to the invocation of `SEND_SIGNAL`.
+Logically equivalent to the statement `list.Join(glue, start, end)`.
----
+### remove(list: userdata, ...any): integer
-## Execution Limit
+Logically equivalent to the DM statement `list.Remove(...)`.
-In order to prevent freezing the server with infinite loops, auxlua enforces an execution limit, defaulting to 100ms. When a single lua state has been executing for longer than this limit, it will eventually stop and produce an error.
+### remove_all(list: userdata, ...any): integer
-To avoid exceeding the execution limit, call `sleep()` or `coroutine.yield()` before the execution limit is reached.
+Logically equivalent to the DM statement `list.RemoveAll(...)`.
-### over_exec_usage(fraction = 0.95)
+### splice(list: userdata, start?: integer, end?: integer, ...any): ()
-This function returns whether the current run of the Lua VM has executed for longer than the specified fraction of the execution limit. You can use this function to branch to a call to `sleep()` or `coroutine.yield()` to maximize the amount of work done in a single run of the Lua VM. If nil, `fraction` will default to 0.95, otherwise, it will be clamped to the range \[0, 1\].
+Logically equivalent to the DM statement `list.Splice(start, end, ...)`.
----
+### swap(list: userdata, index_1: integer, index_2: integer): ()
-## Task management
-The Lua Scripting subsystem manages the execution of tasks for each Lua state. A single fire of the subsystem behaves as follows:
-- All tasks that slept since the last fire are resumed in the order they slept.
-- For each queued resume, the corresponding task is resumed.
+Logically equivalent to the DM statement `list.Swap(index_1, index_2)`.
-### sleep()
-Yields the current thread, scheduling it to be resumed during the next fire of SSlua. Use this function to prevent your Lua code from exceeding its allowed execution duration. Under the hood, `sleep` performs the following:
+### to_table(list: userdata, deep?: boolean): table
-- Sets the [`sleep_flag`](#sleep_flag)
-- Calls `coroutine.yield()`
-- Clears the sleep flag when determining whether the task slept or yielded
-- Ignores the return values of `coroutine.yield()` once resumed
+Creates a table that is a copy of `list`. If `deep` is true, `to_table` will be called on any lists inside that list.
----
+### from_table(table: table): userdata
-## The SS13 package
+Creates a list that is a copy of `table`. This is not strictly necessary, as tables are automatically converted to lists when passed back into DM, using the same internal logic as `from_table`.
+
+### filter(list: userdata, path: string): userdata
+
+Returns a copy of `list`, containing only elements that are objects descended from `path`.
+
+## pointer
+
+The `pointer` module contains utility functions for interacting with pointers.
+Keep in mind that passing DM pointers into luau and manipulating them in this way can bypass wrapper procs.
+
+### read(pointer: userdata): any
+
+Gets the underlying data the pointer references.
+
+### write(pointer: userdata, value: any): ()
+
+Writes `value` to the underlying data the pointer references.
+
+### unwrap(possible_pointer: any): any
+
+If `possible_pointer` is a pointer, reads it. Otherwise, it is returned as-is.
+
+# The SS13 package
The `SS13` package contains various helper functions that use code specific to tgstation.
-### SS13.state
+## SS13.state
A reference to the state datum (`/datum/lua_state`) handling this Lua state.
-### SS13.get_runner_ckey()
+## SS13.get_runner_ckey()
The ckey of the user who ran the lua script in the current context. Can be unreliable if accessed after sleeping.
-### SS13.get_runner_client()
+## SS13.get_runner_client()
Returns the client of the user who ran the lua script in the current context. Can be unreliable if accessed after sleeping.
-### SS13.global_proc
+## SS13.global_proc
A wrapper for the magic string used to tell `WrapAdminProcCall` to call a global proc.
For instance, `/datum/callback` must be instantiated with `SS13.global_proc` as its first argument to specify that it will be invoking a global proc.
The following example declares a callback which will execute the global proc `to_chat`:
@@ -152,25 +227,18 @@ The following example declares a callback which will execute the global proc `to
local callback = SS13.new("/datum/callback", SS13.global_proc, "to_chat", dm.world, "Hello World")
```
-### SS13.istype(thing, type)
+## SS13.istype(thing, type)
Equivalent to the DM statement `istype(thing, text2path(type))`.
-### SS13.new(type, ...)
-Instantiates a datum of type `type` with `...` as the arguments passed to `/proc/_new`
-The following example spawns a singularity at the caller's current turf:
-```lua
-SS13.new("/obj/singularity", dm.global_proc("_get_step", dm.usr, 0))
-```
-
-### SS13.new_untracked(type, ...)
-Works exactly like SS13.new but it does not store the value to the lua state's `references` list variable. This means that the variable could end up deleted if nothing holds a reference to it.
+## SS13.new(type, ...)
+An alias for `dm.new`
-### SS13.is_valid(datum)
+## SS13.is_valid(datum)
Can be used to determine if the datum passed is not nil, not undefined and not qdel'd all in one. A helper function that allows you to check the validity from only one function.
Example usage:
```lua
local datum = SS13.new("/datum")
-dm.global_proc("qdel", datum)
+dm.global_procs.qdel(datum)
print(SS13.is_valid(datum)) -- false
local null = nil
@@ -180,13 +248,13 @@ local datum = SS13.new("/datum")
print(SS13.is_valid(datum)) -- true
```
-### SS13.type(string)
-Converts a string into a type. Equivalent to doing `dm.global_proc("_text2path", "/path/to/type")`
+## SS13.type(string)
+Converts a string into a typepath. Equivalent to doing `dm.global_proc("_text2path", "/path/to/type")`
-### SS13.qdel(datum)
+## SS13.qdel(datum)
Deletes a datum. You shouldn't try to reference it after calling this function. Equivalent to doing `dm.global_proc("qdel", datum)`
-### SS13.await(thing_to_call, proc_to_call, ...)
+## SS13.await(thing_to_call, proc_to_call, ...)
Calls `proc_to_call` on `thing_to_call`, with `...` as its arguments, and sleeps until that proc returns.
Returns two return values - the first is the return value of the proc, and the second is the message of any runtime exception thrown by the called proc.
The following example calls and awaits the return of `poll_ghost_candidates`:
@@ -194,59 +262,59 @@ The following example calls and awaits the return of `poll_ghost_candidates`:
local ghosts, runtime = SS13.await(SS13.global_proc, "poll_ghost_candidates", "Would you like to be considered for something?")
```
-### SS13.wait(time, timer)
+## SS13.wait(time, timer)
Waits for a number of **seconds** specified with the `time` argument. You can optionally specify a timer subsystem using the `timer` argument.
Internally, this function creates a timer that will resume the current task after `time` seconds, then yields the current task by calling `coroutine.yield` with no arguments and ignores the return values. If the task is prematurely resumed, the timer will be safely deleted.
-### SS13.register_signal(datum, signal, func, make_easy_clear_function)
+## SS13.register_signal(datum, signal, func)
Registers the Lua function `func` as a handler for `signal` on `datum`.
Like with signal handlers written in DM, Lua signal handlers should not sleep (either by calling `sleep` or `coroutine.yield`).
-If `make_easy_clear_function` is truthy, a member function taking no arguments will be created in the `SS13` table to easily unregister the signal handler.
-
-This function returns the `/datum/callback` created to call `func` from DM.
+This function returns whether the signal registration was successful.
The following example defines a function which will register a signal that makes `target` make a honking sound any time it moves:
```lua
function honk(target)
SS13.register_signal(target, "movable_moved", function(source)
- dm.global_proc("playsound", target, "sound/items/bikehorn.ogg", 100, true)
+ dm.global_procs.playsound(target, "sound/items/bikehorn.ogg", 100, true)
end)
end
```
-### SS13.unregister_signal(datum, signal, callback)
-Unregister a signal previously registered using `SS13.register_signal`. `callback` should be a `datum/callback` previously returned by `SS13.register_signal`. If `callback` is not specified, **ALL** signal handlers registered on `datum` for `signal` will be unregistered.
+NOTE: if `func` is an anonymous function declared inside the call to `SS13.register_signal`, it cannot be referenced in order to unregister that signal with `SS13.unregister_signal`
-### SS13.set_timeout(time, func)
+## SS13.unregister_signal(datum, signal, func)
+Unregister a signal previously registered using `SS13.register_signal`. `func` must be a function for which a handler for the specified signal has already been registered. If `func` is `nil`, all handlers for that signal will be unregistered.
+
+## SS13.set_timeout(time, func)
Creates a timer which will execute `func` after `time` **seconds**. `func` should not expect to be passed any arguments, as it will not be passed any. Unlike `SS13.wait`, `SS13.set_timeout` does not yield or sleep the current task, making it suitable for use in signal handlers for `SS13.register_signal`
The following example will output a message to chat after 5 seconds:
```lua
SS13.set_timeout(5, function()
- dm.global_proc("to_chat", dm.world, "Hello World!")
+ dm.global_procs.to_chat(dm.world, "Hello World!")
end)
```
-### SS13.start_loop(time, amount, func)
+## SS13.start_loop(time, amount, func)
Creates a timer which will execute `func` after `time` **seconds**. `func` should not expect to be passed any arguments, as it will not be passed any. Works exactly the same as `SS13.set_timeout` except it will loop the timer `amount` times. If `amount` is set to -1, it will loop indefinitely. Returns a number value, which represents the timer's id. Can be stopped with `SS13.end_loop`
Returns a number, the timer id, which is needed to stop indefinite timers.
The following example will output a message to chat every 5 seconds, repeating 10 times:
```lua
SS13.start_loop(5, 10, function()
- dm.global_proc("to_chat", dm.world, "Hello World!")
+ dm.global_procs.to_chat(dm.world, "Hello World!")
end)
```
The following example will output a message to chat every 5 seconds, until `SS13.end_loop(timerid)` is called:
```lua
local timerid = SS13.start_loop(5, -1, function()
- dm.global_proc("to_chat", dm.world, "Hello World!")
+ dm.global_proc.to_chat(dm.world, "Hello World!")
end)
```
-### SS13.end_loop(id)
+## SS13.end_loop(id)
Prematurely ends a loop that hasn't ended yet, created with `SS13.start_loop`. Silently fails if there is no started loop with the specified id.
The following example will output a message to chat every 5 seconds and delete it after it has repeated 20 times:
```lua
@@ -254,7 +322,7 @@ local repeated_amount = 0
-- timerid won't be in the looping function's scope if declared before the function is declared.
local timerid
timerid = SS13.start_loop(5, -1, function()
- dm.global_proc("to_chat", dm.world, "Hello World!")
+ dm.global_procs.to_chat(dm.world, "Hello World!")
repeated_amount += 1
if repeated_amount >= 20 then
SS13.end_loop(timerid)
@@ -262,35 +330,6 @@ timerid = SS13.start_loop(5, -1, function()
end)
```
-### SS13.stop_all_loops()
+## SS13.stop_all_loops()
Stops all current running loops that haven't ended yet.
Useful in case you accidentally left a indefinite loop running without storing the id anywhere.
-
-### SS13.stop_tracking(datum)
-Stops tracking a datum that was created via `SS13.new` so that it can be garbage collected and deleted without having to qdel. Should be used for things like callbacks and other such datums where the reference to the variable is no longer needed.
-
----
-
-## Internal globals
-
-Auxlua defines several registry values for each state. Note that there is no way to access registry values from lua code.
-
-### sleep_flag
-
-This flag is used to designate that a yielding task should be put in the sleep queue instead of the yield table. Once auxlua determines that a task should sleep, `sleep_flag` is cleared.
-
-### sleep_queue
-
-A sequence of threads, each corresponding to a task that has slept. When calling `/proc/__lua_awaken`, auxlua will dequeue the first thread from the sequence and resume it.
-
-### yield_table
-
-A table of threads, each corresponding to a coroutine that has yielded. When calling `/proc/__lua_resume`, auxlua will look for a thread at the index specified in the `index` argument, and resume it with the arguments specified in the `arguments` argument.
-
-### task_info
-
-A table of key-value-pairs, where the keys are threads, and the values are tables consisting of the following fields:
-
-- name: A string containing the name of the task
-- status: A string, either "sleep" or "yield"
-- index: The task's index in `sleep_queue` or `yield_table`
diff --git a/code/modules/admin/verbs/lua/_hooks.dm b/code/modules/admin/verbs/lua/_hooks.dm
deleted file mode 100644
index a092947e06ec9..0000000000000
--- a/code/modules/admin/verbs/lua/_hooks.dm
+++ /dev/null
@@ -1,239 +0,0 @@
-/datum
- var/__auxtools_weakref_id //used by auxtools for weak references
-
-/**
- * Sets a global proc to call in place of just outright setting a datum's var to a given value
- *
- * The proc will be called with the arguments (datum/datum_to_modify, var_name, value)
- *
- * required wrapper text the name of the proc to use as the wrapper
- */
-/proc/__lua_set_set_var_wrapper(wrapper)
- CRASH("auxlua not loaded")
-
-/**
- * Sets a global proc to call in place of just outright calling a given proc on a datum
- *
- * The proc will be called with the arguments (datum/thing_to_call, proc_to_call, list/arguments)
- *
- * required wrapper text the name of the proc to use as the wrapper
- */
-/proc/__lua_set_datum_proc_call_wrapper(wrapper)
- CRASH("auxlua not loaded")
-
-/**
- * Sets a global proc to call in place of just outright calling a given global proc
- *
- * The proc will be called with the arguments (proc_to_call, list/arguments)
- *
- * required wrapper text the name of the proc to use as the wrapper
- */
-/proc/__lua_set_global_proc_call_wrapper(wrapper)
- CRASH("auxlua not loaded")
-
-/**
- * Sets a global proc as a wrapper for lua's print function
- *
- * The proc will be called with the arguments (state_id, list/arguments)
- *
- * required wrapper text the name of the proc to use as the wrapper
- */
-/proc/__lua_set_print_wrapper(wrapper)
- CRASH("auxlua not loaded")
-
-/**
- * Sets the maximum amount of time a lua chunk or function can execute without sleeping or yielding.
- * Chunks/functions that exceed this duration will produce an error.
- *
- * required limit number the execution limit, in milliseconds
- */
-/proc/__lua_set_execution_limit(limit)
- CRASH("auxlua not loaded")
-
-/**
- * Creates a new lua state.
- *
- * return text a pointer to the created state.
- */
-/proc/__lua_new_state()
- CRASH("auxlua not loaded")
-
-/**
- * Loads a chunk of lua source code and executes it
- *
- * required state text a pointer to the state
- * in which to execute the code
- * required script text the lua source code to execute
- * optional name text a name to give to the chunk
- *
- * return list|text a list of lua return information
- * or an error message if the state was corrupted
- *
- * Lua return information is formatted as followed:
- * - ["status"]: How the chunk or function stopped code execution
- * - "sleeping": The chunk or function called dm.sleep,
- * placing it in the sleep queue. Items in the sleep
- * queue can be resumed using /proc/__lua_awaken
- * - "yielded": The chunk or function called coroutine.yield,
- * placing it in the yield table. Items in the yield
- * table can can be resumed by passing their index
- * to /proc/__lua_resume
- * - "finished": The chunk or function finished
- * - "errored": The chunk or function produced an error
- * - "bad return": The chunk or function yielded or finished,
- * but its return value could not be converted to DM values
- * - ["param"]: Depends on status.
- * - "sleeping": null
- * - "yielded" or "finished": The return/yield value(s)
- * - "errored" or "bad return": The error message
- * - ["yield_index"]: The index in the yield table where the
- * chunk or function is located, for calls to __lua_resume
- * - ["name"]: The name of the chunk or function, for logging
- */
-/proc/__lua_load(state, script, name)
- CRASH("auxlua not loaded")
-
-/**
- * Calls a lua function
- *
- * required state text a pointer to the state
- * in which to call the function
- * required function text the name of the function to call
- * optional arguments list arguments to pass to the function
- *
- * return list|text a list of lua return information
- * or an error message if the state was corrupted
- *
- * Lua return information is formatted as followed:
- * - ["status"]: How the chunk or function stopped code execution
- * - "sleeping": The chunk or function called dm.sleep,
- * placing it in the sleep queue. Items in the sleep
- * queue can be resumed using /proc/__lua_awaken
- * - "yielded": The chunk or function called coroutine.yield,
- * placing it in the yield table. Items in the yield
- * table can can be resumed by passing their index
- * to /proc/__lua_resume
- * - "finished": The chunk or function finished
- * - "errored": The chunk or function produced an error
- * - "bad return": The chunk or function yielded or finished,
- * but its return value could not be converted to DM values
- * - ["param"]: Depends on status.
- * - "sleeping": null
- * - "yielded" or "finished": The return/yield value(s)
- * - "errored" or "bad return": The error message
- * - ["yield_index"]: The index in the yield table where the
- * chunk or function is located, for calls to __lua_resume
- * - ["name"]: The name of the chunk or function, for logging
- */
-/proc/__lua_call(state, function, arguments)
- CRASH("auxlua not loaded")
-
-/**
- * Dequeues the task at the front of the sleep queue and resumes it
- *
- * required state text a pointer to the state in which
- * to resume a task
- *
- * return list|text|null a list of lua return information,
- * an error message if the state is corrupted,
- * or null if the sleep queue is empty
- *
- * Lua return information is formatted as followed:
- * - ["status"]: How the chunk or function stopped code execution
- * - "sleeping": The chunk or function called dm.sleep,
- * placing it in the sleep queue. Items in the sleep
- * queue can be resumed using /proc/__lua_awaken
- * - "yielded": The chunk or function called coroutine.yield,
- * placing it in the yield table. Items in the yield
- * table can can be resumed by passing their index
- * to /proc/__lua_resume
- * - "finished": The chunk or function finished
- * - "errored": The chunk or function produced an error
- * - "bad return": The chunk or function yielded or finished,
- * but its return value could not be converted to DM values
- * - ["param"]: Depends on status.
- * - "sleeping": null
- * - "yielded" or "finished": The return/yield value(s)
- * - "errored" or "bad return": The error message
- * - ["yield_index"]: The index in the yield table where the
- * chunk or function is located, for calls to __lua_resume
- * - ["name"]: The name of the chunk or function, for logging
- */
-/proc/__lua_awaken(state)
- CRASH("auxlua not loaded")
-
-/**
- * Removes the task at the specified index from the yield table
- * and resumes it
- *
- * required state text a pointer to the state in which to
- * resume a task
- * required index number the index in the yield table of the
- * task to resume
- * optional arguments list the arguments to resume the task with
- *
- * return list|text|null a list of lua return information,
- * an error message if the state is corrupted,
- * or null if there is no task at the specified index
- *
- * Lua return information is formatted as followed:
- * - ["status"]: How the chunk or function stopped code execution
- * - "sleeping": The chunk or function called dm.sleep,
- * placing it in the sleep queue. Items in the sleep
- * queue can be resumed using /proc/__lua_awaken
- * - "yielded": The chunk or function called coroutine.yield,
- * placing it in the yield table. Items in the yield
- * table can can be resumed by passing their index
- * to /proc/__lua_resume
- * - "finished": The chunk or function finished
- * - "errored": The chunk or function produced an error
- * - "bad return": The chunk or function yielded or finished,
- * but its return value could not be converted to DM values
- * - ["param"]: Depends on status.
- * - "sleeping": null
- * - "yielded" or "finished": The return/yield value(s)
- * - "errored" or "bad return": The error message
- * - ["yield_index"]: The index in the yield table where the
- * chunk or function is located, for calls to __lua_resume
- * - ["name"]: The name of the chunk or function, for logging
- */
-/proc/__lua_resume(state, index, arguments)
- CRASH("auxlua not loaded")
-
-/**
- * Get the variables within a state's environment.
- * Values not convertible to DM values are substituted
- * for their types as text
- *
- * required state text a pointer to the state
- * to get the variables from
- *
- * return list the variables of the state's environment
- */
-/proc/__lua_get_globals(state)
- CRASH("auxlua not loaded")
-
-/**
- * Get a list of all tasks currently in progress within a state
- *
- * required state text a pointer to the state
- * to get the tasks from
- *
- * return list a list of the state's tasks, formatted as follows:
- * - name: The name of the task
- * - status: Whether the task is sleeping or yielding
- * - index: The index of the task in the sleep queue
- * or yield table, whichever is applicable
- */
-/proc/__lua_get_tasks(state)
- CRASH("auxlua not loaded")
-
-/**
- * Kills a task in progress
- *
- * required state text a pointer to the state
- * in which to kill a task
- * required info list the task info
- */
-/proc/__lua_kill_task(state, info)
- CRASH("auxlua not loaded")
diff --git a/code/modules/admin/verbs/lua/_wrappers.dm b/code/modules/admin/verbs/lua/_wrappers.dm
index 8e05453d29d5d..d516f064f847f 100644
--- a/code/modules/admin/verbs/lua/_wrappers.dm
+++ b/code/modules/admin/verbs/lua/_wrappers.dm
@@ -1,3 +1,12 @@
+/proc/wrap_lua_get_var(datum/thing, var_name)
+ SHOULD_NOT_SLEEP(TRUE)
+ if(thing == world)
+ return world.vars[var_name]
+ if(ref(thing) == "\[0xe000001\]") //This weird fucking thing is like global.vars, but it's not a list and vars is not a valid index for it and I really don't fucking know.
+ return global.vars[var_name]
+ if(thing.can_vv_get(var_name))
+ return thing.vars[var_name]
+
/proc/wrap_lua_set_var(datum/thing_to_set, var_name, value)
SHOULD_NOT_SLEEP(TRUE)
thing_to_set.vv_edit_var(var_name, value)
@@ -11,8 +20,6 @@
ret = WrapAdminProcCall(thing_to_call, proc_name, arguments)
else
ret = HandleUserlessProcCall("lua", thing_to_call, proc_name, arguments)
- if(isdatum(ret))
- SSlua.gc_guard += ret
return ret
/proc/wrap_lua_global_proc_call(proc_name, list/arguments)
@@ -24,8 +31,6 @@
ret = WrapAdminProcCall(GLOBAL_PROC, proc_name, arguments)
else
ret = HandleUserlessProcCall("lua", GLOBAL_PROC, proc_name, arguments)
- if(isdatum(ret))
- SSlua.gc_guard += ret
return ret
/proc/wrap_lua_print(state_id, list/arguments)
@@ -38,6 +43,6 @@
if(!target_state)
return
var/print_message = jointext(arguments, "\t")
- var/result = list("status" = "print", "param" = print_message)
+ var/result = list("status" = "print", "message" = print_message)
INVOKE_ASYNC(target_state, TYPE_PROC_REF(/datum/lua_state, log_result), result, TRUE)
log_lua("[target_state]: [print_message]")
diff --git a/code/modules/admin/verbs/lua/helpers.dm b/code/modules/admin/verbs/lua/helpers.dm
index 66b7c835e9ab1..c3072f15e74cd 100644
--- a/code/modules/admin/verbs/lua/helpers.dm
+++ b/code/modules/admin/verbs/lua/helpers.dm
@@ -3,27 +3,27 @@
#define PROMISE_REJECTED 2
/**
- * Auxtools hooks act as "set waitfor = 0" procs. This means that whenever
- * a proc directly called from auxtools sleeps, the hook returns with whatever
+ * Byondapi hooks act as "set waitfor = 0" procs. This means that whenever
+ * a proc directly called from an external library sleeps, the hook returns with whatever
* the called proc had as its return value at the moment it slept. This may not
* be desired behavior, so this datum exists to wrap these procs.
*
* Some procs that don't sleep could take longer than the execution limit would
* allow for. We can wrap these in a promise as well.
*/
-/datum/auxtools_promise
+/datum/promise
var/datum/callback/callback
var/return_value
var/runtime_message
var/status = PROMISE_PENDING
-/datum/auxtools_promise/New(...)
+/datum/promise/New(...)
+ if(!usr)
+ usr = GLOB.lua_usr
callback = CALLBACK(arglist(args))
- perform()
+ INVOKE_ASYNC(src, PROC_REF(perform))
-/datum/auxtools_promise/proc/perform()
- set waitfor = 0
- sleep() //In case we have to call a super-expensive non-sleeping proc (like getFlatIcon)
+/datum/promise/proc/perform()
try
return_value = callback.Invoke()
status = PROMISE_RESOLVED
diff --git a/code/modules/admin/verbs/lua/lua_editor.dm b/code/modules/admin/verbs/lua/lua_editor.dm
index d4a4bc2ee50b7..93e8e50c1a6a4 100644
--- a/code/modules/admin/verbs/lua/lua_editor.dm
+++ b/code/modules/admin/verbs/lua/lua_editor.dm
@@ -16,6 +16,12 @@
/// If set, we will force the editor to look at this chunk
var/force_view_chunk
+ /// If set, we will force the script input to be this
+ var/force_input
+
+ /// If set, the latest code execution performed from the editor raised an error, and this is the message from that error
+ var/last_error
+
/datum/lua_editor/New(state, _quick_log_index)
. = ..()
if(state)
@@ -37,37 +43,52 @@
/datum/lua_editor/ui_state(mob/user)
return GLOB.debug_state
-/datum/lua_editor/ui_static_data(mob/user)
- var/list/data = list()
- data["documentation"] = file2text('code/modules/admin/verbs/lua/README.md')
- data["auxtools_enabled"] = CONFIG_GET(flag/auxtools_enabled)
- data["ss_lua_init"] = SSlua.initialized
- return data
-
/datum/lua_editor/ui_data(mob/user)
var/list/data = list()
- if(!CONFIG_GET(flag/auxtools_enabled) || !SSlua.initialized)
+ data["ss_lua_init"] = SSlua.initialized
+ if(!SSlua.initialized)
return data
data["noStateYet"] = !current_state
data["showGlobalTable"] = show_global_table
if(current_state)
if(current_state.log)
- data["stateLog"] = kvpify_list(refify_list(current_state.log.Copy((page*50)+1, min((page+1)*50+1, current_state.log.len+1))))
+ var/list/logs = current_state.log.Copy((page*50)+1, min((page+1)*50+1, current_state.log.len+1))
+ for(var/i in 1 to logs.len)
+ var/list/log = logs[i]
+ log = log.Copy()
+ if(log["return_values"])
+ log["return_values"] = kvpify_list(prepare_lua_editor_list(deep_copy_without_cycles(log["return_values"])))
+ logs[i] = log
+ data["stateLog"] = logs
data["page"] = page
data["pageCount"] = CEILING(current_state.log.len/50, 1)
data["tasks"] = current_state.get_tasks()
if(show_global_table)
current_state.get_globals()
- data["globals"] = kvpify_list(refify_list(current_state.globals))
- data["states"] = SSlua.states
- data["callArguments"] = kvpify_list(refify_list(arguments))
+ var/list/values = current_state.globals["values"]
+ values = deep_copy_without_cycles(values)
+ values = prepare_lua_editor_list(values)
+ values = kvpify_list(values)
+ var/list/variants = current_state.globals["variants"]
+ data["globals"] = list("values" = values, "variants" = variants)
+ if(last_error)
+ data["lastError"] = last_error
+ last_error = null
+ data["supressRuntimes"] = current_state.supress_runtimes
+ data["states"] = list()
+ for(var/datum/lua_state/state as anything in SSlua.states)
+ data["states"] += state.display_name
+ data["callArguments"] = kvpify_list(prepare_lua_editor_list(deep_copy_without_cycles(arguments)))
if(force_modal)
data["forceModal"] = force_modal
force_modal = null
if(force_view_chunk)
data["forceViewChunk"] = force_view_chunk
force_view_chunk = null
+ if(force_input)
+ data["force_input"] = force_input
+ force_input = null
return data
/datum/lua_editor/proc/traverse_list(list/path, list/root, traversal_depth_offset = 0)
@@ -99,14 +120,24 @@
else
return root
-/datum/lua_editor/ui_act(action, list/params)
+/datum/lua_editor/proc/run_code(code)
+ var/ckey = usr.ckey
+ current_state.ckey_last_runner = ckey
+ var/result = current_state.load_script(code)
+ var/index_with_result = current_state.log_result(result)
+ if(result["status"] == "error")
+ last_error = result["message"]
+ message_admins("[key_name(usr)] executed [length(code)] bytes of lua code. [ADMIN_LUAVIEW_CHUNK(current_state, index_with_result)]")
+
+/datum/lua_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
- if(!check_rights_for(usr.client, R_DEBUG))
+ var/mob/user = ui.user
+ if(!check_rights_for(user.client, R_DEBUG))
return
if(action == "runCodeFile")
- params["code"] = file2text(input(usr, "Input File") as null|file)
+ params["code"] = file2text(input(user, "Input File") as null|file)
if(isnull(params["code"]))
return
action = "runCode"
@@ -116,6 +147,8 @@
if(!length(state_name))
return TRUE
var/datum/lua_state/new_state = new(state_name)
+ if(QDELETED(new_state))
+ return
SSlua.states += new_state
LAZYREMOVEASSOC(SSlua.editors, text_ref(current_state), src)
current_state = new_state
@@ -130,11 +163,14 @@
page = 0
return TRUE
if("runCode")
- var/code = params["code"]
- current_state.ckey_last_runner = usr.ckey
- var/result = current_state.load_script(code)
- var/index_with_result = current_state.log_result(result)
- message_admins("[key_name(usr)] executed [length(code)] bytes of lua code. [ADMIN_LUAVIEW_CHUNK(current_state, index_with_result)]")
+ run_code(params["code"])
+ return TRUE
+ if("runFile")
+ var/code_file = input(user, "Select a script to run.", "Lua") as file|null
+ if(!code_file)
+ return TRUE
+ var/code = file2text(code_file)
+ run_code(code)
return TRUE
if("moveArgUp")
var/list/path = params["path"]
@@ -158,9 +194,9 @@
var/list/path = params["path"]
var/list/target_list = traverse_list(path, arguments)
if(target_list != arguments)
- usr?.client?.mod_list_add(target_list, null, "a lua editor", "arguments")
+ user?.client?.mod_list_add(target_list, null, "a lua editor", "arguments")
else
- var/list/vv_val = usr?.client?.vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
+ var/list/vv_val = user?.client?.vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
var/class = vv_val["class"]
if(!class)
return
@@ -168,53 +204,58 @@
return TRUE
if("callFunction")
var/list/recursive_indices = params["indices"]
- var/list/current_list = kvpify_list(current_state.globals)
+ var/list/current_list = kvpify_list(current_state.globals["values"])
+ var/list/current_variants = current_state.globals["variants"]
var/function = list()
while(LAZYLEN(recursive_indices))
var/index = popleft(recursive_indices)
var/list/element = current_list[index]
var/key = element["key"]
var/value = element["value"]
- if(!(istext(key) || isnum(key)))
- to_chat(usr, span_warning("invalid key \[[key]] for function call (expected text or num)"))
+ var/list/variant_pair = current_variants[index]
+ var/key_variant = variant_pair["key"]
+ if(key_variant == "function" || key_variant == "thread" || key_variant == "userdata" || key_variant == "error_as_value")
+ to_chat(user, span_warning("invalid table key \[[key]] for function call (expected text, num, path, list, or ref, got [key_variant])"))
return
function += key
if(islist(value))
current_list = value
+ current_variants = variant_pair["value"]
else
- var/regex/function_regex = regex("^function: 0x\[0-9a-fA-F]+$")
- if(function_regex.Find(value))
- break
- to_chat(usr, span_warning("invalid path element \[[value]] for function call (expected list or text matching [function_regex])"))
- return
+ if(variant_pair["value"] != "function")
+ to_chat(user, span_warning("invalid value \[[value]] for function call (expected list or function)"))
+ return
var/result = current_state.call_function(arglist(list(function) + arguments))
current_state.log_result(result)
+ if(result["status"] == "error")
+ last_error = result["message"]
arguments.Cut()
- return TRUE
+ return
if("resumeTask")
var/task_index = params["index"]
SSlua.queue_resume(current_state, task_index, arguments)
arguments.Cut()
return TRUE
if("killTask")
- var/task_info = params["info"]
- SSlua.kill_task(current_state, task_info)
+ var/is_sleep = params["is_sleep"]
+ var/index = params["index"]
+ SSlua.kill_task(current_state, is_sleep, index)
return TRUE
if("vvReturnValue")
var/log_entry_index = params["entryIndex"]
var/list/log_entry = current_state.log[log_entry_index]
- var/thing_to_debug = traverse_list(params["tableIndices"], log_entry["param"])
+ var/thing_to_debug = traverse_list(params["indices"], log_entry["return_values"])
if(isweakref(thing_to_debug))
var/datum/weakref/ref = thing_to_debug
thing_to_debug = ref.resolve()
- INVOKE_ASYNC(usr.client, TYPE_PROC_REF(/client, debug_variables), thing_to_debug)
+ INVOKE_ASYNC(user.client, TYPE_PROC_REF(/client, debug_variables), thing_to_debug)
return FALSE
if("vvGlobal")
- var/thing_to_debug = traverse_list(params["indices"], current_state.globals)
+ var/thing_to_debug = traverse_list(params["indices"], current_state.globals["values"])
if(isweakref(thing_to_debug))
var/datum/weakref/ref = thing_to_debug
thing_to_debug = ref.resolve()
- INVOKE_ASYNC(usr.client, TYPE_PROC_REF(/client, debug_variables), thing_to_debug)
+ INVOKE_ASYNC(user.client, TYPE_PROC_REF(/client, debug_variables), thing_to_debug)
return FALSE
if("clearArgs")
arguments.Cut()
@@ -222,12 +263,18 @@
if("toggleShowGlobalTable")
show_global_table = !show_global_table
return TRUE
+ if("toggleSupressRuntimes")
+ current_state.supress_runtimes = !current_state.supress_runtimes
+ return TRUE
if("nextPage")
page = min(page+1, CEILING(current_state.log.len/50, 1)-1)
return TRUE
if("previousPage")
page = max(page-1, 0)
return TRUE
+ if("nukeLog")
+ current_state.log.Cut()
+ return TRUE
/datum/lua_editor/ui_close(mob/user)
. = ..()
diff --git a/code/modules/admin/verbs/lua/lua_state.dm b/code/modules/admin/verbs/lua/lua_state.dm
index bf2bcbd5a9003..577b0e365c2d8 100644
--- a/code/modules/admin/verbs/lua/lua_state.dm
+++ b/code/modules/admin/verbs/lua/lua_state.dm
@@ -1,15 +1,15 @@
#define MAX_LOG_REPEAT_LOOKBACK 5
-GLOBAL_VAR_INIT(IsLuaCall, FALSE)
-GLOBAL_PROTECT(IsLuaCall)
-
GLOBAL_DATUM(lua_usr, /mob)
GLOBAL_PROTECT(lua_usr)
+GLOBAL_LIST_EMPTY_TYPED(lua_state_stack, /datum/weakref)
+GLOBAL_PROTECT(lua_state_stack)
+
/datum/lua_state
- var/name
+ var/display_name
- /// The internal ID of the lua state stored in auxlua's global map
+ /// The internal ID of the lua state stored in dreamluau's state list
var/internal_id
/// A log of every return, yield, and error for each chunk execution and function call
@@ -18,15 +18,15 @@ GLOBAL_PROTECT(lua_usr)
/// A list of all the variables in the state's environment
var/list/globals = list()
- /// A list in which to store datums and lists instantiated in lua, ensuring that they don't get garbage collected
- var/list/references = list()
-
/// Ckey of the last user who ran a script on this lua state.
var/ckey_last_runner = ""
/// Whether the timer.lua script has been included into this lua context state.
var/timer_enabled = FALSE
+ /// Whether to supress logging BYOND runtimes for this state.
+ var/supress_runtimes = FALSE
+
/// Callbacks that need to be ran on next tick
var/list/functions_to_execute = list()
@@ -39,55 +39,67 @@ GLOBAL_PROTECT(lua_usr)
if(SSlua.initialized != TRUE)
qdel(src)
return
- name = _name
- internal_id = __lua_new_state()
+ display_name = _name
+ internal_id = DREAMLUAU_NEW_STATE()
+ if(!isnum(internal_id))
+ stack_trace(internal_id)
+ qdel(src)
/datum/lua_state/proc/check_if_slept(result)
- if(result["status"] == "sleeping")
+ if(result["status"] == "sleep")
SSlua.sleeps += src
/datum/lua_state/proc/log_result(result, verbose = TRUE)
if(!islist(result))
return
- if(!verbose && result["status"] != "errored" && result["status"] != "bad return" \
- && !(result["name"] == "input" && (result["status"] == "finished" || length(result["param"]))))
+ var/status = result["status"]
+ if(!verbose && status != "error" && status != "panic" && status != "runtime" && !(result["name"] == "input" && (status == "finished" || length(result["return_values"]))))
+ return
+ if(status == "runtime" && supress_runtimes)
return
var/append_to_log = TRUE
var/index_of_log
if(log.len)
for(var/index in log.len to max(log.len - MAX_LOG_REPEAT_LOOKBACK, 1) step -1)
var/list/entry = log[index]
- if(entry["status"] == result["status"] \
- && entry["chunk"] == result["chunk"] \
- && entry["name"] == result["name"] \
- && ((entry["param"] == result["param"]) || deep_compare_list(entry["param"], result["param"])))
- if(!entry["repeats"])
- entry["repeats"] = 0
- index_of_log = index
- entry["repeats"]++
- append_to_log = FALSE
- break
+ if(!compare_lua_logs(entry, result))
+ continue
+ if(!entry["repeats"])
+ entry["repeats"] = 0
+ index_of_log = index
+ entry["repeats"]++
+ append_to_log = FALSE
+ break
if(append_to_log)
- if(islist(result["param"]))
- result["param"] = weakrefify_list(encode_text_and_nulls(result["param"]))
+ if(islist(result["return_values"]))
+ add_lua_return_value_variants(result["return_values"], result["variants"])
+ result["return_values"] = weakrefify_list(result["return_values"])
log += list(result)
index_of_log = log.len
INVOKE_ASYNC(src, TYPE_PROC_REF(/datum/lua_state, update_editors))
return index_of_log
+/datum/lua_state/proc/parse_error(message, name)
+ if(copytext(message, 1, 7) == "PANIC:")
+ return list("status" = "panic", "message" = copytext(message, 7), "name" = name)
+ else
+ return list("status" = "error", "message" = message, "name" = name)
+
/datum/lua_state/proc/load_script(script)
- GLOB.IsLuaCall = TRUE
var/tmp_usr = GLOB.lua_usr
GLOB.lua_usr = usr
- var/result = __lua_load(internal_id, script)
- GLOB.IsLuaCall = FALSE
+ DREAMLUAU_SET_USR
+ GLOB.lua_state_stack += WEAKREF(src)
+ var/result = DREAMLUAU_LOAD(internal_id, script, "input")
+ SSlua.needs_gc_cycle |= src
+ pop(GLOB.lua_state_stack)
GLOB.lua_usr = tmp_usr
// Internal errors unrelated to the code being executed are returned as text rather than lists
if(isnull(result))
- result = list("status" = "errored", "param" = "__lua_load returned null (it may have runtimed - check the runtime logs)", "name" = "input")
+ result = list("status" = "error", "message" = "load returned null (it may have runtimed - check the runtime logs)", "name" = "input")
if(istext(result))
- result = list("status" = "errored", "param" = result, "name" = "input")
+ result = parse_error(result, "input")
result["chunk"] = script
check_if_slept(result)
@@ -109,67 +121,106 @@ GLOBAL_PROTECT(lua_usr)
if(islist(function))
var/list/new_function_path = list()
for(var/path_element in function)
- new_function_path += path_element
+ if(isweakref(path_element))
+ var/datum/weakref/weak_ref = path_element
+ var/resolved = weak_ref.hard_resolve()
+ if(!resolved)
+ return list("status" = "error", "message" = "Weakref in function path ([weak_ref] [text_ref(weak_ref)]) resolved to null.", "name" = jointext(function, "."))
+ new_function_path += resolved
+ else
+ new_function_path += path_element
function = new_function_path
+ else
+ function = list(function)
var/tmp_usr = GLOB.lua_usr
GLOB.lua_usr = usr
- GLOB.IsLuaCall = TRUE
- var/result = __lua_call(internal_id, function, call_args)
- GLOB.IsLuaCall = FALSE
+ DREAMLUAU_SET_USR
+ GLOB.lua_state_stack += WEAKREF(src)
+ var/result = DREAMLUAU_CALL_FUNCTION(internal_id, function, call_args)
+ SSlua.needs_gc_cycle |= src
+ pop(GLOB.lua_state_stack)
GLOB.lua_usr = tmp_usr
if(isnull(result))
- result = list("status" = "errored", "param" = "__lua_call returned null (it may have runtimed - check the runtime logs)", "name" = islist(function) ? jointext(function, ".") : function)
+ result = list("status" = "error", "message" = "call_function returned null (it may have runtimed - check the runtime logs)", "name" = jointext(function, "."))
if(istext(result))
- result = list("status" = "errored", "param" = result, "name" = islist(function) ? jointext(function, ".") : function)
+ result = parse_error(result, jointext(function, "."))
check_if_slept(result)
return result
/datum/lua_state/proc/call_function_return_first(function, ...)
+ SHOULD_NOT_SLEEP(TRUE) // This function is meant to be used for signal handlers.
var/list/result = call_function(arglist(args))
- log_result(result, verbose = FALSE)
+ INVOKE_ASYNC(src, PROC_REF(log_result), deep_copy_list(result), /*verbose = */FALSE)
if(length(result))
- if(islist(result["param"]) && length(result["param"]))
- return result["param"][1]
+ if(islist(result["return_values"]) && length(result["return_values"]))
+ var/return_value = result["return_values"][1]
+ var/variant = (islist(result["variants"]) && length(result["variants"])) && result["variants"][1]
+ if(islist(return_value) && islist(variant))
+ remove_non_dm_variants(return_value, variant)
+ return return_value
/datum/lua_state/proc/awaken()
- GLOB.IsLuaCall = TRUE
- var/result = __lua_awaken(internal_id)
- GLOB.IsLuaCall = FALSE
+ DREAMLUAU_SET_USR
+ GLOB.lua_state_stack += WEAKREF(src)
+ var/result = DREAMLUAU_AWAKEN(internal_id)
+ SSlua.needs_gc_cycle |= src
+ pop(GLOB.lua_state_stack)
if(isnull(result))
- result = list("status" = "errored", "param" = "__lua_awaken returned null (it may have runtimed - check the runtime logs)", "name" = "An attempted awaken")
+ result = list("status" = "error", "message" = "awaken returned null (it may have runtimed - check the runtime logs)", "name" = "An attempted awaken")
if(istext(result))
- result = list("status" = "errored", "param" = result, "name" = "An attempted awaken")
+ result = parse_error(result, "An attempted awaken")
check_if_slept(result)
return result
/// Prefer calling SSlua.queue_resume over directly calling this
/datum/lua_state/proc/resume(index, ...)
var/call_args = length(args) > 1 ? args.Copy(2) : list()
- var/msg = "[key_name(usr)] resumed a lua coroutine with arguments: [english_list(call_args)]"
- log_lua(msg)
- GLOB.IsLuaCall = TRUE
- var/result = __lua_resume(internal_id, index, call_args)
- GLOB.IsLuaCall = FALSE
+ DREAMLUAU_SET_USR
+ GLOB.lua_state_stack += WEAKREF(src)
+ var/result = DREAMLUAU_RESUME(internal_id, index, call_args)
+ SSlua.needs_gc_cycle |= src
+ pop(GLOB.lua_state_stack)
if(isnull(result))
- result = list("status" = "errored", "param" = "__lua_resume returned null (it may have runtimed - check the runtime logs)", "name" = "An attempted resume")
+ result = list("status" = "error", "param" = "resume returned null (it may have runtimed - check the runtime logs)", "name" = "An attempted resume")
if(istext(result))
- result = list("status" = "errored", "param" = result, "name" = "An attempted resume")
+ result = parse_error(result, "An attempted resumt")
check_if_slept(result)
return result
/datum/lua_state/proc/get_globals()
- globals = weakrefify_list(encode_text_and_nulls(__lua_get_globals(internal_id)))
+ var/result = DREAMLUAU_GET_GLOBALS(internal_id)
+ if(isnull(result))
+ CRASH("get_globals returned null")
+ if(istext(result))
+ CRASH(result)
+ var/list/new_globals = result
+ var/list/values = new_globals["values"]
+ var/list/variants = new_globals["variants"]
+ add_lua_editor_variants(values, variants)
+ globals = list("values" = weakrefify_list(values), "variants" = variants)
/datum/lua_state/proc/get_tasks()
- return __lua_get_tasks(internal_id)
+ var/result = DREAMLUAU_LIST_THREADS(internal_id)
+ if(isnull(result))
+ CRASH("list_threads returned null")
+ if(istext(result))
+ CRASH(result)
+ return result
+
+/datum/lua_state/proc/kill_task(is_sleep, index)
+ var/result = is_sleep ? DREAMLUAU_KILL_SLEEPING_THREAD(internal_id, index) : DREAMLUAU_KILL_YIELDED_THREAD(internal_id, index)
+ SSlua.needs_gc_cycle |= src
+ return result
-/datum/lua_state/proc/kill_task(task_info)
- __lua_kill_task(internal_id, task_info)
+/datum/lua_state/proc/collect_garbage()
+ var/result = DREAMLUAU_COLLECT_GARBAGE(internal_id)
+ if(!isnull(result))
+ CRASH(result)
/datum/lua_state/proc/update_editors()
var/list/editor_list = LAZYACCESS(SSlua.editors, text_ref(src))
@@ -177,18 +228,4 @@ GLOBAL_PROTECT(lua_usr)
for(var/datum/lua_editor/editor as anything in editor_list)
SStgui.update_uis(editor)
-/// Called by lua scripts when they add an atom to var/list/references so that it gets cleared up on delete.
-/datum/lua_state/proc/clear_on_delete(datum/to_clear)
- RegisterSignal(to_clear, COMSIG_QDELETING, PROC_REF(on_delete))
-
-/// Called by lua scripts when an atom they've added should soft delete and this state should stop tracking it.
-/// Needs to unregister all signals.
-/datum/lua_state/proc/let_soft_delete(datum/to_clear)
- UnregisterSignal(to_clear, COMSIG_QDELETING, PROC_REF(on_delete))
- references -= to_clear
-
-/datum/lua_state/proc/on_delete(datum/to_clear)
- SIGNAL_HANDLER
- references -= to_clear
-
#undef MAX_LOG_REPEAT_LOOKBACK
diff --git a/code/modules/admin/verbs/machine_upgrade.dm b/code/modules/admin/verbs/machine_upgrade.dm
index 258de6bcf4dfd..7495b383d0e00 100644
--- a/code/modules/admin/verbs/machine_upgrade.dm
+++ b/code/modules/admin/verbs/machine_upgrade.dm
@@ -1,5 +1,5 @@
-ADMIN_VERB(machine_upgrade, R_DEBUG, "Tweak Component Ratings", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, obj/machinery/machine in world)
- var/new_rating = input(user, "Enter new rating:","Num") as num|null
+ADMIN_VERB_AND_CONTEXT_MENU(machine_upgrade, R_DEBUG, "Tweak Component Ratings", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, obj/machinery/machine in world)
+ var/new_rating = tgui_input_number(user, "", "Enter new rating:")
if(new_rating && machine.component_parts)
for(var/obj/item/stock_parts/P in machine.component_parts)
P.rating = new_rating
diff --git a/code/modules/admin/verbs/manipulate_organs.dm b/code/modules/admin/verbs/manipulate_organs.dm
index bfb5050dafa21..6c0a86126b607 100644
--- a/code/modules/admin/verbs/manipulate_organs.dm
+++ b/code/modules/admin/verbs/manipulate_organs.dm
@@ -18,10 +18,7 @@ ADMIN_VERB(manipulate_organs, R_DEBUG, "Manipulate Organs", "Manipulate the orga
return
organ_to_grant = organs[organ_to_grant]
organ_to_grant = new organ_to_grant
- if(!organ_to_grant.Insert(carbon_victim))
- to_chat(user, span_notice("[carbon_victim] is unable to carry this organ!"))
- qdel(organ_to_grant)
- return
+ organ_to_grant.Insert(carbon_victim)
log_admin("[key_name(user)] has added organ [organ_to_grant.type] to [key_name(carbon_victim)]")
message_admins("[key_name_admin(user)] has added organ [organ_to_grant.type] to [ADMIN_LOOKUPFLW(carbon_victim)]")
diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm
index c4e4257e84fc7..b70465666f3d0 100644
--- a/code/modules/admin/verbs/playsound.dm
+++ b/code/modules/admin/verbs/playsound.dm
@@ -54,13 +54,13 @@ ADMIN_VERB(play_direct_mob_sound, R_SOUND, "Play Direct Mob Sound", "Play a soun
SEND_SOUND(target, sound)
BLACKBOX_LOG_ADMIN_VERB("Play Direct Mob Sound")
-///Takes an input from either proc/play_web_sound or the request manager and runs it through youtube-dl and prompts the user before playing it to the server.
+///Takes an input from either proc/play_web_sound or the request manager and runs it through yt-dlp and prompts the user before playing it to the server.
/proc/web_sound(mob/user, input, credit)
if(!check_rights(R_SOUND))
return
var/ytdl = CONFIG_GET(string/invoke_youtubedl)
if(!ytdl)
- to_chat(user, span_boldwarning("Youtube-dl was not configured, action unavailable"), confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value
+ to_chat(user, span_boldwarning("yt-dlp was not configured, action unavailable"), confidential = TRUE) //Check config.txt for the INVOKE_YOUTUBEDL value
return
var/web_sound_url = ""
var/stop_web_sounds = FALSE
@@ -73,14 +73,14 @@ ADMIN_VERB(play_direct_mob_sound, R_SOUND, "Play Direct Mob Sound", "Play a soun
var/stdout = output[SHELLEO_STDOUT]
var/stderr = output[SHELLEO_STDERR]
if(errorlevel)
- to_chat(user, span_boldwarning("Youtube-dl URL retrieval FAILED:"), confidential = TRUE)
+ to_chat(user, span_boldwarning("yt-dlp URL retrieval FAILED:"), confidential = TRUE)
to_chat(user, span_warning("[stderr]"), confidential = TRUE)
return
var/list/data
try
data = json_decode(stdout)
catch(var/exception/e)
- to_chat(user, span_boldwarning("Youtube-dl JSON parsing FAILED:"), confidential = TRUE)
+ to_chat(user, span_boldwarning("yt-dlp JSON parsing FAILED:"), confidential = TRUE)
to_chat(user, span_warning("[e]: [stdout]"), confidential = TRUE)
return
if (data["url"])
diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm
index 107d74d9c3975..c8081cadcbd6b 100644
--- a/code/modules/admin/verbs/secrets.dm
+++ b/code/modules/admin/verbs/secrets.dm
@@ -41,7 +41,7 @@ ADMIN_VERB(secrets, R_NONE, "Secrets", "Abuse harder than you ever have before w
#define THUNDERDOME_TEMPLATE_FILE "admin_thunderdome.dmm"
#define HIGHLANDER_DELAY_TEXT "40 seconds (crush the hope of a normal shift)"
-/datum/secrets_menu/ui_act(action, params)
+/datum/secrets_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -419,7 +419,7 @@ ADMIN_VERB(secrets, R_NONE, "Secrets", "Abuse harder than you ever have before w
var/list/storm_appearances = list()
for(var/offset in 0 to SSmapping.max_plane_offset)
var/mutable_appearance/storm = mutable_appearance('icons/obj/machines/engine/energy_ball.dmi', "energy_ball_fast", FLY_LAYER)
- SET_PLANE_W_SCALAR(storm, ABOVE_GAME_PLANE, offset)
+ SET_PLANE_W_SCALAR(storm, GAME_PLANE, offset)
storm.color = prefs["color"]["value"]
storm_appearances += storm
@@ -526,7 +526,7 @@ ADMIN_VERB(secrets, R_NONE, "Secrets", "Abuse harder than you ever have before w
var/forename = names.len > 1 ? names[2] : names[1]
var/newname = "[forename]-[pick(honorifics["[H.gender]"])]"
H.fully_replace_character_name(H.real_name,newname)
- H.update_mutant_bodyparts()
+ H.update_body_parts()
if(animetype == "Yes")
var/seifuku = pick(typesof(/obj/item/clothing/under/costume/schoolgirl))
var/obj/item/clothing/under/costume/schoolgirl/I = new seifuku
diff --git a/code/modules/admin/verbs/server.dm b/code/modules/admin/verbs/server.dm
index fb7bc16cf24e0..a8389ca0bc155 100644
--- a/code/modules/admin/verbs/server.dm
+++ b/code/modules/admin/verbs/server.dm
@@ -16,10 +16,15 @@ ADMIN_VERB(toggle_hub, R_SERVER, "Toggle Hub", "Toggles the server's visilibilit
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggled Hub Visibility", "[GLOB.hub_visibility ? "Enabled" : "Disabled"]")) // If you are copy-pasting this, ensure the 4th parameter is unique to the new proc!
+#define REGULAR_RESTART "Regular Restart"
+#define REGULAR_RESTART_DELAYED "Regular Restart (with delay)"
+#define HARD_RESTART "Hard Restart (No Delay/Feedback Reason)"
+#define HARDEST_RESTART "Hardest Restart (No actions, just reboot)"
+#define TGS_RESTART "Server Restart (Kill and restart DD)"
ADMIN_VERB(restart, R_SERVER, "Reboot World", "Restarts the world immediately.", ADMIN_CATEGORY_SERVER)
- var/list/options = list("Regular Restart", "Regular Restart (with delay)", "Hard Restart (No Delay/Feeback Reason)", "Hardest Restart (No actions, just reboot)")
+ var/list/options = list(REGULAR_RESTART, REGULAR_RESTART_DELAYED, HARD_RESTART, HARDEST_RESTART)
if(world.TgsAvailable())
- options += "Server Restart (Kill and restart DD)";
+ options += TGS_RESTART;
if(SSticker.admin_delay_notice)
if(alert(user, "Are you sure? An admin has already delayed the round end for the following reason: [SSticker.admin_delay_notice]", "Confirmation", "Yes", "No") != "Yes")
@@ -32,12 +37,12 @@ ADMIN_VERB(restart, R_SERVER, "Reboot World", "Restarts the world immediately.",
BLACKBOX_LOG_ADMIN_VERB("Reboot World")
var/init_by = "Initiated by [user.holder.fakekey ? "Admin" : user.key]."
switch(result)
- if("Regular Restart")
+ if(REGULAR_RESTART)
if(!user.is_localhost())
if(alert(user, "Are you sure you want to restart the server?","This server is live", "Restart", "Cancel") != "Restart")
return FALSE
SSticker.Reboot(init_by, "admin reboot - by [user.key] [user.holder.fakekey ? "(stealth)" : ""]", 10)
- if("Regular Restart (with delay)")
+ if(REGULAR_RESTART_DELAYED)
var/delay = input("What delay should the restart have (in seconds)?", "Restart Delay", 5) as num|null
if(!delay)
return FALSE
@@ -45,16 +50,22 @@ ADMIN_VERB(restart, R_SERVER, "Reboot World", "Restarts the world immediately.",
if(alert(user,"Are you sure you want to restart the server?","This server is live", "Restart", "Cancel") != "Restart")
return FALSE
SSticker.Reboot(init_by, "admin reboot - by [user.key] [user.holder.fakekey ? "(stealth)" : ""]", delay * 10)
- if("Hard Restart (No Delay, No Feeback Reason)")
+ if(HARD_RESTART)
to_chat(world, "World reboot - [init_by]")
world.Reboot()
- if("Hardest Restart (No actions, just reboot)")
+ if(HARDEST_RESTART)
to_chat(world, "Hard world reboot - [init_by]")
world.Reboot(fast_track = TRUE)
- if("Server Restart (Kill and restart DD)")
+ if(TGS_RESTART)
to_chat(world, "Server restart - [init_by]")
world.TgsEndProcess()
+#undef REGULAR_RESTART
+#undef REGULAR_RESTART_DELAYED
+#undef HARD_RESTART
+#undef HARDEST_RESTART
+#undef TGS_RESTART
+
ADMIN_VERB(end_round, R_SERVER, "End Round", "Forcibly ends the round and allows the server to restart normally.", ADMIN_CATEGORY_SERVER)
var/confirm = tgui_alert(user, "End the round and restart the game world?", "End Round", list("Yes", "Cancel"))
if(confirm != "Yes")
diff --git a/code/modules/admin/view_variables/filterrific.dm b/code/modules/admin/view_variables/filterrific.dm
index 0bd9f51c114f9..a997d52047743 100644
--- a/code/modules/admin/view_variables/filterrific.dm
+++ b/code/modules/admin/view_variables/filterrific.dm
@@ -24,7 +24,7 @@
data["target_filter_data"] = target.filter_data
return data
-/datum/filter_editor/ui_act(action, list/params)
+/datum/filter_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/admin/view_variables/nobody_wants_to_learn_matrix_math.dm b/code/modules/admin/view_variables/nobody_wants_to_learn_matrix_math.dm
index fa5fde1f20e18..2bf75d01d7dd9 100644
--- a/code/modules/admin/view_variables/nobody_wants_to_learn_matrix_math.dm
+++ b/code/modules/admin/view_variables/nobody_wants_to_learn_matrix_math.dm
@@ -42,7 +42,7 @@
data["pixelated"] = target.appearance_flags & PIXEL_SCALE
return data
-/datum/nobody_wants_to_learn_matrix_math/ui_act(action, list/params)
+/datum/nobody_wants_to_learn_matrix_math/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index d51885d51431f..e658e7c1c7c05 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -128,7 +128,7 @@ GLOBAL_LIST_EMPTY(antagonists)
ui = new(user, src, ui_name, name)
ui.open()
-/datum/antagonist/ui_act(action, params)
+/datum/antagonist/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -536,7 +536,7 @@ GLOBAL_LIST_EMPTY(antagonists)
/// Takes a location, returns an image drawing "on" it that matches this antag datum's hud icon
/datum/antagonist/proc/hud_image_on(mob/hud_loc)
var/image/hud = image(hud_icon, hud_loc, antag_hud_name)
- SET_PLANE_EXPLICIT(hud, ABOVE_GAME_PLANE, hud_loc)
+ SET_PLANE_EXPLICIT(hud, GAME_PLANE, hud_loc)
return hud
///generic helper to send objectives as data through tgui.
diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm
index 440feb91b7622..5f7a7e579d3ee 100644
--- a/code/modules/antagonists/_common/antag_spawner.dm
+++ b/code/modules/antagonists/_common/antag_spawner.dm
@@ -45,7 +45,7 @@
get_asset_datum(/datum/asset/simple/contracts),
)
-/obj/item/antag_spawner/contract/ui_act(action, list/params)
+/obj/item/antag_spawner/contract/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(used || polling || !ishuman(usr))
return
@@ -105,7 +105,7 @@
/// The antag datum applied
var/antag_datum = /datum/antagonist/nukeop/reinforcement
/// Style used by the droppod
- var/pod_style = STYLE_SYNDICATE
+ var/pod_style = /datum/pod_style/syndicate
/// Do we use a random subtype of the outfit?
var/use_subtypes = TRUE
/// Where do we land our pod?
@@ -187,8 +187,8 @@
desc = "A single-use beacon designed to quickly launch reinforcement clown operatives into the field."
special_role_name = ROLE_CLOWN_OPERATIVE
outfit = /datum/outfit/syndicate/clownop/no_crystals
- antag_datum = /datum/antagonist/nukeop/clownop
- pod_style = STYLE_HONK
+ antag_datum = /datum/antagonist/nukeop/reinforcement/clownop
+ pod_style = /datum/pod_style/clown
use_subtypes = FALSE
//////SYNDICATE BORG
@@ -313,7 +313,7 @@
/// The antag datum applied
var/datum/antagonist/antag_datum
/// Style used by the droppod
- var/pod_style = STYLE_SYNDICATE
+ var/pod_style = /datum/pod_style/syndicate
/// Do we use a random subtype of the outfit?
var/use_subtypes = TRUE
/// The antag role we check if the ghosts have enabled to get the poll.
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index 68cf781db9b05..7fc0c565ab1df 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -74,12 +74,13 @@
owner.special_role = ROLE_ABDUCTOR
objectives += team.objectives
finalize_abductor()
- ADD_TRAIT(owner, TRAIT_ABDUCTOR_TRAINING, ABDUCTOR_ANTAGONIST)
+ // We don't want abductors to be converted by other antagonists
+ owner.add_traits(list(TRAIT_ABDUCTOR_TRAINING, TRAIT_UNCONVERTABLE), ABDUCTOR_ANTAGONIST)
return ..()
/datum/antagonist/abductor/on_removal()
owner.special_role = null
- REMOVE_TRAIT(owner, TRAIT_ABDUCTOR_TRAINING, ABDUCTOR_ANTAGONIST)
+ owner.remove_traits(list(TRAIT_ABDUCTOR_TRAINING, TRAIT_UNCONVERTABLE), ABDUCTOR_ANTAGONIST)
return ..()
/datum/antagonist/abductor/greet()
@@ -90,20 +91,20 @@
/datum/antagonist/abductor/proc/finalize_abductor()
//Equip
- var/mob/living/carbon/human/H = owner.current
- H.set_species(/datum/species/abductor)
- var/obj/item/organ/internal/tongue/abductor/T = H.get_organ_slot(ORGAN_SLOT_TONGUE)
- T.mothership = "[team.name]"
+ var/mob/living/carbon/human/new_abductor = owner.current
+ new_abductor.set_species(/datum/species/abductor)
+ var/obj/item/organ/internal/tongue/abductor/abductor_tongue = new_abductor.get_organ_slot(ORGAN_SLOT_TONGUE)
+ abductor_tongue.mothership = "[team.name]"
- H.real_name = "[team.name] [sub_role]"
- H.equipOutfit(outfit)
+ new_abductor.real_name = "[team.name] [sub_role]"
+ new_abductor.equipOutfit(outfit)
// We require that the template be loaded here, so call it in a blocking manner, if its already done loading, this won't block
SSmapping.lazy_load_template(LAZY_TEMPLATE_KEY_ABDUCTOR_SHIPS)
//Teleport to ship
for(var/obj/effect/landmark/abductor/LM in GLOB.landmarks_list)
if(istype(LM, landmark_type) && LM.team_number == team.team_number)
- H.forceMove(LM.loc)
+ new_abductor.forceMove(LM.loc)
break
/datum/antagonist/abductor/scientist/on_gain()
@@ -137,13 +138,13 @@
if(!ishuman(owner.current))
to_chat(admin, span_warning("This only works on humans!"))
return
- var/mob/living/carbon/human/H = owner.current
+ var/mob/living/carbon/human/new_abductor = owner.current
var/gear = tgui_alert(admin,"Agent or Scientist Gear", "Gear", list("Agent", "Scientist"))
if(gear)
if(gear == "Agent")
- H.equipOutfit(/datum/outfit/abductor/agent)
+ new_abductor.equipOutfit(/datum/outfit/abductor/agent)
else
- H.equipOutfit(/datum/outfit/abductor/scientist)
+ new_abductor.equipOutfit(/datum/outfit/abductor/scientist)
/datum/team/abductor_team
member_name = "\improper Abductor"
diff --git a/code/modules/antagonists/abductor/abductor_structures.dm b/code/modules/antagonists/abductor/abductor_structures.dm
index da0ad5de9ea5b..dba46d73f0750 100644
--- a/code/modules/antagonists/abductor/abductor_structures.dm
+++ b/code/modules/antagonists/abductor/abductor_structures.dm
@@ -50,7 +50,7 @@
/obj/structure/table/abductor
name = "alien table"
desc = "Advanced flat surface technology at work!"
- icon = 'icons/obj/smooth_structures/alien_table.dmi'
+ icon = 'icons/obj/structures/smooth/alien_table.dmi'
icon_state = "alien_table-0"
base_icon_state = "alien_table"
buildstack = /obj/item/stack/sheet/mineral/abductor
@@ -116,9 +116,7 @@
/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'
+ icon = /obj/machinery/door/airlock/abductor::icon
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/gear/abductor_clothing.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_clothing.dm
index c54ce6937d86f..e5951473df087 100644
--- a/code/modules/antagonists/abductor/equipment/gear/abductor_clothing.dm
+++ b/code/modules/antagonists/abductor/equipment/gear/abductor_clothing.dm
@@ -103,6 +103,8 @@
/obj/item/clothing/suit/armor/abductor/vest/proc/return_disguise_name(mob/living/carbon/human/source, list/identity)
SIGNAL_HANDLER
+ if(identity[VISIBLE_NAME_FORCED]) // name-forcing overrides disguise
+ return
identity[VISIBLE_NAME_FACE] = disguise.name
identity[VISIBLE_NAME_ID] = ""
diff --git a/code/modules/antagonists/abductor/equipment/gland.dm b/code/modules/antagonists/abductor/equipment/gland.dm
index 29ea5f1e78502..f4fe0fb1875f8 100644
--- a/code/modules/antagonists/abductor/equipment/gland.dm
+++ b/code/modules/antagonists/abductor/equipment/gland.dm
@@ -84,7 +84,7 @@
active_mind_control = FALSE
return TRUE
-/obj/item/organ/internal/heart/gland/Remove(mob/living/carbon/gland_owner, special, movement_flags)
+/obj/item/organ/internal/heart/gland/mob_remove(mob/living/carbon/gland_owner, special, movement_flags)
. = ..()
active = FALSE
if(initial(uses) == 1)
@@ -93,10 +93,8 @@
hud.remove_atom_from_hud(gland_owner)
clear_mind_control()
-/obj/item/organ/internal/heart/gland/Insert(mob/living/carbon/gland_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED)
+/obj/item/organ/internal/heart/gland/mob_insert(mob/living/carbon/gland_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
- if(!.)
- return
if(special != 2 && uses) // Special 2 means abductor surgery
Start()
diff --git a/code/modules/antagonists/abductor/equipment/glands/mindshock.dm b/code/modules/antagonists/abductor/equipment/glands/mindshock.dm
index 87870947f17d6..1a3f140c3415d 100644
--- a/code/modules/antagonists/abductor/equipment/glands/mindshock.dm
+++ b/code/modules/antagonists/abductor/equipment/glands/mindshock.dm
@@ -15,7 +15,7 @@
for(var/mob/living/carbon/target in orange(4,owner_turf))
if(target == owner)
continue
- if(HAS_TRAIT(target, TRAIT_MINDSHIELD))
+ if(HAS_MIND_TRAIT(target, TRAIT_MINDSHIELD))
to_chat(target, span_notice("You hear a faint hum fill your ears, which quickly dies down."))
continue
@@ -41,7 +41,7 @@
if(target_human.stat)
continue
- if(HAS_TRAIT(target_human, TRAIT_MINDSHIELD))
+ if(HAS_MIND_TRAIT(target_human, TRAIT_UNCONVERTABLE))
to_chat(target_human, span_notice("You hear a low drone as something foreign attempts to enter your mind, but the noise fades after a few moments."))
continue
diff --git a/code/modules/antagonists/abductor/machinery/console.dm b/code/modules/antagonists/abductor/machinery/console.dm
index ee729de7068b8..3dcdaf5a5b07e 100644
--- a/code/modules/antagonists/abductor/machinery/console.dm
+++ b/code/modules/antagonists/abductor/machinery/console.dm
@@ -118,7 +118,7 @@
data["vest_lock"] = HAS_TRAIT_FROM(vest, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT)
return data
-/obj/machinery/abductor/console/ui_act(action, list/params)
+/obj/machinery/abductor/console/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/abductor/machinery/dispenser.dm b/code/modules/antagonists/abductor/machinery/dispenser.dm
index 8d8f9e14b8954..416153c50e58f 100644
--- a/code/modules/antagonists/abductor/machinery/dispenser.dm
+++ b/code/modules/antagonists/abductor/machinery/dispenser.dm
@@ -48,7 +48,7 @@
data["glands"] += list(gland_information)
return data
-/obj/machinery/abductor/gland_dispenser/ui_act(action, list/params)
+/obj/machinery/abductor/gland_dispenser/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/abductor/machinery/experiment.dm b/code/modules/antagonists/abductor/machinery/experiment.dm
index a549171b66150..09790f4ba897e 100644
--- a/code/modules/antagonists/abductor/machinery/experiment.dm
+++ b/code/modules/antagonists/abductor/machinery/experiment.dm
@@ -86,7 +86,7 @@
data["occupant_status"] = mob_occupant.stat
return data
-/obj/machinery/abductor/experiment/ui_act(action, list/params)
+/obj/machinery/abductor/experiment/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/blob/blob_antag.dm b/code/modules/antagonists/blob/blob_antag.dm
index 9cad238bb0011..981c200ebd024 100644
--- a/code/modules/antagonists/blob/blob_antag.dm
+++ b/code/modules/antagonists/blob/blob_antag.dm
@@ -43,9 +43,9 @@
/datum/antagonist/blob/get_preview_icon()
var/datum/blobstrain/reagent/reactive_spines/reactive_spines = /datum/blobstrain/reagent/reactive_spines
- var/icon/icon = icon('icons/mob/nonhuman-player/blob.dmi', "blob_core")
+ var/icon/icon = icon('icons/mob/nonhuman-player/blob_tall.dmi', "blob_core")
icon.Blend(initial(reactive_spines.color), ICON_MULTIPLY)
- icon.Blend(icon('icons/mob/nonhuman-player/blob.dmi', "blob_core_overlay"), ICON_OVERLAY)
+ icon.Blend(icon('icons/mob/nonhuman-player/blob_tall.dmi', "blob_core_overlay"), ICON_OVERLAY)
icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
return icon
diff --git a/code/modules/antagonists/blob/overmind.dm b/code/modules/antagonists/blob/overmind.dm
index ea6706f96e999..c314765cb44d9 100644
--- a/code/modules/antagonists/blob/overmind.dm
+++ b/code/modules/antagonists/blob/overmind.dm
@@ -15,7 +15,6 @@ GLOBAL_LIST_EMPTY(blob_nodes)
move_on_shuttle = TRUE
invisibility = INVISIBILITY_OBSERVER
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
see_invisible = SEE_INVISIBLE_LIVING
pass_flags = PASSBLOB
faction = list(ROLE_BLOB)
@@ -73,6 +72,10 @@ GLOBAL_LIST_EMPTY(blob_nodes)
. = ..()
START_PROCESSING(SSobj, src)
GLOB.blob_telepathy_mobs |= src
+ var/mutable_appearance/high_marker = mutable_appearance('icons/mob/silicon/cameramob.dmi', "marker", ABOVE_MOB_LAYER, src, ABOVE_GAME_PLANE)
+ high_marker.pixel_y -= 12
+ add_overlay(high_marker)
+ AddElement(/datum/element/elevation, pixel_shift = 10)
/mob/camera/blob/proc/validate_location()
var/turf/T = get_turf(src)
@@ -206,7 +209,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
if(live_guy.stat != DEAD)
live_guy.investigate_log("has died from blob takeover.", INVESTIGATE_DEATHS)
live_guy.death()
- create_spore(guy_turf)
+ create_spore(guy_turf, spore_type = /mob/living/basic/blob_minion/spore)
else
live_guy.fully_heal()
@@ -217,7 +220,7 @@ GLOBAL_LIST_EMPTY(blob_nodes)
continue
check_area.color = blobstrain.color
check_area.name = "blob"
- check_area.icon = 'icons/mob/nonhuman-player/blob.dmi'
+ check_area.icon = 'icons/mob/nonhuman-player/blob_tall.dmi'
check_area.icon_state = "blob_shield"
check_area.layer = BELOW_MOB_LAYER
check_area.SetInvisibility(INVISIBILITY_NONE)
diff --git a/code/modules/antagonists/blob/powers.dm b/code/modules/antagonists/blob/powers.dm
index 2f3b51741f9b6..88b4d4c9fe000 100644
--- a/code/modules/antagonists/blob/powers.dm
+++ b/code/modules/antagonists/blob/powers.dm
@@ -381,7 +381,7 @@
for (var/unused in 1 to BLOB_POWER_REROLL_CHOICES)
var/datum/blobstrain/strain = pick_n_take(new_strains)
- var/image/strain_icon = image('icons/mob/nonhuman-player/blob.dmi', "blob_core")
+ var/image/strain_icon = image('icons/mob/nonhuman-player/blob_tall.dmi', "blob_core")
strain_icon.color = initial(strain.color)
var/info_text = span_boldnotice("[initial(strain.name)]")
diff --git a/code/modules/antagonists/blob/structures/_blob.dm b/code/modules/antagonists/blob/structures/_blob.dm
index 324c91ea3a529..d48f4163be2d4 100644
--- a/code/modules/antagonists/blob/structures/_blob.dm
+++ b/code/modules/antagonists/blob/structures/_blob.dm
@@ -1,7 +1,7 @@
//I will need to recode parts of this but I am way too tired atm //I don't know who left this comment but they never did come back
/obj/structure/blob
name = "blob"
- icon = 'icons/mob/nonhuman-player/blob.dmi'
+ icon = 'icons/mob/nonhuman-player/blob_tall.dmi'
light_range = 2
desc = "A thick wall of writhing tendrils."
density = TRUE
diff --git a/code/modules/antagonists/blob/structures/core.dm b/code/modules/antagonists/blob/structures/core.dm
index 6eeccc8c361dd..6d5663f9f22b8 100644
--- a/code/modules/antagonists/blob/structures/core.dm
+++ b/code/modules/antagonists/blob/structures/core.dm
@@ -1,6 +1,6 @@
/obj/structure/blob/special/core
name = "blob core"
- icon = 'icons/mob/nonhuman-player/blob.dmi'
+ icon = 'icons/mob/nonhuman-player/blob_tall.dmi'
icon_state = "blank_blob"
desc = "A huge, pulsating yellow mass."
max_integrity = BLOB_CORE_MAX_HP
@@ -47,11 +47,11 @@
/obj/structure/blob/special/core/update_overlays()
. = ..()
- var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/nonhuman-player/blob.dmi', "blob")
+ var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/nonhuman-player/blob_tall.dmi', "blob")
if(overmind)
blob_overlay.color = overmind.blobstrain.color
. += blob_overlay
- . += mutable_appearance('icons/mob/nonhuman-player/blob.dmi', "blob_core_overlay")
+ . += mutable_appearance('icons/mob/nonhuman-player/blob_tall.dmi', "blob_core_overlay")
/obj/structure/blob/special/core/update_icon()
. = ..()
diff --git a/code/modules/antagonists/blob/structures/factory.dm b/code/modules/antagonists/blob/structures/factory.dm
index cee7e9a0ac58d..3ece3f946548d 100644
--- a/code/modules/antagonists/blob/structures/factory.dm
+++ b/code/modules/antagonists/blob/structures/factory.dm
@@ -1,6 +1,6 @@
/obj/structure/blob/special/factory
name = "factory blob"
- icon = 'icons/mob/nonhuman-player/blob.dmi'
+ icon = 'icons/mob/nonhuman-player/blob_tall.dmi'
icon_state = "blob_factory"
desc = "A thick spire of tendrils."
max_integrity = BLOB_FACTORY_MAX_HP
diff --git a/code/modules/antagonists/blob/structures/node.dm b/code/modules/antagonists/blob/structures/node.dm
index d29eca38a6a5a..7b52397dbff06 100644
--- a/code/modules/antagonists/blob/structures/node.dm
+++ b/code/modules/antagonists/blob/structures/node.dm
@@ -1,6 +1,6 @@
/obj/structure/blob/special/node
name = "blob node"
- icon = 'icons/mob/nonhuman-player/blob.dmi'
+ icon = 'icons/mob/nonhuman-player/blob_tall.dmi'
icon_state = "blank_blob"
desc = "A large, pulsating yellow mass."
max_integrity = BLOB_NODE_MAX_HP
@@ -33,14 +33,14 @@
/obj/structure/blob/special/node/update_overlays()
. = ..()
- var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/nonhuman-player/blob.dmi', "blob")
+ var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/nonhuman-player/blob_tall.dmi', "blob")
if(overmind)
blob_overlay.color = overmind.blobstrain.color
var/area/A = get_area(src)
if(!(A.area_flags & BLOBS_ALLOWED))
blob_overlay.color = BlendRGB(overmind.blobstrain.color, COLOR_WHITE, 0.5) //lighten it to indicate an off-station blob
. += blob_overlay
- . += mutable_appearance('icons/mob/nonhuman-player/blob.dmi', "blob_node_overlay")
+ . += mutable_appearance('icons/mob/nonhuman-player/blob_tall.dmi', "blob_node_overlay")
/obj/structure/blob/special/node/creation_action()
if(overmind)
diff --git a/code/modules/antagonists/blob/structures/resource.dm b/code/modules/antagonists/blob/structures/resource.dm
index 3fa5416d32370..688921668fa5f 100644
--- a/code/modules/antagonists/blob/structures/resource.dm
+++ b/code/modules/antagonists/blob/structures/resource.dm
@@ -1,6 +1,6 @@
/obj/structure/blob/special/resource
name = "resource blob"
- icon = 'icons/mob/nonhuman-player/blob.dmi'
+ icon = 'icons/mob/nonhuman-player/blob_tall.dmi'
icon_state = "blob_resource"
desc = "A thin spire of slightly swaying tendrils."
max_integrity = BLOB_RESOURCE_MAX_HP
diff --git a/code/modules/antagonists/blob/structures/shield.dm b/code/modules/antagonists/blob/structures/shield.dm
index 737e9108555a4..c1895c3a6da3d 100644
--- a/code/modules/antagonists/blob/structures/shield.dm
+++ b/code/modules/antagonists/blob/structures/shield.dm
@@ -1,6 +1,6 @@
/obj/structure/blob/shield
name = "strong blob"
- icon = 'icons/mob/nonhuman-player/blob.dmi'
+ icon = 'icons/mob/nonhuman-player/blob_tall.dmi'
icon_state = "blob_shield"
desc = "A solid wall of slightly twitching tendrils."
var/damaged_desc = "A wall of twitching tendrils."
diff --git a/code/modules/antagonists/brother/brother.dm b/code/modules/antagonists/brother/brother.dm
index 5f4622bd910a4..4f535ece8cef0 100644
--- a/code/modules/antagonists/brother/brother.dm
+++ b/code/modules/antagonists/brother/brother.dm
@@ -94,7 +94,7 @@
flashed.balloon_alert(source, "[flashed.p_theyre()] loyal to someone else!")
return
- if (HAS_TRAIT(flashed, TRAIT_MINDSHIELD))
+ if (HAS_TRAIT(flashed, TRAIT_UNCONVERTABLE))
flashed.balloon_alert(source, "[flashed.p_they()] resist!")
return
@@ -221,6 +221,9 @@
return
. = ..()
member.remove_antag_datum(/datum/antagonist/brother)
+ if (!length(members))
+ qdel(src)
+ return
if (isnull(member.current))
return
for (var/datum/mind/brother_mind as anything in members)
diff --git a/code/modules/antagonists/changeling/cellular_emporium.dm b/code/modules/antagonists/changeling/cellular_emporium.dm
index 68e83ea25e668..754d2343d5cd5 100644
--- a/code/modules/antagonists/changeling/cellular_emporium.dm
+++ b/code/modules/antagonists/changeling/cellular_emporium.dm
@@ -69,7 +69,7 @@
return data
-/datum/cellular_emporium/ui_act(action, params)
+/datum/cellular_emporium/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/changeling/headslug_eggs.dm b/code/modules/antagonists/changeling/headslug_eggs.dm
index 8f861aec2ec80..75c0881c55167 100644
--- a/code/modules/antagonists/changeling/headslug_eggs.dm
+++ b/code/modules/antagonists/changeling/headslug_eggs.dm
@@ -11,11 +11,11 @@
/// When this egg last got removed from a body. If -1, the egg hasn't been removed from a body.
var/removal_time = -1
-/obj/item/organ/internal/body_egg/changeling_egg/Insert(mob/living/carbon/egg_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED)
+/obj/item/organ/internal/body_egg/changeling_egg/mob_insert(mob/living/carbon/egg_owner, special = FALSE, movement_flags = DELETE_IF_REPLACED)
. = ..()
hatch_time = world.time + (removal_time == -1 ? EGG_INCUBATION_TIME : (hatch_time - removal_time))
-/obj/item/organ/internal/body_egg/changeling_egg/Remove(mob/living/carbon/egg_owner, special, movement_flags)
+/obj/item/organ/internal/body_egg/changeling_egg/mob_remove(mob/living/carbon/egg_owner, special, movement_flags)
. = ..()
removal_time = world.time
diff --git a/code/modules/antagonists/changeling/powers/adrenaline.dm b/code/modules/antagonists/changeling/powers/adrenaline.dm
index 3b6a550b18b0f..72bf91f1919f2 100644
--- a/code/modules/antagonists/changeling/powers/adrenaline.dm
+++ b/code/modules/antagonists/changeling/powers/adrenaline.dm
@@ -14,7 +14,7 @@
if(!.)
return FALSE
- if(HAS_TRAIT_FROM(user, TRAIT_IGNOREDAMAGESLOWDOWN, CHANGELING_TRAIT))
+ if(HAS_TRAIT_FROM(user, TRAIT_PARALYSIS_L_ARM, CHANGELING_TRAIT) || HAS_TRAIT_FROM(user, TRAIT_PARALYSIS_R_ARM, CHANGELING_TRAIT))
user.balloon_alert(user, "already boosted!")
return FALSE
@@ -40,7 +40,8 @@
var/our_leg_zones = (GLOB.all_body_zones - GLOB.leg_zones)
user.regenerate_limbs(excluded_zones = our_leg_zones) // why is this exclusive rather than inclusive
- user.add_traits(list(TRAIT_IGNOREDAMAGESLOWDOWN, TRAIT_PARALYSIS_L_ARM, TRAIT_PARALYSIS_R_ARM), CHANGELING_TRAIT)
+ user.add_traits(list(TRAIT_PARALYSIS_L_ARM, TRAIT_PARALYSIS_R_ARM), CHANGELING_TRAIT)
+ user.add_movespeed_mod_immunities(type, /datum/movespeed_modifier/damage_slowdown)
// Revert above mob changes.
addtimer(CALLBACK(src, PROC_REF(unsting_action), user), 20 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE)
@@ -57,4 +58,5 @@
/datum/action/changeling/adrenaline/proc/unsting_action(mob/living/user)
to_chat(user, span_changeling("The muscles in our limbs shift back to their usual places."))
- user.remove_traits(list(TRAIT_IGNOREDAMAGESLOWDOWN, TRAIT_PARALYSIS_L_ARM, TRAIT_PARALYSIS_R_ARM), CHANGELING_TRAIT)
+ user.remove_traits(list(TRAIT_PARALYSIS_L_ARM, TRAIT_PARALYSIS_R_ARM), CHANGELING_TRAIT)
+ user.remove_movespeed_mod_immunities(type, /datum/movespeed_modifier/damage_slowdown)
diff --git a/code/modules/antagonists/clown_ops/clown_weapons.dm b/code/modules/antagonists/clown_ops/clown_weapons.dm
index 130b6c9af5f1b..bd2dfd4b0da01 100644
--- a/code/modules/antagonists/clown_ops/clown_weapons.dm
+++ b/code/modules/antagonists/clown_ops/clown_weapons.dm
@@ -17,6 +17,7 @@
desc = "advanced clown shoes that protect the wearer and render them nearly immune to slipping on their own peels. They also squeak at 100% capacity."
clothing_traits = list(TRAIT_NO_SLIP_WATER)
slowdown = SHOES_SLOWDOWN
+ body_parts_covered = FEET|LEGS
armor_type = /datum/armor/clown_shoes_combat
strip_delay = 70
resistance_flags = NONE
@@ -49,6 +50,7 @@
strip_delay = 70
resistance_flags = NONE
always_noslip = TRUE
+ body_parts_covered = FEET|LEGS
/datum/armor/banana_shoes_combat
melee = 25
@@ -192,7 +194,7 @@
//BOMBANANA
/obj/item/seeds/banana/bombanana
- name = "pack of bombanana seeds"
+ name = "bombanana seed pack"
desc = "They're seeds that grow into bombanana trees. When grown, give to the clown."
plantname = "Bombanana Tree"
product = /obj/item/food/grown/banana/bombanana
diff --git a/code/modules/antagonists/clown_ops/clownop.dm b/code/modules/antagonists/clown_ops/clownop.dm
index 85cf2c0f631e7..07c1cc84ad756 100644
--- a/code/modules/antagonists/clown_ops/clownop.dm
+++ b/code/modules/antagonists/clown_ops/clownop.dm
@@ -65,6 +65,11 @@
if(liver)
ADD_TRAIT(liver, TRAIT_COMEDY_METABOLISM, CLOWNOP_TRAIT)
+// Clown op reinforcements
+/datum/antagonist/nukeop/reinforcement/clownop
+ name = "Clown Operative Reinforcement"
+ nukeop_outfit = /datum/outfit/syndicate/clownop/no_crystals
+
/datum/outfit/clown_operative
name = "Clown Operative (Preview only)"
diff --git a/code/modules/antagonists/clown_ops/outfits.dm b/code/modules/antagonists/clown_ops/outfits.dm
index 7dc84b56d856f..fb025e40dbd20 100644
--- a/code/modules/antagonists/clown_ops/outfits.dm
+++ b/code/modules/antagonists/clown_ops/outfits.dm
@@ -24,6 +24,7 @@
id_trim = /datum/id_trim/chameleon/operative/clown
/datum/outfit/syndicate/clownop/no_crystals
+ name = "Clown Operative - Reinforcement"
tc = 0
/datum/outfit/syndicate/clownop/leader
diff --git a/code/modules/antagonists/cult/blood_magic.dm b/code/modules/antagonists/cult/blood_magic.dm
index eb55138a4483a..2abe51fa02d49 100644
--- a/code/modules/antagonists/cult/blood_magic.dm
+++ b/code/modules/antagonists/cult/blood_magic.dm
@@ -12,7 +12,7 @@
default_button_position = DEFAULT_BLOODSPELLS
var/list/spells = list()
var/channeling = FALSE
- /// If the magic has been enhanced somehow, likely due to a crimson focus.
+ /// If the magic has been enhanced somehow, likely due to a crimson medallion.
var/magic_enhanced = FALSE
/datum/action/innate/cult/blood_magic/Remove()
@@ -162,14 +162,14 @@
name = "Stun"
desc = "Empowers your hand to stun and mute a victim on contact. Gets weaker depending on how many have joined the Cult."
button_icon_state = "hand"
- magic_path = "/obj/item/melee/blood_magic/stun"
+ magic_path = /obj/item/melee/blood_magic/stun
health_cost = 10
/datum/action/innate/cult/blood_spell/teleport
name = "Teleport"
desc = "Empowers your hand to teleport yourself or another cultist to a teleport rune on contact."
button_icon_state = "tele"
- magic_path = "/obj/item/melee/blood_magic/teleport"
+ magic_path = /obj/item/melee/blood_magic/teleport
health_cost = 7
/datum/action/innate/cult/blood_spell/emp
@@ -194,20 +194,20 @@
desc = "Empowers your hand to start handcuffing victim on contact, and mute them if successful."
button_icon_state = "cuff"
charges = 4
- magic_path = "/obj/item/melee/blood_magic/shackles"
+ magic_path = /obj/item/melee/blood_magic/shackles
/datum/action/innate/cult/blood_spell/construction
name = "Twisted Construction"
desc = "Empowers your hand to corrupt certain metalic objects. Converts: Plasteel into runed metal 50 metal into a construct shell Living cyborgs into constructs after a delay Cyborg shells into construct shells Purified soulstones (and any shades inside) into cultist soulstones Airlocks into brittle runed airlocks after a delay (harm intent)"
button_icon_state = "transmute"
- magic_path = "/obj/item/melee/blood_magic/construction"
+ magic_path = /obj/item/melee/blood_magic/construction
health_cost = 12
/datum/action/innate/cult/blood_spell/equipment
name = "Summon Combat Equipment"
desc = "Empowers your hand to summon combat gear onto a cultist you touch, including cult armor, a cult bola, and a cult sword. Not recommended for use before the blood cult's presence has been revealed."
button_icon_state = "equip"
- magic_path = "/obj/item/melee/blood_magic/armor"
+ magic_path = /obj/item/melee/blood_magic/armor
/datum/action/innate/cult/blood_spell/dagger
name = "Summon Ritual Dagger"
@@ -339,7 +339,7 @@
invocation = "Fel'th Dol Ab'orod!"
button_icon_state = "manip"
charges = 5
- magic_path = "/obj/item/melee/blood_magic/manipulator"
+ magic_path = /obj/item/melee/blood_magic/manipulator
deletes_on_empty = FALSE
// The "magic hand" items
@@ -433,13 +433,15 @@
/obj/item/melee/blood_magic/stun/cast_spell(mob/living/target, mob/living/carbon/user)
if(!istype(target) || IS_CULTIST(target))
return
- var/datum/antagonist/cult/cultist = IS_CULTIST(user)
- var/datum/team/cult/cult_team = cultist.get_team()
+ var/datum/antagonist/cult/cultist = GET_CULTIST(user)
+ var/datum/team/cult/cult_team = cultist?.get_team()
var/effect_coef = 1
- if(cult_team.cult_ascendent)
+ if(cult_team?.cult_ascendent)
effect_coef = 0.1
- else if(cult_team.cult_risen)
+ else if(cult_team?.cult_risen)
effect_coef = 0.4
+ if(IS_CULTIST(user) && isnull(GET_CULTIST(user)))
+ effect_coef = 0.2
user.visible_message(
span_warning("[user] holds up [user.p_their()] hand, which explodes in a flash of red light!"),
span_cult_italic("You attempt to stun [target] with the spell!"),
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index 307b4b7324bd8..5226039a86b69 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -19,6 +19,7 @@
inhand_x_dimension = 32
inhand_y_dimension = 32
w_class = WEIGHT_CLASS_SMALL
+ slot_flags = ITEM_SLOT_BELT
force = 15
throwforce = 25
block_chance = 25
@@ -211,12 +212,13 @@ Striking a noncultist, however, will tear their flesh."}
awakener = awakener,\
allow_renaming = FALSE,\
allow_channeling = FALSE,\
+ allow_exorcism = FALSE,\
)
// Get the heretic's new body and antag datum.
trapped_entity = trapped_mind?.current
trapped_entity.key = trapped_mind?.key
- var/datum/antagonist/heretic/heretic_holder = IS_HERETIC(trapped_entity)
+ var/datum/antagonist/heretic/heretic_holder = GET_HERETIC(trapped_entity)
if(!heretic_holder)
stack_trace("[soul_to_bind] in but not a heretic on the heretic soul blade.")
@@ -377,7 +379,7 @@ Striking a noncultist, however, will tear their flesh."}
worn_icon = 'icons/mob/clothing/suits/armor.dmi'
inhand_icon_state = "cultrobes"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
- allowed = list(/obj/item/tome, /obj/item/melee/cultblade)
+ allowed = list(/obj/item/tome, /obj/item/melee/cultblade, /obj/item/melee/sickly_blade/cursed)
armor_type = /datum/armor/hooded_cultrobes
flags_inv = HIDEJUMPSUIT
cold_protection = CHEST|GROIN|LEGS|ARMS
@@ -594,7 +596,6 @@ Striking a noncultist, however, will tear their flesh."}
/obj/item/clothing/suit/hooded/cultrobes/berserker
name = "flagellant's robes"
desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage. Provides an even greater speed boost if its hood is worn."
- allowed = list(/obj/item/tome, /obj/item/melee/cultblade)
armor_type = /datum/armor/cultrobes_berserker
slowdown = -0.3 //the hood gives an additional -0.3 if you have it flipped up, for a total of -0.6
hoodtype = /obj/item/clothing/head/hooded/cult_hoodie/berserkerhood
@@ -1277,7 +1278,6 @@ Striking a noncultist, however, will tear their flesh."}
return FALSE
/obj/item/shield/mirror/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
- var/turf/impact_turf = get_turf(hit_atom)
if(isliving(hit_atom))
var/mob/living/target = hit_atom
@@ -1290,14 +1290,9 @@ Striking a noncultist, however, will tear their flesh."}
return
if(!..())
target.Paralyze(30)
- var/mob/thrower = throwingdatum?.get_thrower()
- if(thrower)
- for(var/mob/living/Next in orange(2, impact_turf))
- if(!Next.density || IS_CULTIST(Next))
- continue
- throw_at(Next, 3, 1, thrower)
- return
- throw_at(thrower, 7, 1, null)
+ new /obj/effect/temp_visual/cult/sparks(target)
+ playsound(target, 'sound/effects/glassbr3.ogg', 100)
+ qdel(src)
else
..()
diff --git a/code/modules/antagonists/cult/cult_other.dm b/code/modules/antagonists/cult/cult_other.dm
index 9435baedba11a..f9e1462a30efe 100644
--- a/code/modules/antagonists/cult/cult_other.dm
+++ b/code/modules/antagonists/cult/cult_other.dm
@@ -25,8 +25,6 @@
return FALSE
#endif
- if(target.mind.unconvertable)
- return FALSE
if(ishuman(target) && target.mind.holy_role)
return FALSE
if(specific_cult?.is_sacrifice_target(target.mind))
@@ -36,6 +34,6 @@
return FALSE
if(IS_HERETIC_OR_MONSTER(target))
return FALSE
- if(HAS_TRAIT(target, TRAIT_MINDSHIELD) || issilicon(target) || isbot(target) || isdrone(target))
+ if(HAS_MIND_TRAIT(target, TRAIT_UNCONVERTABLE) || issilicon(target) || isbot(target) || isdrone(target))
return FALSE //can't convert machines, shielded, or braindead
return TRUE
diff --git a/code/modules/antagonists/cult/cult_structure_archives.dm b/code/modules/antagonists/cult/cult_structure_archives.dm
index d3a96dd1f77aa..9917d9505f7be 100644
--- a/code/modules/antagonists/cult/cult_structure_archives.dm
+++ b/code/modules/antagonists/cult/cult_structure_archives.dm
@@ -2,7 +2,7 @@
#define CULT_BLINDFOLD "Zealot's Blindfold"
#define CURSE_ORB "Shuttle Curse"
#define VEIL_WALKER "Veil Walker Set"
-#define CRIMSON_FOCUS "Crimson Focus"
+#define CRIMSON_MEDALLION "Crimson Medallion"
// Cult archives. Gives out utility items.
/obj/structure/destructible/cult/item_dispenser/archives
@@ -38,11 +38,11 @@
options += extra_item
/obj/structure/destructible/cult/item_dispenser/archives/extra_options()
- if(!cult_team?.unlocked_heretic_items[CRIMSON_FOCUS_UNLOCKED])
+ if(!cult_team?.unlocked_heretic_items[CRIMSON_MEDALLION_UNLOCKED])
return
- return list(CRIMSON_FOCUS = list(
- PREVIEW_IMAGE = image(icon = 'icons/obj/clothing/neck.dmi', icon_state = "crimson_focus"),
- OUTPUT_ITEMS = list(/obj/item/clothing/neck/heretic_focus/crimson_focus),
+ return list(CRIMSON_MEDALLION = list(
+ PREVIEW_IMAGE = image(icon = 'icons/obj/clothing/neck.dmi', icon_state = "crimson_medallion"),
+ OUTPUT_ITEMS = list(/obj/item/clothing/neck/heretic_focus/crimson_medallion),
),
)
@@ -56,4 +56,4 @@
#undef CULT_BLINDFOLD
#undef CURSE_ORB
#undef VEIL_WALKER
-#undef CRIMSON_FOCUS
+#undef CRIMSON_MEDALLION
diff --git a/code/modules/antagonists/cult/datums/cult_team.dm b/code/modules/antagonists/cult/datums/cult_team.dm
index 09d4a25a321c4..4d77f65f588df 100644
--- a/code/modules/antagonists/cult/datums/cult_team.dm
+++ b/code/modules/antagonists/cult/datums/cult_team.dm
@@ -22,7 +22,7 @@
/// List that keeps track of which items have been unlocked after a heretic was sacked.
var/list/unlocked_heretic_items = list(
CURSED_BLADE_UNLOCKED = FALSE,
- CRIMSON_FOCUS_UNLOCKED = FALSE,
+ CRIMSON_MEDALLION_UNLOCKED = FALSE,
PROTEON_ORB_UNLOCKED = FALSE,
)
@@ -65,7 +65,7 @@
for(var/datum/mind/mind as anything in members)
if(mind.current)
SEND_SOUND(mind.current, 'sound/ambience/antag/bloodcult/bloodcult_halos.ogg')
- to_chat(mind.current, span_cult_large(span_warning("Your cult is ascendent and the red harvest approaches - you cannot hide your true nature for much longer!!")))
+ to_chat(mind.current, span_cult_large(span_warning("Your cult is ascendant and the red harvest approaches - you cannot hide your true nature for much longer!!")))
mind.current.AddElement(/datum/element/cult_halo)
cult_ascendent = TRUE
log_game("The blood cult has ascended with [cultplayers] players.")
@@ -134,6 +134,7 @@
return "
[parts.Join(" ")]
"
/datum/team/cult/proc/is_sacrifice_target(datum/mind/mind)
+
for(var/datum/objective/sacrifice/sac_objective in objectives)
if(mind == sac_objective.target)
return TRUE
diff --git a/code/modules/antagonists/cult/datums/cultist.dm b/code/modules/antagonists/cult/datums/cultist.dm
index f56d79a8f4b18..de415f59c7482 100644
--- a/code/modules/antagonists/cult/datums/cultist.dm
+++ b/code/modules/antagonists/cult/datums/cultist.dm
@@ -281,3 +281,4 @@
///Used to check if the owner is counted as a secondary invoker for runes.
/datum/antagonist/cult/proc/check_invoke_validity()
return TRUE
+
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index d8ce241caf3c8..a365060b509e4 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -200,16 +200,23 @@ structure_check() searches for nearby cultist structures required for the invoca
invocation = "Ra'sha yoka!"
invoke_damage = 30
can_be_scribed = FALSE
+ var/randomized = TRUE
/obj/effect/rune/malformed/Initialize(mapload, set_keyword)
. = ..()
+ if(!randomized)
+ return
icon_state = "[rand(1,7)]"
color = rgb(rand(0,255), rand(0,255), rand(0,255))
+
/obj/effect/rune/malformed/invoke(list/invokers)
..()
qdel(src)
+/obj/effect/rune/malformed/norandom
+ randomized = FALSE
+
//Rite of Offering: Converts or sacrifices a target.
/obj/effect/rune/convert
cultist_name = "Offer"
diff --git a/code/modules/antagonists/cult/sword_fling.dm b/code/modules/antagonists/cult/sword_fling.dm
index 766f97917de60..83238b0d8a2f4 100644
--- a/code/modules/antagonists/cult/sword_fling.dm
+++ b/code/modules/antagonists/cult/sword_fling.dm
@@ -55,6 +55,8 @@
particle_to_spawn = /obj/effect/particle_effect/sparks/electricity
new particle_to_spawn(get_turf(loccer))
+ loccer.shake_up_animation()
+ playsound(loccer, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
if(prob(resist_chance))
flinged_sword.forceMove(get_turf(loccer))
@@ -82,6 +84,9 @@
flinged_sword.throw_at(cast_on, cast_range, flinged_sword.throw_speed, owner)
flinged_sword.visible_message(\
span_warning("\the [flinged_sword] lunges at \the [cast_on]!"))
+ playsound(flinged_sword, 'sound/items/haunted/ghostitemattack.ogg', 100, TRUE)
+ flinged_sword.add_filter("cool_glow", 2, list("type" = "outline", "color" = COLOR_HERETIC_GREEN, "size" = 0.7))
+ addtimer(CALLBACK(flinged_sword, TYPE_PROC_REF(/datum, remove_filter), "cool_glow"), 0.7 SECONDS)
/obj/effect/temp_visual/eldritch_sparks
icon_state = "purplesparkles"
diff --git a/code/modules/antagonists/ert/ert.dm b/code/modules/antagonists/ert/ert.dm
index 5d266227a1053..f8257b1cbbe39 100644
--- a/code/modules/antagonists/ert/ert.dm
+++ b/code/modules/antagonists/ert/ert.dm
@@ -238,6 +238,10 @@
H.open_internals(H.get_item_for_held_index(2))
H.equipOutfit(outfit)
+ if(isplasmaman(H))
+ var/obj/item/mod/control/our_modsuit = locate() in H.get_equipped_items()
+ if(our_modsuit)
+ our_modsuit.install(new /obj/item/mod/module/plasma_stabilizer)
/datum/antagonist/ert/greet()
if(!ert_team)
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index f31639c508c73..f9b95ea0dab4d 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -88,7 +88,7 @@
/// List that keeps track of which items have been gifted to the heretic after a cultist was sacrificed. Used to alter drop chances to reduce dupes.
var/list/unlocked_heretic_items = list(
/obj/item/melee/sickly_blade/cursed = 0,
- /obj/item/clothing/neck/heretic_focus/crimson_focus = 0,
+ /obj/item/clothing/neck/heretic_focus/crimson_medallion = 0,
/mob/living/basic/construct/harvester/heretic = 0,
)
/// Simpler version of above used to limit amount of loot that can be hoarded
@@ -218,7 +218,7 @@
return data
-/datum/antagonist/heretic/ui_act(action, params)
+/datum/antagonist/heretic/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/heretic/heretic_knowledge.dm b/code/modules/antagonists/heretic/heretic_knowledge.dm
index 5b78d1b7efb6c..06ea0d6c0bd09 100644
--- a/code/modules/antagonists/heretic/heretic_knowledge.dm
+++ b/code/modules/antagonists/heretic/heretic_knowledge.dm
@@ -228,7 +228,7 @@
/**
* A knowledge subtype for knowledge that can only
- * have a limited amount of it's resulting atoms
+ * have a limited amount of its resulting atoms
* created at once.
*/
/datum/heretic_knowledge/limited_amount
@@ -540,7 +540,7 @@
var/mob/living/mob_to_summon
/datum/heretic_knowledge/summon/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
- summon_ritual_mob(user, loc, mob_to_summon)
+ return summon_ritual_mob(user, loc, mob_to_summon)
/**
* Creates the ritual mob and grabs a ghost for it
@@ -584,7 +584,6 @@
var/datum/antagonist/heretic_monster/heretic_monster = summoned.mind.add_antag_datum(/datum/antagonist/heretic_monster)
heretic_monster.set_owner(user.mind)
- summoned.RegisterSignal(user, COMSIG_LIVING_DEATH, TYPE_PROC_REF(/mob/living/, on_master_death))
var/datum/objective/heretic_summon/summon_objective = locate() in user.mind.get_all_objectives()
summon_objective?.num_summoned++
@@ -641,7 +640,6 @@
/obj/item/restraints/handcuffs/cable/zipties,
/obj/item/circular_saw,
/obj/item/scalpel,
- /obj/item/binoculars,
/obj/item/clothing/gloves/color/yellow,
/obj/item/melee/baton/security,
/obj/item/clothing/glasses/sunglasses,
@@ -678,7 +676,7 @@
return !was_completed
/datum/heretic_knowledge/knowledge_ritual/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
- var/datum/antagonist/heretic/our_heretic = IS_HERETIC(user)
+ var/datum/antagonist/heretic/our_heretic = GET_HERETIC(user)
our_heretic.knowledge_points += KNOWLEDGE_RITUAL_POINTS
was_completed = TRUE
@@ -725,7 +723,7 @@
return TRUE
/datum/heretic_knowledge/ultimate/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
if(!can_be_invoked(heretic_datum))
return FALSE
@@ -746,7 +744,7 @@
return (sacrifice.stat == DEAD) && !ismonkey(sacrifice)
/datum/heretic_knowledge/ultimate/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
heretic_datum.ascended = TRUE
// Show the cool red gradiant in our UI
diff --git a/code/modules/antagonists/heretic/heretic_living_heart.dm b/code/modules/antagonists/heretic/heretic_living_heart.dm
index b616d300097e9..3e8f39fef4f94 100644
--- a/code/modules/antagonists/heretic/heretic_living_heart.dm
+++ b/code/modules/antagonists/heretic/heretic_living_heart.dm
@@ -104,7 +104,7 @@
return ..()
/datum/action/cooldown/track_target/Activate(atom/target)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(owner)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(owner)
var/datum/heretic_knowledge/sac_knowledge = heretic_datum.get_knowledge(/datum/heretic_knowledge/hunt_and_sacrifice)
if(!LAZYLEN(heretic_datum.sac_targets))
owner.balloon_alert(owner, "no targets, visit a rune!")
diff --git a/code/modules/antagonists/heretic/heretic_monsters.dm b/code/modules/antagonists/heretic/heretic_monsters.dm
index fcdea51287980..5bc7041cd461d 100644
--- a/code/modules/antagonists/heretic/heretic_monsters.dm
+++ b/code/modules/antagonists/heretic/heretic_monsters.dm
@@ -28,6 +28,7 @@
*/
/datum/antagonist/heretic_monster/proc/set_owner(datum/mind/master)
src.master = master
+ owner.enslave_mind_to_creator(master.current)
var/datum/objective/master_obj = new()
master_obj.owner = owner
@@ -38,7 +39,3 @@
owner.announce_objectives()
to_chat(owner, span_boldnotice("You are a [ishuman(owner.current) ? "shambling corpse returned":"horrible creation brought"] to this plane through the Gates of the Mansus."))
to_chat(owner, span_notice("Your master is [master]. Assist them to all ends."))
-
- if(istype(owner.current, /mob/living/basic/construct/harvester/heretic))
- var/mob/living/basic/construct/harvester/heretic/shitcode = owner.current
- shitcode.master = master
diff --git a/code/modules/antagonists/heretic/influences.dm b/code/modules/antagonists/heretic/influences.dm
index 662af81249a5c..4fff7667f1ecc 100644
--- a/code/modules/antagonists/heretic/influences.dm
+++ b/code/modules/antagonists/heretic/influences.dm
@@ -248,7 +248,7 @@
return SECONDARY_ATTACK_CALL_NORMAL
if(being_drained)
- balloon_alert(user, "already being drained!")
+ loc.balloon_alert(user, "already being drained!")
else
INVOKE_ASYNC(src, PROC_REF(drain_influence), user, 1)
@@ -280,17 +280,17 @@
/obj/effect/heretic_influence/proc/drain_influence(mob/living/user, knowledge_to_gain)
being_drained = TRUE
- balloon_alert(user, "draining influence...")
+ loc.balloon_alert(user, "draining influence...")
if(!do_after(user, 10 SECONDS, src, hidden = TRUE))
being_drained = FALSE
- balloon_alert(user, "interrupted!")
+ loc.balloon_alert(user, "interrupted!")
return
// We don't need to set being_drained back since we delete after anyways
- balloon_alert(user, "influence drained")
+ loc.balloon_alert(user, "influence drained")
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
heretic_datum.knowledge_points += knowledge_to_gain
// Aaand now we delete it
diff --git a/code/modules/antagonists/heretic/items/eldritch_painting.dm b/code/modules/antagonists/heretic/items/eldritch_painting.dm
index 3e9d3675b1351..5155737a12f7f 100644
--- a/code/modules/antagonists/heretic/items/eldritch_painting.dm
+++ b/code/modules/antagonists/heretic/items/eldritch_painting.dm
@@ -7,7 +7,6 @@
flags_1 = NONE
icon_state = "eldritch_painting_debug"
result_path = /obj/structure/sign/painting/eldritch
- pixel_shift = 30
/obj/structure/sign/painting/eldritch
name = "The Blank Canvas: A Study in Default Subtypes"
diff --git a/code/modules/antagonists/heretic/items/forbidden_book.dm b/code/modules/antagonists/heretic/items/forbidden_book.dm
index 38f42b58c5e82..2591a1fd752a9 100644
--- a/code/modules/antagonists/heretic/items/forbidden_book.dm
+++ b/code/modules/antagonists/heretic/items/forbidden_book.dm
@@ -47,7 +47,7 @@
update_weight_class(WEIGHT_CLASS_NORMAL)
/obj/item/codex_cicatrix/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
if(!heretic_datum)
return NONE
if(isopenturf(interacting_with))
diff --git a/code/modules/antagonists/heretic/items/heretic_blades.dm b/code/modules/antagonists/heretic/items/heretic_blades.dm
index ddfec8db20cf7..4d2636fa19e8c 100644
--- a/code/modules/antagonists/heretic/items/heretic_blades.dm
+++ b/code/modules/antagonists/heretic/items/heretic_blades.dm
@@ -10,6 +10,7 @@
inhand_x_dimension = 64
inhand_y_dimension = 64
obj_flags = CONDUCTS_ELECTRICITY
+ slot_flags = ITEM_SLOT_BELT
sharpness = SHARP_EDGED
w_class = WEIGHT_CLASS_NORMAL
force = 20
@@ -229,7 +230,7 @@
/obj/item/melee/sickly_blade/cursed/interact_with_atom(atom/target, mob/living/user, list/modifiers)
. = ..()
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
if(!heretic_datum)
return NONE
diff --git a/code/modules/antagonists/heretic/items/heretic_necks.dm b/code/modules/antagonists/heretic/items/heretic_necks.dm
index 3f140dc99df1d..5c73c22a4e793 100644
--- a/code/modules/antagonists/heretic/items/heretic_necks.dm
+++ b/code/modules/antagonists/heretic/items/heretic_necks.dm
@@ -9,16 +9,16 @@
. = ..()
AddElement(/datum/element/heretic_focus)
-/obj/item/clothing/neck/heretic_focus/crimson_focus
- name = "Crimson Focus"
+/obj/item/clothing/neck/heretic_focus/crimson_medallion
+ name = "Crimson Medallion"
desc = "A blood-red focusing glass that provides a link to the world beyond, and worse. Its eye is constantly twitching and gazing in all directions. It almost seems to be silently screaming..."
- icon_state = "crimson_focus"
+ icon_state = "crimson_medallion"
/// The aura healing component. Used to delete it when taken off.
var/datum/component/component
/// If active or not, used to add and remove its cult and heretic buffs.
var/active = FALSE
-/obj/item/clothing/neck/heretic_focus/crimson_focus/equipped(mob/living/user, slot)
+/obj/item/clothing/neck/heretic_focus/crimson_medallion/equipped(mob/living/user, slot)
. = ..()
if(!(slot & ITEM_SLOT_NECK))
return
@@ -51,7 +51,7 @@
healing_color = team_color, \
)
-/obj/item/clothing/neck/heretic_focus/crimson_focus/dropped(mob/living/user)
+/obj/item/clothing/neck/heretic_focus/crimson_medallion/dropped(mob/living/user)
. = ..()
if(!istype(user))
@@ -75,7 +75,7 @@
magic_holder?.magic_enhanced = FALSE
-/obj/item/clothing/neck/heretic_focus/crimson_focus/attack_self(mob/living/user, modifiers)
+/obj/item/clothing/neck/heretic_focus/crimson_medallion/attack_self(mob/living/user, modifiers)
. = ..()
to_chat(user, span_danger("You start tightly squeezing [src]..."))
if(!do_after(user, 1.25 SECONDS, src))
@@ -90,7 +90,7 @@
user.reagents?.add_reagent(/datum/reagent/eldritch, rand(6, 10))
qdel(src)
-/obj/item/clothing/neck/heretic_focus/crimson_focus/examine(mob/user)
+/obj/item/clothing/neck/heretic_focus/crimson_medallion/examine(mob/user)
. = ..()
var/magic_dude
diff --git a/code/modules/antagonists/heretic/knowledge/ash_lore.dm b/code/modules/antagonists/heretic/knowledge/ash_lore.dm
index b74569f1a1447..0f532197b6e66 100644
--- a/code/modules/antagonists/heretic/knowledge/ash_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/ash_lore.dm
@@ -1,7 +1,5 @@
/**
* # The path of Ash.
- * Spell names are in this language: OLD NORDIC
- * Both are related: Nordic Mythology-Yggdrassil-Ash Tree Genus-Ash
*
* Goes as follows:
*
@@ -53,7 +51,7 @@
cost = 1
route = PATH_ASH
depth = 3
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "grasp_ash"
/datum/heretic_knowledge/ashen_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
@@ -163,7 +161,7 @@
His city, the people he swore to watch... and watch he did, as they all burnt to cinders."
next_knowledge = list(/datum/heretic_knowledge/spell/flame_birth)
route = PATH_ASH
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "blade_upgrade_ash"
/datum/heretic_knowledge/blade_upgrade/ash/do_melee_effects(mob/living/source, mob/living/target, obj/item/melee/sickly_blade/blade)
@@ -233,7 +231,7 @@
text = "[generate_heretic_text()] Fear the blaze, for the Ashlord, [user.real_name] has ascended! The flames shall consume all! [generate_heretic_text()]",
title = "[generate_heretic_text()]",
sound = 'sound/ambience/antag/heretic/ascend_ash.ogg',
- color_override = "white",
+ color_override = "pink",
)
var/datum/action/cooldown/spell/fire_sworn/circle_spell = new(user.mind)
diff --git a/code/modules/antagonists/heretic/knowledge/blade_lore.dm b/code/modules/antagonists/heretic/knowledge/blade_lore.dm
index 357e789416d1d..96b75f087185a 100644
--- a/code/modules/antagonists/heretic/knowledge/blade_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/blade_lore.dm
@@ -1,7 +1,5 @@
/**
* # The path of Blades. Stab stab.
- * Spell names are in this language: ARAMAIC
- * Both are related: Aramaic-Damascus-Blade
*
* Goes as follows:
*
@@ -57,7 +55,7 @@
cost = 1
route = PATH_BLADE
depth = 3
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "grasp_blade"
/datum/heretic_knowledge/blade_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
@@ -325,7 +323,7 @@
a flurry of blades, neither hitting their mark, for the Champion was indomitable."
next_knowledge = list(/datum/heretic_knowledge/spell/furious_steel)
route = PATH_BLADE
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "blade_upgrade_blade"
/// How much force do we apply to the offhand?
var/offand_force_decrement = 0
diff --git a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm
index 8a94aada74a67..f77f6424f1258 100644
--- a/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/cosmic_lore.dm
@@ -1,7 +1,5 @@
/**
* # The path of Cosmos.
- * Spell names are in this language: SUMERIAN
- * Both are related: Sumerian-Original-Primordial-Cosmic
*
* Goes as follows:
*
@@ -53,7 +51,7 @@
cost = 1
route = PATH_COSMIC
depth = 3
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "grasp_cosmos"
/datum/heretic_knowledge/cosmic_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
@@ -145,7 +143,7 @@
The blades now glistened with fragmented power. I fell to the ground and wept at the beast's feet."
next_knowledge = list(/datum/heretic_knowledge/spell/cosmic_expansion)
route = PATH_COSMIC
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "blade_upgrade_cosmos"
/// Storage for the second target.
var/datum/weakref/second_target
@@ -285,7 +283,7 @@
text = "[generate_heretic_text()] A Star Gazer has arrived into the station, [user.real_name] has ascended! This station is the domain of the Cosmos! [generate_heretic_text()]",
title = "[generate_heretic_text()]",
sound = 'sound/ambience/antag/heretic/ascend_cosmic.ogg',
- color_override = "purple",
+ color_override = "pink",
)
var/mob/living/basic/heretic_summon/star_gazer/star_gazer_mob = new /mob/living/basic/heretic_summon/star_gazer(loc)
star_gazer_mob.maxHealth = INFINITY
diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
index e0b82651bc9d6..a71da1dce0b3d 100644
--- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
@@ -5,8 +5,6 @@
/**
* # The path of Flesh.
- * Spell names are in this language: LATIN
- * Both are related: Latin-Rome-Hedonism-Flesh
*
* Goes as follows:
*
@@ -69,7 +67,7 @@
cost = 1
route = PATH_FLESH
depth = 3
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "grasp_flesh"
/datum/heretic_knowledge/limited_amount/flesh_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
@@ -144,7 +142,7 @@
limit = 2
cost = 1
route = PATH_FLESH
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "ghoul_voiceless"
depth = 4
@@ -271,7 +269,7 @@
I finally began to understand. And then, blood rained from the heavens."
next_knowledge = list(/datum/heretic_knowledge/summon/stalker)
route = PATH_FLESH
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "blade_upgrade_flesh"
///What type of wound do we apply on hit
var/wound_type = /datum/wound/slash/flesh/severe
@@ -332,14 +330,14 @@
text = "[generate_heretic_text()] Ever coiling vortex. Reality unfolded. ARMS OUTREACHED, THE LORD OF THE NIGHT, [user.real_name] has ascended! Fear the ever twisting hand! [generate_heretic_text()]",
title = "[generate_heretic_text()]",
sound = 'sound/ambience/antag/heretic/ascend_flesh.ogg',
- color_override = "red",
+ color_override = "pink",
)
var/datum/action/cooldown/spell/shapeshift/shed_human_form/worm_spell = new(user.mind)
worm_spell.Grant(user)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
var/datum/heretic_knowledge/limited_amount/flesh_grasp/grasp_ghoul = heretic_datum.get_knowledge(/datum/heretic_knowledge/limited_amount/flesh_grasp)
grasp_ghoul.limit *= 3
var/datum/heretic_knowledge/limited_amount/flesh_ghoul/ritual_ghoul = heretic_datum.get_knowledge(/datum/heretic_knowledge/limited_amount/flesh_ghoul)
diff --git a/code/modules/antagonists/heretic/knowledge/general_side.dm b/code/modules/antagonists/heretic/knowledge/general_side.dm
index 27f0e11b4467b..10ff108c5f8fa 100644
--- a/code/modules/antagonists/heretic/knowledge/general_side.dm
+++ b/code/modules/antagonists/heretic/knowledge/general_side.dm
@@ -18,7 +18,7 @@
/datum/heretic_knowledge/reroll_targets/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
// Check first if they have a Living Heart. If it's missing, we should
// throw a fail to show the heretic that there's no point in rerolling
// if you don't have a heart to track the targets in the first place.
@@ -29,7 +29,7 @@
return TRUE
/datum/heretic_knowledge/reroll_targets/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
for(var/mob/living/carbon/human/target as anything in heretic_datum.sac_targets)
heretic_datum.remove_sacrifice_target(target)
diff --git a/code/modules/antagonists/heretic/knowledge/lock_lore.dm b/code/modules/antagonists/heretic/knowledge/lock_lore.dm
index b238d6dd3c7f3..0e55fb10703ee 100644
--- a/code/modules/antagonists/heretic/knowledge/lock_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/lock_lore.dm
@@ -1,7 +1,5 @@
/**
* # The path of Lock.
- * Spell names are in this language: EGYPTIAN
- * Both are related: Egyptian-Mysteries-Secrets-Lock
*
* Goes as follows:
*
@@ -54,7 +52,7 @@
cost = 1
route = PATH_LOCK
depth = 3
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "grasp_lock"
/datum/heretic_knowledge/lock_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
@@ -138,7 +136,7 @@
/datum/heretic_knowledge/limited_amount/concierge_rite // item that creates 3 max at a time heretic only barriers, probably should limit to 1 only, holy people can also pass
name = "Concierge's Rite"
- desc = "Allows you to transmute a white crayon, a wooden plank, and a multitool to create a Labyrinth Handbook. \
+ desc = "Allows you to transmute a stick of chalk, a wooden plank, and a multitool to create a Labyrinth Handbook. \
It can materialize a barricade at range that only you and people resistant to magic can pass. 3 uses."
gain_text = "The Concierge scribbled my name into the Handbook. \"Welcome to your new home, fellow Steward.\""
required_atoms = list(
@@ -178,7 +176,7 @@
next_knowledge = list(/datum/heretic_knowledge/spell/caretaker_refuge)
route = PATH_LOCK
wound_type = /datum/wound/slash/flesh/critical
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "blade_upgrade_lock"
var/chance = 35
@@ -243,14 +241,14 @@
text = "Delta-class dimensional anomaly detec[generate_heretic_text()] Reality rended, torn. Gates open, doors open, [user.real_name] has ascended! Fear the tide! [generate_heretic_text()]",
title = "[generate_heretic_text()]",
sound = 'sound/ambience/antag/heretic/ascend_knock.ogg',
- color_override = "yellow",
+ color_override = "pink",
)
// buffs
var/datum/action/cooldown/spell/shapeshift/eldritch/ascension/transform_spell = new(user.mind)
transform_spell.Grant(user)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
var/datum/heretic_knowledge/blade_upgrade/flesh/lock/blade_upgrade = heretic_datum.get_knowledge(/datum/heretic_knowledge/blade_upgrade/flesh/lock)
blade_upgrade.chance += 30
new /obj/structure/lock_tear(loc, user.mind)
diff --git a/code/modules/antagonists/heretic/knowledge/moon_lore.dm b/code/modules/antagonists/heretic/knowledge/moon_lore.dm
index 5ba55b64058cb..5cc26ee904551 100644
--- a/code/modules/antagonists/heretic/knowledge/moon_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/moon_lore.dm
@@ -1,7 +1,5 @@
/**
* # The path of Moon.
- * Spell names are in this language: ANCIENT HEBREW
- * Both are related: Ancient Hebrew-Moon Mysticism-Moon
*
* Goes as follows:
*
@@ -56,7 +54,7 @@
cost = 1
route = PATH_MOON
depth = 3
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "grasp_moon"
/datum/heretic_knowledge/moon_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
@@ -150,7 +148,7 @@
gain_text = "His wit was sharp as a blade, cutting through the lie to bring us joy."
next_knowledge = list(/datum/heretic_knowledge/spell/moon_ringleader)
route = PATH_MOON
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "blade_upgrade_moon"
/datum/heretic_knowledge/blade_upgrade/moon/do_melee_effects(mob/living/source, mob/living/target, obj/item/melee/sickly_blade/blade)
@@ -211,7 +209,7 @@
The truth shall finally devour the lie! [generate_heretic_text()]",
title = "[generate_heretic_text()]",
sound = 'sound/ambience/antag/heretic/ascend_moon.ogg',
- color_override = "blue",
+ color_override = "pink",
)
ADD_TRAIT(user, TRAIT_MADNESS_IMMUNE, REF(src))
@@ -236,7 +234,7 @@
to_chat(crewmate, span_boldwarning("[user]'s rise is influencing those who are weak willed. Their minds shall rend." ))
continue
// Mindshielded and anti-magic folks are immune against this effect because this is a magical mind effect
- if(HAS_TRAIT(crewmate, TRAIT_MINDSHIELD) || crewmate.can_block_magic(MAGIC_RESISTANCE))
+ if(HAS_MIND_TRAIT(crewmate, TRAIT_UNCONVERTABLE) || crewmate.can_block_magic(MAGIC_RESISTANCE))
to_chat(crewmate, span_boldwarning("You feel shielded from something." ))
continue
if(amount_of_lunatics > length(GLOB.human_list) * 0.2)
diff --git a/code/modules/antagonists/heretic/knowledge/rust_lore.dm b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
index 5e96119135f71..c1de33d9a58b5 100644
--- a/code/modules/antagonists/heretic/knowledge/rust_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
@@ -1,6 +1,5 @@
/**
* # The path of Rust.
- * Spell names are in this language: OLD SLAVIC
*
* Goes as follows:
*
@@ -56,7 +55,7 @@
cost = 1
route = PATH_RUST
depth = 3
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "grasp_rust"
/datum/heretic_knowledge/rust_fist/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
@@ -169,7 +168,7 @@
The heavy rust weights it down. You stare deeply into it. The Rusted Hills call for you, now."
next_knowledge = list(/datum/heretic_knowledge/spell/entropic_plume)
route = PATH_RUST
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "blade_upgrade_rust"
/datum/heretic_knowledge/blade_upgrade/rust/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
@@ -199,7 +198,7 @@
/datum/heretic_knowledge/spell/entropic_plume/on_gain(mob/user)
. = ..()
- var/datum/antagonist/heretic/our_heretic = IS_HERETIC(user)
+ var/datum/antagonist/heretic/our_heretic = GET_HERETIC(user)
our_heretic.increase_rust_strength(TRUE)
/datum/heretic_knowledge/ultimate/rust_final
@@ -220,7 +219,6 @@
/// A static list of traits we give to the heretic when on rust.
var/static/list/conditional_immunities = list(
TRAIT_BOMBIMMUNE,
- TRAIT_IGNOREDAMAGESLOWDOWN,
TRAIT_IGNORESLOWDOWN,
TRAIT_NO_SLIP_ALL,
TRAIT_NOBREATH,
@@ -258,7 +256,7 @@
text = "[generate_heretic_text()] Fear the decay, for the Rustbringer, [user.real_name] has ascended! None shall escape the corrosion! [generate_heretic_text()]",
title = "[generate_heretic_text()]",
sound = 'sound/ambience/antag/heretic/ascend_rust.ogg',
- color_override = "brown",
+ color_override = "pink",
)
trigger(loc)
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
@@ -306,7 +304,7 @@
*
* Gives our heretic ([source]) buffs if they stand on rust.
*/
-/datum/heretic_knowledge/ultimate/rust_final/proc/on_move(mob/source, atom/old_loc, dir, forced, list/old_locs)
+/datum/heretic_knowledge/ultimate/rust_final/proc/on_move(mob/living/source, atom/old_loc, dir, forced, list/old_locs)
SIGNAL_HANDLER
// If we're on a rusty turf, and haven't given out our traits, buff our guy
@@ -314,12 +312,14 @@
if(HAS_TRAIT(our_turf, TRAIT_RUSTY))
if(!immunities_active)
source.add_traits(conditional_immunities, type)
+ source.add_movespeed_mod_immunities(type, /datum/movespeed_modifier/damage_slowdown)
immunities_active = TRUE
// If we're not on a rust turf, and we have given out our traits, nerf our guy
else
if(immunities_active)
source.remove_traits(conditional_immunities, type)
+ source.remove_movespeed_mod_immunities(type, /datum/movespeed_modifier/damage_slowdown)
immunities_active = FALSE
/**
diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
index 8da3b90494899..476e3968b2bfd 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_knowledge.dm
@@ -66,7 +66,7 @@
CRASH("Failed to lazy load heretic sacrifice template!")
/datum/heretic_knowledge/hunt_and_sacrifice/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
// First we have to check if the heretic has a Living Heart.
// You may wonder why we don't straight up prevent them from invoking the ritual if they don't have one -
// Hunt and sacrifice should always be invokable for clarity's sake, even if it'll fail immediately.
@@ -99,7 +99,7 @@
return FALSE
/datum/heretic_knowledge/hunt_and_sacrifice/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
// Force it to work if the sacrifice is a cultist, even if there's no targets.
var/mob/living/carbon/human/sac = selected_atoms[1]
if(!LAZYLEN(heretic_datum.sac_targets) && !IS_CULTIST(sac))
@@ -193,7 +193,7 @@
*/
/datum/heretic_knowledge/hunt_and_sacrifice/proc/sacrifice_process(mob/living/user, list/selected_atoms, turf/loc)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
var/mob/living/carbon/human/sacrifice = locate() in selected_atoms
if(!sacrifice)
CRASH("[type] sacrifice_process didn't have a human in the atoms list. How'd it make it so far?")
@@ -207,14 +207,14 @@
var/feedback = "Your patrons accept your offer"
var/sac_job_flag = sacrifice.mind?.assigned_role?.job_flags | sacrifice.last_mind?.assigned_role?.job_flags
- var/datum/antagonist/cult/cultist_datum = IS_CULTIST(sacrifice)
+ var/datum/antagonist/cult/cultist_datum = GET_CULTIST(sacrifice)
// Heads give 3 points, cultists give 1 point (and a special reward), normal sacrifices give 2 points.
heretic_datum.total_sacrifices++
if((sac_job_flag & JOB_HEAD_OF_STAFF))
heretic_datum.knowledge_points += 3
heretic_datum.high_value_sacrifices++
feedback += " graciously"
- else if(cultist_datum)
+ if(cultist_datum)
heretic_datum.knowledge_points += 1
grant_reward(user, sacrifice, loc)
// easier to read
@@ -229,9 +229,9 @@
span_narsie(" one of our own. Destroy and sacrifice the infidel before it claims more!")
to_chat(mind.current, message)
// he(retic) gets a warn too
- var/message = span_cult_bold("You feel that your action has attracted") + \
- span_cult_bold_italic(" attention.")
- to_chat(user, message)
+ to_chat(user, span_narsiesmall("How DARE you!? I will see you destroyed for this."))
+ var/non_flavor_warning = span_cult_bold("You feel that your action has attracted ") + span_hypnophrase("attention") + span_cult_bold(".")
+ to_chat(user, non_flavor_warning)
return
else
heretic_datum.knowledge_points += 2
@@ -260,15 +260,9 @@
sacrifice.dust(TRUE, TRUE)
// Increase reward counter
- var/datum/antagonist/heretic/antag = IS_HERETIC(user)
+ var/datum/antagonist/heretic/antag = GET_HERETIC(user)
antag.rewards_given++
- // We limit the amount so the heretic doesn't just turn into a frickin' god (early)
- to_chat(user, span_hierophant("You feel the rotten energies of the infidel warp and twist, mixing with that of your own..."))
- if(prob(8 * antag.rewards_given))
- to_chat(user, span_hierophant("Faint, dark red sparks flit around the dust, then fade. It looks like your patrons weren't able to fashion something out of it."))
- return
-
// Cool effect for the rune as well as the item
var/obj/effect/heretic_rune/rune = locate() in range(2, user)
if(rune)
@@ -285,10 +279,10 @@
/datum/heretic_knowledge/hunt_and_sacrifice/proc/deposit_reward(mob/user, turf/loc, loop = 0, obj/rune)
if(loop > 5) // Max limit for retrying a reward
return
- // Remove the rays, we don't need them anymore.
+ // Remove the outline, we don't need it anymore.
rune?.remove_filter("reward_outline")
playsound(loc, 'sound/magic/repulse.ogg', 75, TRUE)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
ASSERT(heretic_datum)
// This list will be almost identical to unlocked_heretic_items, with the same keys, the difference being the values will be 1 to 5.
var/list/rewards = heretic_datum.unlocked_heretic_items.Copy()
@@ -422,20 +416,13 @@
usable_organs -= /obj/item/organ/internal/lungs/corrupt // Their lungs are already more cursed than anything I could give them
var/total_implant = rand(2, 4)
- var/gave_any = FALSE
for (var/i in 1 to total_implant)
if (!length(usable_organs))
- break
+ return
var/organ_path = pick_n_take(usable_organs)
var/obj/item/organ/internal/to_give = new organ_path
- if (!to_give.Insert(sac_target))
- qdel(to_give)
- else
- gave_any = TRUE
-
- if (!gave_any)
- return
+ to_give.Insert(sac_target)
new /obj/effect/gibspawner/human/bodypartless(get_turf(sac_target))
sac_target.visible_message(span_boldwarning("Several organs force themselves out of [sac_target]!"))
diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm
index bc6cb750b219a..721af03b2edfd 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_map.dm
@@ -68,17 +68,7 @@ GLOBAL_LIST_EMPTY(heretic_sacrifice_landmarks)
fire_brightness = 3.5
bulb_power = 0.5
-/obj/machinery/light/very_dim/directional/north
- dir = NORTH
-
-/obj/machinery/light/very_dim/directional/south
- dir = SOUTH
-
-/obj/machinery/light/very_dim/directional/east
- dir = EAST
-
-/obj/machinery/light/very_dim/directional/west
- dir = WEST
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/machinery/light/very_dim)
// Rooms for where heretic sacrifices send people.
/area/centcom/heretic_sacrifice
@@ -87,12 +77,11 @@ GLOBAL_LIST_EMPTY(heretic_sacrifice_landmarks)
has_gravity = STANDARD_GRAVITY
ambience_index = AMBIENCE_SPOOKY
sound_environment = SOUND_ENVIRONMENT_CAVE
- area_flags = UNIQUE_AREA | NOTELEPORT | HIDDEN_AREA | BLOCK_SUICIDE
+ area_flags = UNIQUE_AREA | NOTELEPORT | HIDDEN_AREA | BLOCK_SUICIDE | NO_BOH
/area/centcom/heretic_sacrifice/Initialize(mapload)
if(!ambientsounds)
- ambientsounds = GLOB.ambience_assoc[ambience_index]
- ambientsounds += 'sound/ambience/ambiatm1.ogg'
+ ambientsounds = GLOB.ambience_assoc[ambience_index] + 'sound/ambience/ambiatm1.ogg'
return ..()
/area/centcom/heretic_sacrifice/ash //also, the default
diff --git a/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm b/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm
index 758ee0548d5fc..02d0737be18a0 100644
--- a/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_ash_moon.dm
@@ -40,7 +40,7 @@
curse_color = "#f19a9a"
cost = 1
route = PATH_SIDE
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "curse_paralysis"
depth = 8
@@ -82,12 +82,3 @@
route = PATH_SIDE
poll_ignore_define = POLL_IGNORE_ASH_SPIRIT
depth = 10
-
-/datum/heretic_knowledge/summon/ashy/cleanup_atoms(list/selected_atoms)
- var/obj/item/bodypart/head/ritual_head = locate() in selected_atoms
- if(!ritual_head)
- CRASH("[type] required a head bodypart, yet did not have one in selected_atoms when it reached cleanup_atoms.")
-
- // Spill out any brains or stuff before we delete it.
- ritual_head.drop_organs()
- return ..()
diff --git a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm
index 11918c66a2906..05d513438cb4a 100644
--- a/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_rust_cosmos.dm
@@ -67,7 +67,7 @@
curse_color = "#c1ffc9"
cost = 1
route = PATH_SIDE
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "curse_corrosion"
depth = 8
@@ -104,11 +104,3 @@
poll_ignore_define = POLL_IGNORE_RUST_SPIRIT
depth = 8
-/datum/heretic_knowledge/summon/rusty/cleanup_atoms(list/selected_atoms)
- var/obj/item/bodypart/head/ritual_head = locate() in selected_atoms
- if(!ritual_head)
- CRASH("[type] required a head bodypart, yet did not have one in selected_atoms when it reached cleanup_atoms.")
-
- // Spill out any brains or stuff before we delete it.
- ritual_head.drop_organs()
- return ..()
diff --git a/code/modules/antagonists/heretic/knowledge/side_void_blade.dm b/code/modules/antagonists/heretic/knowledge/side_void_blade.dm
index 56945262e3cb0..8bd4880cc1ecd 100644
--- a/code/modules/antagonists/heretic/knowledge/side_void_blade.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_void_blade.dm
@@ -23,7 +23,7 @@
limit = 1
cost = 1
route = PATH_SIDE
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "ghoul_shattered"
depth = 4
diff --git a/code/modules/antagonists/heretic/knowledge/starting_lore.dm b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
index 7cb3b82a39ac2..54440b62a8c11 100644
--- a/code/modules/antagonists/heretic/knowledge/starting_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
@@ -1,5 +1,4 @@
// Heretic starting knowledge.
-// Default heretic language is Ancient Greek, because, uh, they're like ancient and shit.
/// Global list of all heretic knowledge that have route = PATH_START. List of PATHS.
GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
@@ -108,7 +107,7 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
return TRUE
/datum/heretic_knowledge/living_heart/recipe_snowflake_check(mob/living/user, list/atoms, list/selected_atoms, turf/loc)
- var/datum/antagonist/heretic/our_heretic = IS_HERETIC(user)
+ var/datum/antagonist/heretic/our_heretic = GET_HERETIC(user)
var/obj/item/organ/our_living_heart = user.get_organ_slot(our_heretic.living_heart_organ_slot)
// Obviously you need a heart in your chest to do a ritual on your... heart
if(!our_living_heart)
@@ -146,7 +145,7 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
return FALSE
/datum/heretic_knowledge/living_heart/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
- var/datum/antagonist/heretic/our_heretic = IS_HERETIC(user)
+ var/datum/antagonist/heretic/our_heretic = GET_HERETIC(user)
var/obj/item/organ/our_new_heart = user.get_organ_slot(our_heretic.living_heart_organ_slot)
// Our heart is robotic or synthetic - we need to replace it, and we fortunately should have one by here
@@ -310,20 +309,26 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
required_atoms = list()
research_tree_icon_path = 'icons/mob/actions/actions_animal.dmi'
research_tree_icon_state = "god_transmit"
+ /// amount of research points granted
+ var/reward = 5
/datum/heretic_knowledge/feast_of_owls/can_be_invoked(datum/antagonist/heretic/invoker)
return !invoker.feast_of_owls
/datum/heretic_knowledge/feast_of_owls/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
- //amount of research points granted
- var/reward = 5
var/alert = tgui_alert(user,"Do you really want to forsake your ascension? This action cannot be reverted.", "Feast of Owls", list("Yes I'm sure", "No"), 30 SECONDS)
- if( alert != "Yes I'm sure")
+ if(alert != "Yes I'm sure" || QDELETED(user) || QDELETED(src) || get_dist(user, loc) > 2)
return FALSE
- user.set_temp_blindness(reward SECONDS)
- user.AdjustParalyzed(reward SECONDS)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
+ if(QDELETED(heretic_datum) || heretic_datum.feast_of_owls)
+ return FALSE
+
+ . = TRUE
+
+ heretic_datum.feast_of_owls = TRUE
+ user.set_temp_blindness(reward * 1 SECONDS)
+ user.AdjustParalyzed(reward * 1 SECONDS)
user.playsound_local(get_turf(user), 'sound/ambience/antag/heretic/heretic_gain_intense.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE)
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
for(var/i in 1 to reward)
user.emote("scream")
playsound(loc, 'sound/items/eatfood.ogg', 100, TRUE)
@@ -331,7 +336,10 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
to_chat(user, span_danger("You feel something invisible tearing away at your very essence!"))
user.do_jitter_animation()
sleep(1 SECONDS)
- heretic_datum.feast_of_owls = TRUE
+ if(QDELETED(user) || QDELETED(heretic_datum))
+ return FALSE
+
to_chat(user, span_danger(span_big("Your ambition is ravaged, but something powerful remains in its wake...")))
var/drain_message = pick(strings(HERETIC_INFLUENCE_FILE, "drain_message"))
to_chat(user, span_hypnophrase(span_big("[drain_message]")))
+ return .
diff --git a/code/modules/antagonists/heretic/knowledge/void_lore.dm b/code/modules/antagonists/heretic/knowledge/void_lore.dm
index 482de8184401b..3533279d67e38 100644
--- a/code/modules/antagonists/heretic/knowledge/void_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/void_lore.dm
@@ -1,7 +1,5 @@
/**
* # The path of VOID.
- * Spell names are in this language: PALI
- * Both are related: Pali-Buddhism-Nothingness-Void
*
* Goes as follows:
*
@@ -63,7 +61,7 @@
cost = 1
route = PATH_VOID
depth = 3
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "grasp_void"
/datum/heretic_knowledge/void_grasp/on_gain(mob/user, datum/antagonist/heretic/our_heretic)
@@ -155,7 +153,7 @@
gain_text = "Fleeting memories, fleeting feet. I mark my way with frozen blood upon the snow. Covered and forgotten."
next_knowledge = list(/datum/heretic_knowledge/spell/void_pull)
route = PATH_VOID
- research_tree_icon_path = 'icons/ui_icons/antags/heretic/knowledge.dmi'
+ research_tree_icon_path = 'icons/ui/antags/heretic/knowledge.dmi'
research_tree_icon_state = "blade_upgrade_void"
/datum/heretic_knowledge/blade_upgrade/void/do_ranged_effects(mob/living/user, mob/living/target, obj/item/melee/sickly_blade/blade)
@@ -221,7 +219,7 @@
text = "[generate_heretic_text()] The nobleman of void [user.real_name] has arrived, stepping along the Waltz that ends worlds! [generate_heretic_text()]",
title = "[generate_heretic_text()]",
sound = 'sound/ambience/antag/heretic/ascend_void.ogg',
- color_override = "blue",
+ color_override = "pink",
)
ADD_TRAIT(user, TRAIT_RESISTLOWPRESSURE, MAGIC_TRAIT)
diff --git a/code/modules/antagonists/heretic/magic/aggressive_spread.dm b/code/modules/antagonists/heretic/magic/aggressive_spread.dm
index 0c14550f0b269..dfb4a94847406 100644
--- a/code/modules/antagonists/heretic/magic/aggressive_spread.dm
+++ b/code/modules/antagonists/heretic/magic/aggressive_spread.dm
@@ -10,8 +10,8 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 30 SECONDS
- invocation = "Agresiv'noe rasprostra-neniye!"
- invocation_type = INVOCATION_SHOUT
+ invocation = "A'GRSV SPR'D"
+ invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
aoe_radius = 2
diff --git a/code/modules/antagonists/heretic/magic/apetravulnera.dm b/code/modules/antagonists/heretic/magic/apetravulnera.dm
index eedef71f4b6c8..e80d08911848c 100644
--- a/code/modules/antagonists/heretic/magic/apetravulnera.dm
+++ b/code/modules/antagonists/heretic/magic/apetravulnera.dm
@@ -10,8 +10,8 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 45 SECONDS
- invocation = "Shea' shen-eh!"
- invocation_type = INVOCATION_SHOUT
+ invocation = "AP'TRA VULN'RA!"
+ invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
cast_range = 4
diff --git a/code/modules/antagonists/heretic/magic/ash_ascension.dm b/code/modules/antagonists/heretic/magic/ash_ascension.dm
index 70422a7c48a37..8b564198a61eb 100644
--- a/code/modules/antagonists/heretic/magic/ash_ascension.dm
+++ b/code/modules/antagonists/heretic/magic/ash_ascension.dm
@@ -10,7 +10,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 70 SECONDS
- invocation = "EID'R-ELDR!!!"
+ invocation = "FL'MS"
invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
@@ -72,8 +72,8 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 30 SECONDS
- invocation = "ILLA-LASARA'FOSS!!!"
- invocation_type = INVOCATION_SHOUT
+ invocation = "C'SC'DE"
+ invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
/// The radius the flames will go around the caster.
@@ -112,7 +112,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 300
- invocation = "Eld'sky!"
+ invocation = "F'RE"
invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/ash_jaunt.dm b/code/modules/antagonists/heretic/magic/ash_jaunt.dm
index 4f8c59d635145..41242063a9098 100644
--- a/code/modules/antagonists/heretic/magic/ash_jaunt.dm
+++ b/code/modules/antagonists/heretic/magic/ash_jaunt.dm
@@ -10,7 +10,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 15 SECONDS
- invocation = "Askgraar' goetur!"
+ invocation = "ASH'N P'SSG'"
invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/blood_cleave.dm b/code/modules/antagonists/heretic/magic/blood_cleave.dm
index b3370a3ccc614..d5317f23e344b 100644
--- a/code/modules/antagonists/heretic/magic/blood_cleave.dm
+++ b/code/modules/antagonists/heretic/magic/blood_cleave.dm
@@ -10,7 +10,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 45 SECONDS
- invocation = "Fer're!"
+ invocation = "CL'VE!"
invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/blood_siphon.dm b/code/modules/antagonists/heretic/magic/blood_siphon.dm
index 6280353a072a5..1e3d6258826d4 100644
--- a/code/modules/antagonists/heretic/magic/blood_siphon.dm
+++ b/code/modules/antagonists/heretic/magic/blood_siphon.dm
@@ -11,7 +11,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 15 SECONDS
- invocation = "Sanguis suctio!"
+ invocation = "FL'MS O'ET'RN'ITY."
invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/burglar_finesse.dm b/code/modules/antagonists/heretic/magic/burglar_finesse.dm
index c5264119bb48e..a90acb8495f14 100644
--- a/code/modules/antagonists/heretic/magic/burglar_finesse.dm
+++ b/code/modules/antagonists/heretic/magic/burglar_finesse.dm
@@ -9,7 +9,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 40 SECONDS
- invocation = "Khenem"
+ invocation = "Y'O'K!"
invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/cosmic_expansion.dm b/code/modules/antagonists/heretic/magic/cosmic_expansion.dm
index 6869dc0df51c0..3fb197d392cb0 100644
--- a/code/modules/antagonists/heretic/magic/cosmic_expansion.dm
+++ b/code/modules/antagonists/heretic/magic/cosmic_expansion.dm
@@ -11,7 +11,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 45 SECONDS
- invocation = "An'gar baltil!"
+ invocation = "C'SM'S 'XP'ND"
invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/cosmic_runes.dm b/code/modules/antagonists/heretic/magic/cosmic_runes.dm
index e07aa4fbe8b8a..207b60ae9393a 100644
--- a/code/modules/antagonists/heretic/magic/cosmic_runes.dm
+++ b/code/modules/antagonists/heretic/magic/cosmic_runes.dm
@@ -11,7 +11,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 15 SECONDS
- invocation = "Is'zara-runen"
+ invocation = "ST'R R'N'"
invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/eldritch_blind.dm b/code/modules/antagonists/heretic/magic/eldritch_blind.dm
index 413ff4fe67810..8df20503821b0 100644
--- a/code/modules/antagonists/heretic/magic/eldritch_blind.dm
+++ b/code/modules/antagonists/heretic/magic/eldritch_blind.dm
@@ -5,7 +5,7 @@
overlay_icon_state = "bg_heretic_border"
school = SCHOOL_FORBIDDEN
- invocation = "Caecus"
+ invocation = "E'E'S"
spell_requirements = NONE
cast_range = 10
diff --git a/code/modules/antagonists/heretic/magic/eldritch_emplosion.dm b/code/modules/antagonists/heretic/magic/eldritch_emplosion.dm
index 4028f80f84cea..c68ed07c81f8c 100644
--- a/code/modules/antagonists/heretic/magic/eldritch_emplosion.dm
+++ b/code/modules/antagonists/heretic/magic/eldritch_emplosion.dm
@@ -8,7 +8,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 30 SECONDS
- invocation = "Pulsus Energiae"
+ invocation = "E'P"
invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/eldritch_shapeshift.dm b/code/modules/antagonists/heretic/magic/eldritch_shapeshift.dm
index bde032a3b39fd..e598f1f9215b9 100644
--- a/code/modules/antagonists/heretic/magic/eldritch_shapeshift.dm
+++ b/code/modules/antagonists/heretic/magic/eldritch_shapeshift.dm
@@ -7,7 +7,7 @@
overlay_icon_state = "bg_heretic_border"
school = SCHOOL_FORBIDDEN
- invocation = "Forma"
+ invocation = "SH'PE"
invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/fire_blast.dm b/code/modules/antagonists/heretic/magic/fire_blast.dm
index 8c6d632be9f2d..4c17ca5ffc0de 100644
--- a/code/modules/antagonists/heretic/magic/fire_blast.dm
+++ b/code/modules/antagonists/heretic/magic/fire_blast.dm
@@ -12,7 +12,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 45 SECONDS
- invocation = "Eld'fjall!"
+ invocation = "V'LC'N!"
invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
channel_time = 5 SECONDS
diff --git a/code/modules/antagonists/heretic/magic/flesh_ascension.dm b/code/modules/antagonists/heretic/magic/flesh_ascension.dm
index add0704a8d61a..a2d792080e058 100644
--- a/code/modules/antagonists/heretic/magic/flesh_ascension.dm
+++ b/code/modules/antagonists/heretic/magic/flesh_ascension.dm
@@ -9,7 +9,7 @@
school = SCHOOL_FORBIDDEN
- invocation = "REALITAS EXSERPAT!!"
+ invocation = "REALITY UNCOIL!"
invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/flesh_surgery.dm b/code/modules/antagonists/heretic/magic/flesh_surgery.dm
index 96ccb8450f97b..ff474f063198f 100644
--- a/code/modules/antagonists/heretic/magic/flesh_surgery.dm
+++ b/code/modules/antagonists/heretic/magic/flesh_surgery.dm
@@ -11,8 +11,8 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 20 SECONDS
- invocation = "Carnis chirurgia"
- invocation_type = INVOCATION_WHISPER
+ invocation = "CL'M M'N!" // "CLAIM MINE", but also almost "KALI MA"
+ invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
hand_path = /obj/item/melee/touch_attack/flesh_surgery
@@ -83,7 +83,7 @@
return .
-/// If cast on an organ, we'll restore it's health and even un-fail it.
+/// If cast on an organ, we'll restore its health and even un-fail it.
/datum/action/cooldown/spell/touch/flesh_surgery/proc/heal_organ(obj/item/melee/touch_attack/hand, obj/item/organ/to_heal, mob/living/carbon/caster)
if(to_heal.damage == 0)
to_heal.balloon_alert(caster, "already in good condition!")
diff --git a/code/modules/antagonists/heretic/magic/furious_steel.dm b/code/modules/antagonists/heretic/magic/furious_steel.dm
index 36c7c07608bcb..0ab882a9289e1 100644
--- a/code/modules/antagonists/heretic/magic/furious_steel.dm
+++ b/code/modules/antagonists/heretic/magic/furious_steel.dm
@@ -11,7 +11,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 60 SECONDS
- invocation = "Ham'sana-qasep!"
+ invocation = "F'LSH'NG S'LV'R!"
invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/manse_link.dm b/code/modules/antagonists/heretic/magic/manse_link.dm
index e077c6db2b45f..06fd4dd9863f4 100644
--- a/code/modules/antagonists/heretic/magic/manse_link.dm
+++ b/code/modules/antagonists/heretic/magic/manse_link.dm
@@ -11,7 +11,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 20 SECONDS
- invocation = "Diaperaste' to-myalo!"
+ invocation = "PI'RC' TH' M'ND."
invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
antimagic_flags = MAGIC_RESISTANCE|MAGIC_RESISTANCE_MIND
diff --git a/code/modules/antagonists/heretic/magic/mansus_grasp.dm b/code/modules/antagonists/heretic/magic/mansus_grasp.dm
index 803bdd3d218e5..43dde25374f89 100644
--- a/code/modules/antagonists/heretic/magic/mansus_grasp.dm
+++ b/code/modules/antagonists/heretic/magic/mansus_grasp.dm
@@ -10,7 +10,7 @@
school = SCHOOL_EVOCATION
cooldown_time = 10 SECONDS
- invocation = "Ad verum per aspera!"
+ invocation = "R'CH T'H TR'TH!"
invocation_type = INVOCATION_SHOUT
// Mimes can cast it. Chaplains can cast it. Anyone can cast it, so long as they have a hand.
spell_requirements = SPELL_CASTABLE_WITHOUT_INVOCATION
diff --git a/code/modules/antagonists/heretic/magic/mind_gate.dm b/code/modules/antagonists/heretic/magic/mind_gate.dm
index aa6b8ef20af4d..c5a6e74452a61 100644
--- a/code/modules/antagonists/heretic/magic/mind_gate.dm
+++ b/code/modules/antagonists/heretic/magic/mind_gate.dm
@@ -11,7 +11,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 20 SECONDS
- invocation = "Sha'ar ha-da'at"
+ invocation = "Op' 'oY 'Mi'd"
invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
cast_range = 6
diff --git a/code/modules/antagonists/heretic/magic/moon_parade.dm b/code/modules/antagonists/heretic/magic/moon_parade.dm
index 4919500e351de..3b7f1d007cd6e 100644
--- a/code/modules/antagonists/heretic/magic/moon_parade.dm
+++ b/code/modules/antagonists/heretic/magic/moon_parade.dm
@@ -11,7 +11,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 30 SECONDS
- invocation = "Tsiyun' levani!"
+ invocation = "L'N'R P'RAD"
invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/moon_ringleader.dm b/code/modules/antagonists/heretic/magic/moon_ringleader.dm
index e62c34bb990bb..3c0b1d2aedb52 100644
--- a/code/modules/antagonists/heretic/magic/moon_ringleader.dm
+++ b/code/modules/antagonists/heretic/magic/moon_ringleader.dm
@@ -12,7 +12,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 1 MINUTES
- invocation = "Manahel-qomem!"
+ invocation = "R''S 'E"
invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/moon_smile.dm b/code/modules/antagonists/heretic/magic/moon_smile.dm
index 236fd257e385d..35f2d77e3e6b6 100644
--- a/code/modules/antagonists/heretic/magic/moon_smile.dm
+++ b/code/modules/antagonists/heretic/magic/moon_smile.dm
@@ -12,7 +12,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 20 SECONDS
- invocation = "Hiyuk-levana!"
+ invocation = "Mo'N S'M'LE"
invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
cast_range = 6
diff --git a/code/modules/antagonists/heretic/magic/nightwatcher_rebirth.dm b/code/modules/antagonists/heretic/magic/nightwatcher_rebirth.dm
index 8a9b60644b6b7..4e37f5db17fed 100644
--- a/code/modules/antagonists/heretic/magic/nightwatcher_rebirth.dm
+++ b/code/modules/antagonists/heretic/magic/nightwatcher_rebirth.dm
@@ -11,7 +11,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 1 MINUTES
- invocation = "Dyrth-a Vaktry'ggjandi"
+ invocation = "GL'RY T' TH' N'GHT'W'TCH'ER"
invocation_type = INVOCATION_WHISPER
spell_requirements = SPELL_REQUIRES_HUMAN
diff --git a/code/modules/antagonists/heretic/magic/realignment.dm b/code/modules/antagonists/heretic/magic/realignment.dm
index dbce0fe0940dd..d3ddc03fbbef3 100644
--- a/code/modules/antagonists/heretic/magic/realignment.dm
+++ b/code/modules/antagonists/heretic/magic/realignment.dm
@@ -14,8 +14,8 @@
cooldown_reduction_per_rank = -6 SECONDS // we're not a wizard spell but we use the levelling mechanic
spell_max_level = 10 // we can get up to / over a minute duration cd time
- invocation = "Rasut"
- invocation_type = INVOCATION_WHISPER
+ invocation = "R'S'T."
+ invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
/datum/action/cooldown/spell/realignment/is_valid_target(atom/cast_on)
diff --git a/code/modules/antagonists/heretic/magic/rust_wave.dm b/code/modules/antagonists/heretic/magic/rust_wave.dm
index 7ecb3fd0ffbba..0282a32b2b687 100644
--- a/code/modules/antagonists/heretic/magic/rust_wave.dm
+++ b/code/modules/antagonists/heretic/magic/rust_wave.dm
@@ -13,8 +13,8 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 30 SECONDS
- invocation = "Entro'pichniy-plim!"
- invocation_type = INVOCATION_SHOUT
+ invocation = "'NTR'P'C PL'M'"
+ invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
cone_levels = 5
@@ -78,8 +78,8 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 35 SECONDS
- invocation = "Diffunde' verbum!"
- invocation_type = INVOCATION_SHOUT
+ invocation = "SPR'D TH' WO'D"
+ invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
projectile_type = /obj/projectile/magic/aoe/rust_wave
diff --git a/code/modules/antagonists/heretic/magic/space_crawl.dm b/code/modules/antagonists/heretic/magic/space_crawl.dm
index 49677e3bb5086..90f74a37047f3 100644
--- a/code/modules/antagonists/heretic/magic/space_crawl.dm
+++ b/code/modules/antagonists/heretic/magic/space_crawl.dm
@@ -84,8 +84,8 @@
RegisterSignal(jaunter, SIGNAL_REMOVETRAIT(TRAIT_ALLOW_HERETIC_CASTING), PROC_REF(on_focus_lost))
RegisterSignal(jaunter, COMSIG_MOB_STATCHANGE, PROC_REF(on_stat_change))
- our_turf.visible_message(span_warning("[jaunter] sinks into [our_turf]!"))
playsound(our_turf, 'sound/magic/cosmic_energy.ogg', 50, TRUE, -1)
+ our_turf.visible_message(span_warning("[jaunter] sinks into [our_turf]!"))
new /obj/effect/temp_visual/space_explosion(our_turf)
jaunter.extinguish_mob()
@@ -102,7 +102,6 @@
if(!exit_jaunt(jaunter, our_turf))
return FALSE
-
our_turf.visible_message(span_boldwarning("[jaunter] rises out of [our_turf]!"))
return TRUE
diff --git a/code/modules/antagonists/heretic/magic/star_blast.dm b/code/modules/antagonists/heretic/magic/star_blast.dm
index 294608a03b998..3eb62b7ada814 100644
--- a/code/modules/antagonists/heretic/magic/star_blast.dm
+++ b/code/modules/antagonists/heretic/magic/star_blast.dm
@@ -10,7 +10,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 20 SECONDS
- invocation = "Pi-rig is'zara!"
+ invocation = "R'T'T' ST'R!"
invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/star_touch.dm b/code/modules/antagonists/heretic/magic/star_touch.dm
index dff56df4e3f1f..89c5d02e7d498 100644
--- a/code/modules/antagonists/heretic/magic/star_touch.dm
+++ b/code/modules/antagonists/heretic/magic/star_touch.dm
@@ -13,7 +13,7 @@
sound = 'sound/items/welder.ogg'
school = SCHOOL_FORBIDDEN
cooldown_time = 15 SECONDS
- invocation = "An'gar sig!"
+ invocation = "ST'R 'N'RG'!"
invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
antimagic_flags = MAGIC_RESISTANCE
diff --git a/code/modules/antagonists/heretic/magic/void_cold_cone.dm b/code/modules/antagonists/heretic/magic/void_cold_cone.dm
index 40dc9612a50f6..92c45dc10b010 100644
--- a/code/modules/antagonists/heretic/magic/void_cold_cone.dm
+++ b/code/modules/antagonists/heretic/magic/void_cold_cone.dm
@@ -11,7 +11,7 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 30 SECONDS
- invocation = "Sunya'kop!"
+ invocation = "FR'ZE!"
invocation_type = INVOCATION_SHOUT
spell_requirements = NONE
diff --git a/code/modules/antagonists/heretic/magic/void_phase.dm b/code/modules/antagonists/heretic/magic/void_phase.dm
index f3f0864224c4c..350ca0f29c100 100644
--- a/code/modules/antagonists/heretic/magic/void_phase.dm
+++ b/code/modules/antagonists/heretic/magic/void_phase.dm
@@ -12,8 +12,8 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 30 SECONDS
- invocation = "Sunya'sthiti!"
- invocation_type = INVOCATION_SHOUT
+ invocation = "RE'L'TY PH'S'E."
+ invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
cast_range = 9
diff --git a/code/modules/antagonists/heretic/magic/void_pull.dm b/code/modules/antagonists/heretic/magic/void_pull.dm
index dc4673b0714ce..2021bf8a04e4f 100644
--- a/code/modules/antagonists/heretic/magic/void_pull.dm
+++ b/code/modules/antagonists/heretic/magic/void_pull.dm
@@ -11,8 +11,8 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 40 SECONDS
- invocation = "Sunya'apamkti!"
- invocation_type = INVOCATION_SHOUT
+ invocation = "BR'NG F'RTH TH'M T' M'."
+ invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
aoe_radius = 7
diff --git a/code/modules/antagonists/heretic/magic/wave_of_desperation.dm b/code/modules/antagonists/heretic/magic/wave_of_desperation.dm
index b9502f08967bb..3b78b56ddc0ba 100644
--- a/code/modules/antagonists/heretic/magic/wave_of_desperation.dm
+++ b/code/modules/antagonists/heretic/magic/wave_of_desperation.dm
@@ -11,8 +11,8 @@
school = SCHOOL_FORBIDDEN
cooldown_time = 5 MINUTES
- invocation = "Kher' Sekh-em waaef'k!"
- invocation_type = INVOCATION_SHOUT
+ invocation = "F'K 'FF."
+ invocation_type = INVOCATION_WHISPER
spell_requirements = NONE
aoe_radius = 3
diff --git a/code/modules/antagonists/heretic/status_effects/buffs.dm b/code/modules/antagonists/heretic/status_effects/buffs.dm
index 35a6ab9268784..1668ea5a11ef7 100644
--- a/code/modules/antagonists/heretic/status_effects/buffs.dm
+++ b/code/modules/antagonists/heretic/status_effects/buffs.dm
@@ -53,11 +53,11 @@
alert_type = /atom/movable/screen/alert/status_effect/marshal
/datum/status_effect/marshal/on_apply()
- ADD_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, STATUS_EFFECT_TRAIT)
+ owner.add_movespeed_mod_immunities(id, /datum/movespeed_modifier/damage_slowdown)
return TRUE
/datum/status_effect/marshal/on_remove()
- REMOVE_TRAIT(owner, TRAIT_IGNOREDAMAGESLOWDOWN, STATUS_EFFECT_TRAIT)
+ owner.remove_movespeed_mod_immunities(id, /datum/movespeed_modifier/damage_slowdown)
/datum/status_effect/marshal/tick(seconds_between_ticks)
if(!iscarbon(owner))
@@ -232,6 +232,7 @@
blade_orbit_radius = 20,
time_between_initial_blades = 0.25 SECONDS,
blade_recharge_time = 1 MINUTES,
+ blade_type = /obj/effect/floating_blade,
)
src.blade_recharge_time = blade_recharge_time
diff --git a/code/modules/antagonists/heretic/transmutation_rune.dm b/code/modules/antagonists/heretic/transmutation_rune.dm
index 5e6ad0fb1cf7f..3f7072201590f 100644
--- a/code/modules/antagonists/heretic/transmutation_rune.dm
+++ b/code/modules/antagonists/heretic/transmutation_rune.dm
@@ -50,7 +50,7 @@
/obj/effect/heretic_rune/proc/try_rituals(mob/living/user)
is_in_use = TRUE
- var/datum/antagonist/heretic/heretic_datum = IS_HERETIC(user)
+ var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
var/list/rituals = heretic_datum.get_rituals()
if(!length(rituals))
loc.balloon_alert(user, "no rituals available!")
diff --git a/code/modules/antagonists/highlander/highlander.dm b/code/modules/antagonists/highlander/highlander.dm
index 077bd2158b8f6..a1c31241e0b75 100644
--- a/code/modules/antagonists/highlander/highlander.dm
+++ b/code/modules/antagonists/highlander/highlander.dm
@@ -12,6 +12,7 @@
TRAIT_NODISMEMBER,
TRAIT_NOFIRE,
TRAIT_NOGUNS,
+ TRAIT_TOSS_GUN_HARD,
TRAIT_SHOCKIMMUNE,
)
diff --git a/code/modules/antagonists/malf_ai/malf_ai.dm b/code/modules/antagonists/malf_ai/malf_ai.dm
index 358b618df99aa..4dc2568fe0e89 100644
--- a/code/modules/antagonists/malf_ai/malf_ai.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai.dm
@@ -207,7 +207,7 @@
return data
-/datum/antagonist/malf_ai/ui_act(action, list/params)
+/datum/antagonist/malf_ai/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/malf_ai/malf_ai_module_picker.dm b/code/modules/antagonists/malf_ai/malf_ai_module_picker.dm
index 0ac27c14c97bf..5e95e11c41bca 100644
--- a/code/modules/antagonists/malf_ai/malf_ai_module_picker.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai_module_picker.dm
@@ -62,7 +62,7 @@
return data
-/datum/module_picker/ui_act(action, list/params)
+/datum/module_picker/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
index a80ccec73bf37..fbc1d94811c15 100644
--- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
@@ -777,7 +777,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
/datum/action/innate/ai/emergency_lights
name = "Disable Emergency Lights"
desc = "Disables all emergency lighting. Note that emergency lights can be restored through reboot at an APC."
- button_icon = 'icons/obj/lighting.dmi'
+ button_icon = 'icons/obj/machines/lighting.dmi'
button_icon_state = "floor_emergency"
uses = 1
@@ -995,7 +995,7 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
data["selected"] = say_span || owner.speech_span
return data
-/obj/machinery/ai_voicechanger/ui_act(action, params)
+/obj/machinery/ai_voicechanger/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
if(..())
return
switch(action)
@@ -1044,14 +1044,14 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module))
owner.speech_span = say_span
to_chat(usr, span_notice("Voice set to [selection]."))
if("verb")
- say_verb = params["verb"]
+ say_verb = strip_html(params["verb"], MAX_NAME_LEN)
if(changing_voice)
owner.verb_say = say_verb
owner.verb_ask = say_verb
owner.verb_exclaim = say_verb
owner.verb_yell = say_verb
if("name")
- say_name = params["name"]
+ say_name = strip_html(params["name"], MAX_NAME_LEN)
/datum/ai_module/utility/emag
name = "Targeted Safeties Override"
diff --git a/code/modules/antagonists/nightmare/nightmare_equipment.dm b/code/modules/antagonists/nightmare/nightmare_equipment.dm
index ec07639cb449c..6fbe6c6097bec 100644
--- a/code/modules/antagonists/nightmare/nightmare_equipment.dm
+++ b/code/modules/antagonists/nightmare/nightmare_equipment.dm
@@ -52,10 +52,9 @@
if(!has_crit)
return
playsound(target, 'sound/effects/wounds/crackandbleed.ogg', 100, TRUE)
- var/datum/dna/target_dna = target.has_dna()
if(target.stat == DEAD)
user.visible_message(span_warning("[user] gores [target] with [src]!"), span_warning("You gore [target] with [src], which doesn't accomplish much, but it does make you feel a little better."))
- else if(!target_dna?.check_mutation(/datum/mutation/human/hulk) && (iscarbon(target) || issilicon(target)))
+ else if(!HAS_TRAIT(target, TRAIT_HULK) && (iscarbon(target) || issilicon(target)))
user.visible_message(span_boldwarning("[user] gores [target] with [src], bringing them to a halt!"), span_userdanger("You gore [target] with [src], bringing them to a halt!"))
target.Paralyze(issilicon(target) ? 2 SECONDS : 1 SECONDS)
else
diff --git a/code/modules/antagonists/nightmare/nightmare_organs.dm b/code/modules/antagonists/nightmare/nightmare_organs.dm
index 576b911652fa0..19328f20378de 100644
--- a/code/modules/antagonists/nightmare/nightmare_organs.dm
+++ b/code/modules/antagonists/nightmare/nightmare_organs.dm
@@ -65,11 +65,14 @@
/obj/item/organ/internal/heart/nightmare
name = "heart of darkness"
desc = "An alien organ that twists and writhes when exposed to light."
+ visual = TRUE
icon_state = "demon_heart-on"
base_icon_state = "demon_heart"
- visual = TRUE
+
color = COLOR_CRAYON_BLACK
decay_factor = 0
+ // No love is to be found in a heart so twisted.
+ food_reagents = list(/datum/reagent/consumable/nutriment = 5)
/// How many life ticks in the dark the owner has been dead for. Used for nightmare respawns.
var/respawn_progress = 0
/// The armblade granted to the host of this heart.
diff --git a/code/modules/antagonists/ninja/energy_net_nets.dm b/code/modules/antagonists/ninja/energy_net_nets.dm
index 111d1f2651548..84d5cbda8e0b5 100644
--- a/code/modules/antagonists/ninja/energy_net_nets.dm
+++ b/code/modules/antagonists/ninja/energy_net_nets.dm
@@ -15,7 +15,6 @@
density = TRUE //Can't pass through.
anchored = TRUE //Can't drag/grab the net.
layer = ABOVE_ALL_MOB_LAYER
- plane = ABOVE_GAME_PLANE
max_integrity = 60 //How much health it has.
can_buckle = TRUE
buckle_lying = 0
diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_authentication_disk.dm b/code/modules/antagonists/nukeop/equipment/nuclear_authentication_disk.dm
index 7e06dd0d6e050..fa2718833d36f 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclear_authentication_disk.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclear_authentication_disk.dm
@@ -26,7 +26,7 @@
/obj/item/disk/nuclear/Initialize(mapload)
. = ..()
- AddElement(/datum/element/bed_tuckable, mapload, 6, -6, 0)
+ AddElement(/datum/element/bed_tuckable, mapload, 6, 6, 0)
AddComponent(/datum/component/stationloving, !fake)
if(!fake)
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 8d2e746f0a220..fb23cae705fab 100644
--- a/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
+++ b/code/modules/antagonists/nukeop/equipment/nuclear_bomb/_nuclear_bomb.dm
@@ -328,7 +328,7 @@ GLOBAL_VAR(station_nuke_source)
return data
-/obj/machinery/nuclearbomb/ui_act(action, params)
+/obj/machinery/nuclearbomb/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/nukeop/equipment/overwatch_tools.dm b/code/modules/antagonists/nukeop/equipment/overwatch_tools.dm
index 852c0d7d32fbd..94c16d86dbeb5 100644
--- a/code/modules/antagonists/nukeop/equipment/overwatch_tools.dm
+++ b/code/modules/antagonists/nukeop/equipment/overwatch_tools.dm
@@ -42,5 +42,4 @@ Happy hunting!
icon_state = "sunhudmed"
flags_cover = GLASSESCOVERSEYES
flash_protect = FLASH_PROTECTION_WELDER
- clothing_traits = list(TRAIT_REAGENT_SCANNER)
- var/list/hudlist = list(DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED, DATA_HUD_SECURITY_ADVANCED)
+ clothing_traits = list(TRAIT_REAGENT_SCANNER, TRAIT_SECURITY_HUD, TRAIT_MEDICAL_HUD, TRAIT_DIAGNOSTIC_HUD)
diff --git a/code/modules/antagonists/nukeop/equipment/pinpointer.dm b/code/modules/antagonists/nukeop/equipment/pinpointer.dm
index 7f7722cc53d02..f842b5d6b6e33 100644
--- a/code/modules/antagonists/nukeop/equipment/pinpointer.dm
+++ b/code/modules/antagonists/nukeop/equipment/pinpointer.dm
@@ -54,7 +54,7 @@
/obj/item/pinpointer/nuke/proc/switch_mode_to(new_mode)
if(isliving(loc))
var/mob/living/L = loc
- to_chat(L, span_userdanger("Your [name] beeps as it reconfigures it's tracking algorithms."))
+ to_chat(L, span_userdanger("Your [name] beeps as it reconfigures its tracking algorithms."))
playsound(L, 'sound/machines/triple_beep.ogg', 50, TRUE)
mode = new_mode
scan_for_target()
diff --git a/code/modules/antagonists/nukeop/outfits.dm b/code/modules/antagonists/nukeop/outfits.dm
index 8c2820d11be68..bd6dbfacf457a 100644
--- a/code/modules/antagonists/nukeop/outfits.dm
+++ b/code/modules/antagonists/nukeop/outfits.dm
@@ -52,8 +52,7 @@
if(command_radio)
radio.command = TRUE
radio.use_command = TRUE
-
- if(ispath(uplink_type, /obj/item/uplink/nuclear) || tc) // /obj/item/uplink/nuclear understands 0 tc
+ if(ispath(uplink_type, /obj/item/uplink) || tc) // /obj/item/uplink understands 0 tc
var/obj/item/uplink = new uplink_type(nukie, nukie.key, tc)
nukie.equip_to_slot_or_del(uplink, ITEM_SLOT_BACKPACK, indirect_action = TRUE)
@@ -103,8 +102,8 @@
name = "Syndicate Operative - Reinforcement"
tc = 0
backpack_contents = list(
- /obj/item/gun/ballistic/automatic/plastikov = 1,
- /obj/item/ammo_box/magazine/plastikov9mm = 2,
+ /obj/item/gun/ballistic/automatic/smartgun = 1,
+ /obj/item/ammo_box/magazine/smartgun = 2,
)
var/faction = "The Syndicate"
@@ -153,7 +152,7 @@
head = /obj/item/clothing/head/utility/hardhat/orange
shoes = /obj/item/clothing/shoes/workboots
glasses = /obj/item/clothing/glasses/meson
- faction = "the Donk Corporation"
+ faction = "Donk Company"
/datum/outfit/syndicate/reinforcement/waffle
name = "Syndicate Operative - Waffle Reinforcement"
diff --git a/code/modules/antagonists/pirate/pirate_outfits.dm b/code/modules/antagonists/pirate/pirate_outfits.dm
index 72318fe4987ca..14ba07cad274f 100644
--- a/code/modules/antagonists/pirate/pirate_outfits.dm
+++ b/code/modules/antagonists/pirate/pirate_outfits.dm
@@ -42,6 +42,7 @@
belt = /obj/item/gun/magic/midas_hand
l_pocket = /obj/item/coin/gold/doubloon
+
/datum/outfit/pirate/space
name = "Space Pirate (EVA)"
@@ -55,6 +56,8 @@
head = /obj/item/clothing/head/helmet/space/pirate
+ id_trim = /datum/id_trim/pirate/captain
+
/datum/outfit/pirate/silverscale
name = "Silver Scale Member"
@@ -151,6 +154,8 @@
suit = /obj/item/clothing/suit/jacket/oversized
head = /obj/item/clothing/head/costume/crown
+ id_trim = /datum/id_trim/pirate/captain
+
/datum/outfit/pirate/medieval
name = "Medieval Warmonger"
@@ -181,3 +186,5 @@
belt = /obj/item/gun/magic/hook
l_pocket = /obj/item/tank/internals/emergency_oxygen
r_pocket = /obj/item/flashlight/lantern
+
+ skillchips = list(/obj/item/skillchip/big_pointer) //they don't have an id, so this is needed
diff --git a/code/modules/antagonists/pirate/pirate_roles.dm b/code/modules/antagonists/pirate/pirate_roles.dm
index 78a3d3fd12acf..3ea82d4c2002e 100644
--- a/code/modules/antagonists/pirate/pirate_roles.dm
+++ b/code/modules/antagonists/pirate/pirate_roles.dm
@@ -146,7 +146,7 @@
desc = "A surprisingly clean cryogenic sleeper. You can see your reflection on the sides!"
density = FALSE
you_are_text = "You are an agent working for the space IRS"
- flavour_text = "Not even in the expanse of the expanding universe can someone evade the tax man! Whether you are just a well disciplined and professional pirate gang or an actual agent from a local polity. You will squeeze the station dry of it's income regardless! Through peaceful means or otherwise..."
+ flavour_text = "Not even in the expanse of the expanding universe can someone evade the tax man! Whether you are just a well disciplined and professional pirate gang or an actual agent from a local polity. You will squeeze the station dry of its income regardless! Through peaceful means or otherwise..."
icon = 'icons/obj/machines/sleeper.dmi'
icon_state = "sleeper"
prompt_name = "An agent of the space IRS"
@@ -208,7 +208,7 @@
/obj/effect/mob_spawn/ghost_role/human/pirate/medieval/special(mob/living/carbon/spawned_mob)
. = ..()
if(rank == "Footsoldier")
- ADD_TRAIT(spawned_mob, TRAIT_NOGUNS, INNATE_TRAIT)
+ spawned_mob.add_traits(list(TRAIT_NOGUNS, TRAIT_TOSS_GUN_HARD), INNATE_TRAIT)
spawned_mob.AddComponent(/datum/component/unbreakable)
var/datum/action/cooldown/mob_cooldown/dash/dodge = new(spawned_mob)
dodge.Grant(spawned_mob)
diff --git a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
index 16cad321853d9..602fcbe8b071f 100644
--- a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
+++ b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
@@ -161,11 +161,14 @@
desc = "A disk that contains advanced surgery procedures, must be loaded into an Operating Console."
surgeries = list(
/datum/surgery/advanced/lobotomy,
+ /datum/surgery/advanced/lobotomy/mechanic,
/datum/surgery/advanced/bioware/vein_threading,
+ /datum/surgery/advanced/bioware/vein_threading/mechanic,
/datum/surgery/advanced/bioware/nerve_splicing,
+ /datum/surgery/advanced/bioware/nerve_splicing/mechanic,
/datum/surgery_step/heal/combo/upgraded,
/datum/surgery_step/pacify,
- /datum/surgery_step/revive,
+ /datum/surgery_step/pacify/mechanic,
)
//Pad & Pad Terminal
@@ -261,7 +264,7 @@
data["status_report"] = status_report
return data
-/obj/machinery/computer/piratepad_control/ui_act(action, params)
+/obj/machinery/computer/piratepad_control/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/antagonists/revenant/revenant_blight.dm b/code/modules/antagonists/revenant/revenant_blight.dm
index dcbc9bc8181fe..13a1ff7e1d606 100644
--- a/code/modules/antagonists/revenant/revenant_blight.dm
+++ b/code/modules/antagonists/revenant/revenant_blight.dm
@@ -19,7 +19,6 @@
if(affected_mob)
affected_mob.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, "#1d2953")
if(affected_mob.dna && affected_mob.dna.species)
- affected_mob.dna.species.handle_mutant_bodyparts(affected_mob)
affected_mob.set_haircolor(null, override = TRUE)
to_chat(affected_mob, span_notice("You feel better."))
..()
@@ -66,7 +65,6 @@
affected_mob.adjustStaminaLoss(22.5 * seconds_per_tick, updating_stamina = FALSE)
new /obj/effect/temp_visual/revenant(affected_mob.loc)
if(affected_mob.dna && affected_mob.dna.species)
- affected_mob.dna.species.handle_mutant_bodyparts(affected_mob,"#1d2953")
affected_mob.set_haircolor("#1d2953", override = TRUE)
affected_mob.visible_message(span_warning("[affected_mob] looks terrifyingly gaunt..."), span_revennotice("You suddenly feel like your skin is wrong..."))
affected_mob.add_atom_colour("#1d2953", TEMPORARY_COLOUR_PRIORITY)
diff --git a/code/modules/antagonists/revolution/revolution.dm b/code/modules/antagonists/revolution/revolution.dm
index fa07215cf6715..88dabd337887c 100644
--- a/code/modules/antagonists/revolution/revolution.dm
+++ b/code/modules/antagonists/revolution/revolution.dm
@@ -15,9 +15,7 @@
/datum/antagonist/rev/can_be_owned(datum/mind/new_owner)
if(new_owner.assigned_role.job_flags & JOB_HEAD_OF_STAFF)
return FALSE
- if(new_owner.unconvertable)
- return FALSE
- if(new_owner.current && HAS_TRAIT(new_owner.current, TRAIT_MINDSHIELD))
+ if(new_owner.current && HAS_MIND_TRAIT(new_owner.current, TRAIT_UNCONVERTABLE))
return FALSE
return ..()
diff --git a/code/modules/antagonists/spy/spy.dm b/code/modules/antagonists/spy/spy.dm
index 8bcc113f08939..2468eb27cad3f 100644
--- a/code/modules/antagonists/spy/spy.dm
+++ b/code/modules/antagonists/spy/spy.dm
@@ -8,6 +8,8 @@
hijack_speed = 1
ui_name = "AntagInfoSpy"
preview_outfit = /datum/outfit/spy
+ can_assign_self_objectives = TRUE
+ default_custom_objective = "Rob the station blind."
/// Whether an uplink has been created (successfully or at all)
var/uplink_created = FALSE
/// String displayed in the antag panel pointing the spy to where their uplink is.
diff --git a/code/modules/antagonists/spy/spy_bounty.dm b/code/modules/antagonists/spy/spy_bounty.dm
index 28984ce2272bd..01a1a1baf7b9a 100644
--- a/code/modules/antagonists/spy/spy_bounty.dm
+++ b/code/modules/antagonists/spy/spy_bounty.dm
@@ -186,7 +186,7 @@
var/datum/market_item/stolen_good/new_item = new(thing, item_price)
- return SSblackmarket.markets[/datum/market/blackmarket].add_item(new_item)
+ return SSmarket.markets[/datum/market/blackmarket].add_item(new_item)
/// Steal an item
/datum/spy_bounty/objective_item
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index 733e22461c795..bbdfa77eceacd 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -47,7 +47,8 @@
/// The uplink handler that this traitor belongs to.
var/datum/uplink_handler/uplink_handler
- var/uplink_sale_count = 3
+ var/uplink_sales_min = 4
+ var/uplink_sales_max = 6
///the final objective the traitor has to accomplish, be it escaping, hijacking, or just martyrdom.
var/datum/objective/ending_objective
@@ -98,14 +99,14 @@
var/list/uplink_items = list()
for(var/datum/uplink_item/item as anything in SStraitor.uplink_items)
- if(item.item && !item.cant_discount && (item.purchasable_from & uplink_handler.uplink_flag) && item.cost > 1)
+ if(item.item && !item.cant_discount && (item.purchasable_from & uplink_handler.uplink_flag) && item.cost >= TRAITOR_DISCOUNT_MIN_PRICE)
if(!length(item.restricted_roles) && !length(item.restricted_species))
uplink_items += item
continue
if((uplink_handler.assigned_role in item.restricted_roles) || (uplink_handler.assigned_species in item.restricted_species))
uplink_items += item
continue
- uplink_handler.extra_purchasable += create_uplink_sales(uplink_sale_count, /datum/uplink_category/discounts, 1, uplink_items)
+ uplink_handler.extra_purchasable += create_uplink_sales(rand(uplink_sales_min, uplink_sales_max), /datum/uplink_category/discounts, 1, uplink_items)
if(give_objectives)
forge_traitor_objectives()
diff --git a/code/modules/antagonists/traitor/objectives/final_objective/battle_royale.dm b/code/modules/antagonists/traitor/objectives/final_objective/battle_royale.dm
index 9b8f519da959d..e5208ff8331b9 100644
--- a/code/modules/antagonists/traitor/objectives/final_objective/battle_royale.dm
+++ b/code/modules/antagonists/traitor/objectives/final_objective/battle_royale.dm
@@ -38,6 +38,6 @@
equipped = TRUE
podspawn(list(
"target" = get_turf(user),
- "style" = STYLE_SYNDICATE,
+ "style" = /datum/pod_style/syndicate,
"spawn" = /obj/item/storage/box/syndie_kit/battle_royale,
))
diff --git a/code/modules/antagonists/traitor/objectives/final_objective/battlecruiser.dm b/code/modules/antagonists/traitor/objectives/final_objective/battlecruiser.dm
index b136a6f695cb3..7da84f6002619 100644
--- a/code/modules/antagonists/traitor/objectives/final_objective/battlecruiser.dm
+++ b/code/modules/antagonists/traitor/objectives/final_objective/battlecruiser.dm
@@ -44,6 +44,6 @@
emag_card.team = team
podspawn(list(
"target" = get_turf(user),
- "style" = STYLE_SYNDICATE,
+ "style" = /datum/pod_style/syndicate,
"spawn" = emag_card,
))
diff --git a/code/modules/antagonists/traitor/objectives/final_objective/infect_ai.dm b/code/modules/antagonists/traitor/objectives/final_objective/infect_ai.dm
index c8a4037d20af7..d47a2c6aabc59 100644
--- a/code/modules/antagonists/traitor/objectives/final_objective/infect_ai.dm
+++ b/code/modules/antagonists/traitor/objectives/final_objective/infect_ai.dm
@@ -51,6 +51,6 @@
sent_board = TRUE
podspawn(list(
"target" = get_turf(user),
- "style" = STYLE_SYNDICATE,
+ "style" = /datum/pod_style/syndicate,
"spawn" = /obj/item/ai_module/malf,
))
diff --git a/code/modules/antagonists/traitor/objectives/final_objective/objective_dark_matteor.dm b/code/modules/antagonists/traitor/objectives/final_objective/objective_dark_matteor.dm
index 6f68891bcfe7d..b90fdba73d03a 100644
--- a/code/modules/antagonists/traitor/objectives/final_objective/objective_dark_matteor.dm
+++ b/code/modules/antagonists/traitor/objectives/final_objective/objective_dark_matteor.dm
@@ -48,7 +48,7 @@
sent_satellites = TRUE
podspawn(list(
"target" = get_turf(user),
- "style" = STYLE_SYNDICATE,
+ "style" = /datum/pod_style/syndicate,
"spawn" = /obj/structure/closet/crate/engineering/smuggled_meteor_shields,
))
diff --git a/code/modules/antagonists/traitor/objectives/final_objective/romerol.dm b/code/modules/antagonists/traitor/objectives/final_objective/romerol.dm
index 0bfa29f3f168c..09edc4b0c7395 100644
--- a/code/modules/antagonists/traitor/objectives/final_objective/romerol.dm
+++ b/code/modules/antagonists/traitor/objectives/final_objective/romerol.dm
@@ -41,6 +41,6 @@
sent_romerol = TRUE
podspawn(list(
"target" = get_turf(user),
- "style" = STYLE_SYNDICATE,
+ "style" = /datum/pod_style/syndicate,
"spawn" = /obj/item/storage/box/syndie_kit/romerol,
))
diff --git a/code/modules/antagonists/traitor/objectives/final_objective/supermatter_cascade.dm b/code/modules/antagonists/traitor/objectives/final_objective/supermatter_cascade.dm
index 6c7dfaa2f095d..2e9396c90b070 100644
--- a/code/modules/antagonists/traitor/objectives/final_objective/supermatter_cascade.dm
+++ b/code/modules/antagonists/traitor/objectives/final_objective/supermatter_cascade.dm
@@ -52,6 +52,6 @@
sent_crystal = TRUE
podspawn(list(
"target" = get_turf(user),
- "style" = STYLE_SYNDICATE,
+ "style" = /datum/pod_style/syndicate,
"spawn" = /obj/item/destabilizing_crystal,
))
diff --git a/code/modules/antagonists/traitor/uplink_handler.dm b/code/modules/antagonists/traitor/uplink_handler.dm
index f78ddb0247892..2d27f3c4a0eff 100644
--- a/code/modules/antagonists/traitor/uplink_handler.dm
+++ b/code/modules/antagonists/traitor/uplink_handler.dm
@@ -126,6 +126,21 @@
on_update()
return TRUE
+/datum/uplink_handler/proc/purchase_raw_tc(mob/user, amount, atom/movable/source)
+ if(shop_locked)
+ return FALSE
+ if(telecrystals < amount)
+ return FALSE
+
+ telecrystals -= amount
+ var/tcs = new /obj/item/stack/telecrystal(get_turf(user), amount)
+ user.put_in_hands(tcs)
+
+ log_uplink("[key_name(user)] purchased [amount] raw telecrystals from [source]'s uplink")
+ on_update()
+ return TRUE
+
+
/// Generates objectives for this uplink handler
/datum/uplink_handler/proc/generate_objectives()
var/potential_objectives_left = maximum_potential_objectives - (length(potential_objectives) + length(active_objectives))
diff --git a/code/modules/antagonists/voidwalker/voidwalker.dm b/code/modules/antagonists/voidwalker/voidwalker.dm
new file mode 100644
index 0000000000000..6222dc0c35fbe
--- /dev/null
+++ b/code/modules/antagonists/voidwalker/voidwalker.dm
@@ -0,0 +1,60 @@
+/// Space antagonist that harasses people near space and cursed them if they get the chance
+/datum/antagonist/voidwalker
+ name = "\improper Voidwalker"
+ antagpanel_category = ANTAG_GROUP_ABOMINATIONS
+ job_rank = ROLE_VOIDWALKER
+ show_in_antagpanel = TRUE
+ antagpanel_category = "Voidwalker"
+ show_name_in_check_antagonists = TRUE
+ show_to_ghosts = TRUE
+ ui_name = "AntagInfoVoidwalker"
+ suicide_cry = "FOR THE VOID!!"
+ preview_outfit = /datum/outfit/voidwalker
+
+/datum/antagonist/voidwalker/greet()
+ . = ..()
+ owner.announce_objectives()
+
+/datum/antagonist/voidwalker/on_gain()
+ . = ..()
+
+ var/mob/living/carbon/human/body = owner.current
+ if(ishuman(body))
+ body.set_species(/datum/species/voidwalker)
+
+ forge_objectives()
+
+/datum/antagonist/voidwalker/on_removal()
+ var/mob/living/carbon/human/body = owner.current
+ if(ishuman(body))
+ body.set_species(/datum/species/human)
+
+ return ..()
+
+/datum/antagonist/voidwalker/forge_objectives()
+ var/datum/objective/voidwalker_objective/objective = new
+ objective.owner = owner
+ objectives += objective
+
+/datum/outfit/voidwalker
+ name = "Voidwalker (Preview only)"
+
+/datum/outfit/voidwalker/post_equip(mob/living/carbon/human/human, visualsOnly)
+ human.set_species(/datum/species/voidwalker)
+
+/datum/objective/voidwalker_objective
+
+/datum/objective/voidwalker_objective/New()
+ var/list/explanation_texts = list(
+ "Show them the beauty of the void. Drag them into the cosmic abyss, then impart the truth of the void unto them. Seek to enlighten, not destroy.",
+ "They must see what you have seen. They must walk where you have walked. Bring them to the void and show them the truth. The dead cannot know what you know.",
+ "Recover what you have lost. Bring your children into the inky black and return them to your flock.",
+ )
+ explanation_text = pick(explanation_texts)
+
+ if(prob(5))
+ explanation_text = "Man I fucking love glass."
+ ..()
+
+/datum/objective/voidwalker_objective/check_completion()
+ return owner.current.stat != DEAD
diff --git a/code/modules/antagonists/voidwalker/voidwalker_abilities.dm b/code/modules/antagonists/voidwalker/voidwalker_abilities.dm
new file mode 100644
index 0000000000000..4fe88f50d01c6
--- /dev/null
+++ b/code/modules/antagonists/voidwalker/voidwalker_abilities.dm
@@ -0,0 +1,67 @@
+/// Remain in someones view without breaking line of sight
+/datum/action/cooldown/spell/pointed/unsettle
+ name = "Unsettle"
+ desc = "Stare directly into someone who doesn't see you. Remain in their view for a bit to stun them for 2 seconds and announce your presence to them. "
+ button_icon_state = "terrify"
+ background_icon_state = "bg_void"
+ panel = null
+ spell_requirements = NONE
+ cooldown_time = 12 SECONDS
+ cast_range = 9
+ active_msg = "You prepare to stare down a target..."
+ deactive_msg = "You refocus your eyes..."
+ /// how long we need to stare at someone to unsettle them (woooooh)
+ var/stare_time = 8 SECONDS
+ /// how long we stun someone on succesful cast
+ var/stun_time = 2 SECONDS
+ /// stamina damage we doooo
+ var/stamina_damage = 80
+
+/datum/action/cooldown/spell/pointed/unsettle/is_valid_target(atom/cast_on)
+ . = ..()
+
+ if(!isliving(cast_on))
+ cast_on.balloon_alert(owner, "cannot be targeted!")
+ return FALSE
+
+ if(!check_if_in_view(cast_on))
+ owner.balloon_alert(owner, "cannot see you!")
+ return FALSE
+
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/unsettle/cast(mob/living/carbon/human/cast_on)
+ . = ..()
+
+ if(do_after(owner, stare_time, cast_on, IGNORE_TARGET_LOC_CHANGE | IGNORE_USER_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(check_if_in_view), cast_on), hidden = TRUE))
+ spookify(cast_on)
+ return
+ owner.balloon_alert(owner, "line of sight broken!")
+ return SPELL_CANCEL_CAST
+
+/datum/action/cooldown/spell/pointed/unsettle/proc/check_if_in_view(mob/living/carbon/human/target)
+ SIGNAL_HANDLER
+
+ if(target.is_blind() || !(owner in view(target, world.view)))
+ return FALSE
+ return TRUE
+
+/datum/action/cooldown/spell/pointed/unsettle/proc/spookify(mob/living/carbon/human/target)
+ target.Paralyze(stun_time)
+ target.adjustStaminaLoss(stamina_damage)
+ target.apply_status_effect(/datum/status_effect/speech/slurring/generic)
+ target.emote("scream")
+
+ new /obj/effect/temp_visual/circle_wave/unsettle(get_turf(owner))
+ new /obj/effect/temp_visual/circle_wave/unsettle(get_turf(target))
+ SEND_SIGNAL(owner, COMSIG_ATOM_REVEAL)
+
+/obj/effect/temp_visual/circle_wave/unsettle
+ color = COLOR_PURPLE
+
+/datum/action/cooldown/spell/list_target/telepathy/voidwalker
+ name = "Transmit"
+ background_icon_state = "bg_void"
+ button_icon = 'icons/mob/actions/actions_voidwalker.dmi'
+ button_icon_state = "voidwalker_telepathy"
+ panel = null
diff --git a/code/modules/antagonists/voidwalker/voidwalker_bodyparts.dm b/code/modules/antagonists/voidwalker/voidwalker_bodyparts.dm
new file mode 100644
index 0000000000000..a8e5b8de30f39
--- /dev/null
+++ b/code/modules/antagonists/voidwalker/voidwalker_bodyparts.dm
@@ -0,0 +1,147 @@
+///Turn the damage overlays glassy
+#define GLASSY_OVERLAY_MATRIX list(\
+ 1, 2, 2, 0, \
+ 0, 1, 0, 0, \
+ 0, 0, 1, 0, \
+ 0, 0, 0, 1, \
+ 0, 0, 0, 0)
+
+/obj/item/bodypart/head/voidwalker
+ texture_bodypart_overlay = /datum/bodypart_overlay/texture/spacey
+ icon_greyscale = 'icons/mob/human/species/voidwalker.dmi'
+ limb_id = SPECIES_VOIDWALKER
+ is_dimorphic = FALSE
+ bodypart_traits = list(TRAIT_MUTE)
+ head_flags = NONE
+ blocks_emissive = EMISSIVE_BLOCK_NONE
+
+ damage_overlay_color = GLASSY_OVERLAY_MATRIX
+
+ light_brute_msg = "splintered"
+ medium_brute_msg = "cracked"
+ heavy_brute_msg = "shattered"
+
+ light_burn_msg = "bent"
+ medium_burn_msg = "deformed"
+ heavy_burn_msg = "warped"
+
+ damage_examines = list(
+ BRUTE = GLASSY_BRUTE_EXAMINE_TEXT,
+ BURN = GLASSY_BURN_EXAMINE_TEXT,
+ )
+
+/obj/item/bodypart/chest/voidwalker
+ texture_bodypart_overlay = /datum/bodypart_overlay/texture/spacey
+ icon_greyscale = 'icons/mob/human/species/voidwalker.dmi'
+ limb_id = SPECIES_VOIDWALKER
+ is_dimorphic = FALSE
+ blocks_emissive = EMISSIVE_BLOCK_NONE
+
+ damage_overlay_color = GLASSY_OVERLAY_MATRIX
+
+ light_brute_msg = "splintered"
+ medium_brute_msg = "cracked"
+ heavy_brute_msg = "shattered"
+
+ light_burn_msg = "bent"
+ medium_burn_msg = "deformed"
+ heavy_burn_msg = "warped"
+
+ damage_examines = list(
+ BRUTE = GLASSY_BRUTE_EXAMINE_TEXT,
+ BURN = GLASSY_BURN_EXAMINE_TEXT,
+ )
+
+/obj/item/bodypart/arm/left/voidwalker
+ texture_bodypart_overlay = /datum/bodypart_overlay/texture/spacey
+ icon_greyscale = 'icons/mob/human/species/voidwalker.dmi'
+ limb_id = SPECIES_VOIDWALKER
+ is_dimorphic = FALSE
+ blocks_emissive = EMISSIVE_BLOCK_NONE
+
+ bodypart_traits = list(TRAIT_CHUNKYFINGERS)
+
+ damage_overlay_color = GLASSY_OVERLAY_MATRIX
+
+ light_brute_msg = "splintered"
+ medium_brute_msg = "cracked"
+ heavy_brute_msg = "shattered"
+
+ light_burn_msg = "bent"
+ medium_burn_msg = "deformed"
+ heavy_burn_msg = "warped"
+
+ damage_examines = list(
+ BRUTE = GLASSY_BRUTE_EXAMINE_TEXT,
+ BURN = GLASSY_BURN_EXAMINE_TEXT,
+ )
+
+/obj/item/bodypart/arm/right/voidwalker
+ texture_bodypart_overlay = /datum/bodypart_overlay/texture/spacey
+ icon_greyscale = 'icons/mob/human/species/voidwalker.dmi'
+ limb_id = SPECIES_VOIDWALKER
+ is_dimorphic = FALSE
+ blocks_emissive = EMISSIVE_BLOCK_NONE
+
+ bodypart_traits = list(TRAIT_CHUNKYFINGERS)
+
+ damage_overlay_color = GLASSY_OVERLAY_MATRIX
+
+ light_brute_msg = "splintered"
+ medium_brute_msg = "cracked"
+ heavy_brute_msg = "shattered"
+
+ light_burn_msg = "bent"
+ medium_burn_msg = "deformed"
+ heavy_burn_msg = "warped"
+
+ damage_examines = list(
+ BRUTE = GLASSY_BRUTE_EXAMINE_TEXT,
+ BURN = GLASSY_BURN_EXAMINE_TEXT,
+ )
+
+/obj/item/bodypart/leg/left/voidwalker
+ texture_bodypart_overlay = /datum/bodypart_overlay/texture/spacey
+ icon_greyscale = 'icons/mob/human/species/voidwalker.dmi'
+ limb_id = SPECIES_VOIDWALKER
+ is_dimorphic = FALSE
+ blocks_emissive = EMISSIVE_BLOCK_NONE
+
+ damage_overlay_color = GLASSY_OVERLAY_MATRIX
+
+ light_brute_msg = "splintered"
+ medium_brute_msg = "cracked"
+ heavy_brute_msg = "shattered"
+
+ light_burn_msg = "bent"
+ medium_burn_msg = "deformed"
+ heavy_burn_msg = "warped"
+
+ damage_examines = list(
+ BRUTE = GLASSY_BRUTE_EXAMINE_TEXT,
+ BURN = GLASSY_BURN_EXAMINE_TEXT,
+ )
+
+/obj/item/bodypart/leg/right/voidwalker
+ texture_bodypart_overlay = /datum/bodypart_overlay/texture/spacey
+ icon_greyscale = 'icons/mob/human/species/voidwalker.dmi'
+ limb_id = SPECIES_VOIDWALKER
+ is_dimorphic = FALSE
+ blocks_emissive = EMISSIVE_BLOCK_NONE
+
+ damage_overlay_color = GLASSY_OVERLAY_MATRIX
+
+ light_brute_msg = "splintered"
+ medium_brute_msg = "cracked"
+ heavy_brute_msg = "shattered"
+
+ light_burn_msg = "bent"
+ medium_burn_msg = "deformed"
+ heavy_burn_msg = "warped"
+
+ damage_examines = list(
+ BRUTE = GLASSY_BRUTE_EXAMINE_TEXT,
+ BURN = GLASSY_BURN_EXAMINE_TEXT,
+ )
+
+#undef GLASSY_OVERLAY_MATRIX
diff --git a/code/modules/antagonists/voidwalker/voidwalker_kidnap.dm b/code/modules/antagonists/voidwalker/voidwalker_kidnap.dm
new file mode 100644
index 0000000000000..f0d4c4349cef7
--- /dev/null
+++ b/code/modules/antagonists/voidwalker/voidwalker_kidnap.dm
@@ -0,0 +1,131 @@
+/// A global assoc list for the drop of point
+GLOBAL_LIST_EMPTY(voidwalker_void)
+
+/// Lardmarks meant to designate where voidwalker kidnapees are sent
+/obj/effect/landmark/voidwalker_void
+ name = "default voidwalker void landmark"
+ icon_state = "x"
+
+/obj/effect/landmark/voidwalker_void/Initialize(mapload)
+ . = ..()
+ GLOB.voidwalker_void += src
+
+/obj/effect/landmark/voidwalker_void/Destroy()
+ GLOB.voidwalker_void -= src
+ return ..()
+
+/// Voidwalker void where the people go
+/area/centcom/voidwalker_void
+ name = "Voidwalker void"
+ icon_state = "voidwalker"
+ has_gravity = STANDARD_GRAVITY
+ ambience_index = AMBIENCE_SPOOKY
+ sound_environment = SOUND_ENVIRONMENT_CAVE
+ area_flags = UNIQUE_AREA | NOTELEPORT | HIDDEN_AREA | BLOCK_SUICIDE
+
+/// Mini car where people drive around in in their mangled corpse to heal a bit before they get dumped back on station
+/obj/effect/wisp_mobile
+ name = "wisp"
+
+ icon = 'icons/obj/weapons/voidwalker_items.dmi'
+ icon_state = "wisp"
+
+ light_system = OVERLAY_LIGHT
+ light_color = COLOR_WHITE
+ light_range = 4
+ light_power = 1
+ light_on = TRUE
+
+ /// Delay between movements
+ var/move_delay = 0.5 SECONDS
+ /// when can we move again?
+ var/can_move
+ /// what do we eatt?
+ var/food_type = /obj/effect/wisp_food
+ /// how much do we heal per food?
+ var/heal_per_food = 15
+ /// Traits given to the wisp driver
+ var/wisp_driver_traits = list(TRAIT_STASIS, TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_HANDS_BLOCKED)
+
+/obj/effect/wisp_mobile/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
+ . = ..()
+
+ if(!isliving(arrived))
+ return
+
+ var/mob/living/driver = arrived
+ driver.forceMove(src)
+ driver.add_traits(wisp_driver_traits, REF(src))
+ add_atom_colour(random_color(), FIXED_COLOUR_PRIORITY)
+
+ addtimer(CALLBACK(driver, TYPE_PROC_REF(/atom/movable, forceMove), get_safe_random_station_turf()), 60 SECONDS)
+
+/obj/effect/wisp_mobile/relaymove(mob/living/user, direction)
+ if(can_move >= world.time)
+ return
+ can_move = world.time + move_delay
+
+ if(isturf(loc))
+ can_move = world.time + move_delay
+ try_step_multiz(direction)
+
+/obj/effect/wisp_mobile/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
+ . = ..()
+
+ maybe_loop_us(movement_dir)
+
+ var/obj/food = locate(food_type) in loc
+ if(!food)
+ return
+
+ qdel(food)
+
+ // make new food
+ var/area/our_area = get_area(src)
+ new food_type(pick(get_area_turfs(our_area)))
+
+ var/mob/living/driver = locate(/mob/living) in contents
+ if(driver)
+ driver.heal_ordered_damage(heal_per_food, list(BRUTE, BURN, OXY))
+ playsound(src, 'sound/misc/server-ready.ogg', 50, TRUE, -1)
+
+/obj/effect/wisp_mobile/Exited(atom/movable/gone, direction)
+ . = ..()
+
+ gone.remove_traits(wisp_driver_traits, REF(src))
+ to_chat(gone, span_boldwarning("You feel it would be very bad to get caught again."))
+ qdel(src)
+
+/// Loop us around, maybe, if we're going to bump into a wall
+/obj/effect/wisp_mobile/proc/maybe_loop_us(movement_dir)
+ var/turf/check_turf = get_step(get_turf(src), movement_dir)
+ if(!check_turf?.density) //we're not facing a wall, so dont do anything
+ return
+
+ // Loop us to the other side
+ var/reversed_dir = turn(movement_dir, 180)
+ check_turf = get_turf(src)
+
+ while(!check_turf.density)
+ check_turf = get_step(check_turf, reversed_dir)
+
+ // We found the wall on the opposite side, so take two steps back (one to get off the wall, another to not be wall adjacent)
+ check_turf = get_step(get_step(check_turf, movement_dir), movement_dir)
+ forceMove(check_turf)
+
+/// we only exist to be eaten by wisps for food 😔👊
+/obj/effect/wisp_food
+ name = "wisp"
+ icon = 'icons/obj/weapons/voidwalker_items.dmi'
+ icon_state = "wisp"
+
+ color = COLOR_YELLOW
+
+ light_system = OVERLAY_LIGHT
+ light_color = COLOR_WHITE
+ light_range = 4
+ light_power = 1
+ light_on = TRUE
+
+/obj/item/restraints/handcuffs/energy/void
+ breakouttime = INFINITY
diff --git a/code/modules/antagonists/voidwalker/voidwalker_loot.dm b/code/modules/antagonists/voidwalker/voidwalker_loot.dm
new file mode 100644
index 0000000000000..73ed9c7cd2207
--- /dev/null
+++ b/code/modules/antagonists/voidwalker/voidwalker_loot.dm
@@ -0,0 +1,43 @@
+/// Gives someone the stable voided trauma and then self destructs
+/obj/item/cosmic_skull
+ name = "cosmic skull"
+ desc = "You can see and feel the surrounding space pulsing through it..."
+
+ icon = 'icons/obj/weapons/voidwalker_items.dmi'
+ icon_state = "cosmic_skull_charged"
+
+ light_on = TRUE
+ light_color = "#CC00CC"
+ light_range = 3
+ /// Icon state for when drained
+ var/drained_icon_state = "cosmic_skull_drained"
+ /// How many uses does it have left?
+ var/uses = 1
+
+/obj/item/cosmic_skull/attack_self(mob/user, modifiers)
+ . = ..()
+
+ if(!uses || !ishuman(user))
+ return
+
+ var/mob/living/carbon/human/hewmon = user
+ if(is_species(hewmon, /datum/species/voidwalker))
+ to_chat(user, span_bolddanger("OH GOD NOO!!!! WHYYYYYYYYY!!!!! WHO WOULD DO THIS?!!"))
+ return
+
+ to_chat(user, span_purple("You begin staring into the [name]..."))
+
+ if(!do_after(user, 10 SECONDS, src))
+ return
+
+ var/mob/living/carbon/human/starer = user
+ starer.cure_trauma_type(/datum/brain_trauma/voided) //this wouldn't make much sense to have anymore
+
+ starer.gain_trauma(/datum/brain_trauma/voided/stable)
+ to_chat(user, span_purple("And a whole world opens up to you."))
+ playsound(get_turf(user), 'sound/effects/curse5.ogg', 60)
+
+ uses--
+ if(uses <= 0 )
+ icon_state = drained_icon_state
+ light_on = FALSE
diff --git a/code/modules/antagonists/voidwalker/voidwalker_organs.dm b/code/modules/antagonists/voidwalker/voidwalker_organs.dm
new file mode 100644
index 0000000000000..e6e3d028ac8ef
--- /dev/null
+++ b/code/modules/antagonists/voidwalker/voidwalker_organs.dm
@@ -0,0 +1,132 @@
+/// Voidwalker eyes with nightvision and thermals
+/obj/item/organ/internal/eyes/voidwalker
+ name = "blackened orbs"
+ desc = "These orbs will withstand the light of the sun, yet still see within the darkest voids."
+ eye_icon_state = null
+ pepperspray_protect = TRUE
+ flash_protect = FLASH_PROTECTION_WELDER
+ color_cutoffs = list(20, 10, 40)
+ sight_flags = SEE_MOBS
+
+/// Voidwalker brain stacked with a lot of the abilities
+/obj/item/organ/internal/brain/voidwalker
+ name = "cosmic brain"
+ desc = "A mind fully integrated into the cosmic thread."
+ icon = 'icons/obj/medical/organs/shadow_organs.dmi'
+ can_smoothen_out = FALSE
+
+ /// Alpha we have in space
+ var/space_alpha = 15
+ /// Alpha we have elsewhere
+ var/non_space_alpha = 255
+ /// We settle the un
+ var/datum/action/unsettle = /datum/action/cooldown/spell/pointed/unsettle
+ /// Regen effect we have in space
+ var/datum/status_effect/regen = /datum/status_effect/space_regeneration
+ /// Speed modifier given when in gravity
+ var/datum/movespeed_modifier/speed_modifier = /datum/movespeed_modifier/grounded_voidwalker
+ /// The void eater weapon
+ var/obj/item/glass_breaker = /obj/item/void_eater
+ /// Our brain transmit telepathy spell
+ var/datum/action/transmit = /datum/action/cooldown/spell/list_target/telepathy/voidwalker
+
+/obj/item/organ/internal/brain/voidwalker/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
+ . = ..()
+
+ RegisterSignal(organ_owner, COMSIG_ATOM_ENTERING, PROC_REF(on_atom_entering))
+
+ organ_owner.AddComponent(/datum/component/space_camo, space_alpha, non_space_alpha, 5 SECONDS)
+
+ organ_owner.AddElement(/datum/element/only_pull_living)
+ organ_owner.AddElement(/datum/element/glass_pacifist)
+ organ_owner.AddElement(/datum/element/no_crit_hitting)
+
+ organ_owner.apply_status_effect(regen)
+
+ unsettle = new unsettle(organ_owner)
+ unsettle.Grant(organ_owner)
+
+ transmit = new transmit(organ_owner)
+ transmit.Grant(organ_owner)
+
+ glass_breaker = new/obj/item/void_eater
+ organ_owner.put_in_hands(glass_breaker)
+
+/obj/item/organ/internal/brain/voidwalker/on_mob_remove(mob/living/carbon/organ_owner, special)
+ . = ..()
+
+ UnregisterSignal(organ_owner, COMSIG_ENTER_AREA)
+ alpha = 255
+
+ qdel(organ_owner.GetComponent(/datum/component/space_camo))
+
+ organ_owner.RemoveElement(/datum/element/only_pull_living)
+ organ_owner.RemoveElement(/datum/element/glass_pacifist)
+ organ_owner.RemoveElement(/datum/element/no_crit_hitting)
+
+ organ_owner.remove_status_effect(regen)
+
+ unsettle.Remove(organ_owner)
+ unsettle = initial(unsettle)
+
+ transmit.Remove(organ_owner)
+ transmit = initial(transmit)
+
+ if(glass_breaker)
+ qdel(glass_breaker)
+
+/obj/item/organ/internal/brain/voidwalker/proc/on_atom_entering(mob/living/carbon/organ_owner, atom/entering)
+ SIGNAL_HANDLER
+
+ if(!isturf(entering))
+ return
+
+ var/turf/new_turf = entering
+
+ //apply debufs for being in gravity
+ if(new_turf.has_gravity())
+ organ_owner.add_movespeed_modifier(speed_modifier)
+ //remove debufs for not being in gravity
+ else
+ organ_owner.remove_movespeed_modifier(speed_modifier)
+
+/obj/item/organ/internal/brain/voidwalker/on_death()
+ . = ..()
+
+ var/turf/spawn_loc = get_turf(owner)
+ new /obj/effect/spawner/random/glass_shards (spawn_loc)
+ new /obj/item/cosmic_skull (spawn_loc)
+ playsound(get_turf(owner), SFX_SHATTER, 100)
+
+ qdel(owner)
+
+/obj/item/implant/radio/voidwalker
+ radio_key = /obj/item/encryptionkey/heads/captain
+ actions_types = null
+
+/obj/effect/spawner/random/glass_shards
+ loot = list(/obj/item/shard = 2, /obj/item/shard/plasma = 1, /obj/item/shard/titanium = 1, /obj/item/shard/plastitanium = 1)
+ spawn_random_offset = TRUE
+
+ /// Min shards we generate
+ var/min_spawn = 4
+ /// Max shards we generate
+ var/max_spawn = 6
+
+/obj/effect/spawner/random/glass_shards/Initialize(mapload)
+ spawn_loot_count = rand(min_spawn, max_spawn)
+
+ return ..()
+
+/obj/effect/spawner/random/glass_shards/mini
+ min_spawn = 1
+ max_spawn = 2
+
+/obj/effect/spawner/random/glass_debris
+ /// Weighted list for the debris we spawn
+ loot = list(
+ /obj/effect/decal/cleanable/glass = 2,
+ /obj/effect/decal/cleanable/glass/plasma = 1,
+ /obj/effect/decal/cleanable/glass/titanium = 1,
+ /obj/effect/decal/cleanable/glass/plastitanium = 1,
+ )
diff --git a/code/modules/antagonists/voidwalker/voidwalker_particles.dm b/code/modules/antagonists/voidwalker/voidwalker_particles.dm
new file mode 100644
index 0000000000000..8ffbd4abd449e
--- /dev/null
+++ b/code/modules/antagonists/voidwalker/voidwalker_particles.dm
@@ -0,0 +1,15 @@
+/particles/void_kidnap
+ icon = 'icons/effects/particles/voidwalker.dmi'
+ icon_state = list("kidnap_1" = 1, "kidnap_2" = 1, "kidnap_3" = 2)
+ width = 100
+ height = 300
+ count = 1000
+ spawning = 20
+ lifespan = 1.5 SECONDS
+ fade = 1 SECONDS
+ velocity = list(0, 0.4, 0)
+ position = generator(GEN_SPHERE, 12, 12, NORMAL_RAND)
+ drift = generator(GEN_SPHERE, 0, 1, NORMAL_RAND)
+ friction = 0.2
+ gravity = list(0.95, 0)
+ grow = 0.05
diff --git a/code/modules/antagonists/voidwalker/voidwalker_species.dm b/code/modules/antagonists/voidwalker/voidwalker_species.dm
new file mode 100644
index 0000000000000..069582fbb2c7e
--- /dev/null
+++ b/code/modules/antagonists/voidwalker/voidwalker_species.dm
@@ -0,0 +1,73 @@
+/// Species for the voidwalker antagonist
+/datum/species/voidwalker
+ name = "\improper Voidling"
+ id = SPECIES_VOIDWALKER
+ sexes = FALSE
+ inherent_traits = list(
+ TRAIT_NOBREATH,
+ TRAIT_NO_UNDERWEAR,
+ TRAIT_RADIMMUNE,
+ TRAIT_VIRUSIMMUNE,
+ TRAIT_NOBLOOD,
+ TRAIT_NODISMEMBER,
+ TRAIT_NEVER_WOUNDED,
+ TRAIT_MOVE_FLYING,
+ TRAIT_RESISTCOLD,
+ TRAIT_RESISTHIGHPRESSURE,
+ TRAIT_RESISTLOWPRESSURE,
+ TRAIT_NOHUNGER,
+ TRAIT_FREE_HYPERSPACE_MOVEMENT,
+ TRAIT_ADVANCEDTOOLUSER,
+ TRAIT_NO_BLOOD_OVERLAY,
+ TRAIT_NO_THROWING,
+ )
+ changesource_flags = MIRROR_BADMIN
+
+ bodypart_overrides = list(
+ BODY_ZONE_HEAD = /obj/item/bodypart/head/voidwalker,
+ BODY_ZONE_CHEST = /obj/item/bodypart/chest/voidwalker,
+ BODY_ZONE_L_ARM = /obj/item/bodypart/arm/left/voidwalker,
+ BODY_ZONE_R_ARM = /obj/item/bodypart/arm/right/voidwalker,
+ BODY_ZONE_L_LEG = /obj/item/bodypart/leg/left/voidwalker,
+ BODY_ZONE_R_LEG = /obj/item/bodypart/leg/right/voidwalker,
+ )
+
+ no_equip_flags = ITEM_SLOT_OCLOTHING | ITEM_SLOT_ICLOTHING | ITEM_SLOT_GLOVES | ITEM_SLOT_MASK | ITEM_SLOT_HEAD | ITEM_SLOT_FEET | ITEM_SLOT_BACK | ITEM_SLOT_EARS | ITEM_SLOT_EYES
+
+ mutantbrain = /obj/item/organ/internal/brain/voidwalker
+ mutanteyes = /obj/item/organ/internal/eyes/voidwalker
+ mutantheart = null
+ mutantlungs = null
+ mutanttongue = null
+
+ siemens_coeff = 0
+
+/datum/species/voidwalker/on_species_gain(mob/living/carbon/human/human_who_gained_species, datum/species/old_species, pref_load)
+ . = ..()
+
+ human_who_gained_species.AddComponent(/datum/component/glass_passer)
+ human_who_gained_species.AddComponent(/datum/component/space_dive)
+ human_who_gained_species.AddComponent(/datum/component/space_kidnap)
+
+ var/obj/item/implant/radio = new /obj/item/implant/radio/voidwalker (human_who_gained_species)
+ radio.implant(human_who_gained_species, null, TRUE, TRUE)
+
+ human_who_gained_species.AddComponent(/datum/component/planet_allergy)
+
+ human_who_gained_species.fully_replace_character_name(null, pick(GLOB.voidwalker_names))
+
+/datum/species/voidwalker/on_species_loss(mob/living/carbon/human/human, datum/species/new_species, pref_load)
+ . = ..()
+
+ qdel(human.GetComponent(/datum/component/glass_passer))
+ qdel(human.GetComponent(/datum/component/space_dive))
+ qdel(human.GetComponent(/datum/component/space_kidnap))
+
+ var/obj/item/implant/radio = locate(/obj/item/implant/radio/voidwalker) in human
+ if(radio)
+ qdel(radio)
+
+ qdel(human.GetComponent(/datum/component/planet_allergy))
+
+/datum/species/voidwalker/check_roundstart_eligible()
+ return FALSE
diff --git a/code/modules/antagonists/voidwalker/voidwalker_status_effects.dm b/code/modules/antagonists/voidwalker/voidwalker_status_effects.dm
new file mode 100644
index 0000000000000..7934e757077af
--- /dev/null
+++ b/code/modules/antagonists/voidwalker/voidwalker_status_effects.dm
@@ -0,0 +1,46 @@
+/// THE GRAVITY!!! IT WEIGHS!!!
+/datum/movespeed_modifier/grounded_voidwalker
+ multiplicative_slowdown = 1.1
+
+/// Regenerate in space
+/datum/status_effect/space_regeneration
+ id = "space_regeneration"
+ duration = INFINITE
+ alert_type = null
+ // How much do we heal per tick?
+ var/healing = 1.5
+
+/datum/status_effect/space_regeneration/tick(effect)
+ heal_owner()
+
+/// Regenerate health whenever this status effect is applied or reapplied
+/datum/status_effect/space_regeneration/proc/heal_owner()
+ if(isspaceturf(get_turf(owner)))
+ owner.heal_ordered_damage(healing, list(BRUTE, BURN, OXY, STAMINA, TOX, BRAIN))
+
+/datum/status_effect/planet_allergy
+ id = "planet_allergy"
+ duration = INFINITE
+ alert_type = /atom/movable/screen/alert/status_effect/veryhighgravity
+
+/datum/status_effect/planet_allergy/tick()
+ owner.adjustBruteLoss(1)
+
+/atom/movable/screen/alert/status_effect/veryhighgravity
+ name = "Crushing Gravity"
+ desc = "You're getting crushed by high gravity, picking up items and movement will be slowed. You'll also accumulate brute damage!"
+ icon_state = "paralysis"
+
+/datum/status_effect/void_eatered
+ duration = 10 SECONDS
+ remove_on_fullheal = TRUE
+
+/datum/status_effect/void_eatered/on_apply()
+ . = ..()
+
+ ADD_TRAIT(owner, TRAIT_NODEATH, REF(src))
+
+/datum/status_effect/void_eatered/on_remove()
+ . = ..()
+
+ REMOVE_TRAIT(owner, TRAIT_NODEATH, REF(src))
diff --git a/code/modules/antagonists/voidwalker/voidwalker_traumas.dm b/code/modules/antagonists/voidwalker/voidwalker_traumas.dm
new file mode 100644
index 0000000000000..cf4f389a0d6f3
--- /dev/null
+++ b/code/modules/antagonists/voidwalker/voidwalker_traumas.dm
@@ -0,0 +1,91 @@
+/// Curse brain trauma that makes someone space textured, mute, pacifist and forbids them from entering space
+/datum/brain_trauma/voided
+ name = "Voided"
+ desc = "They've seen the secrets of the cosmos, in exchange for a curse that keeps them chained."
+ scan_desc = "cosmic neural pattern"
+ gain_text = ""
+ lose_text = ""
+ resilience = TRAUMA_RESILIENCE_LOBOTOMY
+ random_gain = FALSE
+ /// Type for the bodypart texture we add
+ var/bodypart_overlay_type = /datum/bodypart_overlay/texture/spacey
+ ///traits we give on gain
+ var/list/traits_to_apply = list(TRAIT_MUTE, TRAIT_PACIFISM)
+ /// Do we ban the person from entering space?
+ var/ban_from_space = TRUE
+
+/datum/brain_trauma/voided/on_gain()
+ . = ..()
+
+ owner.add_traits(traits_to_apply, REF(src))
+ if(ban_from_space)
+ owner.AddComponent(/datum/component/banned_from_space)
+ owner.AddComponent(/datum/component/planet_allergy)
+ RegisterSignal(owner, COMSIG_CARBON_ATTACH_LIMB, PROC_REF(texture_limb)) //also catch new limbs being attached
+ RegisterSignal(owner, COMSIG_CARBON_REMOVE_LIMB, PROC_REF(untexture_limb)) //and remove it from limbs if they go away
+
+ for(var/obj/item/bodypart as anything in owner.bodyparts)
+ texture_limb(owner, bodypart)
+
+ //your underwear is belong to us
+ if(ishuman(owner))
+ var/mob/living/carbon/human/human = owner //CARBON WILL NEVER BE REAL!!!!!
+ human.underwear = "Nude"
+ human.undershirt = "Nude"
+ human.socks = "Nude"
+
+ owner.update_body()
+
+/datum/brain_trauma/voided/on_lose()
+ . = ..()
+
+ owner.remove_traits(traits_to_apply, REF(src))
+ UnregisterSignal(owner, list(COMSIG_CARBON_ATTACH_LIMB, COMSIG_CARBON_REMOVE_LIMB))
+ if(ban_from_space)
+ qdel(owner.GetComponent(/datum/component/banned_from_space))
+ qdel(owner.GetComponent(/datum/component/planet_allergy))
+
+ for(var/obj/item/bodypart/bodypart as anything in owner.bodyparts)
+ untexture_limb(owner, bodypart)
+
+/// Apply the space texture
+/datum/brain_trauma/voided/proc/texture_limb(atom/source, obj/item/bodypart/limb)
+ SIGNAL_HANDLER
+
+ limb.add_bodypart_overlay(new bodypart_overlay_type)
+ if(istype(limb, /obj/item/bodypart/head))
+ var/obj/item/bodypart/head/head = limb
+ head.head_flags &= ~HEAD_EYESPRITES
+
+/datum/brain_trauma/voided/proc/untexture_limb(atom/source, obj/item/bodypart/limb)
+ SIGNAL_HANDLER
+
+ var/overlay = locate(bodypart_overlay_type) in limb.bodypart_overlays
+ if(overlay)
+ limb.remove_bodypart_overlay(overlay)
+
+ if(istype(limb, /obj/item/bodypart/head))
+ var/obj/item/bodypart/head/head = limb
+ head.head_flags = initial(head.head_flags)
+
+/datum/brain_trauma/voided/on_death()
+ . = ..()
+
+ if(is_on_a_planet(owner))
+ qdel(src)
+
+/// Positive version of the previous. Get space immunity and the ability to slowly move through glass (but you still get muted)
+/datum/brain_trauma/voided/stable
+ scan_desc = "stable cosmic neural pattern"
+ traits_to_apply = list(TRAIT_MUTE, TRAIT_RESISTLOWPRESSURE, TRAIT_RESISTCOLD)
+ ban_from_space = FALSE
+
+/datum/brain_trauma/voided/stable/on_gain()
+ . = ..()
+
+ owner.AddComponent(/datum/component/glass_passer, 2 SECONDS)
+
+/datum/brain_trauma/voided/stable/on_lose()
+ . = ..()
+
+ qdel(owner.GetComponent(/datum/component/glass_passer))
diff --git a/code/modules/antagonists/voidwalker/voidwalker_void_eater.dm b/code/modules/antagonists/voidwalker/voidwalker_void_eater.dm
new file mode 100644
index 0000000000000..9df3eabab3d21
--- /dev/null
+++ b/code/modules/antagonists/voidwalker/voidwalker_void_eater.dm
@@ -0,0 +1,99 @@
+/**
+ * An armblade that pops windows
+ */
+/obj/item/void_eater
+ name = "void eater" //as opposed to full eater
+ desc = "A deformed appendage, capable of shattering any glass and any flesh."
+ icon = 'icons/obj/weapons/voidwalker_items.dmi'
+ icon_state = "tentacle"
+ inhand_icon_state = "tentacle"
+ force = 25
+ armour_penetration = 35
+ lefthand_file = 'icons/mob/inhands/antag/voidwalker_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/antag/voidwalker_righthand.dmi'
+ blocks_emissive = EMISSIVE_BLOCK_NONE
+ item_flags = ABSTRACT | DROPDEL
+ resistance_flags = INDESTRUCTIBLE | ACID_PROOF | FIRE_PROOF | LAVA_PROOF | UNACIDABLE
+ w_class = WEIGHT_CLASS_HUGE
+ tool_behaviour = TOOL_MINING
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ wound_bonus = -30
+ bare_wound_bonus = 20
+
+ /// Damage we loss per hit
+ var/damage_loss_per_hit = 0.5
+ /// The minimal damage we can reach
+ var/damage_minimum = 15
+
+/obj/item/void_eater/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NODROP, HAND_REPLACEMENT_TRAIT)
+
+ AddComponent(/datum/component/temporary_glass_shatterer)
+
+/obj/item/void_eater/equipped(mob/user)
+ . = ..()
+
+ RegisterSignal(user, COMSIG_VOIDWALKER_SUCCESFUL_KIDNAP, PROC_REF(refresh))
+
+/obj/item/void_eater/dropped(mob/user, silent)
+ . = ..()
+ UnregisterSignal(user, COMSIG_VOIDWALKER_SUCCESFUL_KIDNAP)
+
+/obj/item/void_eater/examine(mob/user)
+ . = ..()
+ . += span_notice("The [name] weakens each hit, recharge it by kidnapping someone!")
+ . += span_notice("Sharpness: [round(force)]/[initial(force)]")
+
+/obj/item/void_eater/attack(mob/living/target_mob, mob/living/user, params)
+ if(!ishuman(target_mob))
+ return ..()
+
+ var/mob/living/carbon/human/hewmon = target_mob
+
+ if(hewmon.has_trauma_type(/datum/brain_trauma/voided))
+ var/turf/spawnloc = get_turf(hewmon)
+
+ if(hewmon.stat != DEAD)
+ hewmon.balloon_alert(user, "already voided!")
+ playsound(hewmon, SFX_SHATTER, 60)
+ new /obj/effect/spawner/random/glass_shards/mini (spawnloc)
+ hewmon.adjustBruteLoss(10) // BONUS DAMAGE
+ else
+ hewmon.balloon_alert(user, "shattering...")
+ if(do_after(user, 4 SECONDS, hewmon))
+ new /obj/effect/spawner/random/glass_shards (spawnloc)
+ var/obj/item/organ/brain = hewmon.get_organ_by_type(/obj/item/organ/internal/brain)
+ if(brain)
+ brain.Remove(hewmon)
+ brain.forceMove(spawnloc)
+ brain.balloon_alert(user, "shattered!")
+ playsound(hewmon, SFX_SHATTER, 100)
+ qdel(hewmon)
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+ if(hewmon.stat == HARD_CRIT && !hewmon.has_trauma_type(/datum/brain_trauma/voided))
+ target_mob.balloon_alert(user, "is in crit!")
+ return COMPONENT_CANCEL_ATTACK_CHAIN
+
+ target_mob.apply_status_effect(/datum/status_effect/void_eatered)
+
+ if(force == damage_minimum + damage_loss_per_hit)
+ user.balloon_alert(user, "void eater blunted!")
+
+ force = max(force - damage_loss_per_hit, damage_minimum)
+
+ if(prob(5))
+ new /obj/effect/spawner/random/glass_debris (get_turf(user))
+ return ..()
+
+/// Called when the voidwalker kidnapped someone
+/obj/item/void_eater/proc/refresh(mob/living/carbon/human/voidwalker)
+ SIGNAL_HANDLER
+
+ force = initial(force)
+
+ color = "#000000"
+ animate(src, color = null, time = 1 SECONDS)//do a color flashy woosh
+
+ to_chat(voidwalker, span_boldnotice("Your [name] refreshes!"))
diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm
index 9176558c7a166..259164beb1b2c 100644
--- a/code/modules/antagonists/wizard/equipment/artefact.dm
+++ b/code/modules/antagonists/wizard/equipment/artefact.dm
@@ -117,7 +117,6 @@
anchored = TRUE
density = TRUE
move_resist = INFINITY
- plane = MASSIVE_OBJ_PLANE
plane = ABOVE_LIGHTING_PLANE
light_range = 6
appearance_flags = LONG_GLIDE
@@ -345,7 +344,6 @@
icon = 'icons/effects/magic.dmi'
icon_state = "tornado"
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
randomdir = FALSE
duration = 8 SECONDS
movement_type = PHASING
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index 925f368fe3b54..ce1d527cb0bc1 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -298,7 +298,7 @@
return FALSE
if(!forced)
- var/datum/antagonist/cult/cultist = IS_CULTIST(user)
+ var/datum/antagonist/cult/cultist = GET_CULTIST(user)
if(cultist)
var/datum/team/cult/cult_team = cultist.get_team()
if(victim.mind && cult_team.is_sacrifice_target(victim.mind))
@@ -511,7 +511,7 @@
playsound(newstruct, 'sound/effects/constructform.ogg', 50)
if(stoner)
newstruct.faction |= "[REF(stoner)]"
- newstruct.master = stoner
+ newstruct.construct_master = stoner
var/datum/action/innate/seek_master/seek_master = new
seek_master.Grant(newstruct)
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm
index 7dc220d516f01..18e2dae715c34 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/_entry.dm
@@ -70,6 +70,15 @@
for(var/spell in user.actions)
if(is_type_in_typecache(spell, no_coexistance_typecache))
return FALSE
+ var/datum/antagonist/wizard/wizard_datum = user.mind.has_antag_datum(/datum/antagonist/wizard)
+ if(!wizard_datum)
+ return TRUE
+ for(var/perks in wizard_datum.perks)
+ if(is_type_in_typecache(perks, no_coexistance_typecache))
+ return FALSE
+ if(is_type_in_list(src, wizard_datum.perks))
+ to_chat(user, span_warning("This perk already learned!"))
+ return FALSE
return TRUE
/**
@@ -137,6 +146,9 @@
* Return TRUE if it can refunded, FALSE otherwise
*/
/datum/spellbook_entry/proc/can_refund(mob/living/carbon/human/user, obj/item/spellbook/book)
+ if(HAS_TRAIT(user, TRAIT_SPELLS_LOTTERY))
+ to_chat(user, span_notice("No refund."))
+ return FALSE
if(!refundable)
return FALSE
if(!book.refunds_allowed)
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm
index a66d99c21c88d..e7c204a39e214 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/defensive.dm
@@ -55,7 +55,7 @@
it will become easier for others to find your item of power."
spell_type = /datum/action/cooldown/spell/lichdom
category = SPELLBOOK_CATEGORY_DEFENSIVE
- no_coexistance_typecache = list(/datum/action/cooldown/spell/splattercasting)
+ no_coexistance_typecache = list(/datum/action/cooldown/spell/splattercasting, /datum/spellbook_entry/perks/wormborn)
/datum/spellbook_entry/chuunibyou
name = "Chuuni Invocations"
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm
index 9eb35cbf7b42e..6b8272ed5b7ad 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/offensive.dm
@@ -39,6 +39,13 @@
category = SPELLBOOK_CATEGORY_OFFENSIVE
cost = 1
+/datum/spellbook_entry/tie_shoes
+ name = "Tie Shoes"
+ desc = "This unassuming spell first unties, then knots the target's shoes. While weak at first glance, each upgrade quietens the spell, allowing it to untie laceless footwear and even summon shoes to knot!"
+ spell_type = /datum/action/cooldown/spell/pointed/untie_shoes
+ category = SPELLBOOK_CATEGORY_OFFENSIVE
+ cost = 1
+
/datum/spellbook_entry/mutate
name = "Mutate"
desc = "Causes you to turn into a hulk and gain laser vision for a short while."
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm
new file mode 100644
index 0000000000000..0230274b8e88b
--- /dev/null
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm
@@ -0,0 +1,184 @@
+#define SPELLBOOK_CATEGORY_PERKS "Perks"
+
+/datum/spellbook_entry/perks
+ desc = "Main node of perks"
+ category = SPELLBOOK_CATEGORY_PERKS
+ refundable = FALSE // no refund
+ requires_wizard_garb = FALSE
+
+/datum/spellbook_entry/perks/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy)
+ var/datum/antagonist/wizard/wizard_datum = user.mind.has_antag_datum(/datum/antagonist/wizard)
+ if(wizard_datum)
+ wizard_datum.perks += src
+ to_chat(user, span_notice("You got a new perk: [src.name]."))
+ return TRUE
+
+/datum/spellbook_entry/perks/fourhands
+ name = "Four Hands"
+ desc = "Gives you even more hands to perform magic"
+
+/datum/spellbook_entry/perks/fourhands/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy)
+ . = ..()
+ user.change_number_of_hands(4)
+
+/datum/spellbook_entry/perks/wormborn
+ name = "Worm Born"
+ desc = "Your soul is infested with mana worms. When you die, you will be reborn as a large worm. \
+ When the worm dies, it has no such luck. Parasitic infection prevents you from binding your soul to objects."
+ no_coexistance_typecache = list(/datum/action/cooldown/spell/lichdom)
+
+/datum/spellbook_entry/perks/wormborn/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy)
+ . = ..()
+ user.AddComponent(/datum/component/wormborn)
+
+/datum/spellbook_entry/perks/dejavu
+ name = "Déjà vu"
+ desc = "Every 60 seconds returns you to the place where you were 60 seconds ago with the same amount of health as you had 60 seconds ago."
+
+/datum/spellbook_entry/perks/dejavu/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy)
+ . = ..()
+ RegisterSignal(user, COMSIG_ENTER_AREA, PROC_REF(give_dejavu))
+
+/datum/spellbook_entry/perks/dejavu/proc/give_dejavu(mob/living/carbon/human/wizard, area/new_area)
+ SIGNAL_HANDLER
+
+ if(istype(new_area, /area/centcom))
+ return
+ wizard.AddComponent(/datum/component/dejavu/wizard, 1, 60 SECONDS, TRUE)
+ UnregisterSignal(wizard, COMSIG_ENTER_AREA)
+
+/datum/spellbook_entry/perks/spell_lottery
+ name = "Spells Lottery"
+ desc = "Spells Lottery gives you the chance to get something from the book absolutely free, but you can no longer refund any purchases."
+
+/datum/spellbook_entry/perks/spell_lottery/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy)
+ . = ..()
+ ADD_TRAIT(user, TRAIT_SPELLS_LOTTERY, REF(src))
+
+/datum/spellbook_entry/perks/gamble
+ name = "Gamble"
+ desc = "You get 2 random perks."
+
+/datum/spellbook_entry/perks/gamble/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy)
+ . = ..()
+ var/datum/antagonist/wizard/check_perks = user.mind.has_antag_datum(/datum/antagonist/wizard)
+ var/perks_allocated = 0
+ var/list/taking_perks = list()
+ for(var/datum/spellbook_entry/perks/generate_perk in book.entries)
+ if(istype(generate_perk, src))
+ continue
+ if(check_perks && is_type_in_list(generate_perk, check_perks.perks))
+ continue
+ taking_perks += generate_perk
+ perks_allocated++
+ if(perks_allocated >= 2)
+ break
+ if(taking_perks.len < 1)
+ to_chat(user, span_warning("Gamble cannot give 2 perks, so points are returned"))
+ return FALSE
+ taking_perks = shuffle(taking_perks)
+ for(var/datum/spellbook_entry/perks/perks_ready in taking_perks)
+ perks_ready.buy_spell(user, book, log_buy)
+
+/datum/spellbook_entry/perks/heart_eater
+ name = "Heart Eater"
+ desc = "Gives you ability to obtain a person's life force by eating their heart. \
+ By eating someone's heart you can increase your damage resistance or gain random mutation. \
+ Heart also give strong healing buff."
+
+/datum/spellbook_entry/perks/heart_eater/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy)
+ . = ..()
+ user.AddComponent(/datum/component/heart_eater)
+
+/datum/spellbook_entry/perks/slime_friends
+ name = "Slime Friends"
+ desc = "Slimes are your friends. \
+ Every 15 seconds you lose some nutriments and summon a random evil slime to fight on your side."
+
+/datum/spellbook_entry/perks/slime_friends/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy)
+ . = ..()
+ user.AddComponent(/datum/component/slime_friends)
+
+/datum/spellbook_entry/perks/transparence
+ name = "Transparence"
+ desc = "You become a little closer to the world of the dead. \
+ Projectiles pass through you, but you lose 25% of your health and you are hunted by a terrible curse which wants to return you to the afterlife."
+
+/datum/spellbook_entry/perks/transparence/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy)
+ . = ..()
+ user.maxHealth *= 0.75
+ user.alpha = 125
+ ADD_TRAIT(user, TRAIT_UNHITTABLE_BY_PROJECTILES, REF(src))
+ RegisterSignal(user, COMSIG_ENTER_AREA, PROC_REF(make_stalker))
+
+/datum/spellbook_entry/perks/transparence/proc/make_stalker(mob/living/carbon/human/wizard, area/new_area)
+ SIGNAL_HANDLER
+
+ if(new_area == GLOB.areas_by_type[/area/centcom/wizard_station])
+ return
+ wizard.gain_trauma(/datum/brain_trauma/magic/stalker)
+ UnregisterSignal(wizard, COMSIG_ENTER_AREA)
+
+/datum/spellbook_entry/perks/magnetism
+ name = "Magnetism"
+ desc = "You get a small gravity anomaly that orbit around you. \
+ Nearby things will be attracted to you."
+
+/datum/spellbook_entry/perks/magnetism/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy)
+ . = ..()
+ var/atom/movable/magnitizm = new /obj/effect/wizard_magnetism(get_turf(user))
+ magnitizm.orbit(user, 20)
+
+/obj/effect/wizard_magnetism
+ name = "magnetic anomaly"
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "shield2"
+ /// We need to orbit around someone.
+ var/datum/weakref/owner
+
+/obj/effect/wizard_magnetism/New(loc, ...)
+ . = ..()
+ transform *= 0.4
+
+/obj/effect/wizard_magnetism/orbit(atom/new_owner, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
+ . = ..()
+ if(!isliving(new_owner))
+ return
+ owner = WEAKREF(new_owner)
+ RegisterSignal(new_owner, COMSIG_ENTER_AREA, PROC_REF(check_area))
+ RegisterSignal(new_owner, COMSIG_LIVING_DEATH, PROC_REF(on_owner_death))
+
+/obj/effect/wizard_magnetism/proc/check_area(mob/living/wizard, area/new_area)
+ SIGNAL_HANDLER
+
+ if(new_area == GLOB.areas_by_type[/area/centcom/wizard_station])
+ return
+ START_PROCESSING(SSprocessing, src)
+ UnregisterSignal(wizard, COMSIG_ENTER_AREA)
+
+/obj/effect/wizard_magnetism/proc/on_owner_death()
+ SIGNAL_HANDLER
+
+ stop_orbit()
+
+/obj/effect/wizard_magnetism/process(seconds_per_tick)
+ if(isnull(owner))
+ stop_orbit()
+ return
+ var/mob/living/wizard = owner.resolve()
+ var/list/things_in_range = orange(5, wizard) - orange(1, wizard)
+ for(var/obj/take_object in things_in_range)
+ if(!take_object.anchored)
+ step_towards(take_object, wizard)
+ for(var/mob/living/living_mov in things_in_range)
+ if(wizard)
+ if(living_mov == wizard)
+ continue
+ if(!living_mov.mob_negates_gravity())
+ step_towards(living_mov, wizard)
+
+/obj/effect/wizard_magnetism/stop_orbit()
+ STOP_PROCESSING(SSprocessing, src)
+ qdel(src)
+
+#undef SPELLBOOK_CATEGORY_PERKS
diff --git a/code/modules/antagonists/wizard/equipment/wizard_spellbook.dm b/code/modules/antagonists/wizard/equipment/wizard_spellbook.dm
index f13b53b12edd4..18374e9bcff25 100644
--- a/code/modules/antagonists/wizard/equipment/wizard_spellbook.dm
+++ b/code/modules/antagonists/wizard/equipment/wizard_spellbook.dm
@@ -172,7 +172,7 @@
data["full_random_bonus"] = initial(uses) + full_random_bonus
return data
-/obj/item/spellbook/ui_act(action, params)
+/obj/item/spellbook/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -229,6 +229,10 @@
return FALSE
to_buy.times++
+ if(HAS_TRAIT(user, TRAIT_SPELLS_LOTTERY))
+ if(prob(50 / to_buy.cost))
+ to_chat(user, span_notice("This spell was given to you for free!"))
+ return TRUE
uses -= to_buy.cost
return TRUE
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm b/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm
index d2f4a5a076358..7c228574a0694 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/clown.dm
@@ -9,7 +9,16 @@
/datum/grand_finale/clown/trigger(mob/living/carbon/human/invoker)
for(var/mob/living/carbon/human/victim as anything in GLOB.human_list)
victim.Unconscious(3 SECONDS)
- if (!victim.mind || IS_HUMAN_INVADER(victim) || victim == invoker)
+ if (victim == invoker)
+ if(locate(/datum/action/cooldown/spell/pointed/untie_shoes) in invoker.actions)
+ continue
+ var/datum/action/cooldown/spell/pointed/untie_shoes/newer_spell = new(invoker)
+ newer_spell.Grant(invoker)
+ for(var/i in 1 to newer_spell.spell_max_level)
+ newer_spell.level_spell()
+ newer_spell.invocation_type = INVOCATION_SHOUT
+ continue
+ if (!victim.mind || IS_HUMAN_INVADER(victim))
continue
if (HAS_TRAIT(victim, TRAIT_CLOWN_ENJOYER))
victim.add_mood_event("clown_world", /datum/mood_event/clown_world)
@@ -23,6 +32,8 @@
if (is_clown_job(victim.mind.assigned_role))
var/datum/action/cooldown/spell/conjure_item/clown_pockets/new_spell = new(victim)
new_spell.Grant(victim)
+ var/datum/action/cooldown/spell/pointed/untie_shoes/newer_spell = new(victim)
+ newer_spell.Grant(victim)
continue
dress_as_magic_clown(victim)
if (prob(15))
diff --git a/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm b/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
index 79364a80a198a..15900a6ac0b0a 100644
--- a/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
@@ -19,7 +19,7 @@
pixel_y = 16
pixel_z = -48
anchored = TRUE
- interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND
+ interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_ATTACK_PAW
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
layer = SIGIL_LAYER
/// How many prior grand rituals have been completed?
diff --git a/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm b/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm
index ffc2ea5d590af..a760115a61b9e 100644
--- a/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm
@@ -335,7 +335,7 @@
/obj/effect/abstract/local_food_rain/proc/drop_food(turf/landing_zone)
podspawn(list(
"target" = landing_zone,
- "style" = STYLE_SEETHROUGH,
+ "style" = /datum/pod_style/seethrough,
"spawn" = get_random_food(),
"delays" = list(POD_TRANSIT = 0, POD_FALLING = (3 SECONDS), POD_OPENING = 0, POD_LEAVING = 0),
"effectStealth" = TRUE,
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index e6da5195b0254..335c934a32052 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -25,6 +25,8 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
show_to_ghosts = TRUE
/// This mob's Grand Ritual ability
var/datum/action/cooldown/grand_ritual/ritual
+ /// Perks that wizard learn
+ var/list/perks = list()
/datum/antagonist/wizard_minion
name = "Wizard Minion"
diff --git a/code/modules/art/paintings.dm b/code/modules/art/paintings.dm
index 0bb3bcfe8ead7..09583a66a7dce 100644
--- a/code/modules/art/paintings.dm
+++ b/code/modules/art/paintings.dm
@@ -5,6 +5,7 @@
///////////
/obj/structure/easel
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "easel"
desc = "Only for the finest of art!"
icon = 'icons/obj/art/artstuff.dmi'
@@ -57,6 +58,8 @@
var/icon/generated_icon
///boolean that blocks persistence from saving it. enabled from printing copies, because we do not want to save copies.
var/no_save = FALSE
+ /// What icon file do we get our frame sprites from?
+ var/frame_icon = 'icons/obj/art/painting_frames.dmi'
///reference to the last patron's mind datum, used to allow them (and no others) to change the frame before the round ends.
var/datum/weakref/last_patron
@@ -64,8 +67,11 @@
var/datum/painting/painting_metadata
// Painting overlay offset when framed
- var/framed_offset_x = 11
- var/framed_offset_y = 10
+ var/framed_offset_x = 10
+ var/framed_offset_y = 9
+
+ /// Additional offset to apply to the parent while framed
+ var/wall_y_offset = 0
/**
* How big the grid cells that compose the painting are in the UI (multiplied by zoom).
@@ -76,7 +82,7 @@
///A list that keeps track of the current zoom value for each current viewer.
var/list/zoom_by_observer
- SET_BASE_PIXEL(11, 10)
+ SET_BASE_PIXEL(10, 9)
custom_price = PAYCHECK_CREW
@@ -91,6 +97,14 @@
painting_metadata.height = height
ADD_KEEP_TOGETHER(src, INNATE_TRAIT)
+/obj/item/canvas/Destroy()
+ last_patron = null
+ if(istype(loc,/obj/structure/sign/painting))
+ var/obj/structure/sign/painting/frame = loc
+ frame.remove_art_element(painting_metadata.credit_value)
+ painting_metadata = null
+ return ..()
+
/obj/item/canvas/proc/reset_grid()
grid = new/list(width,height)
for(var/x in 1 to width)
@@ -102,6 +116,8 @@
ui_interact(user)
/obj/item/canvas/ui_state(mob/user)
+ if(isobserver(user))
+ return GLOB.observer_state
if(finalized)
return GLOB.physical_obscured_state
else
@@ -149,11 +165,14 @@
. = ..()
ui_interact(user)
-/obj/item/canvas/ui_act(action, params)
+/obj/item/canvas/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
var/mob/user = usr
+ ///this is here to allow observers to zoom in and out but not do anything else.
+ if(action != "zoom_in" && action != "zoom_out" && isobserver(user))
+ return
switch(action)
if("paint")
if(finalized)
@@ -286,10 +305,16 @@
curator.adjust_money(curator_cut, "Painting: Patronage cut")
curator.bank_card_talk("Cut on patronage received, account now holds [curator.account_balance] cr.")
+ if(istype(loc, /obj/structure/sign/painting))
+ var/obj/structure/sign/painting/frame = loc
+ frame.remove_art_element(painting_metadata.credit_value)
+ frame.add_art_element(offer_amount)
+
painting_metadata.patron_ckey = user.ckey
painting_metadata.patron_name = user.real_name
painting_metadata.credit_value = offer_amount
last_patron = WEAKREF(user.mind)
+
to_chat(user, span_notice("Nanotrasen Trust Foundation thanks you for your contribution. You're now an official patron of this painting."))
var/list/possible_frames = SSpersistent_paintings.get_available_frames(offer_amount)
if(possible_frames.len <= 1) // Not much room for choices here.
@@ -308,7 +333,7 @@
var/possible_frames = candidates || SSpersistent_paintings.get_available_frames(painting_metadata.credit_value)
var/list/radial_options = list()
for(var/frame_name in possible_frames)
- radial_options[frame_name] = image(icon, "[icon_state]frame_[frame_name]")
+ radial_options[frame_name] = image(frame_icon, "[icon_state]frame_[frame_name]")
var/result = show_radial_menu(user, loc, radial_options, radius = 60, custom_check = CALLBACK(src, PROC_REF(can_select_frame), user), tooltips = TRUE)
if(!result)
return
@@ -418,29 +443,32 @@
icon_state = "19x19"
width = 19
height = 19
- SET_BASE_PIXEL(7, 7)
- framed_offset_x = 7
+ SET_BASE_PIXEL(6, 7)
+ framed_offset_x = 6
framed_offset_y = 7
+ wall_y_offset = -2
/obj/item/canvas/twentythree_nineteen
name = "canvas (23x19)"
icon_state = "23x19"
width = 23
height = 19
- SET_BASE_PIXEL(5, 7)
- framed_offset_x = 5
+ SET_BASE_PIXEL(4, 7)
+ framed_offset_x = 4
framed_offset_y = 7
pixels_per_unit = 8
+ wall_y_offset = -2
/obj/item/canvas/twentythree_twentythree
name = "canvas (23x23)"
icon_state = "23x23"
width = 23
height = 23
- SET_BASE_PIXEL(5, 5)
- framed_offset_x = 5
- framed_offset_y = 5
+ SET_BASE_PIXEL(4, 7)
+ framed_offset_x = 4
+ framed_offset_y = 7
pixels_per_unit = 8
+ wall_y_offset = -5
/obj/item/canvas/twentyfour_twentyfour
name = "canvas (24x24) (AI Universal Standard)"
@@ -448,9 +476,10 @@
icon_state = "24x24"
width = 24
height = 24
- SET_BASE_PIXEL(4, 4)
+ SET_BASE_PIXEL(4, 11)
framed_offset_x = 4
- framed_offset_y = 4
+ framed_offset_y = 7
+ wall_y_offset = -6
pixels_per_unit = 8
/obj/item/canvas/thirtysix_twentyfour
@@ -459,10 +488,11 @@
icon_state = "24x24" //The vending spritesheet needs the icons to be 32x32. We'll set the actual icon on Initialize.
width = 36
height = 24
- SET_BASE_PIXEL(-4, 4)
+ SET_BASE_PIXEL(-4, 7)
framed_offset_x = 14
- framed_offset_y = 4
+ framed_offset_y = 7
pixels_per_unit = 7
+ wall_y_offset = 1
w_class = WEIGHT_CLASS_BULKY
custom_price = PAYCHECK_CREW * 1.25
@@ -471,6 +501,7 @@
. = ..()
AddElement(/datum/element/item_scaling, 1, 0.8)
icon = 'icons/obj/art/artstuff_64x64.dmi'
+ frame_icon = 'icons/obj/art/artstuff_64x64.dmi'
icon_state = "36x24"
/obj/item/canvas/fortyfive_twentyseven
@@ -479,10 +510,11 @@
icon_state = "24x24" //Ditto
width = 45
height = 27
- SET_BASE_PIXEL(-8, 2)
+ SET_BASE_PIXEL(-8, 7)
framed_offset_x = 9
- framed_offset_y = 4
+ framed_offset_y = 7
pixels_per_unit = 6
+ wall_y_offset = -1
w_class = WEIGHT_CLASS_BULKY
custom_price = PAYCHECK_CREW * 1.75
@@ -491,26 +523,27 @@
. = ..()
AddElement(/datum/element/item_scaling, 1, 0.7)
icon = 'icons/obj/art/artstuff_64x64.dmi'
+ frame_icon = 'icons/obj/art/artstuff_64x64.dmi'
icon_state = "45x27"
/obj/item/wallframe/painting
name = "painting frame"
desc = "The perfect showcase for your favorite deathtrap memories."
- icon = 'icons/obj/signs.dmi'
- custom_materials = list(/datum/material/wood =SHEET_MATERIAL_AMOUNT)
+ icon = 'icons/obj/art/painting_frames.dmi'
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT)
resistance_flags = FLAMMABLE
flags_1 = NONE
icon_state = "frame-empty"
result_path = /obj/structure/sign/painting
- pixel_shift = 30
+ north_only = TRUE
/obj/structure/sign/painting
name = "Painting"
desc = "Art or \"Art\"? You decide."
- icon = 'icons/obj/signs.dmi'
+ icon = 'icons/obj/art/painting_frames.dmi'
icon_state = "frame-empty"
base_icon_state = "frame"
- custom_materials = list(/datum/material/wood =SHEET_MATERIAL_AMOUNT)
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT)
resistance_flags = FLAMMABLE
buildable_sign = FALSE
///Canvas we're currently displaying.
@@ -526,12 +559,14 @@
/obj/item/canvas/twentythree_twentythree,
/obj/item/canvas/twentyfour_twentyfour,
)
+ /// the type of wallframe it 'disassembles' into
+ var/wallframe_type = /obj/item/wallframe/painting
+
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/painting)
/obj/structure/sign/painting/Initialize(mapload, dir, building)
. = ..()
SSpersistent_paintings.painting_frames += src
- if(dir)
- setDir(dir)
/obj/structure/sign/painting/Destroy()
. = ..()
@@ -546,6 +581,16 @@
else
return ..()
+/obj/structure/sign/painting/knock_down(mob/living/user)
+ var/turf/drop_turf
+ if(user)
+ drop_turf = get_turf(user)
+ else
+ drop_turf = drop_location()
+ current_canvas?.forceMove(drop_turf)
+ var/obj/item/wallframe/frame = new wallframe_type(drop_turf)
+ frame.update_integrity(get_integrity()) //Transfer how damaged it is.
+
/obj/structure/sign/painting/examine(mob/user)
. = ..()
if(persistence_id)
@@ -566,6 +611,8 @@
/obj/structure/sign/painting/Exited(atom/movable/movable, atom/newloc)
. = ..()
if(movable == current_canvas)
+ if(!QDELETED(current_canvas))
+ remove_art_element(current_canvas.painting_metadata.credit_value)
current_canvas = null
update_appearance()
@@ -585,6 +632,8 @@
if(!current_canvas.finalized)
current_canvas.finalize(user)
to_chat(user,span_notice("You frame [current_canvas]."))
+ pixel_y += current_canvas.wall_y_offset
+ add_art_element()
update_appearance()
return TRUE
return FALSE
@@ -619,7 +668,7 @@
painting.pixel_y = current_canvas.framed_offset_y
. += painting
var/frame_type = current_canvas.painting_metadata.frame_type
- . += mutable_appearance(current_canvas.icon,"[current_canvas.icon_state]frame_[frame_type]") //add the frame
+ . += mutable_appearance(current_canvas.frame_icon, "[current_canvas.icon_state]frame_[frame_type]") //add the frame
/**
* Loads a painting from SSpersistence. Called globally by said subsystem when it inits
@@ -654,10 +703,31 @@
new_canvas.finalized = TRUE
new_canvas.name = "painting - [painting.title]"
current_canvas = new_canvas
+ add_art_element()
current_canvas.update_appearance()
update_appearance()
return TRUE
+/obj/structure/sign/painting/proc/add_art_element()
+ var/artistic_value = get_art_value(current_canvas.painting_metadata.credit_value)
+ if(artistic_value)
+ AddElement(/datum/element/art, artistic_value)
+
+/obj/structure/sign/painting/proc/remove_art_element(patronage)
+ var/artistic_value = get_art_value(patronage)
+ if(artistic_value)
+ RemoveElement(/datum/element/art, artistic_value)
+
+/obj/structure/sign/painting/proc/get_art_value(patronage)
+ switch(patronage)
+ if(PATRONAGE_SUPERB_FRAME to INFINITY)
+ return GREAT_ART
+ if(PATRONAGE_EXCELLENT_FRAME to PATRONAGE_SUPERB_FRAME)
+ return GOOD_ART
+ if(PATRONAGE_NICE_FRAME to PATRONAGE_EXCELLENT_FRAME)
+ return OK_ART
+ return 0
+
/obj/structure/sign/painting/proc/save_persistent()
if(!persistence_id || !current_canvas || current_canvas.no_save || current_canvas.painting_metadata.loaded_from_json)
return
@@ -694,9 +764,10 @@
name = "large painting frame"
desc = "The perfect showcase for your favorite deathtrap memories. Make sure you have enough space to mount this one to the wall."
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT*2)
- icon_state = "frame-large-empty"
+ icon = 'icons/obj/art/artstuff_64x64.dmi'
+ icon_state = "frame-empty"
+ base_pixel_x = -17
result_path = /obj/structure/sign/painting/large
- pixel_shift = 0 //See [/obj/structure/sign/painting/large/proc/finalize_size]
custom_price = PAYCHECK_CREW * 1.25
/obj/item/wallframe/painting/large/try_build(turf/on_wall, mob/user)
@@ -725,6 +796,7 @@
/obj/item/canvas/thirtysix_twentyfour,
/obj/item/canvas/fortyfive_twentyseven,
)
+ wallframe_type = /obj/item/wallframe/painting/large
/obj/structure/sign/painting/large/Initialize(mapload)
. = ..()
@@ -738,11 +810,13 @@
* of the way it's designed, the pixel_shift variable from the wallframe item won't do.
* Also we want higher bounds so it actually covers an extra wall turf, so that it can count toward check_wall_item calls for
* that wall turf.
+ *
+ * I havent figured out if we still want this in wallening yet?
*/
/obj/structure/sign/painting/large/proc/finalize_size()
switch(dir)
if(SOUTH)
- pixel_y = -32
+ pixel_y = -11
bound_width = 64
if(NORTH)
bound_width = 64
@@ -753,6 +827,8 @@
bound_height = 64
if(EAST)
bound_height = 64
+ if (!isnull(current_canvas))
+ pixel_y += current_canvas.wall_y_offset
/obj/structure/sign/painting/large/frame_canvas(mob/user, obj/item/canvas/new_canvas)
. = ..()
@@ -795,30 +871,40 @@
desc_with_canvas = "A piece of art (or \"art\"). Anyone could've hung it."
persistence_id = "library"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/painting/library)
+
/obj/structure/sign/painting/library_secure
name = "\improper Curated Painting Exhibit mounting"
desc = "For masterpieces hand-picked by the curator."
desc_with_canvas = "A masterpiece hand-picked by the curator, supposedly."
persistence_id = "library_secure"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/painting/library_secure)
+
/obj/structure/sign/painting/library_private // keep your smut away from prying eyes, or non-librarians at least
name = "\improper Private Painting Exhibit mounting"
desc = "For art pieces deemed too subversive or too illegal to be shared outside of curators."
desc_with_canvas = "A painting hung away from lesser minds."
persistence_id = "library_private"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/painting/library_private)
+
/obj/structure/sign/painting/large/library
name = "\improper Large Painting Exhibit mounting"
desc = "For the bulkier art pieces, hand-picked by the curator."
desc_with_canvas = "A curated, large piece of art (or \"art\"). Hopefully the price of the canvas was worth it."
persistence_id = "library_large"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/painting/large/library)
+
/obj/structure/sign/painting/large/library_private
name = "\improper Private Painting Exhibit mounting"
desc = "For the privier and less tasteful compositions that oughtn't to be shown in a parlor nor to the masses."
desc_with_canvas = "A painting that oughn't to be shown to the less open-minded commoners."
persistence_id = "library_large_private"
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/sign/painting/large/library_private)
+
#define AVAILABLE_PALETTE_SPACE 14 // Enough to fill two radial menu pages
diff --git a/code/modules/art/statues.dm b/code/modules/art/statues.dm
index fd64d212f3e80..58b0eae188c10 100644
--- a/code/modules/art/statues.dm
+++ b/code/modules/art/statues.dm
@@ -3,6 +3,7 @@
#define SCULPT_SOUND_INCREMENT 4
/obj/structure/statue
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "statue"
desc = "Placeholder. Yell at Firecage if you SOMEHOW see this."
icon = 'icons/obj/art/statue.dmi'
diff --git a/code/modules/assembly/health.dm b/code/modules/assembly/health.dm
index 20316ebfd66d1..1f918888610da 100644
--- a/code/modules/assembly/health.dm
+++ b/code/modules/assembly/health.dm
@@ -100,7 +100,7 @@
data["target"] = health_target
return data
-/obj/item/assembly/health/ui_act(action, params)
+/obj/item/assembly/health/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return .
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index 8dd4573fcfde1..addbad2704e1b 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -107,28 +107,14 @@
var/turf/last_turf = final_turfs[length(final_turfs)]
buffer_turf = get_step(last_turf, dir)
- var/beam_target_x = pixel_x
- var/beam_target_y = pixel_y
- // The beam by default will go to middle of turf (because items are in the middle of turfs)
- // So we need to offset it
- if(dir & NORTH)
- beam_target_y += 16
- else if(dir & SOUTH)
- beam_target_y -= 16
- if(dir & WEST)
- beam_target_x -= 16
- else if(dir & EAST)
- beam_target_x += 16
-
active_beam = start_loc.Beam(
BeamTarget = last_turf,
beam_type = /obj/effect/ebeam/reacting/infrared,
icon = 'icons/effects/beam.dmi',
- icon_state = "1-full",
- beam_color = COLOR_RED,
+ icon_state = "infrared",
emissive = TRUE,
- override_target_pixel_x = beam_target_x,
- override_target_pixel_y = beam_target_y,
+ override_target_pixel_x = pixel_x,
+ override_target_pixel_y = pixel_y,
)
RegisterSignal(active_beam, COMSIG_BEAM_ENTERED, PROC_REF(beam_entered))
RegisterSignal(active_beam, COMSIG_BEAM_TURFS_CHANGED, PROC_REF(beam_turfs_changed))
@@ -305,7 +291,7 @@
data["visible"] = visible
return data
-/obj/item/assembly/infra/ui_act(action, params)
+/obj/item/assembly/infra/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return .
diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm
index 031a3f78eadeb..61153738ee714 100644
--- a/code/modules/assembly/proximity.dm
+++ b/code/modules/assembly/proximity.dm
@@ -154,7 +154,7 @@
data["sensitivity"] = sensitivity
return data
-/obj/item/assembly/prox_sensor/ui_act(action, params)
+/obj/item/assembly/prox_sensor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm
index 8366bb0f2b382..fad912d42229a 100644
--- a/code/modules/assembly/timer.dm
+++ b/code/modules/assembly/timer.dm
@@ -67,6 +67,9 @@
if(!timing)
return
time -= seconds_per_tick
+ if (time == 9 || time == 19 || time == 29)
+ update_appearance()
+
if(time <= 0)
timing = FALSE
timer_end()
@@ -79,9 +82,14 @@
/obj/item/assembly/timer/update_overlays()
. = ..()
attached_overlays = list()
- if(timing)
- . += "timer_timing"
- attached_overlays += "timer_timing"
+ if(!timing)
+ return
+
+ attached_overlays += "timer_timing"
+ for (var/i in 1 to clamp(ceil(time / 10), 1, 3))
+ var/mutable_appearance/timer_light = mutable_appearance(icon, "timer_light", layer, src)
+ timer_light.pixel_x = (i - 1) * 2
+ . += timer_light
/obj/item/assembly/timer/ui_status(mob/user, datum/ui_state/state)
if(is_secured(user))
@@ -102,7 +110,7 @@
data["loop"] = loop
return data
-/obj/item/assembly/timer/ui_act(action, params)
+/obj/item/assembly/timer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/asset_cache/assets/crafting.dm b/code/modules/asset_cache/assets/crafting.dm
index 99088ed1a70da..eea933ebc1553 100644
--- a/code/modules/asset_cache/assets/crafting.dm
+++ b/code/modules/asset_cache/assets/crafting.dm
@@ -55,8 +55,8 @@
TOOL_WRENCH = icon('icons/obj/tools.dmi', "wrench"),
TOOL_WELDER = icon('icons/obj/tools.dmi', "welder"),
TOOL_ANALYZER = icon('icons/obj/devices/scanner.dmi', "analyzer"),
- TOOL_MINING = icon('icons/obj/mining.dmi', "minipick"),
- TOOL_SHOVEL = icon('icons/obj/mining.dmi', "spade"),
+ TOOL_MINING = icon('icons/obj/mining_zones/equipment.dmi', "minipick"),
+ TOOL_SHOVEL = icon('icons/obj/mining_zones/equipment.dmi', "spade"),
TOOL_RETRACTOR = icon('icons/obj/medical/surgery_tools.dmi', "retractor"),
TOOL_HEMOSTAT = icon('icons/obj/medical/surgery_tools.dmi', "hemostat"),
TOOL_CAUTERY = icon('icons/obj/medical/surgery_tools.dmi', "cautery"),
diff --git a/code/modules/asset_cache/assets/mecha.dm b/code/modules/asset_cache/assets/mecha.dm
index 3c2403cf1c375..87d3b3ee5c848 100644
--- a/code/modules/asset_cache/assets/mecha.dm
+++ b/code/modules/asset_cache/assets/mecha.dm
@@ -3,4 +3,4 @@
/datum/asset/spritesheet/mecha_equipment/create_spritesheets()
InsertAll("", 'icons/obj/devices/mecha_equipment.dmi')
- InsertAll("", 'icons/obj/ore.dmi')
+ InsertAll("", 'icons/obj/mining_zones/ore.dmi')
diff --git a/code/modules/asset_cache/assets/paper.dm b/code/modules/asset_cache/assets/paper.dm
index fdd2e748dec21..70d808b285bec 100644
--- a/code/modules/asset_cache/assets/paper.dm
+++ b/code/modules/asset_cache/assets/paper.dm
@@ -1,20 +1,20 @@
/datum/asset/spritesheet/simple/paper
name = "paper"
assets = list(
- "stamp-clown" = 'icons/stamp_icons/large_stamp-clown.png',
- "stamp-deny" = 'icons/stamp_icons/large_stamp-deny.png',
- "stamp-ok" = 'icons/stamp_icons/large_stamp-ok.png',
- "stamp-void" = 'icons/stamp_icons/large_stamp-void.png',
- "stamp-hop" = 'icons/stamp_icons/large_stamp-hop.png',
- "stamp-cmo" = 'icons/stamp_icons/large_stamp-cmo.png',
- "stamp-ce" = 'icons/stamp_icons/large_stamp-ce.png',
- "stamp-hos" = 'icons/stamp_icons/large_stamp-hos.png',
- "stamp-rd" = 'icons/stamp_icons/large_stamp-rd.png',
- "stamp-cap" = 'icons/stamp_icons/large_stamp-cap.png',
- "stamp-qm" = 'icons/stamp_icons/large_stamp-qm.png',
- "stamp-law" = 'icons/stamp_icons/large_stamp-law.png',
- "stamp-chap" = 'icons/stamp_icons/large_stamp-chap.png',
- "stamp-mime" = 'icons/stamp_icons/large_stamp-mime.png',
- "stamp-centcom" = 'icons/stamp_icons/large_stamp-centcom.png',
- "stamp-syndicate" = 'icons/stamp_icons/large_stamp-syndicate.png'
+ "stamp-clown" = 'icons/ui/stamp_icons/large_stamp-clown.png',
+ "stamp-deny" = 'icons/ui/stamp_icons/large_stamp-deny.png',
+ "stamp-ok" = 'icons/ui/stamp_icons/large_stamp-ok.png',
+ "stamp-void" = 'icons/ui/stamp_icons/large_stamp-void.png',
+ "stamp-hop" = 'icons/ui/stamp_icons/large_stamp-hop.png',
+ "stamp-cmo" = 'icons/ui/stamp_icons/large_stamp-cmo.png',
+ "stamp-ce" = 'icons/ui/stamp_icons/large_stamp-ce.png',
+ "stamp-hos" = 'icons/ui/stamp_icons/large_stamp-hos.png',
+ "stamp-rd" = 'icons/ui/stamp_icons/large_stamp-rd.png',
+ "stamp-cap" = 'icons/ui/stamp_icons/large_stamp-cap.png',
+ "stamp-qm" = 'icons/ui/stamp_icons/large_stamp-qm.png',
+ "stamp-law" = 'icons/ui/stamp_icons/large_stamp-law.png',
+ "stamp-chap" = 'icons/ui/stamp_icons/large_stamp-chap.png',
+ "stamp-mime" = 'icons/ui/stamp_icons/large_stamp-mime.png',
+ "stamp-centcom" = 'icons/ui/stamp_icons/large_stamp-centcom.png',
+ "stamp-syndicate" = 'icons/ui/stamp_icons/large_stamp-syndicate.png'
)
diff --git a/code/modules/asset_cache/assets/rcd.dm b/code/modules/asset_cache/assets/rcd.dm
index 16dee49c60fea..41521e196f662 100644
--- a/code/modules/asset_cache/assets/rcd.dm
+++ b/code/modules/asset_cache/assets/rcd.dm
@@ -1,3 +1,4 @@
+
/datum/asset/spritesheet/rcd
name = "rcd-tgui"
@@ -14,32 +15,35 @@
var/sprite_name
var/icon/sprite_icon
for(var/list/design as anything in designs)
- var/atom/movable/path = design[RCD_DESIGN_PATH]
+ var/atom/path = design[RCD_DESIGN_PATH]
if(!ispath(path))
continue
- sprite_name = initial(path.name)
-
- //icon for windows are blended with grills if required and loaded from radial menu
+ sprite_name = RCD_SPRITESHEET_PATH_KEY(path)
+ //icon for windows are blended with frames if required and loaded from radial menu
if(ispath(path, /obj/structure/window))
- if(path == /obj/structure/window)
- sprite_icon = icon(icon = 'icons/hud/radial.dmi', icon_state = "windowsize")
- else if(path == /obj/structure/window/reinforced)
- sprite_icon = icon(icon = 'icons/hud/radial.dmi', icon_state = "windowtype")
- else if(path == /obj/structure/window/fulltile || path == /obj/structure/window/reinforced/fulltile)
+ var/obj/structure/window/window_path = path
+ if(initial(window_path.fulltile))
+ sprite_icon = icon(icon = 'icons/obj/structures/smooth/window_frames/window_frame_normal.dmi', icon_state = "window_frame_normal-0", dir = SOUTH)
+
+ var/obj/structure/window_frame/frame_path = /obj/structure/window_frame
+
+ sprite_icon.Blend(icon(icon = initial(frame_path.grille_black_icon), icon_state = "[initial(frame_path.grille_icon_state)]_black-[0]"), ICON_OVERLAY)
+ sprite_icon.Blend(icon(icon = initial(frame_path.grille_icon), icon_state = "[initial(frame_path.grille_icon_state)]-[0]"), ICON_OVERLAY)
+ sprite_icon.Blend(icon(icon = initial(path.icon), icon_state = initial(path.icon_state)), ICON_OVERLAY)
+ else
sprite_icon = icon(icon = initial(path.icon), icon_state = initial(path.icon_state))
- sprite_icon.Blend(icon(icon = 'icons/obj/structures.dmi', icon_state = "grille"), ICON_UNDERLAY)
//icons for solid airlocks have an added solid overlay on top of their glass icons
else if(ispath(path, /obj/machinery/door/airlock))
var/obj/machinery/door/airlock/airlock_path = path
var/airlock_icon = initial(airlock_path.icon)
- sprite_icon = icon(icon = airlock_icon, icon_state = "closed")
+ sprite_icon = icon(icon = airlock_icon, icon_state = "closed", dir = SOUTH)
if(!initial(airlock_path.glass))
sprite_icon.Blend(icon(icon = airlock_icon, icon_state = "fill_closed"), ICON_OVERLAY)
//for all other icons we load the paths default icon & icon state
else
- sprite_icon = icon(icon = initial(path.icon), icon_state = initial(path.icon_state))
+ sprite_icon = icon(icon = initial(path.icon), icon_state = initial(path.icon_state), dir = SOUTH)
Insert(sanitize_css_class_name(sprite_name), sprite_icon)
diff --git a/code/modules/asset_cache/assets/supplypods.dm b/code/modules/asset_cache/assets/supplypods.dm
index fd4c961f103e6..3807c080f6200 100644
--- a/code/modules/asset_cache/assets/supplypods.dm
+++ b/code/modules/asset_cache/assets/supplypods.dm
@@ -2,26 +2,26 @@
name = "supplypods"
/datum/asset/spritesheet/supplypods/create_spritesheets()
- for (var/style in 1 to length(GLOB.podstyles))
- if (style == STYLE_SEETHROUGH)
- Insert("pod_asset[style]", icon('icons/obj/supplypods.dmi' , "seethrough-icon"))
+ for (var/datum/pod_style/style as anything in typesof(/datum/pod_style))
+ if (ispath(style, /datum/pod_style/seethrough))
+ Insert("pod_asset[style::id]", icon('icons/obj/supplypods.dmi' , "seethrough-icon"))
continue
- var/base = GLOB.podstyles[style][POD_BASE]
+ var/base = style::icon_state
if (!base)
- Insert("pod_asset[style]", icon('icons/obj/supplypods.dmi', "invisible-icon"))
+ Insert("pod_asset[style::id]", icon('icons/obj/supplypods.dmi', "invisible-icon"))
continue
var/icon/podIcon = icon('icons/obj/supplypods.dmi', base)
- var/door = GLOB.podstyles[style][POD_DOOR]
+ var/door = style::has_door
if (door)
door = "[base]_door"
podIcon.Blend(icon('icons/obj/supplypods.dmi', door), ICON_OVERLAY)
- var/shape = GLOB.podstyles[style][POD_SHAPE]
- if (shape == POD_SHAPE_NORML)
- var/decal = GLOB.podstyles[style][POD_DECAL]
+ var/shape = style::shape
+ if (shape == POD_SHAPE_NORMAL)
+ var/decal = style::decal_icon
if (decal)
podIcon.Blend(icon('icons/obj/supplypods.dmi', decal), ICON_OVERLAY)
- var/glow = GLOB.podstyles[style][POD_GLOW]
+ var/glow = style::glow_color
if (glow)
glow = "pod_glow_[glow]"
podIcon.Blend(icon('icons/obj/supplypods.dmi', glow), ICON_OVERLAY)
- Insert("pod_asset[style]", podIcon)
+ Insert("pod_asset[style::id]", podIcon)
diff --git a/code/modules/asset_cache/assets/uplink.dm b/code/modules/asset_cache/assets/uplink.dm
index e85ee1b35b5c1..35a907a234dfa 100644
--- a/code/modules/asset_cache/assets/uplink.dm
+++ b/code/modules/asset_cache/assets/uplink.dm
@@ -18,10 +18,13 @@
for(var/datum/uplink_item/item_path as anything in subtypesof(/datum/uplink_item))
var/datum/uplink_item/item = new item_path()
+ var/atom/actual_item = item.item
if(item.item) {
items += list(list(
"id" = item_path,
"name" = item.name,
+ "icon" = actual_item.icon,
+ "icon_state" = actual_item.icon_state,
"cost" = item.cost,
"desc" = item.desc,
"category" = item.category ? initial(item.category.name) : null,
diff --git a/code/modules/asset_cache/assets/vending.dm b/code/modules/asset_cache/assets/vending.dm
index 4d99eeefdc9d0..caec9bb4f8218 100644
--- a/code/modules/asset_cache/assets/vending.dm
+++ b/code/modules/asset_cache/assets/vending.dm
@@ -6,22 +6,28 @@
var/target_items = list()
for(var/obj/machinery/vending/vendor as anything in typesof(/obj/machinery/vending))
vendor = new vendor() // It seems `initial(list var)` has nothing. need to make a type.
- for(var/each in list(vendor.products, vendor.premium, vendor.contraband))
- target_items |= each
+ target_items |= vendor.products
+ target_items |= vendor.premium
+ target_items |= vendor.contraband
qdel(vendor)
// building icons for each item
- for (var/k in target_items)
- var/atom/item = k
+ for (var/atom/item as anything in target_items)
if (!ispath(item, /atom))
continue
var/icon_file
- if (initial(item.greyscale_colors) && initial(item.greyscale_config))
+ var/icon_state = initial(item.icon_state)
+ var/icon_color = initial(item.color)
+ // GAGS icons must be pregenerated
+ if(initial(item.greyscale_config) && initial(item.greyscale_colors))
icon_file = SSgreyscale.GetColoredIconByType(initial(item.greyscale_config), initial(item.greyscale_colors))
- else
+ // Colored atoms must be pregenerated
+ else if(icon_color && icon_state)
icon_file = initial(item.icon)
- var/icon_state = initial(item.icon_state)
+ // Otherwise we can rely on DMIcon, so skip it to save init time
+ else
+ continue
if (PERFORM_ALL_TESTS(focus_only/invalid_vending_machine_icon_states))
var/icon_states_list = icon_states(icon_file)
@@ -36,11 +42,10 @@
stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)]([text_ref(icon_state)]), icon_states=[icon_states_string]")
continue
- var/icon/I = icon(icon_file, icon_state, SOUTH)
- var/c = initial(item.color)
- if (!isnull(c) && c != COLOR_WHITE)
- I.Blend(c, ICON_MULTIPLY)
+ var/icon/produced = icon(icon_file, icon_state, SOUTH)
+ if (!isnull(icon_color) && icon_color != COLOR_WHITE)
+ produced.Blend(icon_color, ICON_MULTIPLY)
var/imgid = replacetext(replacetext("[item]", "/obj/item/", ""), "/", "-")
- Insert(imgid, I)
+ Insert(imgid, produced)
diff --git a/code/modules/atmospherics/Atmospherics.md b/code/modules/atmospherics/Atmospherics.md
index 1203ed232e184..8d280c6dcdcb5 100644
--- a/code/modules/atmospherics/Atmospherics.md
+++ b/code/modules/atmospherics/Atmospherics.md
@@ -38,7 +38,7 @@ Now then, into the breach.
The air controller is, at its core, quite simple, yet it is absolutely fundamental to the atmospheric system. The air controller is the clock which triggers all continuous actions within the atmos system, such as vents distributing air or gas moving between tiles. The actions taken by the air controller are quite simple, and will be enumerated here. Much of the substance of the air ticker is due to the game's master controller, whose intricacies I will not delve into for this document. I will however go into more detail about how SSAir in particular works in Chapter 6. In any case, this is a simplified list of the air controller's actions in a single tick:
1. Rebuild Pipenets
- Runs each time SSAir processes, sometimes out of order. It ensures that no pipeline sit unresolved or unbuilt
- - Processes the `rebuild_queue` list into the `expansion_queue` list, and then builds a full pipeline piecemeal. We do a ton of fenagling here to reduce overrun
+ - Processes the `rebuild_queue` list into the `expansion_queue` list, and then builds a full pipeline piecemeal. We do a ton of fenagling here to reduce overrun
2. Pipenets
- Updates the internal gasmixes of attached pipe machinery, and reacts the gases in a pipeline
- Calls `process()` on each `/datum/pipenet` in the `networks` list
@@ -180,7 +180,7 @@ You may notice something like this in `process_cell()`. It's not quite the same
Back in the old FEA days, neighbor count was hardcoded to 4 (Likely because this is what cell sharing on an infinite grid would look like). This means that turf A -> turf B is the same as turf B -> turf A, because they're each portioning up the gas in the same way.
-But when we moved to LINDA, we started using the length of our atmos_adjacent_turfs list (or an analog).
+But when we moved to LINDA, we started using the length of our atmos_adjacent_turfs list (or an analog).
We need this so things like multiz can work, and so tiles in a corner share in a way that makes sense.
Because of this, turf A -> turf B was no longer the same as turf B -> turf A, assuming one of those turfs had a different neighbor count, from I DON'T KNOW WALLS?
@@ -269,7 +269,7 @@ I've been talking kinda abstractly about turfs sleeping. That's because turfs on
### A brief romp to talk about excited groups and LAST_SHARE_CHECK
-Excited groups can tell the amount of diff being shared by hooking into a value `share()` sets on gasmixes, the absolute amount of gas shared by each tile. The issue is this isn't pressure, it's molar count. So heat being shared in a sealed room causes excited groups to break down, then reform from sources. This isn't a major issue due to how breakdown evens things out, but it's worth knowing.
+Excited groups can tell the amount of diff being shared by hooking into a value `share()` sets on gasmixes, the absolute amount of gas shared by each tile. The issue is this isn't pressure, its molar count. So heat being shared in a sealed room causes excited groups to break down, then reform from sources. This isn't a major issue due to how breakdown evens things out, but it's worth knowing.
### Back to the main thread
diff --git a/code/modules/atmospherics/environmental/LINDA_fire.dm b/code/modules/atmospherics/environmental/LINDA_fire.dm
index 00bb94d6bc31f..17ae90bacdfe4 100644
--- a/code/modules/atmospherics/environmental/LINDA_fire.dm
+++ b/code/modules/atmospherics/environmental/LINDA_fire.dm
@@ -15,6 +15,31 @@
/turf/proc/hotspot_expose(exposed_temperature, exposed_volume, soh = 0)
return
+/turf/open/proc/set_active_hotspot(obj/effect/hotspot/new_lad)
+ if(active_hotspot == new_lad)
+ return
+ var/hotspot_around = NONE
+ if(active_hotspot)
+ if(new_lad)
+ hotspot_around = active_hotspot.smoothing_junction
+ if(!QDELETED(active_hotspot))
+ QDEL_NULL(active_hotspot)
+ else
+ for(var/direction in GLOB.cardinals)
+ var/turf/potentially_open = get_step(src, direction)
+ if(!isopenturf(potentially_open))
+ continue
+ var/turf/open/potentially_hotboxed = potentially_open
+ if(!potentially_hotboxed.active_hotspot)
+ continue
+ var/existing_directions = potentially_hotboxed.active_hotspot.smoothing_junction
+ potentially_hotboxed.active_hotspot.set_smoothed_icon_state(existing_directions | REVERSE_DIR(direction))
+ hotspot_around |= direction
+
+ active_hotspot = new_lad
+ if(active_hotspot)
+ active_hotspot.set_smoothed_icon_state(hotspot_around)
+
/**
* Handles the creation of hotspots and initial activation of turfs.
* Setting the conditions for the reaction to actually happen for gasmixtures
@@ -55,10 +80,10 @@
if(((exposed_temperature > PLASMA_MINIMUM_BURN_TEMPERATURE) && (plas > 0.5 || trit > 0.5 || h2 > 0.5)) || \
((exposed_temperature < FREON_MAXIMUM_BURN_TEMPERATURE) && (freon > 0.5)))
- active_hotspot = new /obj/effect/hotspot(src, exposed_volume*25, exposed_temperature)
+ set_active_hotspot(new /obj/effect/hotspot(src, exposed_volume*25, exposed_temperature))
active_hotspot.just_spawned = (current_cycle < SSair.times_fired)
- //remove just_spawned protection if no longer processing this cell
+ //remove just_spawned protection if no longer processing this cell
SSair.add_to_active(src)
/**
@@ -68,16 +93,18 @@
/obj/effect/hotspot
anchored = TRUE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
- icon = 'icons/effects/fire.dmi'
- icon_state = "1"
+ icon = 'icons/effects/atmos/fire.dmi'
+ icon_state = "light"
layer = GASFIRE_LAYER
- plane = ABOVE_GAME_PLANE
blend_mode = BLEND_ADD
light_system = OVERLAY_LIGHT
light_range = LIGHT_RANGE_FIRE
light_power = 1
light_color = LIGHT_COLOR_FIRE
+ /// base sprite used for our icon states when smoothing
+ /// BAAAASICALY the same as icon_state but is helpful to avoid duplicated work
+ var/fire_stage = ""
/**
* Volume is the representation of how big and healthy a fire is.
* Hotspot volume will be divided by turf volume to get the ratio for temperature setting on non bypassing mode.
@@ -94,7 +121,6 @@
///Are we burning freon?
var/cold_fire = FALSE
-
/obj/effect/hotspot/Initialize(mapload, starting_volume, starting_temperature)
. = ..()
SSair.hotspots += src
@@ -111,6 +137,20 @@
)
AddElement(/datum/element/connect_loc, loc_connections)
+/obj/effect/hotspot/set_smoothed_icon_state(new_junction)
+ smoothing_junction = new_junction
+ // If we have a connection down offset physically down so we render correctly
+ if(new_junction & SOUTH)
+ // this ensures things physically below us but visually overlapping us render how we would want
+ pixel_y = -16
+ pixel_z = 16
+ // Otherwise render normally, to avoid weird layering
+ else
+ pixel_y = 0
+ pixel_z = 0
+
+ update_color()
+
/**
* Perform interactions between the hotspot and the gasmixture.
*
@@ -130,7 +170,7 @@
if(!istype(location) || !(location.air))
return
- location.active_hotspot = src
+ location.set_active_hotspot(src)
bypassing = !just_spawned && (volume > CELL_VOLUME*0.95)
@@ -171,11 +211,18 @@
/obj/effect/hotspot/proc/update_color()
cut_overlays()
+ if(!(smoothing_junction & NORTH))
+ var/mutable_appearance/frill = mutable_appearance('icons/effects/atmos/fire.dmi', "[fire_stage]_frill")
+ frill.pixel_z = 32
+ add_overlay(frill)
var/heat_r = heat2colour_r(temperature)
var/heat_g = heat2colour_g(temperature)
var/heat_b = heat2colour_b(temperature)
var/heat_a = 255
var/greyscale_fire = 1 //This determines how greyscaled the fire is.
+ // Note:
+ // Some of the overlays applied to hotspots are not 3/4th'd. They COULD be but we have not gotten to that point yet.
+ // Wallening todo?
if(cold_fire)
heat_r = 0
@@ -199,12 +246,16 @@
sparkle_overlay.alpha = sparkle_amt * 255
add_overlay(sparkle_overlay)
if(temperature > 400000 && temperature < 1500000) //Lightning because very anime.
- var/mutable_appearance/lightning_overlay = mutable_appearance(icon, "overcharged")
+ var/mutable_appearance/lightning_overlay = mutable_appearance('icons/effects/atmos/fire.dmi', "overcharged")
+ if(!(smoothing_junction & NORTH))
+ var/mutable_appearance/frill = mutable_appearance('icons/effects/atmos/fire.dmi', "overcharged_frill")
+ frill.pixel_z = 32
+ lightning_overlay.add_overlay(frill)
lightning_overlay.blend_mode = BLEND_ADD
add_overlay(lightning_overlay)
if(temperature > 4500000) //This is where noblium happens. Some fusion-y effects.
var/fusion_amt = temperature < LERP(4500000,12000000,0.5) ? gauss_lerp(temperature, 4500000, 12000000) : 1
- var/mutable_appearance/fusion_overlay = mutable_appearance('icons/effects/atmospherics.dmi', "fusion_gas")
+ var/mutable_appearance/fusion_overlay = mutable_appearance('icons/effects/atmos/atmospherics.dmi', "fusion_gas")
fusion_overlay.blend_mode = BLEND_ADD
fusion_overlay.alpha = fusion_amt * 255
var/mutable_appearance/rainbow_overlay = mutable_appearance('icons/hud/screen_gen.dmi', "druggy")
@@ -264,7 +315,7 @@
perform_exposure()
if(bypassing)
- icon_state = "3"
+ set_fire_stage("heavy")
if(!cold_fire)
location.burn_tile()
@@ -280,20 +331,28 @@
else
if(volume > CELL_VOLUME*0.4)
- icon_state = "2"
+ set_fire_stage("medium")
else
- icon_state = "1"
+ set_fire_stage("light")
if((visual_update_tick++ % 7) == 0)
update_color()
return TRUE
+/obj/effect/hotspot/proc/set_fire_stage(stage)
+ if(fire_stage == stage)
+ return
+ fire_stage = stage
+ icon_state = stage
+ dir = pick(GLOB.cardinals)
+ update_color()
+
/obj/effect/hotspot/Destroy()
SSair.hotspots -= src
var/turf/open/T = loc
if(istype(T) && T.active_hotspot == src)
- T.active_hotspot = null
+ T.set_active_hotspot(null)
return ..()
/obj/effect/hotspot/proc/on_entered(datum/source, atom/movable/arrived, atom/old_loc, list/atom/old_locs)
diff --git a/code/modules/atmospherics/environmental/LINDA_system.dm b/code/modules/atmospherics/environmental/LINDA_system.dm
index 5f2b30a0d1969..bfeb9853648f1 100644
--- a/code/modules/atmospherics/environmental/LINDA_system.dm
+++ b/code/modules/atmospherics/environmental/LINDA_system.dm
@@ -133,6 +133,7 @@
UNSETEMPTY(atmos_adjacent_turfs)
src.atmos_adjacent_turfs = atmos_adjacent_turfs
SEND_SIGNAL(src, COMSIG_TURF_CALCULATED_ADJACENT_ATMOS)
+ rebuild_atmos_smoothing()
/**
* returns a list of adjacent turfs that can share air with this one.
diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
index 85b95feb6d549..026f64f91c5ed 100644
--- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
+++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
@@ -25,6 +25,9 @@
**/
var/initial_gas_mix = OPENTURF_DEFAULT_ATMOS
+ ///gas IDs of current active gas overlays -> the overlay they are currently generating
+ var/list/atmos_overlay_types
+
/turf/open
//used for spacewind
///Pressure difference between two turfs
@@ -47,8 +50,6 @@
/// exists so things like space can ask to take 100% of a tile's gas
var/run_later = FALSE
- ///gas IDs of current active gas overlays
- var/list/atmos_overlay_types
var/significant_share_ticker = 0
#ifdef TRACK_MAX_SHARE
var/max_share = 0
@@ -160,7 +161,6 @@
/////////////////////////GAS OVERLAYS//////////////////////////////
-
/turf/open/proc/update_visuals()
var/list/atmos_overlay_types = src.atmos_overlay_types // Cache for free performance
@@ -171,24 +171,91 @@
src.atmos_overlay_types = null
return
+ // Cache for sonic speed
+ var/offset = GET_Z_PLANE_OFFSET(z) + 1
+ var/list/new_overlay_types = list()
+ // This is an extracted version of GAS_OVERLAYS() with logic build to handle gas smoothing
+ // The two should be kept up to date (I'm sorry for the code debt)
+ var/list/non_overlaying_gases = GLOB.nonoverlaying_gases
var/list/gases = air.gases
-
- var/list/new_overlay_types
- GAS_OVERLAYS(gases, new_overlay_types, src)
+ for(var/gas_id in gases)
+ if(non_overlaying_gases[gas_id])
+ continue
+ var/list/gas_list = gases[gas_id]
+ var/list/gas_metadata = gas_list[GAS_META]
+ if(gas_list[MOLES] <= gas_metadata[META_GAS_MOLES_VISIBLE])
+ continue
+ var/list/gas_overlays = gas_metadata[META_GAS_OVERLAY][offset]
+
+ var/gas_junction = NONE
+ var/obj/effect/overlay/gas/existing_overlay = atmos_overlay_types?[gas_id]
+ if(existing_overlay)
+ vis_contents -= existing_overlay
+ gas_junction = existing_overlay.smoothing_junction
+ else // cold path so it's ok that it kinda fuckin sucks
+ // Alright if we're just being added, we need to poll our neighbors to update them
+ for(var/direction_to in GLOB.cardinals)
+ var/turf/neighbor = get_step(src, direction_to)
+ var/obj/effect/overlay/gas/their_overlay = neighbor.atmos_overlay_types?[gas_id]
+ if(!their_overlay)
+ continue
+ gas_junction |= dir_to_junction(direction_to)
+ var/updated_junction = their_overlay.smoothing_junction | dir_to_junction(REVERSE_DIR(direction_to))
+ var/obj/effect/overlay/gas/new_overlay = their_overlay.get_smoothed_overlay(updated_junction)
+ neighbor.atmos_overlay_types[gas_id] = new_overlay
+ neighbor.vis_contents -= their_overlay
+ neighbor.vis_contents += new_overlay
+
+ var/obj/effect/overlay/gas/new_overlay = gas_overlays[min(TOTAL_VISIBLE_STATES, CEILING(gas_list[MOLES] / MOLES_GAS_VISIBLE_STEP, 1))][gas_junction + 1]
+ vis_contents += new_overlay
+ new_overlay_types[gas_id] = new_overlay
if (atmos_overlay_types)
- for(var/overlay in atmos_overlay_types-new_overlay_types) //doesn't remove overlays that would only be added
- vis_contents -= overlay
-
- if (length(new_overlay_types))
- if (atmos_overlay_types)
- vis_contents += new_overlay_types - atmos_overlay_types //don't add overlays that already exist
- else
- vis_contents += new_overlay_types
+ // If we don't have an id anymore, remove it from our vis_contents and
+ // yell to our neighbors so they stop smoothing with us
+ for(var/old_gas_id in atmos_overlay_types - new_overlay_types)
+ var/obj/effect/overlay/gas/old_overlay = atmos_overlay_types[old_gas_id]
+ vis_contents -= old_overlay
+ for(var/direction_to in GLOB.cardinals)
+ var/turf/neighbor = get_step(src, direction_to)
+ var/obj/effect/overlay/gas/their_overlay = neighbor.atmos_overlay_types?[old_gas_id]
+ if(!their_overlay)
+ continue
+ var/updated_junction = their_overlay.smoothing_junction & ~dir_to_junction(REVERSE_DIR(direction_to))
+ var/obj/effect/overlay/gas/new_overlay = their_overlay.get_smoothed_overlay(updated_junction)
+ neighbor.atmos_overlay_types[old_gas_id] = new_overlay
+ neighbor.vis_contents -= their_overlay
+ neighbor.vis_contents += new_overlay
UNSETEMPTY(new_overlay_types)
src.atmos_overlay_types = new_overlay_types
+/// Fully recalculates the smoothing of our atmos overlays
+/// Call when their inputs outside of the system (like, what adjacent turfs we have) change
+/turf/proc/rebuild_atmos_smoothing()
+ PRIVATE_PROC(TRUE)
+ var/list/atmos_overlay_types = src.atmos_overlay_types
+ for(var/direction in GLOB.cardinals)
+ var/turf/paired_turf = get_step(src, direction)
+ if(!paired_turf)
+ continue
+ var/list/paired_atmos_overlay_types = paired_turf.atmos_overlay_types
+ if(!paired_atmos_overlay_types)
+ continue
+ var/dir_to_us = REVERSE_DIR(direction)
+
+ for(var/gas_id as anything in paired_atmos_overlay_types - atmos_overlay_types)
+ var/obj/effect/overlay/gas/their_overlay = paired_atmos_overlay_types[gas_id]
+ if(!their_overlay)
+ continue
+ if(!(their_overlay.smoothing_junction & dir_to_us))
+ continue
+ var/updated_junction = their_overlay.smoothing_junction & ~dir_to_junction(dir_to_us)
+ var/obj/effect/overlay/gas/new_overlay = their_overlay.get_smoothed_overlay(updated_junction)
+ paired_atmos_overlay_types[gas_id] = new_overlay
+ paired_turf.vis_contents -= their_overlay
+ paired_turf.vis_contents += new_overlay
+
/proc/typecache_of_gases_with_no_overlays()
. = list()
for (var/gastype in subtypesof(/datum/gas))
@@ -271,6 +338,12 @@
#endif
for(var/turf/open/enemy_tile as anything in adjacent_turfs)
+ #ifdef UNIT_TESTS
+ if(!istype(enemy_tile))
+ stack_trace("closed turf inside of adjacent turfs")
+ continue
+ #endif
+
// This var is only rarely set, exists so turfs can request to share at the end of our sharing
// We need this so we can assume share is communative, which we need to do to avoid a hellish amount of garbage_collect()s
if(enemy_tile.run_later)
diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
index db2732be83fad..1f318f21dfbd6 100644
--- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm
+++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
@@ -134,7 +134,8 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
/// Gets the gas visuals for everything in this mixture
/datum/gas_mixture/proc/return_visuals(turf/z_context)
var/list/output
- GAS_OVERLAYS(gases, output, z_context)
+ // We almost always want a normal gas tile, so let's give em a normal gas tile
+ GAS_OVERLAYS(gases, output, z_context, NONE)
return output
/// Calculate thermal energy in joules
diff --git a/code/modules/atmospherics/gasmixtures/gas_types.dm b/code/modules/atmospherics/gasmixtures/gas_types.dm
index 32acadb3bee52..477fd4c880abf 100644
--- a/code/modules/atmospherics/gasmixtures/gas_types.dm
+++ b/code/modules/atmospherics/gasmixtures/gas_types.dm
@@ -19,12 +19,15 @@
/proc/generate_gas_overlays(old_offset, new_offset, datum/gas/gas_type)
var/list/to_return = list()
- for(var/i in old_offset to new_offset)
- var/fill = list()
- to_return += list(fill)
- for(var/j in 1 to TOTAL_VISIBLE_STATES)
- var/obj/effect/overlay/gas/gas = new (initial(gas_type.gas_overlay), log(4, (j+0.4*TOTAL_VISIBLE_STATES) / (0.35*TOTAL_VISIBLE_STATES)) * 255, i)
- fill += gas
+ for(var/offset in old_offset to new_offset)
+ var/offset_layer = list()
+ to_return += list(offset_layer)
+ for(var/alpha in 1 to TOTAL_VISIBLE_STATES)
+ var/list/alpha_layer = new /list(CARDINAL_SMOOTHING_JUNCTIONS + 1) // +1 to handle 0
+ offset_layer += list(alpha_layer)
+ for(var/junction in 0 to CARDINAL_SMOOTHING_JUNCTIONS)
+ var/obj/effect/overlay/gas/gas = new (initial(gas_type.gas_overlay), log(4, (alpha+0.4*TOTAL_VISIBLE_STATES) / (0.35*TOTAL_VISIBLE_STATES)) * 255, offset, junction, alpha_layer)
+ alpha_layer[junction + 1] = gas
return to_return
/proc/gas_id2path(id)
@@ -45,13 +48,13 @@
|||| only by meta_gas_list(). ||||
\*||||||||||||||||||||||||||||||||||||||||*/
-//This is a plot created using the values for gas exports. Each gas has a value that works as it's kind of soft-cap, which limits you from making billions of credits per sale, based on the base_value variable on the gasses themselves. Most of these gasses as a result have a rather low value when sold, like nitrogen and oxygen at 1500 and 600 respectively at their maximum value. The
+//This is a plot created using the values for gas exports. Each gas has a value that works as its kind of soft-cap, which limits you from making billions of credits per sale, based on the base_value variable on the gasses themselves. Most of these gasses as a result have a rather low value when sold, like nitrogen and oxygen at 1500 and 600 respectively at their maximum value. The
/datum/gas
var/id = ""
var/specific_heat = 0
var/name = ""
- ///icon_state in icons/effects/atmospherics.dmi
- var/gas_overlay = ""
+ ///dmi to pull our gas sprites from
+ var/gas_overlay = null
var/moles_visible = null
///currently used by canisters
var/dangerous = FALSE
@@ -103,19 +106,19 @@
id = GAS_PLASMA
specific_heat = 200
name = "Plasma"
- gas_overlay = "plasma"
+ gas_overlay = 'icons/effects/atmos/plasma.dmi'
moles_visible = MOLES_GAS_VISIBLE
dangerous = TRUE
rarity = 800
base_value = 1.5
- desc = "A flammable gas with many other curious properties. It's research is one of NT's primary objective."
+ desc = "A flammable gas with many other curious properties. Its research is one of NT's primary objective."
primary_color = "#ffc0cb"
/datum/gas/water_vapor
id = GAS_WATER_VAPOR
specific_heat = 40
name = "Water Vapor"
- gas_overlay = "water_vapor"
+ gas_overlay = 'icons/effects/atmos/water_vapor.dmi'
moles_visible = MOLES_GAS_VISIBLE
fusion_power = 8
rarity = 500
@@ -128,7 +131,7 @@
id = GAS_HYPER_NOBLIUM
specific_heat = 2000
name = "Hyper-noblium"
- gas_overlay = "freon"
+ gas_overlay = 'icons/effects/atmos/freon.dmi'
moles_visible = MOLES_GAS_VISIBLE
fusion_power = 10
rarity = 50
@@ -140,7 +143,7 @@
id = GAS_N2O
specific_heat = 40
name = "Nitrous Oxide"
- gas_overlay = "nitrous_oxide"
+ gas_overlay = 'icons/effects/atmos/nitrous_oxide.dmi'
moles_visible = MOLES_GAS_VISIBLE * 2
fusion_power = 10
dangerous = TRUE
@@ -155,7 +158,7 @@
specific_heat = 10
name = "Nitrium"
fusion_power = 7
- gas_overlay = "nitrium"
+ gas_overlay = 'icons/effects/atmos/nitrium.dmi'
moles_visible = MOLES_GAS_VISIBLE
dangerous = TRUE
rarity = 1
@@ -167,7 +170,7 @@
id = GAS_TRITIUM
specific_heat = 10
name = "Tritium"
- gas_overlay = "tritium"
+ gas_overlay = 'icons/effects/atmos/tritium.dmi'
moles_visible = MOLES_GAS_VISIBLE
dangerous = TRUE
fusion_power = 5
@@ -203,7 +206,7 @@
specific_heat = 20
name = "Miasma"
dangerous = TRUE
- gas_overlay = "miasma"
+ gas_overlay = 'icons/effects/atmos/miasma.dmi'
moles_visible = MOLES_GAS_VISIBLE * 60
rarity = 250
base_value = 1
@@ -215,12 +218,12 @@
specific_heat = 600
name = "Freon"
dangerous = TRUE
- gas_overlay = "freon"
+ gas_overlay = 'icons/effects/atmos/freon.dmi'
moles_visible = MOLES_GAS_VISIBLE *30
fusion_power = -5
rarity = 10
base_value = 5
- desc = "A coolant gas. Mainly used for it's endothermic reaction with oxygen."
+ desc = "A coolant gas. Mainly used for its endothermic reaction with oxygen."
primary_color = "#afeeee"
/datum/gas/hydrogen
@@ -239,7 +242,7 @@
specific_heat = 10
name = "Healium"
dangerous = TRUE
- gas_overlay = "healium"
+ gas_overlay = 'icons/effects/atmos/healium.dmi'
moles_visible = MOLES_GAS_VISIBLE
rarity = 300
base_value = 5.5
@@ -251,7 +254,7 @@
specific_heat = 30
name = "Proto Nitrate"
dangerous = TRUE
- gas_overlay = "proto_nitrate"
+ gas_overlay = 'icons/effects/atmos/proto_nitrate.dmi'
moles_visible = MOLES_GAS_VISIBLE
rarity = 200
base_value = 2.5
@@ -263,11 +266,11 @@
specific_heat = 350
name = "Zauker"
dangerous = TRUE
- gas_overlay = "zauker"
+ gas_overlay = 'icons/effects/atmos/zauker.dmi'
moles_visible = MOLES_GAS_VISIBLE
rarity = 1
base_value = 7
- desc = "A highly toxic gas, it's production is highly regulated on top of being difficult. It also breaks down when in contact with nitrogen."
+ desc = "A highly toxic gas, its production is highly regulated on top of being difficult. It also breaks down when in contact with nitrogen."
primary_color = "#006400"
/datum/gas/halon
@@ -275,7 +278,7 @@
specific_heat = 175
name = "Halon"
dangerous = TRUE
- gas_overlay = "halon"
+ gas_overlay = 'icons/effects/atmos/halon.dmi'
moles_visible = MOLES_GAS_VISIBLE
rarity = 300
base_value = 4
@@ -289,7 +292,7 @@
fusion_power = 7
rarity = 50
base_value = 3.5
- desc = "A very inert gas produced by the fusion of hydrogen and it's derivatives."
+ desc = "A very inert gas produced by the fusion of hydrogen and its derivatives."
primary_color = "#f0f8ff"
/datum/gas/antinoblium
@@ -297,7 +300,7 @@
specific_heat = 1
name = "Antinoblium"
dangerous = TRUE
- gas_overlay = "antinoblium"
+ gas_overlay = 'icons/effects/atmos/antinoblium.dmi'
moles_visible = MOLES_GAS_VISIBLE
fusion_power = 20
rarity = 1
@@ -306,24 +309,44 @@
primary_color = COLOR_MAROON
/obj/effect/overlay/gas
- icon = 'icons/effects/atmospherics.dmi'
+ // Default to make tests happy
+ icon = 'icons/effects/atmos/plasma.dmi'
+ icon_state = "-0"
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE // should only appear in vis_contents, but to be safe
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
appearance_flags = TILE_BOUND
vis_flags = NONE
// The visual offset we are "on".
// Can't use the tradtional loc because we are stored in nullspace, and we can't set plane before init because of the helping that SET_PLANE_EXPLICIT does IN init
var/plane_offset = 0
+ // The alpha list we are a member of, for fast junction changing
+ var/list/alpha_list
-/obj/effect/overlay/gas/New(state, alph, offset)
+/obj/effect/overlay/gas/New(icon, alpha, plane_offset, operating_junction, list/alpha_list)
. = ..()
- icon_state = state
- alpha = alph
- plane_offset = offset
+ src.icon = icon
+ src.alpha = alpha
+ src.plane_offset = plane_offset
+ src.alpha_list = alpha_list
+ set_smoothed_icon_state(operating_junction)
/obj/effect/overlay/gas/Initialize(mapload)
. = ..()
SET_PLANE_W_SCALAR(src, initial(plane), plane_offset)
+/obj/effect/overlay/gas/set_smoothed_icon_state(new_junction)
+ . = ..()
+ // If we have a connection down offset physically down so we render correctly
+ if(new_junction & SOUTH)
+ // this ensures things physically below us but visually overlapping us render how we would want
+ pixel_y = -16
+ pixel_z = 16
+ // Otherwise render normally, to avoid weird layering
+ else
+ pixel_y = 0
+ pixel_z = 0
+
+/// Returns the gas overlay we'd prefer if we had different smoothing
+/obj/effect/overlay/gas/proc/get_smoothed_overlay(new_junction)
+ return alpha_list[new_junction + 1]
diff --git a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
index 0a6aa23b34d58..aab1cf98fee85 100644
--- a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
+++ b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
@@ -3,12 +3,12 @@
/obj/machinery/airalarm
name = "air alarm"
desc = "A machine that monitors atmosphere levels. Goes off if the area is dangerous."
- icon = 'icons/obj/machines/wallmounts.dmi'
- icon_state = "alarmp"
+ icon = 'icons/obj/machines/air_alarm.dmi'
+ icon_state = "alarm"
idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION * 0.05
active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 0.02
power_channel = AREA_USAGE_ENVIRON
- req_access = list(ACCESS_ATMOSPHERICS)
+ req_access = list(ACCESS_ENGINEERING)
max_integrity = 250
integrity_failure = 0.33
armor_type = /datum/armor/machinery_airalarm
@@ -17,6 +17,8 @@
/// Current alert level of our air alarm.
/// [AIR_ALARM_ALERT_NONE], [AIR_ALARM_ALERT_MINOR], [AIR_ALARM_ALERT_SEVERE]
var/danger_level = AIR_ALARM_ALERT_NONE
+ /// Current alert level of the area of our air alarm.
+ var/area_danger = FALSE
/// Currently selected mode of the alarm. An instance of [/datum/air_alarm_mode].
var/datum/air_alarm_mode/selected_mode
@@ -83,9 +85,12 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
fire = 90
acid = 30
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/machinery/airalarm)
+
/obj/machinery/airalarm/Initialize(mapload, ndir, nbuild)
. = ..()
set_wires(new /datum/wires/airalarm(src))
+
if(ndir)
setDir(ndir)
@@ -121,6 +126,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
/obj/item/circuit_component/air_alarm_scrubbers,
/obj/item/circuit_component/air_alarm_vents
))
+ AddComponent(/datum/component/examine_balloon)
GLOB.air_alarms += src
update_appearance()
@@ -137,7 +143,6 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
/obj/machinery/airalarm/Destroy()
if(my_area)
my_area = null
- QDEL_NULL(wires)
QDEL_NULL(alarm_manager)
GLOB.air_alarms -= src
return ..()
@@ -233,7 +238,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
data["siliconUser"] = HAS_SILICON_ACCESS(user)
data["emagged"] = (obj_flags & EMAGGED ? 1 : 0)
data["dangerLevel"] = danger_level
- data["atmosAlarm"] = !!my_area.active_alarms[ALARM_ATMOS]
+ data["atmosAlarm"] = !!area_danger
data["fireAlarm"] = my_area.fire
data["faultStatus"] = my_area.fault_status
data["faultLocation"] = my_area.fault_location
@@ -343,15 +348,15 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
return data
-/obj/machinery/airalarm/ui_act(action, params)
+/obj/machinery/airalarm/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(. || buildstage != AIR_ALARM_BUILD_COMPLETE)
return
- if((locked && !HAS_SILICON_ACCESS(usr)) || (HAS_SILICON_ACCESS(usr) && aidisabled))
+ var/mob/user = ui.user
+ if((locked && !HAS_SILICON_ACCESS(user)) || (HAS_SILICON_ACCESS(user) && aidisabled))
return
- var/mob/user = usr
var/area/area = connected_sensor ? get_area(connected_sensor) : get_area(src)
ASSERT(!isnull(area))
@@ -466,7 +471,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
var/threshold_type = params["threshold_type"]
var/value = params["value"]
tlv.set_value(threshold_type, value)
- investigate_log("threshold value for [threshold]:[threshold_type] was set to [value] by [key_name(usr)]", INVESTIGATE_ATMOS)
+ investigate_log("threshold value for [threshold]:[threshold_type] was set to [value] by [key_name(user)]", INVESTIGATE_ATMOS)
check_enviroment()
@@ -477,7 +482,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
return
var/threshold_type = params["threshold_type"]
tlv.reset_value(threshold_type)
- investigate_log("threshold value for [threshold]:[threshold_type] was reset by [key_name(usr)]", INVESTIGATE_ATMOS)
+ investigate_log("threshold value for [threshold]:[threshold_type] was reset by [key_name(user)]", INVESTIGATE_ATMOS)
check_enviroment()
@@ -494,7 +499,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
disconnect_sensor()
if ("lock")
- togglelock(usr)
+ togglelock(user)
return TRUE
update_appearance()
@@ -511,7 +516,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
var/color
if(danger_level == AIR_ALARM_ALERT_HAZARD)
color = "#FF0022" // red
- else if(danger_level == AIR_ALARM_ALERT_WARNING || my_area.active_alarms[ALARM_ATMOS])
+ else if(danger_level == AIR_ALARM_ALERT_WARNING || area_danger)
color = "#FFAA00" // yellow
else
color = "#00FFCC" // teal
@@ -529,25 +534,26 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
icon_state = "alarm_b1"
return ..()
- icon_state = isnull(connected_sensor) ? "alarmp" : "alarmp_remote"
+ icon_state = isnull(connected_sensor) ? "alarm" : "alarm_remote"
return ..()
/obj/machinery/airalarm/update_overlays()
. = ..()
-
- if(panel_open || (machine_stat & (NOPOWER|BROKEN)) || shorted)
+ // Open panels will only display a light on the final buildstage
+ if(panel_open)
+ if(buildstage == AIR_ALARM_BUILD_COMPLETE)
+ . += mutable_appearance(icon, "light-out", layer, src, plane)
return
- var/state
- if(danger_level == AIR_ALARM_ALERT_HAZARD)
- state = "alarm1"
- else if(danger_level == AIR_ALARM_ALERT_WARNING || my_area.active_alarms[ALARM_ATMOS])
- state = "alarm2"
- else
- state = "alarm0"
+ if((machine_stat & (NOPOWER|BROKEN)) || shorted)
+ . += mutable_appearance(icon, "light-out", layer, src, plane)
+ return ..()
- . += mutable_appearance(icon, state)
- . += emissive_appearance(icon, state, src, alpha = src.alpha)
+ var/alert_level = danger_level
+ if(area_danger)
+ alert_level = 2
+ . += mutable_appearance(icon, "light-[alert_level]")
+ . += emissive_appearance(icon, "light-[alert_level]", src, alpha)
/// Check the current air and update our danger level.
/// [/obj/machinery/airalarm/var/danger_level]
@@ -561,6 +567,8 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
var/old_danger = danger_level
danger_level = AIR_ALARM_ALERT_NONE
+ var/old_area_danger = area_danger
+ area_danger = my_area.active_alarms[ALARM_ATMOS]
var/total_moles = environment.total_moles()
var/pressure = environment.return_pressure()
@@ -606,7 +614,7 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
alarm_manager.clear_alarm(ALARM_ATMOS)
warning_message = null
- if(old_danger != danger_level)
+ if(old_danger != danger_level || old_area_danger != area_danger)
update_appearance()
selected_mode.replace(my_area, pressure)
@@ -622,8 +630,6 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
selected_mode.apply(my_area)
SEND_SIGNAL(src, COMSIG_AIRALARM_UPDATE_MODE, source)
-MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/airalarm, 27)
-
/obj/machinery/airalarm/proc/speak(warning_message)
if(machine_stat & (BROKEN|NOPOWER))
return
diff --git a/code/modules/atmospherics/machinery/air_alarm/air_alarm_interact.dm b/code/modules/atmospherics/machinery/air_alarm/air_alarm_interact.dm
index 1d2dbfa336308..3957c4d25ee8a 100644
--- a/code/modules/atmospherics/machinery/air_alarm/air_alarm_interact.dm
+++ b/code/modules/atmospherics/machinery/air_alarm/air_alarm_interact.dm
@@ -51,7 +51,7 @@
return FALSE
/obj/machinery/airalarm/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- if(rcd_data["[RCD_DESIGN_MODE]"] == RCD_WALLFRAME)
+ if(rcd_data[RCD_DESIGN_MODE] == RCD_WALLFRAME)
balloon_alert(user, "circuit installed")
buildstage = AIR_ALARM_BUILD_NO_WIRES
update_appearance()
@@ -182,7 +182,6 @@
/obj/item/wallframe/airalarm
name = "air alarm frame"
desc = "Used for building Air Alarms."
- icon = 'icons/obj/machines/wallmounts.dmi'
+ icon = 'icons/obj/machines/air_alarm.dmi'
icon_state = "alarm_bitem"
result_path = /obj/machinery/airalarm
- pixel_shift = 27
diff --git a/code/modules/atmospherics/machinery/air_alarm/air_alarm_thresholds.dm b/code/modules/atmospherics/machinery/air_alarm/air_alarm_thresholds.dm
index cfe74f13c346c..197e0f520a61f 100644
--- a/code/modules/atmospherics/machinery/air_alarm/air_alarm_thresholds.dm
+++ b/code/modules/atmospherics/machinery/air_alarm/air_alarm_thresholds.dm
@@ -5,7 +5,7 @@
var/hazard_min = 0
var/hazard_max = 0
-/** Initialize a TLV and set it's values if given arguments, mostly for map varedits.
+/** Initialize a TLV and set its values if given arguments, mostly for map varedits.
* We provide this functionality but please consider not doing this and making proper subtypes.
* Only by doing the latter will [datum/tlv/proc/reset_value] work.
*/
@@ -47,7 +47,7 @@
if(threshold_type & TLV_VAR_HAZARD_MAX)
hazard_max = value
-/** Reset this particular TLV to it's original value.
+/** Reset this particular TLV to its original value.
*
* Arguments:
* * threshold_type: What kind of variable do we want to set. Accepts bitfield subsets of [TLV_VAR_ALL].
diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm
index 29ea43905b8b7..12e6c684079e6 100644
--- a/code/modules/atmospherics/machinery/atmosmachinery.dm
+++ b/code/modules/atmospherics/machinery/atmosmachinery.dm
@@ -112,7 +112,7 @@
turf_loc.add_blueprints_preround(src)
if(hide)
- RegisterSignal(src, COMSIG_OBJ_HIDE, PROC_REF(on_hide))
+ setup_hiding()
SSspatial_grid.add_grid_awareness(src, SPATIAL_GRID_CONTENTS_TYPE_ATMOS)
SSspatial_grid.add_grid_membership(src, turf_loc, SPATIAL_GRID_CONTENTS_TYPE_ATMOS)
@@ -133,9 +133,18 @@
return ..()
/**
- * Handler for `COMSIG_OBJ_HIDE`, connects only if `hide` is set to `TRUE`. Calls `update_cap_visuals` on pipe and its connected nodes
+ * Sets up our pipe hiding logic, consolidated in one place so subtypes may override it.
+ * This lets subtypes implement their own hiding logic without needing to worry about conflicts with the parent hiding logic.
*/
-/obj/machinery/atmospherics/proc/on_hide(datum/source, underfloor_accessibility)
+/obj/machinery/atmospherics/proc/setup_hiding()
+ // Register pipe cap updating when hidden/unhidden
+ RegisterSignal(src, COMSIG_OBJ_HIDE, PROC_REF(on_hide))
+
+/**
+ * Signal handler. Updates both our pipe cap visuals and those of adjacent nodes.
+ * We update adjacent nodes as their pipe caps are based partially on our state, so they need updating as well.
+ */
+/obj/machinery/atmospherics/proc/on_hide(datum/source)
SHOULD_CALL_PARENT(TRUE)
SIGNAL_HANDLER
@@ -327,7 +336,10 @@
return FALSE
//if the target is not in the same piping layer & it does not have the all layer connection flag[which allows it to be connected regardless of layer] then we are out
- if(target.piping_layer != given_layer && !(target.pipe_flags & PIPING_ALL_LAYER))
+ if(target.pipe_flags & PIPING_DISTRO_AND_WASTE_LAYERS)
+ if(ISODD(given_layer))
+ return FALSE
+ else if(target.piping_layer != given_layer && !(target.pipe_flags & PIPING_ALL_LAYER))
return FALSE
//if the target does not have the same color and it does not have all color connection flag[which allows it to be connected regardless of color] & one of the pipes is not gray[allowing for connection regardless] then we are out
@@ -648,7 +660,8 @@
if(HAS_TRAIT(node, TRAIT_UNDERFLOOR))
continue
- if(isplatingturf(get_turf(node)))
+ var/turf/node_turf = get_turf(node)
+ if(isplatingturf(node_turf) || iscatwalkturf(node_turf))
continue
var/connected_dir = get_dir(src, node)
diff --git a/code/modules/atmospherics/machinery/bluespace_vendor.dm b/code/modules/atmospherics/machinery/bluespace_vendor.dm
index 91da04afaf2e7..1efab9b117ec2 100644
--- a/code/modules/atmospherics/machinery/bluespace_vendor.dm
+++ b/code/modules/atmospherics/machinery/bluespace_vendor.dm
@@ -1,10 +1,9 @@
/obj/item/wallframe/bluespace_vendor_mount
name = "bluespace vendor wall mount"
desc = "Used for placing bluespace vendors."
- icon = 'icons/obj/machines/atmospherics/bluespace_gas_selling.dmi'
+ icon = 'icons/obj/machines/atmospherics/bluespace_gas_vendor.dmi'
icon_state = "bluespace_vendor_open"
result_path = /obj/machinery/bluespace_vendor/built
- pixel_shift = 30
///Defines for the mode of the vendor
#define BS_MODE_OFF 1
@@ -13,7 +12,7 @@
#define BS_MODE_OPEN 4
/obj/machinery/bluespace_vendor
- icon = 'icons/obj/machines/atmospherics/bluespace_gas_selling.dmi'
+ icon = 'icons/obj/machines/atmospherics/bluespace_gas_vendor.dmi'
icon_state = "bluespace_vendor_off"
base_icon_state = "bluespace_vendor"
name = "Bluespace Gas Vendor"
@@ -51,7 +50,7 @@
map_spawned = FALSE
mode = BS_MODE_OPEN
-MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/bluespace_vendor, 30)
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/machinery/bluespace_vendor)
/datum/armor/machinery_bluespace_vendor
energy = 100
@@ -69,7 +68,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/bluespace_vendor, 30)
/obj/machinery/bluespace_vendor/Initialize(mapload)
. = ..()
AddComponent(/datum/component/payment, tank_cost, SSeconomy.get_dep_account(ACCOUNT_ENG), PAYMENT_ANGRY)
- find_and_hang_on_wall( FALSE)
+ find_and_hang_on_wall(directional = FALSE)
/obj/machinery/bluespace_vendor/post_machine_initialize()
. = ..()
@@ -94,6 +93,13 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/bluespace_vendor, 30)
icon_state = "[base_icon_state]_open"
return ..()
+/obj/machinery/bluespace_vendor/update_overlays()
+ . = ..()
+ if(mode == BS_MODE_IDLE)
+ . += emissive_appearance(icon, "[base_icon_state]_idle_light-mask", src, alpha)
+ else if(mode == BS_MODE_PUMPING)
+ . += emissive_appearance(icon, "[base_icon_state]_pumping_light-mask", src, alpha)
+
/obj/machinery/bluespace_vendor/Exited(atom/movable/gone, direction)
if(gone == internal_tank)
internal_tank = null
@@ -244,7 +250,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/bluespace_vendor, 30)
data["tank_full"] = total_tank_pressure
return data
-/obj/machinery/bluespace_vendor/ui_act(action, params)
+/obj/machinery/bluespace_vendor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm
index 1d7657dd35276..567cfcc6a843b 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm
@@ -84,7 +84,7 @@ Passive gate is similar to the regular pump except:
data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
return data
-/obj/machinery/atmospherics/components/binary/passive_gate/ui_act(action, params)
+/obj/machinery/atmospherics/components/binary/passive_gate/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pressure_valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/pressure_valve.dm
index ff9cc36d7cd61..b1e03743b5d89 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/pressure_valve.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/pressure_valve.dm
@@ -84,7 +84,7 @@
data["max_pressure"] = round(ONE_ATMOSPHERE*100)
return data
-/obj/machinery/atmospherics/components/binary/pressure_valve/ui_act(action, params)
+/obj/machinery/atmospherics/components/binary/pressure_valve/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
index 1e68bf9ad691a..848a41735c777 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
@@ -81,7 +81,7 @@
data["max_pressure"] = round(MAX_OUTPUT_PRESSURE)
return data
-/obj/machinery/atmospherics/components/binary/pump/ui_act(action, params)
+/obj/machinery/atmospherics/components/binary/pump/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/temperature_gate.dm b/code/modules/atmospherics/machinery/components/binary_devices/temperature_gate.dm
index 879f6b7fab8d3..0b6195f7b8425 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/temperature_gate.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/temperature_gate.dm
@@ -107,7 +107,7 @@
data["max_temperature"] = round(max_temperature)
return data
-/obj/machinery/atmospherics/components/binary/temperature_gate/ui_act(action, params)
+/obj/machinery/atmospherics/components/binary/temperature_gate/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/temperature_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/temperature_pump.dm
index 3976e8db750c1..88ec2265c69f6 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/temperature_pump.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/temperature_pump.dm
@@ -88,7 +88,7 @@
data["max_heat_transfer_rate"] = round(max_heat_transfer_rate)
return data
-/obj/machinery/atmospherics/components/binary/temperature_pump/ui_act(action, params)
+/obj/machinery/atmospherics/components/binary/temperature_pump/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
index ae8965ae0561e..477ea40137632 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
@@ -122,7 +122,7 @@
data["max_rate"] = round(MAX_TRANSFER_RATE)
return data
-/obj/machinery/atmospherics/components/binary/volume_pump/ui_act(action, params)
+/obj/machinery/atmospherics/components/binary/volume_pump/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/components_base.dm b/code/modules/atmospherics/machinery/components/components_base.dm
index cd4e00b3f33a6..ded9110bdfd74 100644
--- a/code/modules/atmospherics/machinery/components/components_base.dm
+++ b/code/modules/atmospherics/machinery/components/components_base.dm
@@ -66,6 +66,8 @@
if(!showpipe)
return ..()
+ if(pipe_flags & PIPING_DISTRO_AND_WASTE_LAYERS)
+ return ..()
var/connected = 0 //Direction bitset
diff --git a/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm b/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm
index 4a811c3435cb7..b504f6758dae2 100644
--- a/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm
+++ b/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm
@@ -2,6 +2,7 @@
#define ELECTROLYZER_MODE_WORKING "working"
/obj/machinery/electrolyzer
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
anchored = FALSE
density = TRUE
interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON | INTERACT_MACHINE_OPEN
diff --git a/code/modules/atmospherics/machinery/components/fusion/hfr_parts.dm b/code/modules/atmospherics/machinery/components/fusion/hfr_parts.dm
index 194e3744ca6d7..96272858a96b7 100644
--- a/code/modules/atmospherics/machinery/components/fusion/hfr_parts.dm
+++ b/code/modules/atmospherics/machinery/components/fusion/hfr_parts.dm
@@ -313,7 +313,7 @@
return data
-/obj/machinery/hypertorus/interface/ui_act(action, params)
+/obj/machinery/hypertorus/interface/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm
index f0482c210a66a..f4a19292b71a0 100644
--- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm
+++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm
@@ -10,6 +10,7 @@
name = "crystallizer"
desc = "Used to crystallize or solidify gases."
layer = ABOVE_MOB_LAYER
+ plane = GAME_PLANE
density = TRUE
max_integrity = 300
armor_type = /datum/armor/binary_crystallizer
@@ -300,7 +301,7 @@
data["gas_input"] = gas_input
return data
-/obj/machinery/atmospherics/components/binary/crystallizer/ui_act(action, params)
+/obj/machinery/atmospherics/components/binary/crystallizer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm
index 95b548998a194..26cb395113519 100644
--- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm
+++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm
@@ -38,3 +38,22 @@
if(uses <= 0)
qdel(src)
return ITEM_INTERACT_SUCCESS
+
+/obj/item/nitrium_crystal
+ desc = "A weird brown crystal, it smokes when broken"
+ name = "nitrium crystal"
+ icon = 'icons/obj/pipes_n_cables/atmos.dmi'
+ icon_state = "nitrium_crystal"
+ var/cloud_size = 1
+
+/obj/item/nitrium_crystal/attack_self(mob/user)
+ . = ..()
+ var/datum/effect_system/fluid_spread/smoke/chem/smoke = new
+ var/turf/location = get_turf(src)
+ create_reagents(5)
+ reagents.add_reagent(/datum/reagent/nitrium_low_metabolization, 3)
+ reagents.add_reagent(/datum/reagent/nitrium_high_metabolization, 2)
+ smoke.attach(location)
+ smoke.set_up(cloud_size, holder = src, location = location, carry = reagents, silent = TRUE)
+ smoke.start()
+ qdel(src)
diff --git a/code/modules/atmospherics/machinery/components/tank.dm b/code/modules/atmospherics/machinery/components/tank.dm
index ee1e61428d196..cc3827586962a 100644
--- a/code/modules/atmospherics/machinery/components/tank.dm
+++ b/code/modules/atmospherics/machinery/components/tank.dm
@@ -272,6 +272,7 @@
var/list/new_underlays = list()
for(var/obj/effect/overlay/gas/gas as anything in air_contents.return_visuals(get_turf(src)))
var/image/new_underlay = image(gas.icon, icon_state = gas.icon_state, layer = FLOAT_LAYER)
+ new_underlay.pixel_z = -12 // Weird offset artifacting? might be my fault idk
new_underlay.filters = alpha_mask_filter(icon = icon(icon, icon_state = "window-bg"))
new_underlays += new_underlay
diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
index 25217de538ce1..593832ec7986b 100644
--- a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
+++ b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm
@@ -6,7 +6,7 @@
desc = "Very useful for filtering gasses."
can_unwrench = TRUE
- construction_type = /obj/item/pipe/trinary/flippable
+ construction_type = /obj/item/pipe/trinary/flippable/filter
pipe_state = "filter"
///Rate of transfer of the gases to the outputs
@@ -141,7 +141,7 @@
return data
-/obj/machinery/atmospherics/components/trinary/filter/ui_act(action, params)
+/obj/machinery/atmospherics/components/trinary/filter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm
index 8f90986baaa46..8f7aa4a2d5716 100644
--- a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm
+++ b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm
@@ -150,7 +150,7 @@
data["node2_concentration"] = round(node2_concentration*100, 1)
return data
-/obj/machinery/atmospherics/components/trinary/mixer/ui_act(action, params)
+/obj/machinery/atmospherics/components/trinary/mixer/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/airlock_pump.dm b/code/modules/atmospherics/machinery/components/unary_devices/airlock_pump.dm
new file mode 100644
index 0000000000000..804f868e9c189
--- /dev/null
+++ b/code/modules/atmospherics/machinery/components/unary_devices/airlock_pump.dm
@@ -0,0 +1,455 @@
+/**
+ * The pump looks up for the airlocks automatically based on airlock_pump_distance_limit and airlock_group_distance_limit values.
+ * When placed, the dir value (direction where the pipes are coming from) is considered as a direction towards the station (internal). The opposite direction is external.
+ * The airlock then tries to find airlocks or walls towards these directions until airlock_pump_distance_limit number of tiles reached.
+ * When it finds a valid object, then it tries to find airlocks, in directions perpendicular to the found tiles.
+ * And then adds them to the corresponding group (external/internal) until airlock_group_distance_limit number of tiles reached
+ *
+ * Example scheme of a valid configuration:
+ * A-----W
+ * A-----A
+ * W--P--A
+ * W-----W
+ * A-----W
+ *
+ * Where:
+ * A - airlocks
+ * W - walls
+ * P - pump
+ */
+/// A vent, scrubber and a sensor in a single device meant specifically for cycling airlocks. Ideal for airlocks of up to 3x3 tiles in size to avoid wind and timing out.
+/obj/machinery/atmospherics/components/unary/airlock_pump
+ name = "external airlock pump"
+ desc = "A pump for cycling an external airlock controlled by the connected doors."
+ icon = 'icons/obj/machines/atmospherics/unary_devices.dmi'
+ icon_state = "airlock_pump"
+ pipe_state = "airlock_pump"
+ use_power = ACTIVE_POWER_USE
+ active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION
+ can_unwrench = TRUE
+ welded = FALSE
+ vent_movement = VENTCRAWL_ALLOWED | VENTCRAWL_CAN_SEE | VENTCRAWL_ENTRANCE_ALLOWED
+ max_integrity = 100
+ paintable = FALSE
+ pipe_flags = PIPING_ONE_PER_TURF | PIPING_DISTRO_AND_WASTE_LAYERS | PIPING_DEFAULT_LAYER_ONLY | PIPING_ALL_COLORS
+ layer = GAS_PUMP_LAYER
+ hide = TRUE
+ device_type = BINARY // Even though it is unary, it has two nodes on one side - used in node count checks
+
+ ///Indicates that the direction of the pump, if ATMOS_DIRECTION_SIPHONING is siphoning, if ATMOS_DIRECTION_RELEASING is releasing
+ var/pump_direction = ATMOS_DIRECTION_SIPHONING
+ ///Target pressure for pressurization cycle
+ var/internal_pressure_target = ONE_ATMOSPHERE
+ ///Target pressure for depressurization cycle
+ var/external_pressure_target = 0
+ ///Target pressure for the current cycle
+ var/cycle_pressure_target
+ ///Allowed error in pressure checks
+ var/allowed_pressure_error = ONE_ATMOSPHERE / 100
+ ///Minimal distro pressure to start cycling
+ var/min_distro_pressure = ONE_ATMOSPHERE / 10
+ ///Rate of the pump to remove gases from the air
+ var/volume_rate = 1000
+ ///The start time of the current cycle to calculate cycle duration
+ var/cycle_start_time
+ ///Max duration of cycle, after which the pump will unlock the airlocks with a warning
+ var/cycle_timeout = 10 SECONDS
+ ///List of the turfs adjacent to the pump for faster cycling and avoiding wind
+ var/list/turf/adjacent_turfs = list()
+ ///Max distance between the airlock and the pump. Used to set up cycling.
+ var/airlock_pump_distance_limit = 2
+ ///Max distance between the central airlock and the side airlocks in a group
+ var/airlock_group_distance_limit = 2
+ ///Type of airlocks required for automatic cycling setup. To avoid hacking bridge doors. Ignored for mapspawn pump.
+ var/valid_airlock_typepath = /obj/machinery/door/airlock/external
+ ///Station-facing airlocks used in cycling
+ var/list/obj/machinery/door/airlock/internal_airlocks
+ ///Space-facing airlocks used in cycling
+ var/list/obj/machinery/door/airlock/external_airlocks
+ ///Whether both airlocks are specified and cycling is available
+ var/cycling_set_up = FALSE
+ ///Whether the pump opens the airlocks up instead of simpy unbolting them on cycle
+ var/open_airlock_on_cycle = TRUE
+ ///Airlocks currently animating
+ var/airlocks_animating = FALSE
+ ///Whether the airlocks comment the cycling details to the chat
+ var/is_cycling_audible = TRUE
+
+ COOLDOWN_DECLARE(check_turfs_cooldown)
+
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/update_icon_nopipes()
+ if(!on || !is_operational || !powered())
+ icon_state = "vent_off"
+ else
+ icon_state = pump_direction ? "vent_out" : "vent_in"
+
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/update_overlays()
+ . = ..()
+ if(!showpipe)
+ return
+
+ var/mutable_appearance/distro_pipe_appearance = get_pipe_image(icon, "pipe_exposed", dir, COLOR_BLUE, piping_layer = 4)
+ if(nodes[1])
+ distro_pipe_appearance = get_pipe_image(icon, "pipe_intact", dir, COLOR_BLUE, piping_layer = 4)
+ . += distro_pipe_appearance
+
+ var/mutable_appearance/waste_pipe_appearance = get_pipe_image(icon, "pipe_exposed", dir, COLOR_RED, piping_layer = 2)
+ if(nodes[2])
+ waste_pipe_appearance = get_pipe_image(icon, "pipe_intact", dir, COLOR_RED, piping_layer = 2)
+ . += waste_pipe_appearance
+
+ var/mutable_appearance/distro_cap_appearance = get_pipe_image(icon, "vent_cap", dir, piping_layer = 4)
+ . += distro_cap_appearance
+
+ var/mutable_appearance/waste_cap_appearance = get_pipe_image(icon, "vent_cap", dir, piping_layer = 2)
+ . += waste_cap_appearance
+
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/atmos_init(list/node_connects)
+ for(var/obj/machinery/atmospherics/target in get_step(src, dir))
+ if(connection_check(target, 4) && !nodes[1])
+ nodes[1] = target // Distro
+ if(connection_check(target, 2) && !nodes[2])
+ nodes[2] = target // Waste
+ update_appearance()
+
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/Initialize(mapload)
+ . = ..()
+ if(mapload)
+ can_unwrench = FALSE
+
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/post_machine_initialize()
+ . = ..()
+ set_links()
+
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/New()
+ . = ..()
+ var/datum/gas_mixture/distro_air = airs[1]
+ var/datum/gas_mixture/waste_air = airs[2]
+ distro_air.volume = 1000
+ waste_air.volume = 1000
+
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/on_deconstruction(disassembled)
+ . = ..()
+ if(cycling_set_up)
+ break_all_links()
+
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/can_unwrench(mob/user)
+ . = ..()
+ if(!.)
+ to_chat(user, span_warning("You cannot unwrench [src], it is secured firmly in place!"))
+ return FALSE
+ if(. && on)
+ to_chat(user, span_warning("You cannot unwrench [src], wait for the cycle completion!"))
+ return FALSE
+
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/process_atmos()
+ if(!on)
+ return
+
+ if(!powered())
+ stop_cycle("No power. Cycle aborted.", unbolt_only = TRUE)
+ return //Couldn't complete the cycle due to power outage
+
+ var/turf/location = get_turf(loc)
+ if(isclosedturf(location))
+ return
+
+ if(COOLDOWN_FINISHED(src, check_turfs_cooldown))
+ check_turfs()
+ COOLDOWN_START(src, check_turfs_cooldown, 2 SECONDS)
+
+ if(world.time - cycle_start_time > cycle_timeout)
+ stop_cycle("Cycling timed out, bolts unlocked.", unbolt_only = TRUE)
+ return //Couldn't complete the cycle before timeout
+
+ var/datum/gas_mixture/distro_air = airs[1]
+ var/datum/gas_mixture/tile_air = loc.return_air()
+ var/tile_air_pressure = tile_air.return_pressure()
+
+ if(pump_direction == ATMOS_DIRECTION_RELEASING) //distro node -> tile
+ var/pressure_delta = cycle_pressure_target - tile_air_pressure
+ if(pressure_delta <= allowed_pressure_error && stop_cycle("Pressurization complete."))
+ return //Internal target pressure reached
+
+ var/available_moles = distro_air.total_moles()
+ var/total_tiles = adjacent_turfs.len + 1
+ var/split_moles = QUANTIZE(available_moles / total_tiles)
+
+ fill_tile(loc, split_moles, pressure_delta)
+ for(var/turf/tile as anything in adjacent_turfs)
+ fill_tile(tile, split_moles, pressure_delta)
+ else //tile -> waste node
+ var/pressure_delta = tile_air_pressure - cycle_pressure_target
+ if(pressure_delta <= allowed_pressure_error && stop_cycle("Decompression complete."))
+ return //External target pressure reached
+
+ siphon_tile(loc)
+ for(var/turf/tile as anything in adjacent_turfs)
+ siphon_tile(tile)
+
+
+/// Fill a tile with air from the distro node
+/obj/machinery/atmospherics/components/unary/airlock_pump/proc/fill_tile(turf/tile, moles, pressure_delta)
+ var/datum/pipeline/distro_pipe = parents[1]
+ var/datum/gas_mixture/distro_air = airs[1]
+ var/datum/gas_mixture/tile_air = tile.return_air()
+ var/transfer_moles = (volume_rate / tile_air.volume) * (pressure_delta * tile_air.volume) / (distro_air.temperature * R_IDEAL_GAS_EQUATION)
+ moles = min(moles, transfer_moles)
+
+ var/datum/gas_mixture/removed_air = distro_air.remove(moles)
+
+ if(!removed_air)
+ return //No air in distro
+
+ tile.assume_air(removed_air)
+ distro_pipe.update = TRUE
+
+
+/// Siphon air from the tile to the waste node within the volume rate limit
+/obj/machinery/atmospherics/components/unary/airlock_pump/proc/siphon_tile(turf/tile)
+ var/datum/pipeline/waste_pipe = parents[2]
+ var/datum/gas_mixture/waste_air = airs[2]
+ var/datum/gas_mixture/tile_air = tile.return_air()
+
+ var/transfer_moles = tile_air.total_moles() * (volume_rate / tile_air.volume)
+ var/datum/gas_mixture/removed_air = tile.remove_air(transfer_moles)
+
+ if(!removed_air)
+ return //No air on the tile
+
+ waste_air.merge(removed_air)
+ waste_pipe.update = TRUE
+
+
+/// Proc for triggering cycle by clicking on a bolted airlock that has a pump assigned
+/obj/machinery/atmospherics/components/unary/airlock_pump/proc/airlock_act(obj/machinery/door/airlock/airlock)
+ if(on)
+ airlock.run_animation(DOOR_DENY_ANIMATION) // Already cycling
+ return
+ if(!cycling_set_up)
+ airlock.say("Airlock pair not found.")
+ return
+ if(airlock in external_airlocks)
+ start_cycle(ATMOS_DIRECTION_SIPHONING, airlock)
+ else if(airlock in internal_airlocks)
+ start_cycle(ATMOS_DIRECTION_RELEASING, airlock)
+
+
+///Start decompression or pressurization cycle depending on the passed direction
+/obj/machinery/atmospherics/components/unary/airlock_pump/proc/start_cycle(cycle_direction, obj/machinery/door/airlock/source_airlock = null)
+ if(on || !cycling_set_up || airlocks_animating || !powered())
+ return FALSE
+
+ pump_direction = cycle_direction
+
+ for(var/obj/machinery/door/airlock/airlock as anything in (internal_airlocks + external_airlocks))
+ INVOKE_ASYNC(airlock, TYPE_PROC_REF(/obj/machinery/door/airlock, secure_close))
+
+ airlocks_animating = TRUE
+ stoplag(1 SECONDS) // Wait for closing animation
+ airlocks_animating = FALSE
+
+ on = TRUE
+ cycle_start_time = world.time
+
+ var/turf/local_turf = get_turf(src)
+ var/tile_air_pressure = max(0, local_turf.return_air().return_pressure())
+
+ if(pump_direction == ATMOS_DIRECTION_RELEASING)
+ cycle_pressure_target = internal_pressure_target
+ var/pressure_delta = cycle_pressure_target - tile_air_pressure
+ if(pressure_delta <= allowed_pressure_error)
+ stop_cycle("Pressure nominal, cycle skipped.")
+ return TRUE
+
+ var/datum/gas_mixture/distro_air = airs[1]
+ if(distro_air.return_pressure() < min_distro_pressure)
+ stop_cycle("Low pipe pressure, cycle skipped. Proceed with caution.", unbolt_only = TRUE)
+ return TRUE
+
+ if(!source_airlock)
+ source_airlock = internal_airlocks[1]
+ if(is_cycling_audible)
+ source_airlock.say("Pressurizing airlock.")
+ else
+ cycle_pressure_target = external_pressure_target
+ var/pressure_delta = tile_air_pressure - cycle_pressure_target
+ if(pressure_delta <= allowed_pressure_error)
+ stop_cycle("Pressure nominal, cycle skipped.")
+ return TRUE
+
+ for(var/obj/machinery/door/airlock/airlock as anything in external_airlocks)
+ if(airlock.shuttledocked)
+ stop_cycle("Shuttle docked, cycle skipped.")
+ return TRUE
+
+ if(!source_airlock)
+ source_airlock = external_airlocks[1]
+ if(is_cycling_audible)
+ source_airlock.say("Decompressing airlock.")
+
+ update_appearance()
+ return TRUE
+
+
+///Complete/Abort cycle with the passed message
+/obj/machinery/atmospherics/components/unary/airlock_pump/proc/stop_cycle(message = null, unbolt_only = FALSE)
+ if(!on)
+ return FALSE
+ on = FALSE
+
+ var/list/obj/machinery/door/airlock/unlocked_airlocks = pump_direction == ATMOS_DIRECTION_RELEASING ? internal_airlocks : external_airlocks
+ for(var/obj/machinery/door/airlock/airlock as anything in unlocked_airlocks)
+ airlock.unbolt()
+ if(open_airlock_on_cycle && !unbolt_only)
+ INVOKE_ASYNC(airlock, TYPE_PROC_REF(/obj/machinery/door/airlock, secure_open)) //Can unbolt, but without audio
+
+ airlocks_animating = TRUE
+ stoplag(1 SECONDS) // Wait for opening animation
+ airlocks_animating = FALSE
+
+ if(message && is_cycling_audible)
+ unlocked_airlocks[1].say(message)
+
+ update_appearance()
+ return TRUE
+
+
+///Update adjacent_turfs with atmospherically adjacent tiles
+/obj/machinery/atmospherics/components/unary/airlock_pump/proc/check_turfs()
+ adjacent_turfs.Cut()
+ var/turf/local_turf = get_turf(src)
+ adjacent_turfs = local_turf.get_atmos_adjacent_turfs(alldir = TRUE)
+
+
+///Find airlocks and link up with them
+/obj/machinery/atmospherics/components/unary/airlock_pump/proc/set_links()
+ var/perpendicular_dirs = NSCOMPONENT(dir) ? WEST|EAST : NORTH|SOUTH
+ var/turf/internal_airlocks_origin = find_density(get_turf(src), dir)
+ var/turf/external_airlocks_origin = find_density(get_turf(src), REVERSE_DIR(dir))
+ internal_airlocks = get_adjacent_airlocks(internal_airlocks_origin, perpendicular_dirs)
+ external_airlocks = get_adjacent_airlocks(external_airlocks_origin, perpendicular_dirs)
+
+ if(!internal_airlocks.len || !internal_airlocks.len)
+ if(!can_unwrench) //maploaded pump
+ CRASH("[type] couldn't find airlocks to cycle with!")
+ internal_airlocks = list()
+ external_airlocks = list()
+ say("Cycling setup failed. No opposite airlocks found.")
+ return
+
+ for(var/obj/machinery/door/airlock/airlock as anything in (internal_airlocks + external_airlocks))
+ airlock.set_cycle_pump(src)
+ RegisterSignal(airlock, COMSIG_QDELETING, PROC_REF(unlink_airlock))
+ if (airlock in external_airlocks)
+ INVOKE_ASYNC(airlock, TYPE_PROC_REF(/obj/machinery/door/airlock, secure_close))
+ else if(open_airlock_on_cycle)
+ INVOKE_ASYNC(airlock, TYPE_PROC_REF(/obj/machinery/door/airlock, secure_open))
+
+ cycle_timeout *= round((internal_airlocks.len + external_airlocks.len) / 2)
+ cycling_set_up = TRUE
+ if(can_unwrench)
+ say("Cycling setup complete.")
+
+
+///Get the turf of the first found airlock or an airtight structure (walls) within the allowed range
+/obj/machinery/atmospherics/components/unary/airlock_pump/proc/find_density(turf/origin, direction, max_distance = airlock_pump_distance_limit)
+ var/turf/next_turf = origin
+ var/limit = max(1, max_distance)
+ while(limit)
+ limit--
+ next_turf = get_step(next_turf, direction)
+ var/obj/machinery/door/airlock/found_airlock = locate() in next_turf
+ if(is_valid_airlock(found_airlock))
+ return found_airlock.loc
+ if(!next_turf.can_atmos_pass)
+ return next_turf
+
+
+///Find airlocks adjacent to the central one, lined up along the provided directions
+/obj/machinery/atmospherics/components/unary/airlock_pump/proc/get_adjacent_airlocks(origin_turf, directions)
+ var/list/airlocks = list()
+
+ var/obj/machinery/door/airlock/origin_airlock = locate() in origin_turf
+ if(is_valid_airlock(origin_airlock))
+ airlocks.Add(origin_airlock)
+
+ for(var/direction in GLOB.cardinals)
+ if(!(direction & directions))
+ continue
+ var/turf/next_turf = origin_turf
+ var/limit = max(0, airlock_group_distance_limit)
+ while(limit)
+ limit--
+ next_turf = get_step(next_turf, direction)
+ var/obj/machinery/door/airlock/found_airlock = locate() in next_turf
+ if (is_valid_airlock(found_airlock))
+ airlocks.Add(found_airlock)
+ else
+ limit = 0
+
+ return airlocks
+
+
+///Whether the passed airlock can be linked with
+/obj/machinery/atmospherics/components/unary/airlock_pump/proc/is_valid_airlock(obj/machinery/door/airlock/airlock)
+ if(!airlock)
+ return FALSE
+ if(airlock.cycle_pump)
+ return FALSE // Already linked
+ if(can_unwrench && !istype(airlock, valid_airlock_typepath))
+ return FALSE // Invalid airlock type and the pump is not mapspawn
+ return TRUE
+
+
+///Find airlocks and link up with them
+/obj/machinery/atmospherics/components/unary/airlock_pump/proc/unlink_airlock(airlock)
+ UnregisterSignal(airlock, COMSIG_QDELETING)
+
+ if(airlock in internal_airlocks)
+ internal_airlocks.Remove(airlock)
+ if(airlock in external_airlocks)
+ external_airlocks.Remove(airlock)
+
+ if(!internal_airlocks.len || !external_airlocks.len)
+ break_all_links()
+
+
+///Break the cycling setup
+/obj/machinery/atmospherics/components/unary/airlock_pump/proc/break_all_links()
+ for(var/obj/machinery/door/airlock/airlock as anything in (internal_airlocks + external_airlocks))
+ UnregisterSignal(airlock, COMSIG_QDELETING)
+
+ external_airlocks = list()
+ internal_airlocks = list()
+ cycle_timeout = initial(cycle_timeout)
+ cycling_set_up = FALSE
+
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/relaymove(mob/living/user, direction)
+ if(initialize_directions & direction)
+ return ..()
+ if((NORTH|EAST) & direction)
+ user.ventcrawl_layer = clamp(user.ventcrawl_layer + 2, PIPING_LAYER_DEFAULT - 1, PIPING_LAYER_DEFAULT + 1)
+ if((SOUTH|WEST) & direction)
+ user.ventcrawl_layer = clamp(user.ventcrawl_layer - 2, PIPING_LAYER_DEFAULT - 1, PIPING_LAYER_DEFAULT + 1)
+ to_chat(user, "You align yourself with the [user.ventcrawl_layer == 2 ? 1 : 2]\th output.")
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/unbolt_only
+ open_airlock_on_cycle = FALSE
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/silent
+ is_cycling_audible = FALSE
+
+/obj/machinery/atmospherics/components/unary/airlock_pump/lavaland
+ external_pressure_target = LAVALAND_EQUIPMENT_EFFECT_PRESSURE
+
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/bluespace_sender.dm b/code/modules/atmospherics/machinery/components/unary_devices/bluespace_sender.dm
index ebe053663f9e0..6baac50e27c82 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/bluespace_sender.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/bluespace_sender.dm
@@ -1,5 +1,5 @@
/obj/machinery/atmospherics/components/unary/bluespace_sender
- icon = 'icons/obj/machines/atmospherics/bluespace_gas_selling.dmi'
+ icon = 'icons/obj/machines/atmospherics/bluespace_gas_vendor.dmi'
icon_state = "bluespace_sender_off"
base_icon_state = "bluespace_sender"
name = "Bluespace Gas Sender"
@@ -198,7 +198,7 @@ GLOBAL_LIST_EMPTY_TYPED(bluespace_senders, /obj/machinery/atmospherics/component
data["credits"] = credits_gained
return data
-/obj/machinery/atmospherics/components/unary/bluespace_sender/ui_act(action, params)
+/obj/machinery/atmospherics/components/unary/bluespace_sender/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm
index 3f87ca671fdc9..c0b584d9029cc 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm
@@ -122,7 +122,7 @@
data["max_rate"] = round(MAX_TRANSFER_RATE)
return data
-/obj/machinery/atmospherics/components/unary/outlet_injector/ui_act(action, params)
+/obj/machinery/atmospherics/components/unary/outlet_injector/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
index 619bf100a170f..cb49e572fd1f8 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm
@@ -283,7 +283,7 @@
data["pressure"] = port.return_pressure()
return data
-/obj/machinery/atmospherics/components/unary/thermomachine/ui_act(action, params)
+/obj/machinery/atmospherics/components/unary/thermomachine/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm
index 02f0d20354496..8a165830cec07 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm
@@ -5,7 +5,7 @@
name = "air vent"
desc = "Has a valve and pump attached to it."
-
+ construction_type = /obj/item/pipe/directional/vent
use_power = IDLE_POWER_USE
idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION * 0.15
can_unwrench = TRUE
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
index 20a0b86b41c29..22ee2f6b414a7 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
@@ -3,6 +3,7 @@
name = "air scrubber"
desc = "Has a valve and pump attached to it."
+ construction_type = /obj/item/pipe/directional/scrubber
use_power = IDLE_POWER_USE
idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION * 0.1
active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 0.15
diff --git a/code/modules/atmospherics/machinery/datum_pipeline.dm b/code/modules/atmospherics/machinery/datum_pipeline.dm
index 35b8fa61bbc22..9d9a5a6292f5d 100644
--- a/code/modules/atmospherics/machinery/datum_pipeline.dm
+++ b/code/modules/atmospherics/machinery/datum_pipeline.dm
@@ -131,7 +131,7 @@
/**
* For a machine to properly "connect" to a pipeline and share gases,
- * the pipeline needs to acknowledge a gas mixture as it's member.
+ * the pipeline needs to acknowledge a gas mixture as its member.
* This is currently handled by the other_airs list in the pipeline datum.
*
* Other_airs itself is populated by gas mixtures through the parents list that each machineries have.
@@ -360,7 +360,7 @@
/obj/effect/abstract/gas_visual/Initialize(mapload)
. = ..()
- color_filter = filter(type="color", color=matrix())
+ color_filter = filter(type="color", color="white")
filters += color_filter
color_filter = filters[filters.len]
if(current_color)
diff --git a/code/modules/atmospherics/machinery/pipes/layermanifold.dm b/code/modules/atmospherics/machinery/pipes/layermanifold.dm
index df919cbeccb1c..fde47b1baf0ee 100644
--- a/code/modules/atmospherics/machinery/pipes/layermanifold.dm
+++ b/code/modules/atmospherics/machinery/pipes/layermanifold.dm
@@ -65,6 +65,10 @@
for(var/i in PIPING_LAYER_MIN to PIPING_LAYER_MAX)
. += get_attached_image(get_dir(src, machine_check), i, COLOR_VERY_LIGHT_GRAY)
return
+ if(istype(machine_check, /obj/machinery/atmospherics/components/unary/airlock_pump))
+ . += get_attached_image(get_dir(src, machine_check), 4, COLOR_BLUE)
+ //. += get_attached_image(get_dir(src, machine_check), 2, COLOR_RED) // Only the distro node is added currently to the pipenet, it doesn't merge the pipenet with the waste node
+ return
. += get_attached_image(get_dir(src, machine_check), machine_check.piping_layer, machine_check.pipe_color)
/obj/machinery/atmospherics/pipe/layer_manifold/proc/get_attached_image(p_dir, p_layer, p_color)
diff --git a/code/modules/atmospherics/machinery/pipes/mapping.dm b/code/modules/atmospherics/machinery/pipes/mapping.dm
index 3615147e4d1bf..dce09be9092ed 100644
--- a/code/modules/atmospherics/machinery/pipes/mapping.dm
+++ b/code/modules/atmospherics/machinery/pipes/mapping.dm
@@ -83,7 +83,7 @@ HELPER(yellow, COLOR_YELLOW)
HELPER(general, COLOR_VERY_LIGHT_GRAY)
HELPER(cyan, COLOR_CYAN)
HELPER(green, COLOR_VIBRANT_LIME)
-HELPER(orange, COLOR_TAN_ORANGE)
+HELPER(orange, COLOR_ENGINEERING_ORANGE)
HELPER(purple, COLOR_PURPLE)
HELPER(dark, COLOR_DARK)
HELPER(brown, COLOR_BROWN)
diff --git a/code/modules/atmospherics/machinery/pipes/pipes.dm b/code/modules/atmospherics/machinery/pipes/pipes.dm
index 230edc9a897f0..ebc31e847b85a 100644
--- a/code/modules/atmospherics/machinery/pipes/pipes.dm
+++ b/code/modules/atmospherics/machinery/pipes/pipes.dm
@@ -27,12 +27,11 @@
volume = 35 * device_type
. = ..()
-///I have no idea why there's a new and at this point I'm too afraid to ask
-/obj/machinery/atmospherics/pipe/Initialize(mapload)
- . = ..()
+/obj/machinery/atmospherics/pipe/setup_hiding()
+ AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) //if changing this, change the subtypes RemoveElements too, because thats how bespoke works
- if(hide)
- AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) //if changing this, change the subtypes RemoveElements too, because thats how bespoke works
+ // Registering on `COMSIG_OBJ_HIDE` would cause order of operations issues with undertile, so we register to run when undertile updates instead
+ RegisterSignal(src, COMSIG_UNDERTILE_UPDATED, PROC_REF(on_hide))
/obj/machinery/atmospherics/pipe/on_deconstruction(disassembled)
//we delete the parent here so it initializes air_temporary for us. See /datum/pipeline/Destroy() which calls temporarily_store_air()
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index af13be0bbbf9b..bedd41aaf100d 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -343,6 +343,7 @@
var/list/window_overlays = list()
for(var/visual in air_contents.return_visuals(get_turf(src)))
var/image/new_visual = image(visual, layer = FLOAT_LAYER)
+ new_visual.pixel_z = -12 // Weird offset artifacting? might be my fault idk
new_visual.filters = alpha_filter
window_overlays += new_visual
window.overlays = window_overlays
@@ -566,7 +567,7 @@
"cellCharge" = internal_cell ? internal_cell.percent() : 0
)
-/obj/machinery/portable_atmospherics/canister/ui_act(action, params)
+/obj/machinery/portable_atmospherics/canister/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/portable/pipe_scrubber.dm b/code/modules/atmospherics/machinery/portable/pipe_scrubber.dm
index cde38f216ad70..434f243d6a3ca 100644
--- a/code/modules/atmospherics/machinery/portable/pipe_scrubber.dm
+++ b/code/modules/atmospherics/machinery/portable/pipe_scrubber.dm
@@ -129,7 +129,7 @@
data["pressureLimitTank"] = internal_tank.pressure_limit
return data
-/obj/machinery/portable_atmospherics/pipe_scrubber/ui_act(action, params)
+/obj/machinery/portable_atmospherics/pipe_scrubber/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm
index 9729c0871451a..adff51233c916 100644
--- a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm
+++ b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm
@@ -1,6 +1,7 @@
#define PORTABLE_ATMOS_IGNORE_ATMOS_LIMIT 0
/obj/machinery/portable_atmospherics
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "portable_atmospherics"
icon = 'icons/obj/pipes_n_cables/atmos.dmi'
use_power = NO_POWER_USE
@@ -50,6 +51,11 @@
AddElement(/datum/element/climbable, climb_time = 3 SECONDS, climb_stun = 3 SECONDS)
AddElement(/datum/element/elevation, pixel_shift = 8)
register_context()
+ update_position()
+
+/obj/machinery/portable_atmospherics/on_construction(mob/user)
+ . = ..()
+ set_anchored(FALSE)
/obj/machinery/portable_atmospherics/on_deconstruction(disassembled)
if(nob_crystal_inserted)
@@ -190,6 +196,20 @@
update_appearance()
return TRUE
+/obj/machinery/portable_atmospherics/set_anchored(anchorvalue)
+ . = ..()
+ update_position()
+
+/obj/machinery/portable_atmospherics/proc/update_position()
+ var/new_base = base_pixel_z
+ if(anchored)
+ new_base = 6 // Lands em all perfectly on pipe connectors
+ else
+ new_base = DEPTH_OFFSET
+
+ animate(src, pixel_z = pixel_z + new_base - base_pixel_z, time = 0.1 SECONDS)
+ base_pixel_z = new_base
+
/obj/machinery/portable_atmospherics/click_alt(mob/living/user)
if(!holding)
return CLICK_ACTION_BLOCKING
diff --git a/code/modules/atmospherics/machinery/portable/pump.dm b/code/modules/atmospherics/machinery/portable/pump.dm
index 7b0dbef314ec0..3efc87e00ac3a 100644
--- a/code/modules/atmospherics/machinery/portable/pump.dm
+++ b/code/modules/atmospherics/machinery/portable/pump.dm
@@ -109,7 +109,7 @@
data["holding"] = null
return data
-/obj/machinery/portable_atmospherics/pump/ui_act(action, params)
+/obj/machinery/portable_atmospherics/pump/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/atmospherics/machinery/portable/scrubber.dm b/code/modules/atmospherics/machinery/portable/scrubber.dm
index b292180683f88..29759e52e4278 100644
--- a/code/modules/atmospherics/machinery/portable/scrubber.dm
+++ b/code/modules/atmospherics/machinery/portable/scrubber.dm
@@ -161,7 +161,7 @@
else if(on && holding)
user.investigate_log("started a transfer into [holding].", INVESTIGATE_ATMOS)
-/obj/machinery/portable_atmospherics/scrubber/ui_act(action, params)
+/obj/machinery/portable_atmospherics/scrubber/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/awaymissions/away_props.dm b/code/modules/awaymissions/away_props.dm
index 6f6a25b47c8cd..0ab3cd7e8e3c6 100644
--- a/code/modules/awaymissions/away_props.dm
+++ b/code/modules/awaymissions/away_props.dm
@@ -1,6 +1,6 @@
/obj/effect/oneway
name = "one way effect"
- desc = "Only lets things in from it's dir."
+ desc = "Only lets things in from its dir."
icon = 'icons/effects/mapping_helpers.dmi'
icon_state = "field_dir"
invisibility = INVISIBILITY_MAXIMUM
@@ -13,7 +13,7 @@
/obj/effect/wind
name = "wind effect"
- desc = "Creates pressure effect in it's direction. Use sparingly."
+ desc = "Creates pressure effect in its direction. Use sparingly."
icon = 'icons/effects/mapping_helpers.dmi'
icon_state = "field_dir"
invisibility = INVISIBILITY_MAXIMUM
@@ -54,7 +54,7 @@
/obj/structure/pitgrate
name = "pit grate"
- icon = 'icons/obj/smooth_structures/lattice.dmi'
+ icon = 'icons/obj/structures/smooth/lattice.dmi'
icon_state = "lattice-255"
layer = ABOVE_OPEN_TURF_LAYER
plane = FLOOR_PLANE
diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm
index e32db92339991..5e3cf1bb21eac 100644
--- a/code/modules/awaymissions/gateway.dm
+++ b/code/modules/awaymissions/gateway.dm
@@ -359,7 +359,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
destinations += list(possible_destination.get_ui_data())
.["destinations"] = destinations
-/obj/machinery/computer/gateway_control/ui_act(action, list/params)
+/obj/machinery/computer/gateway_control/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/awaymissions/mission_code/Beach.dm b/code/modules/awaymissions/mission_code/Beach.dm
new file mode 100644
index 0000000000000..8e05cfe4a5eb1
--- /dev/null
+++ b/code/modules/awaymissions/mission_code/Beach.dm
@@ -0,0 +1,19 @@
+/area/awaymission/beach
+ name = "Beach"
+ icon_state = "away"
+ static_lighting = FALSE
+ base_lighting_alpha = 255
+ base_lighting_color = "#FFFFCC"
+ requires_power = FALSE
+ has_gravity = STANDARD_GRAVITY
+ ambientsounds = list('sound/ambience/shore.ogg', 'sound/ambience/seag1.ogg','sound/ambience/seag2.ogg','sound/ambience/seag2.ogg','sound/ambience/ambiodd.ogg','sound/ambience/ambinice.ogg')
+
+/obj/item/paper/fluff/old_pirate_note
+ name = "rum-stained letter"
+ icon_state = "scrap_mud"
+ desc = "An unsent letter from a terminally drunk pirate."
+ default_raw_text = {"Dear,
+ I'm sorry I won't sail back home soon,
+ son of a biscuit eater walked the plank with me coffer (or treasure chest as landlubbers call'em),
+ so I got me fishing rod, bottles, waiting to fish the booty back.
+ Luv you hun, I hope this letter find you we-"}
diff --git a/code/modules/awaymissions/mission_code/murderdome.dm b/code/modules/awaymissions/mission_code/murderdome.dm
index a59a491d4926f..4ff5b03ba3dff 100644
--- a/code/modules/awaymissions/mission_code/murderdome.dm
+++ b/code/modules/awaymissions/mission_code/murderdome.dm
@@ -38,7 +38,7 @@
/obj/effect/murderdome/dead_barricade
name = "dead barrier"
desc = "It provided cover in fire fights. And now it's gone."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/structures/tall.dmi'
icon_state = "barrier0"
alpha = 100
diff --git a/code/modules/awaymissions/mission_code/stationCollision.dm b/code/modules/awaymissions/mission_code/stationCollision.dm
index aa1242dcfe7cb..5660a4400e11f 100644
--- a/code/modules/awaymissions/mission_code/stationCollision.dm
+++ b/code/modules/awaymissions/mission_code/stationCollision.dm
@@ -128,6 +128,8 @@ GLOBAL_VAR_INIT(sc_safecode5, "[rand(0,9)]")
*/
/obj/structure/secure_safe/sc_ssafe
name = "Captain's secure safe"
+
+WALL_MOUNT_DIRECTIONAL_HELPERS(/obj/structure/secure_safe/sc_ssafe)
/obj/structure/secure_safe/sc_ssafe/Initialize(mapload)
. = ..()
diff --git a/code/modules/awaymissions/super_secret_room.dm b/code/modules/awaymissions/super_secret_room.dm
index d7ecda850182a..9f8be686fef7c 100644
--- a/code/modules/awaymissions/super_secret_room.dm
+++ b/code/modules/awaymissions/super_secret_room.dm
@@ -2,10 +2,9 @@
name = "strange tile"
desc = "A weird tile that beckons you towards it. Maybe it can help you get out of this mess..."
verb_say = "intones"
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/fluff/general.dmi'
icon_state = "speaking_tile"
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
resistance_flags = INDESTRUCTIBLE
var/speaking = FALSE
var/times_spoken_to = 0
diff --git a/code/modules/awaymissions/zlevel.dm b/code/modules/awaymissions/zlevel.dm
index 0fdc5093511f2..b72a6b59dde36 100644
--- a/code/modules/awaymissions/zlevel.dm
+++ b/code/modules/awaymissions/zlevel.dm
@@ -32,15 +32,51 @@ GLOBAL_LIST_INIT(potentialConfigRandomZlevels, generate_map_list_from_directory(
current = D
if(!current)
current = new
+ current.name = name
current.id = id
if(delay)
- current.wait = CONFIG_GET(number/gateway_delay)
+ var/list/waits_by_id = CONFIG_GET(keyed_list/gateway_delays_by_id)
+ var/wait_to_use = !isnull(waits_by_id[id]) ? waits_by_id[id] : CONFIG_GET(number/gateway_delay)
+ current.wait = wait_to_use
GLOB.gateway_destinations += current
current.target_turfs += get_turf(src)
+ return INITIALIZE_HINT_QDEL
/obj/effect/landmark/awaystart/nodelay
delay = FALSE
+/obj/effect/landmark/awaystart/beach
+ name = "beach away spawn"
+ id = AWAYSTART_BEACH
+
+/obj/effect/landmark/awaystart/museum
+ name = "buseum away spawn"
+ id = AWAYSTART_MUSEUM
+
+/obj/effect/landmark/awaystart/research
+ name = "research outpost away spawn"
+ id = AWAYSTART_RESEARCH
+
+/obj/effect/landmark/awaystart/caves
+ name = "caves away spawn"
+ id = AWAYSTART_CAVES
+
+/obj/effect/landmark/awaystart/moonoutpost
+ name = "Moon Outpost 19 away spawn"
+ id = AWAYSTART_MOONOUTPOST
+
+/obj/effect/landmark/awaystart/snowcabin
+ name = "snow cabin away spawn"
+ id = AWAYSTART_SNOWCABIN
+
+/obj/effect/landmark/awaystart/snowdin
+ name = "Snowdin away spawn"
+ id = AWAYSTART_SNOWDIN
+
+/obj/effect/landmark/awaystart/underground
+ name = "Underground Outpost 45 away spawn"
+ id = AWAYSTART_UNDERGROUND
+
/proc/generateMapList(filename)
. = list()
filename = "[global.config.directory]/[SANITIZE_FILENAME(filename)]"
diff --git a/code/modules/basketball/hoop.dm b/code/modules/basketball/hoop.dm
index 72669df017d90..37975e5e51d7b 100644
--- a/code/modules/basketball/hoop.dm
+++ b/code/modules/basketball/hoop.dm
@@ -17,6 +17,9 @@
anchored = TRUE
density = TRUE
layer = ABOVE_MOB_LAYER
+ //physically offset ourself so we render right as a big icon (I think? that's what's goin on here)
+ pixel_y = 16
+ pixel_z = -16
interaction_flags_click = NEED_DEXTERITY | NEED_HANDS | FORBID_TELEKINESIS_REACH
/// Keeps track of the total points scored
var/total_score = 0
@@ -53,7 +56,6 @@
/obj/structure/hoop/update_overlays()
. = ..()
-
var/dir_offset_x = 0
var/dir_offset_y = 0
@@ -68,24 +70,24 @@
dir_offset_x = 32
var/mutable_appearance/scoreboard = mutable_appearance('icons/obj/signs.dmi', "basketball_scorecard")
- scoreboard.pixel_x = dir_offset_x
- scoreboard.pixel_y = dir_offset_y
+ scoreboard.pixel_w = dir_offset_x
+ scoreboard.pixel_z = dir_offset_y
. += scoreboard
var/ones = total_score % 10
var/mutable_appearance/ones_overlay = mutable_appearance('icons/obj/signs.dmi', "days_[ones]", layer + 0.01)
- ones_overlay.pixel_x = 4
+ ones_overlay.pixel_w = 4
var/mutable_appearance/emissive_ones_overlay = emissive_appearance('icons/obj/signs.dmi', "days_[ones]", src, alpha = src.alpha)
- emissive_ones_overlay.pixel_x = 4
+ emissive_ones_overlay.pixel_w = 4
scoreboard.add_overlay(ones_overlay)
scoreboard.add_overlay(emissive_ones_overlay)
var/tens = (total_score / 10) % 10
var/mutable_appearance/tens_overlay = mutable_appearance('icons/obj/signs.dmi', "days_[tens]", layer + 0.01)
- tens_overlay.pixel_x = -5
+ tens_overlay.pixel_w = -5
var/mutable_appearance/emissive_tens_overlay = emissive_appearance('icons/obj/signs.dmi', "days_[tens]", src, alpha = src.alpha)
- emissive_tens_overlay.pixel_x = -5
+ emissive_tens_overlay.pixel_w = -5
scoreboard.add_overlay(tens_overlay)
scoreboard.add_overlay(emissive_tens_overlay)
diff --git a/code/modules/bitrunning/areas.dm b/code/modules/bitrunning/areas.dm
index 720bf0f1e5d2c..4fcf0a0496e47 100644
--- a/code/modules/bitrunning/areas.dm
+++ b/code/modules/bitrunning/areas.dm
@@ -43,8 +43,14 @@
icon_state = "bit_ice"
area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA
-/area/ruin/space/has_grav/powered/virtual_domain
- name = "Virtual Domain Space Ruins"
+/area/ruin/space/virtual_domain
+ name = "Virtual Domain Unexplored Location"
+ icon = 'icons/area/areas_station.dmi'
+ icon_state = "bit_ruin"
+ area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA
+
+/area/space/virtual_domain
+ name = "Virtual Domain Space"
icon = 'icons/area/areas_station.dmi'
icon_state = "bit_space"
area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA
diff --git a/code/modules/bitrunning/components/avatar_connection.dm b/code/modules/bitrunning/components/avatar_connection.dm
index 51263c339319e..abf3a7809fcda 100644
--- a/code/modules/bitrunning/components/avatar_connection.dm
+++ b/code/modules/bitrunning/components/avatar_connection.dm
@@ -20,7 +20,7 @@
help_text,
)
- if(!isliving(parent) || !isliving(old_body) || !server.is_operational || !pod.is_operational)
+ if(!isliving(parent) || !isliving(old_body) || !old_mind || !server.is_operational || !pod.is_operational)
return COMPONENT_INCOMPATIBLE
var/mob/living/avatar = parent
@@ -66,6 +66,9 @@
if(alias && avatar.real_name != alias)
avatar.fully_replace_character_name(avatar.real_name, alias)
+ for(var/skill_type in old_mind.known_skills)
+ avatar.mind.set_experience(skill_type, old_mind.get_skill_exp(skill_type), silent = TRUE)
+
avatar.playsound_local(avatar, 'sound/magic/blink.ogg', 25, TRUE)
avatar.set_static_vision(2 SECONDS)
avatar.set_temp_blindness(1 SECONDS) // I'm in
@@ -281,6 +284,10 @@
if(isnull(old_mind) || isnull(old_body))
return
+ for(var/skill_type in avatar.mind.known_skills)
+ old_mind.set_experience(skill_type, avatar.mind.get_skill_exp(skill_type), silent = TRUE)
+ avatar.mind.set_experience(skill_type, 0, silent = TRUE)
+
ghost.mind = old_mind
if(old_body.stat != DEAD)
old_mind.transfer_to(old_body, force_key_move = TRUE)
diff --git a/code/modules/bitrunning/netpod/_netpod.dm b/code/modules/bitrunning/netpod/_netpod.dm
index 2208c56a7414e..438c21c7219ea 100644
--- a/code/modules/bitrunning/netpod/_netpod.dm
+++ b/code/modules/bitrunning/netpod/_netpod.dm
@@ -2,6 +2,7 @@
/obj/machinery/netpod
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "netpod"
base_icon_state = "netpod"
diff --git a/code/modules/bitrunning/netpod/ui.dm b/code/modules/bitrunning/netpod/ui.dm
index 93719fe64ebef..919ba1e174b53 100644
--- a/code/modules/bitrunning/netpod/ui.dm
+++ b/code/modules/bitrunning/netpod/ui.dm
@@ -27,7 +27,7 @@
return data
-/obj/machinery/netpod/ui_act(action, params)
+/obj/machinery/netpod/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return TRUE
diff --git a/code/modules/bitrunning/netpod/utils.dm b/code/modules/bitrunning/netpod/utils.dm
index 6e0033fe02d93..c593b457e9cc1 100644
--- a/code/modules/bitrunning/netpod/utils.dm
+++ b/code/modules/bitrunning/netpod/utils.dm
@@ -76,6 +76,11 @@
balloon_alert(neo, "nothing loaded!")
return
+ balloon_alert(neo, "establishing connection...")
+ if(!do_after(neo, 2 SECONDS, src))
+ open_machine()
+ return
+
var/mob/living/carbon/current_avatar = avatar_ref?.resolve()
if(isnull(current_avatar) || current_avatar.stat != CONSCIOUS) // We need a viable avatar
current_avatar = server.start_new_connection(neo, netsuit)
@@ -130,9 +135,6 @@
/// Checks for cases to eject/fail connecting an avatar
/obj/machinery/netpod/proc/validate_entry(mob/living/neo, mob/living/avatar)
- if(!do_after(neo, 2 SECONDS, src))
- return FALSE
-
// Very invalid
if(QDELETED(neo) || QDELETED(avatar) || QDELETED(src) || !is_operational)
return FALSE
diff --git a/code/modules/bitrunning/objects/byteforge.dm b/code/modules/bitrunning/objects/byteforge.dm
index b971cdae75ddc..bb1430322ee4b 100644
--- a/code/modules/bitrunning/objects/byteforge.dm
+++ b/code/modules/bitrunning/objects/byteforge.dm
@@ -1,4 +1,5 @@
/obj/machinery/byteforge
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "byteforge"
circuit = /obj/item/circuitboard/machine/byteforge
diff --git a/code/modules/bitrunning/objects/hololadder.dm b/code/modules/bitrunning/objects/hololadder.dm
index 3df41a403e735..feb1847a84c4e 100644
--- a/code/modules/bitrunning/objects/hololadder.dm
+++ b/code/modules/bitrunning/objects/hololadder.dm
@@ -3,7 +3,7 @@
anchored = TRUE
desc = "An abstract representation of the means to disconnect from the virtual domain."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/fluff/general.dmi'
icon_state = "ladder11"
obj_flags = BLOCK_Z_OUT_DOWN
/// Time req to disconnect properly
diff --git a/code/modules/bitrunning/objects/vendor.dm b/code/modules/bitrunning/objects/vendor.dm
index abd63a9e78430..d44630bc3beed 100644
--- a/code/modules/bitrunning/objects/vendor.dm
+++ b/code/modules/bitrunning/objects/vendor.dm
@@ -62,7 +62,7 @@
/obj/machinery/computer/order_console/bitrunning/retrieve_points(obj/item/card/id/id_card)
return round(id_card.registered_account.bitrunning_points)
-/obj/machinery/computer/order_console/bitrunning/ui_act(action, params)
+/obj/machinery/computer/order_console/bitrunning/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(!.)
flick("vendor_off", src)
diff --git a/code/modules/bitrunning/orders/tech.dm b/code/modules/bitrunning/orders/tech.dm
index 7e987e4818104..0b097127ffb71 100644
--- a/code/modules/bitrunning/orders/tech.dm
+++ b/code/modules/bitrunning/orders/tech.dm
@@ -32,7 +32,7 @@
desc = "This disk contains a program that lets you shapeshift into a lesser ashdrake, or a polar bear."
/datum/orderable_item/bitrunning_tech/flip_skillchip
- item_path = /obj/item/skillchip/matrix_flip
+ item_path = /obj/item/skillchip/matrix_taunt
cost_per_order = 2000
/datum/orderable_item/bitrunning_tech/pka_mod
diff --git a/code/modules/bitrunning/server/_parent.dm b/code/modules/bitrunning/server/_parent.dm
index 541d36ad5d72c..d6e3acfaf8e48 100644
--- a/code/modules/bitrunning/server/_parent.dm
+++ b/code/modules/bitrunning/server/_parent.dm
@@ -2,6 +2,7 @@
* The base object for the quantum server
*/
/obj/machinery/quantum_server
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "quantum server"
circuit = /obj/item/circuitboard/machine/quantum_server
diff --git a/code/modules/bitrunning/turfs.dm b/code/modules/bitrunning/turfs.dm
index 4c35153311e53..adf3c3b8c6abe 100644
--- a/code/modules/bitrunning/turfs.dm
+++ b/code/modules/bitrunning/turfs.dm
@@ -1,12 +1,11 @@
-/turf/open/floor/bitrunning_transport
+/turf/open/indestructible/bitrunning_transport
name = "circuit floor"
desc = "Looks complex. You can see the circuits running through the floor."
icon_state = "bitrunning"
/turf/closed/indestructible/binary
name = "tear in the fabric of reality"
- icon = 'icons/turf/floors.dmi'
- icon_state = "binary"
+ icon = 'icons/turf/walls/binary_wall.dmi'
/obj/effect/baseturf_helper/virtual_domain
name = "virtual domain baseturf editor"
diff --git a/code/modules/bitrunning/virtual_domain/domains/island_brawl.dm b/code/modules/bitrunning/virtual_domain/domains/island_brawl.dm
index b745a4746aa24..84eb53e026a29 100644
--- a/code/modules/bitrunning/virtual_domain/domains/island_brawl.dm
+++ b/code/modules/bitrunning/virtual_domain/domains/island_brawl.dm
@@ -21,9 +21,17 @@
for(var/obj/effect/mob_spawn/ghost_role/human/virtual_domain/islander/spawner in created_atoms)
custom_spawns += spawner
+ RegisterSignal(spawner, COMSIG_QDELETING, PROC_REF(on_spawner_qdeleted))
RegisterSignals(spawner, list(COMSIG_GHOSTROLE_SPAWNED, COMSIG_BITRUNNER_SPAWNED), PROC_REF(on_spawn))
+/datum/lazy_template/virtual_domain/island_brawl/proc/on_spawner_qdeleted(obj/effect/mob_spawn/ghost_role/human/virtual_domain/islander/source)
+ SIGNAL_HANDLER
+
+ custom_spawns -= source
+ UnregisterSignal(source, COMSIG_QDELETING)
+
+
/// Someone has spawned in, so we check for their death
/datum/lazy_template/virtual_domain/island_brawl/proc/on_spawn(datum/source, mob/living/spawned_mob)
SIGNAL_HANDLER
diff --git a/code/modules/buildmode/submodes/advanced.dm b/code/modules/buildmode/submodes/advanced.dm
index f4ebb204d6ee0..a26c8cd9a3d80 100644
--- a/code/modules/buildmode/submodes/advanced.dm
+++ b/code/modules/buildmode/submodes/advanced.dm
@@ -47,7 +47,7 @@
T.setDir(BM.build_dir)
else if(ispath(objholder, /obj/effect/turf_decal))
var/turf/T = get_turf(object)
- T.AddElement(/datum/element/decal, initial(objholder.icon), initial(objholder.icon_state), BM.build_dir, null, null, initial(objholder.alpha), initial(objholder.color), null, FALSE, null)
+ T.AddElement(/datum/element/decal, initial(objholder.icon), initial(objholder.icon_state), BM.build_dir, null, null, initial(objholder.alpha), initial(objholder.color), null, null, null, FALSE, null)
log_admin("Build Mode: [key_name(c)] in [AREACOORD(object)] added a [initial(objholder.name)] decal with dir [BM.build_dir] to [T]")
else if(!isnull(objholder))
var/obj/A = new objholder (get_turf(object))
diff --git a/code/modules/buildmode/submodes/fill.dm b/code/modules/buildmode/submodes/fill.dm
index e2bd3a5c16ad5..9b7f6964b5390 100644
--- a/code/modules/buildmode/submodes/fill.dm
+++ b/code/modules/buildmode/submodes/fill.dm
@@ -66,7 +66,7 @@
T = T.ChangeTurf(objholder)
T.setDir(BM.build_dir)
else if(ispath(objholder, /obj/effect/turf_decal))
- T.AddElement(/datum/element/decal, initial(objholder.icon), initial(objholder.icon_state), BM.build_dir, null, null, initial(objholder.alpha), initial(objholder.color), null, FALSE, null)
+ T.AddElement(/datum/element/decal, initial(objholder.icon), initial(objholder.icon_state), BM.build_dir, null, null, initial(objholder.alpha), initial(objholder.color), null, null, null, FALSE, null)
else
var/obj/A = new objholder(T)
A.setDir(BM.build_dir)
diff --git a/code/modules/capture_the_flag/ctf_classes.dm b/code/modules/capture_the_flag/ctf_classes.dm
index 0482bd1ec88eb..8f6a03ba7dfc4 100644
--- a/code/modules/capture_the_flag/ctf_classes.dm
+++ b/code/modules/capture_the_flag/ctf_classes.dm
@@ -55,7 +55,7 @@
var/obj/item/radio/headset = human_to_equip.ears
headset.set_frequency(team_radio_freq)
headset.freqlock = RADIO_FREQENCY_LOCKED
- headset.independent = TRUE
+ headset.special_channels |= RADIO_SPECIAL_CENTCOM
human_to_equip.dna.species.stunmod = 0
/datum/outfit/ctf/instagib
diff --git a/code/modules/capture_the_flag/ctf_game.dm b/code/modules/capture_the_flag/ctf_game.dm
index 38a7318b5548e..1279bbedc71b1 100644
--- a/code/modules/capture_the_flag/ctf_game.dm
+++ b/code/modules/capture_the_flag/ctf_game.dm
@@ -442,7 +442,7 @@
/obj/effect/ctf/dead_barricade
name = "dead barrier"
desc = "It provided cover in fire fights. And now it's gone."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/structures/tall.dmi'
icon_state = "barrier0"
var/game_id = CTF_GHOST_CTF_GAME_ID
var/datum/ctf_controller/ctf_game
diff --git a/code/modules/cargo/bounties/assistant.dm b/code/modules/cargo/bounties/assistant.dm
index d4ef4b6a148e0..23e578c2ed2db 100644
--- a/code/modules/cargo/bounties/assistant.dm
+++ b/code/modules/cargo/bounties/assistant.dm
@@ -213,7 +213,7 @@
/datum/bounty/item/assistant/fish
name = "Fish"
description = "We need fish to populate our aquariums with. Fishes that are dead or bought from cargo will only be paid half as much."
- reward = CARGO_CRATE_VALUE * 9
+ reward = CARGO_CRATE_VALUE * 9.5
required_count = 4
wanted_types = list(/obj/item/fish = TRUE, /obj/item/storage/fish_case = TRUE)
///the penalty for shipping dead/bought fish, which can subtract up to half the reward in total.
@@ -249,7 +249,7 @@
///A subtype of the fish bounty that requires fish with a specific fluid type
/datum/bounty/item/assistant/fish/fluid
- reward = CARGO_CRATE_VALUE * 11
+ reward = CARGO_CRATE_VALUE * 12
///The required fluid type of the fish for it to be shipped
var/fluid_type
@@ -261,42 +261,3 @@
/datum/bounty/item/assistant/fish/fluid/can_ship_fish(obj/item/fish/fishie)
return compatible_fluid_type(fishie.required_fluid_type, fluid_type)
-
-///A subtype of the fish bounty that requires specific fish types. The higher their rarity, the better the pay.
-/datum/bounty/item/assistant/fish/specific
- description = "Our prestigious fish collection is currently lacking a few specific species. Fishes that are dead or bought from cargo will only be paid half as much."
- reward = CARGO_CRATE_VALUE * 16
- required_count = 3
- wanted_types = list(/obj/item/storage/fish_case = TRUE)
-
-/datum/bounty/item/assistant/fish/specific/New()
- var/static/list/choosable_fishes
- if(isnull(choosable_fishes))
- choosable_fishes = list()
- for(var/obj/item/fish/prototype as anything in subtypesof(/obj/item/fish))
- if(initial(prototype.experisci_scannable) && initial(prototype.show_in_catalog))
- choosable_fishes += prototype
-
- var/list/fishes_copylist = choosable_fishes.Copy()
- ///Used to calculate the extra reward
- var/total_rarity = 0
- var/list/name_list = list()
- var/num_paths = rand(2,3)
- for(var/i in 1 to num_paths)
- var/obj/item/fish/chosen_path = pick_n_take(fishes_copylist)
- wanted_types[chosen_path] = TRUE
- name_list += initial(chosen_path.name)
- total_rarity += initial(chosen_path.random_case_rarity) / num_paths
- name = english_list(name_list)
-
- switch(total_rarity)
- if(FISH_RARITY_NOPE to FISH_RARITY_GOOD_LUCK_FINDING_THIS)
- reward += CARGO_CRATE_VALUE * 14
- if(FISH_RARITY_GOOD_LUCK_FINDING_THIS to FISH_RARITY_VERY_RARE)
- reward += CARGO_CRATE_VALUE * 6.5
- if(FISH_RARITY_VERY_RARE to FISH_RARITY_RARE)
- reward += CARGO_CRATE_VALUE * 3
- if(FISH_RARITY_RARE to FISH_RARITY_BASIC-1)
- reward += CARGO_CRATE_VALUE * 1
-
- ..()
diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm
index cf4a5dfc8759f..2e988fcaf4d71 100644
--- a/code/modules/cargo/centcom_podlauncher.dm
+++ b/code/modules/cargo/centcom_podlauncher.dm
@@ -67,10 +67,19 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
var/atom/movable/screen/background/cam_background
var/tabIndex = 1
var/renderLighting = FALSE
+ var/static/list/pod_style_info
+ var/static/list/pod_style_lookup
/datum/centcom_podlauncher/New(user) //user can either be a client or a mob
if (user) //Prevents runtimes on datums being made without clients
setup(user)
+ if (!isnull(pod_style_info))
+ return
+ pod_style_info = list()
+ pod_style_lookup = list()
+ for (var/datum/pod_style/style as anything in typesof(/datum/pod_style))
+ pod_style_info += list(list("id" = style::id, "title" = style::ui_name))
+ pod_style_lookup[style::id] = style
/datum/centcom_podlauncher/proc/setup(user) //H can either be a client or a mob
if (istype(user,/client))
@@ -134,6 +143,7 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
var/list/data = list()
data["mapRef"] = map_name
data["defaultSoundVolume"] = initial(temp_pod.soundVolume) //default volume for pods
+ data["podStyles"] = pod_style_info
return data
/datum/centcom_podlauncher/ui_data(mob/user) //Sends info about the pod to the UI.
@@ -152,7 +162,7 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
data["delays"] = temp_pod.delays
data["rev_delays"] = temp_pod.reverse_delays
data["custom_rev_delay"] = temp_pod.custom_rev_delay
- data["styleChoice"] = temp_pod.style //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the GLOB.podstyles list in cargo.dm defines to get the proper icon/name/desc for the pod.
+ data["styleChoice"] = temp_pod.style::id //Style is a variable that keeps track of what the pod is supposed to look like.
data["effectShrapnel"] = temp_pod.effectShrapnel //If true, creates a cloud of shrapnel of a decided type and magnitude on landing
data["shrapnelType"] = "[temp_pod.shrapnel_type]" //Path2String
data["shrapnelMagnitude"] = temp_pod.shrapnel_magnitude
@@ -181,7 +191,7 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
data["soundVolume"] = temp_pod.soundVolume //Admin sound to play when the pod leaves
return data
-/datum/centcom_podlauncher/ui_act(action, params)
+/datum/centcom_podlauncher/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -331,12 +341,12 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
if("effectName") //Give the supplypod a custom name. Supplypods automatically get their name based on their style (see supplypod/setStyle() proc), so doing this overrides that.
if (temp_pod.adminNamed) //If we're already adminNamed, set the name of the pod back to default
temp_pod.adminNamed = FALSE
- temp_pod.setStyle(temp_pod.style) //This resets the name of the pod based on it's current style (see supplypod/setStyle() proc)
+ temp_pod.setStyle(temp_pod.style) //This resets the name of the pod based on its current style (see supplypod/setStyle() proc)
return
- var/nameInput= tgui_input_text(usr, "Enter a custom name", "Custom name", GLOB.podstyles[temp_pod.style][POD_NAME], MAX_NAME_LEN) //Gather input for name and desc
+ var/nameInput= tgui_input_text(usr, "Enter a custom name", "Custom name", temp_pod.style::name, MAX_NAME_LEN) //Gather input for name and desc
if (isnull(nameInput))
return
- var/descInput = tgui_input_text(usr, "Enter a custom desc", "Custom description", GLOB.podstyles[temp_pod.style][POD_DESC]) //The GLOB.podstyles is used to get the name, desc, or icon state based on the pod's style
+ var/descInput = tgui_input_text(usr, "Enter a custom desc", "Custom description", temp_pod.style::desc)
if (isnull(descInput))
return
temp_pod.name = nameInput
@@ -504,7 +514,6 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
temp_pod.soundVolume = soundInput
. = TRUE
////////////////////////////STYLE CHANGES//////////////////
- //Style is a value that is used to keep track of what the pod is supposed to look like. It can be used with the GLOB.podstyles list (in cargo.dm defines)
//as a way to get the proper icon state, name, and description of the pod.
if("tabSwitch")
tabIndex = params["tabIndex"]
@@ -519,7 +528,7 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
. = TRUE
if("setStyle")
var/chosenStyle = params["style"]
- temp_pod.setStyle(chosenStyle+1)
+ temp_pod.setStyle(pod_style_lookup[chosenStyle])
. = TRUE
if("refresh") //Refresh the Pod bay. User should press this if they spawn something new in the centcom bay. Automatically called whenever the user launches a pod
refreshBay()
@@ -654,11 +663,6 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
preLaunch() //Fill acceptable turfs from orderedArea, then fill launchList from acceptableTurfs (see proc for more info)
refreshView()
-/area/centcom/central_command_areas/supplypod/pod_storage/Initialize(mapload) //temp_pod holding area
- . = ..()
- var/obj/imgbound = locate() in locate(200,SUPPLYPOD_X_OFFSET*-4.5, 1)
- call(GLOB.podlauncher, "RegisterSignal")(imgbound, "ct[GLOB.podstyles[14][9]]", "[GLOB.podstyles[14][10]]dlauncher")
-
/datum/centcom_podlauncher/proc/createOrderedArea(area/area_to_order) //This assumes the area passed in is a continuous square
if (isnull(area_to_order)) //If theres no supplypod bay mapped into centcom, throw an error
to_chat(holder.mob, "No /area/centcom/central_command_areas/supplypod/loading/one (or /two or /three or /four) in the world! You can make one yourself (then refresh) for now, but yell at a mapper to fix this, today!")
@@ -765,7 +769,7 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
else
var/atom/movable/movable_to_launch = thing_to_launch
movable_to_launch.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod
- new /obj/effect/pod_landingzone(target_turf, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to it's location
+ new /obj/effect/pod_landingzone(target_turf, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to its location
if (launchClone)
launchCounter++ //We only need to increment launchCounter if we are cloning objects.
//If we aren't cloning objects, taking and removing the first item each time from the acceptableTurfs list will inherently iterate through the list in order
@@ -820,7 +824,7 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
temp_pod.delays = dataToLoad["delays"]
temp_pod.reverse_delays = dataToLoad["rev_delays"]
temp_pod.custom_rev_delay = dataToLoad["custom_rev_delay"]
- temp_pod.setStyle(dataToLoad["styleChoice"]) //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the GLOB.podstyles list in cargo.dm defines to get the proper icon/name/desc for the pod.
+ temp_pod.setStyle(dataToLoad["styleChoice"]) //Style is a variable that keeps track of what the pod is supposed to look like.
temp_pod.effectShrapnel = dataToLoad["effectShrapnel"] //If true, creates a cloud of shrapnel of a decided type and magnitude on landing
temp_pod.shrapnel_type = text2path(dataToLoad["shrapnelType"])
temp_pod.shrapnel_magnitude = dataToLoad["shrapnelMagnitude"]
@@ -852,14 +856,6 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
refreshView()
GLOBAL_DATUM_INIT(podlauncher, /datum/centcom_podlauncher, new)
-//Proc for admins to enable others to use podlauncher after roundend
-/datum/centcom_podlauncher/proc/give_podlauncher(mob/living/user, override)
- if (SSticker.current_state < GAME_STATE_FINISHED)
- return
- if (!istype(user))
- user = override
- if (user)
- setup(user)//setup the datum
//Set the dropoff location and indicator to either a specific turf or somewhere in an area
/datum/centcom_podlauncher/proc/setDropoff(target)
@@ -879,7 +875,6 @@ GLOBAL_DATUM_INIT(podlauncher, /datum/centcom_podlauncher, new)
image_state = "selector"
image_layer = FLY_LAYER
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
alpha = 150
/obj/effect/client_image_holder/dropoff_location // Shows where revese pods lands
@@ -888,7 +883,6 @@ GLOBAL_DATUM_INIT(podlauncher, /datum/centcom_podlauncher, new)
image_state = "dropoff_indicator"
image_layer = FLY_LAYER
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
alpha = 0
#undef LAUNCH_ALL
diff --git a/code/modules/cargo/exports/fish.dm b/code/modules/cargo/exports/fish.dm
new file mode 100644
index 0000000000000..9c34fd3afcaeb
--- /dev/null
+++ b/code/modules/cargo/exports/fish.dm
@@ -0,0 +1,9 @@
+/datum/export/fish
+ cost = 30
+ unit_name = "fish"
+ export_types = list(/obj/item/fish)
+
+/datum/export/fish/get_cost(obj/item/fish/fish, apply_elastic)
+ var/elastic_cost = ..()
+ var/elastic_percent = elastic_cost / init_cost
+ return fish.get_export_price(elastic_cost, elastic_percent)
diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm
index 4942ea2c06a93..0a2dcfec4b0f0 100644
--- a/code/modules/cargo/expressconsole.dm
+++ b/code/modules/cargo/expressconsole.dm
@@ -128,6 +128,7 @@
if(.)
return
+ var/mob/user = ui.user
switch(action)
if("LZCargo")
usingBeacon = FALSE
@@ -143,7 +144,7 @@
if(D.adjust_money(-BEACON_COST))
cooldown = 10//a ~ten second cooldown for printing beacons to prevent spam
var/obj/item/supplypod_beacon/C = new /obj/item/supplypod_beacon(drop_location())
- C.link_console(src, usr)//rather than in beacon's Initialize(), we can assign the computer to the beacon by reusing this proc)
+ C.link_console(src, user)//rather than in beacon's Initialize(), we can assign the computer to the beacon by reusing this proc)
printed_beacons++//printed_beacons starts at 0, so the first one out will be called beacon # 1
beacon.name = "Supply Pod Beacon #[printed_beacons]"
@@ -159,13 +160,13 @@
CRASH("Unknown supply pack id given by express order console ui. ID: [params["id"]]")
var/name = "*None Provided*"
var/rank = "*None Provided*"
- var/ckey = usr.ckey
- if(ishuman(usr))
- var/mob/living/carbon/human/H = usr
+ var/ckey = user.ckey
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
name = H.get_authentification_name()
rank = H.get_assignment(hand_first = TRUE)
- else if(HAS_SILICON_ACCESS(usr))
- name = usr.real_name
+ else if(HAS_SILICON_ACCESS(user))
+ name = user.real_name
rank = "Silicon"
var/reason = ""
var/list/empty_turfs
diff --git a/code/modules/cargo/goodies.dm b/code/modules/cargo/goodies.dm
index e09c3e2bc958d..2ec505aa334fc 100644
--- a/code/modules/cargo/goodies.dm
+++ b/code/modules/cargo/goodies.dm
@@ -311,7 +311,7 @@
/datum/supply_pack/goody/climbing_hook
name = "Climbing Hook Single-Pack"
- desc = "A less cheap imported climbing hook. Absolutely no use outside of planetary stations."
+ desc = "A less cheap imported climbing hook. Absolutely no use outside of multi-floor stations."
cost = PAYCHECK_CREW * 5
contains = list(/obj/item/climbing_hook)
@@ -321,3 +321,9 @@
cost = PAYCHECK_COMMAND * 18
access_view = ACCESS_WEAPONS
contains = list(/obj/item/gun/ballistic/shotgun/doublebarrel)
+
+/datum/supply_pack/goody/experimental_medication
+ name = "Experimental Medication Single-Pack"
+ desc = "A single bottle of Interdyne brand experimental medication, used for treating people suffering from hereditary manifold disease."
+ cost = PAYCHECK_CREW * 6.5
+ contains = list(/obj/item/storage/pill_bottle/sansufentanyl)
diff --git a/code/modules/cargo/markets/_market.dm b/code/modules/cargo/markets/_market.dm
index 7bef341842f1a..4696d3007a7ae 100644
--- a/code/modules/cargo/markets/_market.dm
+++ b/code/modules/cargo/markets/_market.dm
@@ -6,14 +6,14 @@
var/list/shipping
// Automatic vars, do not touch these.
- /// Items available from this market, populated by SSblackmarket on initialization. Automatically assigned, so don't manually adjust.
+ /// Items available from this market, populated by SSmarket on initialization. Automatically assigned, so don't manually adjust.
var/list/available_items = list()
/// Item categories available from this market, only items which are in these categories can be gotten from this market. Automatically assigned, so don't manually adjust.
var/list/categories = list()
/// Are the items from this market legal or illegal? If illegal, apply a contrband trait to the bought object.
var/legal_status = TRUE
-/// Adds item to the available items and add it's category if it is not in categories yet.
+/// Adds item to the available items and add its category if it is not in categories yet.
/datum/market/proc/add_item(datum/market_item/item)
if(ispath(item, /datum/market_item))
item = new item()
@@ -73,6 +73,20 @@
return FALSE
+/**
+ * A proc that restocks only the EXISTING items of this market.
+ * If you want to selectively restock markets, call SSmarket.restock(market_or_list_of_markets) instead.
+ */
+/datum/market/proc/restock(list/existing_items)
+ for(var/category in available_items)
+ var/category_list = available_items[category]
+ for(var/identifier in category_list)
+ var/datum/market_item/item = category_list[identifier]
+ existing_items |= item.type
+ if(!item.restockable || item.stock >= item.stock_max || !prob(item.availability_prob))
+ continue
+ item.stock += rand(1, item.stock_max - item.stock)
+
/datum/market/blackmarket
name = "Black Market"
shipping = list(
diff --git a/code/modules/cargo/markets/market_item.dm b/code/modules/cargo/markets/market_item.dm
index 5e3ce4efb6c07..faa6c45d795c4 100644
--- a/code/modules/cargo/markets/market_item.dm
+++ b/code/modules/cargo/markets/market_item.dm
@@ -5,7 +5,7 @@
var/desc
/// The category this item belongs to, should be already declared in the market that this item is accessible in.
var/category
- /// "/datum/market"s that this item should be in, used by SSblackmarket on init.
+ /// "/datum/market"s that this item should be in, used by SSmarket on init.
var/list/markets = list(/datum/market/blackmarket)
/// Price for the item, if not set creates a price according to the *_min and *_max vars.
@@ -27,15 +27,21 @@
var/stock_min = 1
/// Maximum amount that there should be of this item in the market if generated randomly.
var/stock_max = 0
- /// Probability for this item to be available. Used by SSblackmarket on init.
+ /// Probability for this item to be available. Used by SSmarket on init.
var/availability_prob
+ /// If set, this icon will be shown in the UI.
+ var/html_icon
+
///The identifier for the market item, generated on runtime and used to access them in the market categories.
var/identifier
///If set, these will override the shipment methods set by the market
var/list/shipping_override
+ /// Can this item be restocked
+ var/restockable = TRUE
+
/datum/market_item/New()
if(isnull(price))
price = rand(price_min, price_max)
@@ -48,9 +54,11 @@
//we're replacing the item to sell, and the old item is an instance!
if(ismovable(item))
UnregisterSignal(item, COMSIG_QDELETING)
+ html_icon = null
item = path_or_ref
identifier = "[path_or_ref]"
if(ismovable(path_or_ref))
+ html_icon = icon2base64(getFlatIcon(item, no_anim=TRUE))
RegisterSignal(item, COMSIG_QDELETING, PROC_REF(on_item_del))
identifier = "[REF(src)]"
@@ -82,7 +90,7 @@
CRASH("Invalid item type for market item [item || "null"]")
/**
- * Buys the item and makes SSblackmarket handle it.
+ * Buys the item and makes SSmarket handle it.
*
* @param uplink The uplink that is buying the item.
* @param buyer The mob that is buying the item.
@@ -102,8 +110,8 @@
// Alright, the item has been purchased.
var/datum/market_purchase/purchase = new(src, uplink, shipping_method, legal_status)
- // SSblackmarket takes care of the shipping.
- if(SSblackmarket.queue_item(purchase))
+ // SSmarket takes care of the shipping.
+ if(SSmarket.queue_item(purchase))
stock--
buyer.log_message("has succesfully purchased [name] using [shipping_method] for shipping.", LOG_ECON)
return TRUE
@@ -139,7 +147,7 @@
/datum/market_purchase/Destroy()
entry = null
uplink = null
- SSblackmarket.queued_purchases -= src
+ SSmarket.queued_purchases -= src
return ..()
/datum/market_purchase/proc/on_instance_del(datum/source)
diff --git a/code/modules/cargo/markets/market_items/clothing.dm b/code/modules/cargo/markets/market_items/clothing.dm
index 82bda848eb8e9..ee0ae6e0a3997 100644
--- a/code/modules/cargo/markets/market_items/clothing.dm
+++ b/code/modules/cargo/markets/market_items/clothing.dm
@@ -4,7 +4,7 @@
/datum/market_item/clothing/ninja_mask
name = "Space Ninja Mask"
- desc = "Apart from being acid, lava, fireproof and being hard to take off someone it does nothing special on it's own."
+ desc = "Apart from being acid, lava, fireproof and being hard to take off someone it does nothing special on its own."
item = /obj/item/clothing/mask/gas/ninja
price_min = CARGO_CRATE_VALUE
@@ -32,13 +32,23 @@
stock_max = 4
availability_prob = 50
+/datum/market_item/tool/medsechud
+ name = "MedSec HUD"
+ desc = "A mostly defunct combination of security and health scanner HUDs. They don't produce these around anymore."
+ item = /obj/item/clothing/glasses/hud/medsechud
+
+ price_min = CARGO_CRATE_VALUE * 2
+ price_max = CARGO_CRATE_VALUE * 3.5
+ stock_max = 3
+ availability_prob = 50
+
/datum/market_item/clothing/full_spacesuit_set
name = "\improper Nanotrasen Branded Spacesuit Box"
desc = "A few boxes of \"Old Style\" space suits fell off the back of a space truck."
item = /obj/item/storage/box
- price_min = CARGO_CRATE_VALUE * 7.5
- price_max = CARGO_CRATE_VALUE * 20
+ price_min = CARGO_CRATE_VALUE * 1.875
+ price_max = CARGO_CRATE_VALUE * 4
stock_max = 3
availability_prob = 30
@@ -66,7 +76,7 @@
item = /obj/item/clothing/shoes/bhop/rocket
price_min = CARGO_CRATE_VALUE * 5
- price_max = CARGO_CRATE_VALUE * 15
+ price_max = CARGO_CRATE_VALUE * 10
stock_max = 1
availability_prob = 40
diff --git a/code/modules/cargo/markets/market_items/consumables.dm b/code/modules/cargo/markets/market_items/consumables.dm
index f002ff994249d..b7eed89a1951f 100644
--- a/code/modules/cargo/markets/market_items/consumables.dm
+++ b/code/modules/cargo/markets/market_items/consumables.dm
@@ -19,10 +19,20 @@
stock_min = 2
stock_max = 5
- price_min = CARGO_CRATE_VALUE * 1.625
- price_max = CARGO_CRATE_VALUE * 2
+ price_min = CARGO_CRATE_VALUE * 1.375
+ price_max = CARGO_CRATE_VALUE * 1.825
availability_prob = 80
+/datum/market_item/consumable/donk_pocket_box/spawn_item(loc)
+ var/static/list/choices
+ if(isnull(choices))
+ choices = list()
+ for(var/boxtype as anything in typesof(/obj/item/storage/box/donkpockets))
+ choices[boxtype] = 3
+ choices[/obj/item/storage/box/donkpockets/donkpocketgondola] = 1
+ item = pick_weight(choices)
+ return ..()
+
/datum/market_item/consumable/suspicious_pills
name = "Bottle of Suspicious Pills"
desc = "A random cocktail of luxury drugs that are sure to put a smile on your face!"
@@ -30,17 +40,18 @@
stock_min = 2
stock_max = 3
- price_min = CARGO_CRATE_VALUE * 2
- price_max = CARGO_CRATE_VALUE * 3.5
+ price_min = CARGO_CRATE_VALUE * 0.625
+ price_max = CARGO_CRATE_VALUE * 1.25
availability_prob = 50
/datum/market_item/consumable/suspicious_pills/spawn_item(loc)
- var/pillbottle = pick(list(/obj/item/storage/pill_bottle/zoom,
- /obj/item/storage/pill_bottle/happy,
- /obj/item/storage/pill_bottle/lsd,
- /obj/item/storage/pill_bottle/aranesp,
- /obj/item/storage/pill_bottle/stimulant))
- item = pillbottle
+ item = pick(list(/obj/item/storage/pill_bottle/zoom,
+ /obj/item/storage/pill_bottle/happy,
+ /obj/item/storage/pill_bottle/lsd,
+ /obj/item/storage/pill_bottle/aranesp,
+ /obj/item/storage/pill_bottle/stimulant,
+ /obj/item/storage/pill_bottle/maintenance_pill,
+ ))
return ..()
/datum/market_item/consumable/floor_pill
diff --git a/code/modules/cargo/markets/market_items/hostages.dm b/code/modules/cargo/markets/market_items/hostages.dm
index ed5b1f10a7fcf..702cea907bdeb 100644
--- a/code/modules/cargo/markets/market_items/hostages.dm
+++ b/code/modules/cargo/markets/market_items/hostages.dm
@@ -5,6 +5,7 @@
stock = 1
availability_prob = 100
shipping_override = list(SHIPPING_METHOD_LTSRBT = 0, SHIPPING_METHOD_SUPPLYPOD = 350)
+ restockable = FALSE
/// temporary reference to the 4 in 7 chances of signaler and electropack.
var/obj/item/assembly/signaler/signaler
diff --git a/code/modules/cargo/markets/market_items/local_goods.dm b/code/modules/cargo/markets/market_items/local_goods.dm
new file mode 100644
index 0000000000000..d81c38fec98ba
--- /dev/null
+++ b/code/modules/cargo/markets/market_items/local_goods.dm
@@ -0,0 +1,25 @@
+///A special category for goods placed on the market by station by someone with the LTSRBT.
+/datum/market_item/local_good
+ category = "Local Goods"
+ abstract_path = /datum/market_item/local_good
+ stock = 1
+ availability_prob = 100
+ restockable = FALSE
+ var/datum/bank_account/seller
+
+/datum/market_item/local_good/New(atom/movable/thing, datum/bank_account/seller)
+ ..()
+ set_item(thing)
+ src.seller = seller
+ if(seller)
+ RegisterSignal(seller, COMSIG_QDELETING, PROC_REF(delete_reference))
+
+/datum/market_item/local_good/buy(obj/item/market_uplink/uplink, mob/buyer, shipping_method, legal_status)
+ . = ..()
+ if(. && seller)
+ seller.adjust_money(round(price * (1 - MARKET_WITHHOLDING_TAX)), "Market: Item Sold")
+ QDEL_IN(src, 10 MINUTES) //This category cannot hold more than 40 items at a time, so we need to clear sold items.
+
+/datum/market_item/local_good/proc/delete_reference(datum/source)
+ SIGNAL_HANDLER
+ seller = null
diff --git a/code/modules/cargo/markets/market_items/misc.dm b/code/modules/cargo/markets/market_items/misc.dm
index 435396c15f251..b0ea89485801c 100644
--- a/code/modules/cargo/markets/market_items/misc.dm
+++ b/code/modules/cargo/markets/market_items/misc.dm
@@ -2,7 +2,7 @@
category = "Miscellaneous"
abstract_path = /datum/market_item/misc
-/datum/market_item/misc/Clear_PDA
+/datum/market_item/misc/clear_pda
name = "Clear PDA"
desc = "Show off your style with this limited edition clear PDA!."
item = /obj/item/modular_computer/pda/clear
@@ -12,7 +12,7 @@
stock_max = 2
availability_prob = 50
-/datum/market_item/misc/jade_Lantern
+/datum/market_item/misc/jade_lantern
name = "Jade Lantern"
desc = "Found in a box labeled 'Danger: Radioactive'. Probably safe."
item = /obj/item/flashlight/lantern/jade
@@ -90,7 +90,7 @@
/datum/market_item/misc/smugglers_satchel
name = "Smuggler's Satchel"
- desc = "This easily hidden satchel can become a versatile tool to anybody with the desire to keep certain items out of sight and out of mind."
+ desc = "This easily hidden satchel can become a versatile tool to anybody with the desire to keep certain items out of sight and out of mind. Its contents cannot be detected by contraband scanners."
item = /obj/item/storage/backpack/satchel/flat/empty
price_min = CARGO_CRATE_VALUE * 3.75
diff --git a/code/modules/cargo/markets/market_items/stolen_goods.dm b/code/modules/cargo/markets/market_items/stolen_goods.dm
index 02a72f05d26d1..cb1932673e0e3 100644
--- a/code/modules/cargo/markets/market_items/stolen_goods.dm
+++ b/code/modules/cargo/markets/market_items/stolen_goods.dm
@@ -4,6 +4,7 @@
abstract_path = /datum/market_item/stolen_good
stock = 1
availability_prob = 100
+ restockable = FALSE
/datum/market_item/stolen_good/New(atom/movable/thing, thing_price)
..()
diff --git a/code/modules/cargo/markets/market_items/tools.dm b/code/modules/cargo/markets/market_items/tools.dm
index 9576810b3a3c9..0c9969756d30f 100644
--- a/code/modules/cargo/markets/market_items/tools.dm
+++ b/code/modules/cargo/markets/market_items/tools.dm
@@ -11,7 +11,7 @@
stock_min = 2
stock_max = 4
price_min = CARGO_CRATE_VALUE * 2.5
- price_max = CARGO_CRATE_VALUE * 3.75
+ price_max = CARGO_CRATE_VALUE * 3.25
availability_prob = 100
/datum/market_item/tool/caravan_wrench
@@ -60,8 +60,8 @@
item = /obj/item/binoculars
stock = 1
- price_min = CARGO_CRATE_VALUE * 2
- price_max = CARGO_CRATE_VALUE * 4.8
+ price_min = CARGO_CRATE_VALUE * 1.75
+ price_max = CARGO_CRATE_VALUE * 4
availability_prob = 30
/datum/market_item/tool/riot_shield
@@ -76,23 +76,13 @@
/datum/market_item/tool/thermite_bottle
name = "Thermite Bottle"
- desc = "30u of Thermite to assist in creating a quick access point or get away!"
+ desc = "50u of Thermite to assist in creating a quick access point or get away!"
item = /obj/item/reagent_containers/cup/bottle/thermite
- price_min = CARGO_CRATE_VALUE * 2.5
- price_max = CARGO_CRATE_VALUE * 7.5
- stock_max = 3
- availability_prob = 30
-
-/datum/market_item/tool/science_goggles
- name = "Science Goggles"
- desc = "These glasses scan the contents of containers and projects their contents to the user in an easy to read format."
- item = /obj/item/clothing/glasses/science
-
price_min = CARGO_CRATE_VALUE * 0.75
price_max = CARGO_CRATE_VALUE
stock_max = 3
- availability_prob = 50
+ availability_prob = 30
/**
* # Fake N-spect scanner black market entry
diff --git a/code/modules/cargo/markets/market_items/weapons.dm b/code/modules/cargo/markets/market_items/weapons.dm
index 3323e16916234..4f20cf865bc9b 100644
--- a/code/modules/cargo/markets/market_items/weapons.dm
+++ b/code/modules/cargo/markets/market_items/weapons.dm
@@ -13,16 +13,15 @@
availability_prob = 40
/datum/market_item/weapon/shotgun_dart
- name = "Shotgun Dart"
+ name = "Box of XL Shotgun Darts"
desc = "These handy darts can be filled up with any chemical and be shot with a shotgun! \
Prank your friends by shooting them with laughter! \
Not recommended for comercial use."
- item = /obj/item/ammo_casing/shotgun/dart
+ item = /obj/item/storage/box/large_dart
- price_min = CARGO_CRATE_VALUE * 0.05
- price_max = CARGO_CRATE_VALUE * 0.25
- stock_min = 10
- stock_max = 60
+ price_min = CARGO_CRATE_VALUE * 1.375
+ price_max = CARGO_CRATE_VALUE * 2.875
+ stock_max = 4
availability_prob = 40
/datum/market_item/weapon/bone_spear
diff --git a/code/modules/cargo/markets/market_telepad.dm b/code/modules/cargo/markets/market_telepad.dm
index 7c5b509a9421d..53a3d73ee486a 100644
--- a/code/modules/cargo/markets/market_telepad.dm
+++ b/code/modules/cargo/markets/market_telepad.dm
@@ -1,3 +1,6 @@
+#define DEFAULT_RESTOCK_COST CARGO_CRATE_VALUE * 3.375
+#define PLACE_ON_MARKET_COST PAYCHECK_LOWER * 1.2
+
/obj/item/circuitboard/machine/ltsrbt
name = "LTSRBT (Machine Board)"
icon_state = "bluespacearray"
@@ -12,12 +15,14 @@
/obj/machinery/ltsrbt
name = "Long-To-Short-Range-Bluespace-Transceiver"
desc = "The LTSRBT is a compact teleportation machine for receiving and sending items outside the station and inside the station.\nUsing teleportation frequencies stolen from NT it is near undetectable.\nEssential for any illegal market operations on NT stations.\n"
- icon = 'icons/obj/machines/telecomms.dmi'
- icon_state = "exonet_node"
+ icon = 'icons/obj/machines/ltsrbt.dmi'
+ icon_state = "ltsrbt_idle"
+ base_icon_state = "ltsrbt"
circuit = /obj/item/circuitboard/machine/ltsrbt
density = TRUE
idle_power_usage = BASE_MACHINE_IDLE_CONSUMPTION * 2
+ interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND
/// Divider for energy_usage_per_teleport.
var/power_efficiency = 1
@@ -35,18 +40,283 @@
var/datum/market_purchase/transmitting
/// Queue for purchases that the machine should receive and send.
var/list/datum/market_purchase/queue = list()
+ /// The name of the market item that we've set on the UI
+ var/current_name = ""
+ /// The desc of the market item that we've set on the UI
+ var/current_desc = ""
+ /// The price of the market item that we've set on the UI
+ var/current_price = CARGO_CRATE_VALUE
+ /**
+ * Attacking the machinery with enough credits will restock the markets, allowing for more/better items.
+ * The cost doubles each time this is done.
+ */
+ var/static/restock_cost = DEFAULT_RESTOCK_COST
/obj/machinery/ltsrbt/Initialize(mapload)
. = ..()
- SSblackmarket.telepads += src
+ register_context()
+ SSmarket.telepads += src
+ ADD_TRAIT(src, TRAIT_SECLUDED_LOCATION, INNATE_TRAIT) //you cannot sell disky, boss.
+ update_appearance()
/obj/machinery/ltsrbt/Destroy()
- SSblackmarket.telepads -= src
+ SSmarket.telepads -= src
// Bye bye orders.
- if(length(SSblackmarket.telepads))
- for(var/datum/market_purchase/P in queue)
- SSblackmarket.queue_item(P)
+ if(length(SSmarket.telepads))
+ for(var/datum/market_purchase/purchase in queue)
+ SSmarket.queue_item(purchase)
+ if(receiving)
+ SSmarket.queue_item(receiving)
+ queue = null
+ receiving = null
+ transmitting = null
+ return ..()
+
+/obj/machinery/ltsrbt/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ if(held_item)
+ if(state_open)
+ context[SCREENTIP_CONTEXT_LMB] = "Insert"
+ return CONTEXTUAL_SCREENTIP_SET
+ if(held_item.get_item_credit_value() && !(machine_stat & NOPOWER))
+ context[SCREENTIP_CONTEXT_LMB] = "Restock"
+ return CONTEXTUAL_SCREENTIP_SET
+ return NONE
+ if(state_open)
+ context[SCREENTIP_CONTEXT_LMB] = "Close"
+ return CONTEXTUAL_SCREENTIP_SET
+ context[SCREENTIP_CONTEXT_LMB] = "Open"
+ if(occupant && !(machine_stat & NOPOWER))
+ context[SCREENTIP_CONTEXT_RMB] = "Place on market"
+ return CONTEXTUAL_SCREENTIP_SET
+
+/obj/machinery/ltsrbt/examine(mob/user)
+ . = ..()
+ if(!(machine_stat & NOPOWER))
+ . += span_info("A small display reads:")
+ . += span_tinynoticeital("Current market restock price: [EXAMINE_HINT("[restock_cost] cr")].")
+ . += span_tinynoticeital("Market placement fee: [EXAMINE_HINT("[PLACE_ON_MARKET_COST] cr")].")
+ . += span_tinynoticeital("Withholding tax on local items: [EXAMINE_HINT("[MARKET_WITHHOLDING_TAX * 100]%")].")
+
+/obj/machinery/ltsrbt/update_icon_state()
+ . = ..()
+ if(machine_stat & NOPOWER)
+ icon_state = "[base_icon_state]_off"
+ else
+ icon_state = "[base_icon_state][(receiving || length(queue) || occupant) ? "" : "_idle"]"
+
+/obj/machinery/ltsrbt/update_overlays()
+ . = ..()
+ if(!state_open)
+ . += "[base_icon_state]_closed"
+ else
+ var/mutable_appearance/overlay = mutable_appearance(icon, "[base_icon_state]_open")
+ overlay.pixel_w -= 2
+ overlay.pixel_z -= 1
+ . += overlay
+
+/obj/machinery/ltsrbt/attack_hand(mob/user, list/modifiers)
+ . = ..()
+ if(.)
+ return
+ if(!state_open)
+ open_machine(density_to_set = TRUE)
+ else
+ close_machine()
+
+/obj/machinery/ltsrbt/open_machine(drop = TRUE, density_to_set = FALSE)
+ . = ..()
+ playsound(src, 'sound/machines/oven/oven_open.ogg', 75, TRUE)
+
+/obj/machinery/ltsrbt/close_machine(atom/movable/target, density_to_set = TRUE)
. = ..()
+ playsound(src, 'sound/machines/oven/oven_close.ogg', 75, TRUE)
+
+/obj/machinery/ltsrbt/set_occupant(obj/item/new_occupant)
+ . = ..()
+ if(new_occupant)
+ current_name = new_occupant.name
+ current_desc = new_occupant.desc
+
+/obj/machinery/ltsrbt/can_be_occupant(atom/movable/atom)
+ return isitem(atom) && !atom.anchored
+
+/obj/machinery/ltsrbt/Exited(atom/movable/gone)
+ if(gone == occupant)
+ current_price = initial(current_price)
+ current_name = ""
+ current_desc = ""
+ update_appearance(UPDATE_ICON_STATE)
+ return ..()
+
+/obj/machinery/ltsrbt/attack_hand_secondary(mob/user, list/modifiers)
+ . = ..()
+ if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
+ return
+ if(state_open)
+ balloon_alert(user, "close it first!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(!occupant)
+ balloon_alert(user, "nothing loaded!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(machine_stat & NOPOWER)
+ balloon_alert(user, "machine unpowered!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ if(!COOLDOWN_FINISHED(src, recharge_cooldown))
+ balloon_alert(user, "on cooldown!")
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+ ui_interact(user)
+ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
+
+/obj/machinery/ltsrbt/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(user.combat_mode)
+ return NONE
+
+ var/creds_value = tool.get_item_credit_value()
+
+ if(state_open)
+ if(locate(/mob/living) in tool.get_all_contents())
+ say("Living being detected, cannot sell!")
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ return ITEM_INTERACT_BLOCKING
+ if(!user.transferItemToLoc(tool, src))
+ balloon_alert(user, "stuck to your hands!")
+ return ITEM_INTERACT_BLOCKING
+ balloon_alert(user, "item loaded")
+ close_machine(tool)
+ return ITEM_INTERACT_SUCCESS
+ else if(!creds_value)
+ balloon_alert(user, "open the machine!")
+ return ITEM_INTERACT_BLOCKING
+
+ if(machine_stat & NOPOWER)
+ return
+
+ if(creds_value < restock_cost)
+ say("Insufficient credits!")
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ return ITEM_INTERACT_BLOCKING
+
+ if(istype(tool, /obj/item/holochip))
+ var/obj/item/holochip/chip = tool
+ chip.spend(restock_cost)
+ else
+ qdel(tool)
+ if(creds_value != restock_cost)
+ var/obj/item/holochip/change = new(loc, creds_value - restock_cost)
+ user.put_in_hands(change)
+
+ SSmarket.restock()
+ restock_cost *= 2
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/ltsrbt/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "LTSRBT", name)
+ ui.open()
+
+/obj/machinery/ltsrbt/ui_state()
+ if(!occupant || !COOLDOWN_FINISHED(src, recharge_cooldown))
+ return GLOB.never_state //close it.
+ else
+ return GLOB.default_state
+
+#define LTSRBT_MIN_PRICE PAYCHECK_LOWER
+#define LTSRBT_MAX_PRICE CARGO_CRATE_VALUE * 50
+
+/obj/machinery/ltsrbt/ui_static_data(mob/user)
+ var/list/data = list()
+ data["loaded_icon"] = icon2base64(getFlatIcon(occupant, no_anim=TRUE))
+ data["min_price"] = LTSRBT_MIN_PRICE
+ data["max_price"] = LTSRBT_MAX_PRICE
+ return data
+
+/obj/machinery/ltsrbt/ui_data(mob/user)
+ var/list/data = list()
+ data["name"] = current_name
+ data["price"] = current_price
+ data["desc"] = current_desc
+ return data
+
+/obj/machinery/ltsrbt/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("change_name")
+ var/value = params["value"]
+ if(!CAN_BYPASS_FILTER(usr) && is_ic_filtered_for_pdas(value))
+ return TRUE
+ current_name = trim(value, MAX_NAME_LEN)
+ return TRUE
+ if("change_desc")
+ var/value = params["value"]
+ if(!CAN_BYPASS_FILTER(usr) && is_ic_filtered_for_pdas(value))
+ return TRUE
+ current_desc = trim(value, MAX_DESC_LEN)
+ return TRUE
+ if("change_price")
+ current_price = clamp(params["value"], LTSRBT_MIN_PRICE, LTSRBT_MAX_PRICE)
+ return TRUE
+ if("place_on_market")
+ place_on_market(usr)
+ return TRUE
+
+#undef LTSRBT_MIN_PRICE
+#undef LTSRBT_MAX_PRICE
+
+#define LTSRBT_MAX_MARKET_ITEMS 40
+/obj/machinery/ltsrbt/proc/place_on_market(mob/user)
+ if(QDELETED(occupant))
+ return
+ if(locate(/mob/living) in occupant.get_all_contents())
+ say("Living being detected, cannot sell!")
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ return
+ var/datum/bank_account/account
+ var/datum/market/our_market = SSmarket.markets[/datum/market/blackmarket]
+ if(!isAdminGhostAI(user))
+ if(!isliving(user))
+ return
+ if(length(our_market.available_items[/datum/market_item/local_good::category]) >= LTSRBT_MAX_MARKET_ITEMS)
+ say("Local market saturated, buy some goods first!")
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ return
+ var/mob/living/living_user = user
+ var/obj/item/card/id/card = living_user.get_idcard(TRUE)
+ if(!(card?.registered_account))
+ say("No bank account to charge market fees detected!")
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ return
+ if(!card.registered_account.adjust_money(-PLACE_ON_MARKET_COST, "Market: Placement Fee"))
+ say("Insufficient credits!")
+ playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ return
+ account = card.registered_account
+
+ var/obj/item/item = occupant //occupant, name, price and desc will be null'd once it exits the machine so we need this.
+ var/name_to_use = current_name || item.name
+ var/desc_to_use = current_desc
+ if(account)
+ desc_to_use += "[current_desc ? " - " : ""]Seller: [account.account_holder]"
+ var/price_to_use = current_price
+ item.moveToNullspace()
+ //Something happened and the item was deleted or relocated as soon as it was moved to nullspace.
+ if(QDELETED(item) || item.loc != null)
+ say("Runtime at market_placement.dm, line 153: item gone!") //metajoke
+ return
+ var/datum/market_item/local_good/new_item = new(item, account)
+ new_item.name = name_to_use
+ new_item.desc = desc_to_use
+ new_item.price = price_to_use
+
+ our_market.add_item(new_item)
+
+ say("Item placed on the market!")
+ playsound(src, 'sound/effects/cashregister.ogg', 40, FALSE)
+ COOLDOWN_START(src, recharge_cooldown, recharge_time * 3)
+
+#undef LTSRBT_MAX_MARKET_ITEMS
/obj/machinery/ltsrbt/RefreshParts()
. = ..()
@@ -54,7 +324,6 @@
// On tier 4 recharge_time should be 20 and by default it is 80 as scanning modules should be tier 1.
for(var/datum/stock_part/scanning_module/scanning_module in component_parts)
recharge_time -= scanning_module.tier * 1 SECONDS
- recharge_cooldown = recharge_time
power_efficiency = 0
for(var/datum/stock_part/micro_laser/laser in component_parts)
@@ -67,6 +336,7 @@
/obj/machinery/ltsrbt/proc/add_to_queue(datum/market_purchase/purchase)
if(!recharge_cooldown && !receiving && !transmitting)
receiving = purchase
+ update_appearance(UPDATE_ICON_STATE)
else
queue += purchase
@@ -80,6 +350,8 @@
if(transmitting == purchase)
transmitting = null
+ update_appearance(UPDATE_ICON_STATE)
+
/obj/machinery/ltsrbt/process(seconds_per_tick)
if(machine_stat & NOPOWER)
return
@@ -102,14 +374,17 @@
transmitting = receiving
receiving = null
- COOLDOWN_START(src, recharge_cooldown, recharge_time)
return
if(transmitting)
if(transmitting.item.loc == turf)
do_teleport(transmitting.item, get_turf(transmitting.uplink))
use_energy(energy_usage_per_teleport / power_efficiency)
QDEL_NULL(transmitting)
+ COOLDOWN_START(src, recharge_cooldown, recharge_time)
return
if(length(queue))
receiving = pick_n_take(queue)
+
+#undef DEFAULT_RESTOCK_COST
+#undef PLACE_ON_MARKET_COST
diff --git a/code/modules/cargo/markets/market_uplink.dm b/code/modules/cargo/markets/market_uplink.dm
index d13f59937b66c..17cad32042668 100644
--- a/code/modules/cargo/markets/market_uplink.dm
+++ b/code/modules/cargo/markets/market_uplink.dm
@@ -20,7 +20,7 @@
/obj/item/market_uplink/Initialize(mapload)
. = ..()
- // We don't want to go through this at mapload because the SSblackmarket isn't initialized yet.
+ // We don't want to go through this at mapload because the SSmarket isn't initialized yet.
if(mapload)
return
@@ -30,7 +30,7 @@
/obj/item/market_uplink/proc/update_viewing_category()
if(accessible_markets.len)
viewing_market = accessible_markets[1]
- var/list/categories = SSblackmarket.markets[viewing_market].categories
+ var/list/categories = SSmarket.markets[viewing_market].categories
if(categories?.len)
viewing_category = categories[1]
@@ -45,7 +45,7 @@
/obj/item/market_uplink/ui_data(mob/user)
var/list/data = list()
- var/datum/market/market = viewing_market ? SSblackmarket.markets[viewing_market] : null
+ var/datum/market/market = viewing_market ? SSmarket.markets[viewing_market] : null
var/obj/item/card/id/id_card
if(isliving(user))
var/mob/living/livin = user
@@ -80,24 +80,25 @@
"name" = item.name,
"cost" = item.price,
"amount" = item.stock,
- "desc" = item.desc || item.name
+ "desc" = item.desc || item.name,
+ "html_icon" = item.html_icon,
))
return data
/obj/item/market_uplink/ui_static_data(mob/user)
var/list/data = list()
- data["delivery_method_description"] = SSblackmarket.shipping_method_descriptions
- data["ltsrbt_built"] = SSblackmarket.telepads.len
+ data["delivery_method_description"] = SSmarket.shipping_method_descriptions
+ data["ltsrbt_built"] = SSmarket.telepads.len
data["markets"] = list()
for(var/M in accessible_markets)
- var/datum/market/BM = SSblackmarket.markets[M]
+ var/datum/market/BM = SSmarket.markets[M]
data["markets"] += list(list(
"id" = M,
"name" = BM.name
))
return data
-/obj/item/market_uplink/ui_act(action, params)
+/obj/item/market_uplink/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -107,7 +108,7 @@
return
if(isnull(viewing_market))
return
- if(!(params["category"] in SSblackmarket.markets[viewing_market].categories))
+ if(!(params["category"] in SSmarket.markets[viewing_market].categories))
return
viewing_category = params["category"]
. = TRUE
@@ -120,7 +121,7 @@
viewing_market = market
- var/list/categories = SSblackmarket.markets[viewing_market].categories
+ var/list/categories = SSmarket.markets[viewing_market].categories
if(categories?.len)
viewing_category = categories[1]
else
@@ -142,7 +143,7 @@
if(isnull(selected_item))
buying = FALSE
return
- var/datum/market/market = SSblackmarket.markets[viewing_market]
+ var/datum/market/market = SSmarket.markets[viewing_market]
market.purchase(selected_item, viewing_category, params["method"], src, usr)
buying = FALSE
diff --git a/code/modules/cargo/materials_market.dm b/code/modules/cargo/materials_market.dm
index 947197d16f298..797ebf5411d6f 100644
--- a/code/modules/cargo/materials_market.dm
+++ b/code/modules/cargo/materials_market.dm
@@ -232,7 +232,7 @@
var/material_str = params["material"]
var/quantity = text2num(params["quantity"])
- //find material from it's name
+ //find material from its name
var/datum/material/material_bought
var/obj/item/stack/sheet/sheet_to_buy
for(var/datum/material/mat as anything in SSstock_market.materials_prices)
@@ -346,7 +346,7 @@
var/datum/material/export_mat
/// Quantity of export material
var/quantity = 0
- /// Is this stock block currently updating it's value with the market (aka fluid)?
+ /// Is this stock block currently updating its value with the market (aka fluid)?
var/fluid = FALSE
/obj/item/stock_block/Initialize(mapload)
@@ -358,9 +358,9 @@
. = ..()
. += span_notice("\The [src] is worth [export_value] cr, from selling [quantity] sheets of [initial(export_mat?.name)].")
if(fluid)
- . += span_warning("\The [src] is currently liquid! It's value is based on the market price.")
+ . += span_warning("\The [src] is currently liquid! Its value is based on the market price.")
else
- . += span_notice("\The [src]'s value is still [span_boldnotice("locked in")]. [span_boldnotice("Sell it")] before it's value becomes liquid!")
+ . += span_notice("\The [src]'s value is still [span_boldnotice("locked in")]. [span_boldnotice("Sell it")] before its value becomes liquid!")
/obj/item/stock_block/proc/value_warning()
visible_message(span_warning("\The [src] is starting to become liquid!"))
diff --git a/code/modules/cargo/packs/costumes_toys.dm b/code/modules/cargo/packs/costumes_toys.dm
index de84a263597da..a25c47c5d9f88 100644
--- a/code/modules/cargo/packs/costumes_toys.dm
+++ b/code/modules/cargo/packs/costumes_toys.dm
@@ -90,7 +90,7 @@
/datum/supply_pack/costumes_toys/knucklebones
name = "Knucklebones Game Crate"
desc = "A fun dice game definitely not invented by a cult. Consult your local chaplain regarding \
- approved religious activity. Contains eighteen d6, one white crayon, and instructions on how to play."
+ approved religious activity. Contains eighteen d6, one stick of chalk, and instructions on how to play."
cost = CARGO_CRATE_VALUE * 2
contains = list(/obj/item/dice/d6 = 18,
/obj/item/paper/guides/knucklebone,
diff --git a/code/modules/cargo/packs/engineering.dm b/code/modules/cargo/packs/engineering.dm
index 771cba47df61d..907ae1598c653 100644
--- a/code/modules/cargo/packs/engineering.dm
+++ b/code/modules/cargo/packs/engineering.dm
@@ -328,3 +328,12 @@
)
crate_name = "radioactive nebula shielding (IMPORTANT)"
crate_type = /obj/structure/closet/crate/engineering
+
+/datum/supply_pack/engineering/portagrav
+ name = "Portable Gravity Unit Crate"
+ desc = "Contains a portable gravity unit, to make the clown float into the ceiling."
+ cost = CARGO_CRATE_VALUE * 4
+ access_view = ACCESS_ENGINEERING
+ contains = list(/obj/machinery/power/portagrav = 1)
+ crate_name = "portable gravity unit crate"
+ crate_type = /obj/structure/closet/crate/engineering
diff --git a/code/modules/cargo/packs/imports.dm b/code/modules/cargo/packs/imports.dm
index f270b1da11f39..98fc4d650212c 100644
--- a/code/modules/cargo/packs/imports.dm
+++ b/code/modules/cargo/packs/imports.dm
@@ -318,3 +318,20 @@
)
crate_name = "floortile camouflauge crate"
crate_type = /obj/structure/closet/crate/secure/weapon
+
+/**
+ * The Long To Short Range Bluespace Teleporter, used to deliver (black) market purchases more effiiently
+ * It can also be used to restock it, if you hit it with enough credits.
+ */
+/datum/supply_pack/imports/blackmarket_telepad
+ name = "Black Market LTSRBT"
+ desc = "Need a faster and better way of transporting your illegal goods from and to the \
+ station? Fear not, the Long-To-Short-Range-Bluespace-Transceiver (LTSRBT for short) \
+ is here to help. Contains a LTSRBT circuit, two bluespace crystals, and one ansible."
+ cost = CARGO_CRATE_VALUE * 10
+ contraband = TRUE
+ contains = list(
+ /obj/item/circuitboard/machine/ltsrbt,
+ /obj/item/stack/ore/bluespace_crystal/artificial = 2,
+ /obj/item/stock_parts/subspace/ansible,
+ )
diff --git a/code/modules/cargo/packs/medical.dm b/code/modules/cargo/packs/medical.dm
index c30a698c06f87..d44e8c42d3783 100644
--- a/code/modules/cargo/packs/medical.dm
+++ b/code/modules/cargo/packs/medical.dm
@@ -121,7 +121,7 @@
/datum/supply_pack/medical/experimentalmedicine
name = "Experimental Medicine Crate"
desc = "A crate containing the medication required for living with Hereditary Manifold Sickness, Sansufentanyl."
- cost = CARGO_CRATE_VALUE * 2
+ cost = CARGO_CRATE_VALUE * 3
contains = list(/obj/item/storage/pill_bottle/sansufentanyl = 2)
crate_name = "experimental medicine crate"
crate_type = /obj/structure/closet/crate/medical
@@ -187,6 +187,6 @@
name = "Strong-Arm Implant Set"
desc = "A crate containing two implants, which can be surgically implanted to empower the strength of human arms. Warranty void if exposed to electromagnetic pulses."
cost = CARGO_CRATE_VALUE * 6
- contains = list(/obj/item/organ/internal/cyberimp/arm/muscle = 2)
+ contains = list(/obj/item/organ/internal/cyberimp/arm/strongarm = 2)
crate_name = "Strong-Arm implant crate"
discountable = SUPPLY_PACK_RARE_DISCOUNTABLE
diff --git a/code/modules/cargo/packs/security.dm b/code/modules/cargo/packs/security.dm
index 05360fe913f0a..8a0765602b342 100644
--- a/code/modules/cargo/packs/security.dm
+++ b/code/modules/cargo/packs/security.dm
@@ -36,7 +36,7 @@
/datum/supply_pack/security/forensics
name = "Forensics Crate"
desc = "Stay hot on the criminal's heels with Nanotrasen's Detective Essentials™. \
- Contains a forensics scanner, six evidence bags, camera, tape recorder, white crayon, \
+ Contains a forensics scanner, six evidence bags, camera, tape recorder, stick of chalk, \
and of course, a fedora."
cost = CARGO_CRATE_VALUE * 2.5
access_view = ACCESS_MORGUE
diff --git a/code/modules/cargo/packs/service.dm b/code/modules/cargo/packs/service.dm
index 228d0d3e2fa46..bf6afe107d6c7 100644
--- a/code/modules/cargo/packs/service.dm
+++ b/code/modules/cargo/packs/service.dm
@@ -195,7 +195,7 @@
/datum/supply_pack/service/greyidbox
name = "Grey ID Card Multipack Crate"
desc = "A convenient crate containing a box of seven cheap ID cards in a handy wallet-sized form factor. \
- Cards come in every colour you can imagne, as long as it's grey."
+ Cards come in every colour you can imagine, as long as it's grey."
cost = CARGO_CRATE_VALUE * 3
contains = list(/obj/item/storage/box/ids)
crate_name = "basic id card crate"
diff --git a/code/modules/cargo/packs/vending_restock.dm b/code/modules/cargo/packs/vending_restock.dm
index 10ae874d5d6c9..6437e6027b893 100644
--- a/code/modules/cargo/packs/vending_restock.dm
+++ b/code/modules/cargo/packs/vending_restock.dm
@@ -19,6 +19,13 @@
crate_name = "cigarette supply crate"
crate_type = /obj/structure/closet/crate
+/datum/supply_pack/vending/science/cytopro
+ name = "Cytology Vendor Supply Crate"
+ desc = "For all your vat-growing needs! Contains a CytoPro machine refill."
+ cost = CARGO_CRATE_VALUE * 3
+ contains = list(/obj/item/vending_refill/cytopro)
+ crate_name = "cytopro supply crate"
+
/datum/supply_pack/vending/dinnerware
name = "Dinnerware Supply Crate"
desc = "More knives for the chef."
diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm
index 417e5519ba00f..9792d8425dca9 100644
--- a/code/modules/cargo/supplypod.dm
+++ b/code/modules/cargo/supplypod.dm
@@ -36,9 +36,9 @@
var/effectQuiet = FALSE //The female sniper. If true, the pod makes no noise (including related explosions, opening sounds, etc)
var/effectMissile = FALSE //If true, the pod deletes the second it lands. If you give it an explosion, it will act like a missile exploding as it hits the ground
var/effectCircle = FALSE //If true, allows the pod to come in at any angle. Bit of a weird feature but whatever its here
- var/style = STYLE_STANDARD //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the GLOB.podstyles list in cargo.dm defines to get the proper icon/name/desc for the pod.
+ var/datum/pod_style/style = /datum/pod_style //Style is a variable that keeps track of what the pod is supposed to look like. Only stores a path, type is set for ease of var access
var/reversing = FALSE //If true, the pod will not send any items. Instead, after opening, it will close again (picking up items/mobs) and fly back to centcom
- var/list/reverse_dropoff_coords //Turf that the reverse pod will drop off it's newly-acquired cargo to
+ var/list/reverse_dropoff_coords //Turf that the reverse pod will drop off its newly-acquired cargo to
var/fallingSoundLength = 11
var/fallingSound = 'sound/weapons/mortar_long_whistle.ogg'//Admin sound to play before the pod lands
var/landingSound //Admin sound to play when the pod lands
@@ -61,7 +61,7 @@
var/list/turfs_in_cargo = list()
/obj/structure/closet/supplypod/bluespacepod
- style = STYLE_BLUESPACE
+ style = /datum/pod_style/advanced
bluespace = TRUE
explosionSize = list(0,0,1,2)
@@ -83,7 +83,7 @@
name = "Syndicate Extraction Pod"
desc = "A specalised, blood-red styled pod for extracting high-value targets out of active mission areas. Targets must be manually stuffed inside the pod for proper delivery."
specialised = TRUE
- style = STYLE_SYNDICATE
+ style = /datum/pod_style/syndicate
bluespace = TRUE
explosionSize = list(0,0,1,2)
delays = list(POD_TRANSIT = 25, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30)
@@ -93,7 +93,7 @@
reverse_option_list = list("Mobs"=TRUE,"Objects"=FALSE,"Anchored"=FALSE,"Underfloor"=FALSE,"Wallmounted"=FALSE,"Floors"=FALSE,"Walls"=FALSE, "Mecha"=FALSE)
/obj/structure/closet/supplypod/centcompod
- style = STYLE_CENTCOM
+ style = /datum/pod_style/centcom
bluespace = TRUE
explosionSize = list(0,0,0,0)
delays = list(POD_TRANSIT = 20, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30)
@@ -121,13 +121,13 @@
desc = "An intimidating supply pod, covered in the blood-red markings"
bluespace = TRUE
explosionSize = list(0,0,0,0)
- style = STYLE_SYNDICATE
+ style = /datum/pod_style/syndicate
specialised = TRUE
/obj/structure/closet/supplypod/deadmatch_missile
name = "cruise missile"
desc = "A big ass missile, likely launched from some far-off deep space missile silo."
- style = STYLE_RED_MISSILE
+ style = /datum/pod_style/missile/syndicate
explosionSize = list(0,1,2,2)
effectShrapnel = TRUE
specialised = TRUE
@@ -153,33 +153,32 @@
style = customStyle
setStyle(style) //Upon initialization, give the supplypod an iconstate, name, and description based on the "style" variable. This system is important for the centcom_podlauncher to function correctly
-/obj/structure/closet/supplypod/proc/setStyle(chosenStyle) //Used to give the sprite an icon state, name, and description.
- style = chosenStyle
- var/base = GLOB.podstyles[chosenStyle][POD_BASE] //GLOB.podstyles is a 2D array we treat as a dictionary. The style represents the verticle index, with the icon state, name, and desc being stored in the horizontal indexes of the 2D array.
- icon_state = base
- decal = GLOB.podstyles[chosenStyle][POD_DECAL]
- rubble_type = GLOB.podstyles[chosenStyle][POD_RUBBLE_TYPE]
+/obj/structure/closet/supplypod/proc/setStyle(datum/pod_style/chosen_style) //Used to give the sprite an icon state, name, and description.
+ style = chosen_style
+ icon_state = chosen_style::icon_state
+ decal = chosen_style::decal_icon
+ rubble_type = chosen_style::rubble_type
if (!adminNamed && !specialised) //We dont want to name it ourselves if it has been specifically named by an admin using the centcom_podlauncher datum
- name = GLOB.podstyles[chosenStyle][POD_NAME]
- desc = GLOB.podstyles[chosenStyle][POD_DESC]
- if (GLOB.podstyles[chosenStyle][POD_DOOR])
- door = "[base]_door"
+ name = chosen_style::name
+ desc = chosen_style::desc
+ if (chosen_style::has_door)
+ door = "[icon_state]_door"
else
door = FALSE
update_appearance()
/obj/structure/closet/supplypod/proc/SetReverseIcon()
fin_mask = "bottomfin"
- if (GLOB.podstyles[style][POD_SHAPE] == POD_SHAPE_NORML)
- icon_state = GLOB.podstyles[style][POD_BASE] + "_reverse"
+ if (style::shape == POD_SHAPE_NORMAL)
+ icon_state = style::icon_state + "_reverse"
pixel_x = initial(pixel_x)
transform = matrix()
update_appearance()
/obj/structure/closet/supplypod/proc/backToNonReverseIcon()
fin_mask = initial(fin_mask)
- if (GLOB.podstyles[style][POD_SHAPE] == POD_SHAPE_NORML)
- icon_state = GLOB.podstyles[style][POD_BASE]
+ if (style::shape == POD_SHAPE_NORMAL)
+ icon_state = style::icon_state
pixel_x = initial(pixel_x)
transform = matrix()
update_appearance()
@@ -189,13 +188,13 @@
/obj/structure/closet/supplypod/update_overlays()
. = ..()
- if(style == STYLE_INVISIBLE)
+ if(ispath(style, /datum/pod_style/invisible))
return
if(rubble)
. += rubble.getForeground(src)
- if(style == STYLE_SEETHROUGH)
+ if(ispath(style, /datum/pod_style/seethrough))
for(var/atom/A in contents)
var/mutable_appearance/itemIcon = new(A)
itemIcon.transform = matrix().Translate(-1 * SUPPLYPOD_X_OFFSET, 0)
@@ -227,7 +226,7 @@
if(decal)
. += decal
return
- else if (GLOB.podstyles[style][POD_SHAPE] != POD_SHAPE_NORML) //If we're not a normal pod shape (aka, if we don't have fins), just add the door without masking
+ else if (style::shape != POD_SHAPE_NORMAL) //If we're not a normal pod shape (aka, if we don't have fins), just add the door without masking
. += door
else
var/icon/masked_door = new(icon, door) //The door we want to apply
@@ -280,7 +279,7 @@
reversing = FALSE //Now that we're done reversing, we set this to false (otherwise we would get stuck in an infinite loop of calling the close proc at the bottom of open_pod() )
bluespace = TRUE //Make it so that the pod doesn't stay in centcom forever
pod_flags &= ~FIRST_SOUNDS //Make it so we play sounds now
- if (!effectQuiet && style != STYLE_SEETHROUGH)
+ if (!effectQuiet && !ispath(style, /datum/pod_style/seethrough))
audible_message(span_notice("The pod hisses, closing and launching itself away from the station."), span_notice("The ground vibrates, and you hear the sound of engines firing."))
stay_after_drop = FALSE
holder.pixel_z = initial(holder.pixel_z)
@@ -351,14 +350,14 @@
opened = TRUE //We set opened to TRUE to avoid spending time trying to open (due to being deleted) during the Destroy() proc
qdel(src)
return
- if (style == STYLE_GONDOLA) //Checks if we are supposed to be a gondola pod. If so, create a gondolapod mob, and move this pod to nullspace. I'd like to give a shout out, to my man oranges
+ if (ispath(style, /datum/pod_style/gondola)) //Checks if we are supposed to be a gondola pod. If so, create a gondolapod mob, and move this pod to nullspace. I'd like to give a shout out, to my man oranges
var/mob/living/basic/pet/gondola/gondolapod/benis = new(turf_underneath, src)
benis.contents |= contents //Move the contents of this supplypod into the gondolapod mob.
for (var/mob/living/mob_in_pod in benis.contents)
mob_in_pod.reset_perspective(null)
moveToNullspace()
addtimer(CALLBACK(src, PROC_REF(open_pod), benis), delays[POD_OPENING]) //After the opening delay passes, we use the open proc from this supplyprod while referencing the contents of the "holder", in this case the gondolapod mob
- else if (style == STYLE_SEETHROUGH)
+ else if (ispath(style, /datum/pod_style/seethrough))
open_pod(src)
else
addtimer(CALLBACK(src, PROC_REF(open_pod), src), delays[POD_OPENING]) //After the opening delay passes, we use the open proc from this supplypod, while referencing this supplypod's contents
@@ -381,11 +380,11 @@
for (var/cargo in holder.contents)
var/atom/movable/movable_cargo = cargo
movable_cargo.forceMove(turf_underneath)
- if (!effectQuiet && !openingSound && style != STYLE_SEETHROUGH && !(pod_flags & FIRST_SOUNDS)) //If we aren't being quiet, play the default pod open sound
+ if (!effectQuiet && !openingSound && !ispath(style, /datum/pod_style/seethrough) && !(pod_flags & FIRST_SOUNDS)) //If we aren't being quiet, play the default pod open sound
playsound(get_turf(holder), open_sound, 15, TRUE, -3)
if (broken) //If the pod is opening because it's been destroyed, we end here
return
- if (style == STYLE_SEETHROUGH)
+ if (ispath(style, /datum/pod_style/seethrough))
startExitSequence(src)
else
if (reversing)
@@ -400,7 +399,7 @@
close(holder)
else if (bluespace) //If we're a bluespace pod, then delete ourselves (along with our holder, if a separate holder exists)
deleteRubble()
- if (!effectQuiet && style != STYLE_INVISIBLE && style != STYLE_SEETHROUGH)
+ if (!effectQuiet && !ispath(style, /datum/pod_style/invisible) && !ispath(style, /datum/pod_style/seethrough))
do_sparks(5, TRUE, holder) //Create some sparks right before closing
qdel(src) //Delete ourselves and the holder
if (holder != src)
@@ -422,18 +421,22 @@
insert(turf_underneath, holder)
/obj/structure/closet/supplypod/insert(atom/to_insert, atom/movable/holder)
- if(insertion_allowed(to_insert))
- if(isturf(to_insert))
- var/turf/turf_to_insert = to_insert
- turfs_in_cargo += turf_to_insert.type
- turf_to_insert.ScrapeAway()
- else
- var/atom/movable/movable_to_insert = to_insert
- movable_to_insert.forceMove(holder)
- return TRUE
- else
+ if(!insertion_allowed(to_insert))
return FALSE
+ if(isturf(to_insert))
+ var/turf/turf_to_insert = to_insert
+ turfs_in_cargo += turf_to_insert.type
+ turf_to_insert.ScrapeAway()
+ return TRUE
+
+ var/atom/movable/movable_to_insert = to_insert
+ if (ismob(movable_to_insert))
+ var/mob/mob_to_insert = movable_to_insert
+ if (!isnull(mob_to_insert.buckled))
+ mob_to_insert.buckled.unbuckle_mob(mob_to_insert, force = TRUE)
+ movable_to_insert.forceMove(holder)
+
/obj/structure/closet/supplypod/insertion_allowed(atom/to_insert)
if(to_insert.invisibility == INVISIBILITY_ABSTRACT)
return FALSE
@@ -537,13 +540,12 @@
update_appearance()
/obj/structure/closet/supplypod/proc/addGlow()
- if (GLOB.podstyles[style][POD_SHAPE] != POD_SHAPE_NORML)
+ if (style::shape != POD_SHAPE_NORMAL)
return
glow_effect = new(src)
- glow_effect.icon_state = "pod_glow_" + GLOB.podstyles[style][POD_GLOW]
+ glow_effect.icon_state = "pod_glow_" + style::glow_color
vis_contents += glow_effect
glow_effect.layer = GASFIRE_LAYER
- SET_PLANE_EXPLICIT(glow_effect, ABOVE_GAME_PLANE, src)
RegisterSignal(glow_effect, COMSIG_QDELETING, PROC_REF(remove_glow))
/obj/structure/closet/supplypod/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
@@ -589,7 +591,6 @@
icon_state = "pod_glow_green"
desc = ""
layer = GASFIRE_LAYER
- plane = ABOVE_GAME_PLANE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
alpha = 255
@@ -629,7 +630,7 @@
if (type == RUBBLE_THIN)
icon_state += "_thin"
foreground += "_thin"
- if (pod.style == STYLE_BOX)
+ if (ispath(pod.style, /datum/pod_style/box))
verticle_offset = -2
else
verticle_offset = initial(verticle_offset)
@@ -651,7 +652,7 @@
transform = matrix() * 1.5
animate(src, transform = matrix()*0.01, time = pod.delays[POD_TRANSIT]+pod.delays[POD_FALLING])
-/obj/effect/pod_landingzone //This is the object that forceMoves the supplypod to it's location
+/obj/effect/pod_landingzone //This is the object that forceMoves the supplypod to its location
name = "Landing Zone Indicator"
desc = "A holographic projection designating the landing zone of something. It's probably best to stand back."
icon = 'icons/obj/supplypods_32x32.dmi'
@@ -717,19 +718,17 @@
pod.transform = matrix().Turn(rotation)
pod.layer = FLY_LAYER
SET_PLANE_EXPLICIT(pod, ABOVE_GAME_PLANE, src)
- if (pod.style != STYLE_INVISIBLE)
+ if (!ispath(pod.style, /datum/pod_style/invisible))
animate(pod, pixel_z = -1 * abs(sin(rotation))*4, pixel_x = SUPPLYPOD_X_OFFSET + (sin(rotation) * 20), time = pod.delays[POD_FALLING], easing = LINEAR_EASING) //Make the pod fall! At an angle!
addtimer(CALLBACK(src, PROC_REF(endLaunch)), pod.delays[POD_FALLING], TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation
/obj/effect/pod_landingzone/proc/setupSmoke(rotation)
- if (pod.style == STYLE_INVISIBLE || pod.style == STYLE_SEETHROUGH)
+ if (ispath(pod.style, /datum/pod_style/invisible) || ispath(pod.style, /datum/pod_style/seethrough))
return
- var/turf/our_turf = get_turf(drop_location())
- for ( var/i in 1 to length(smoke_effects))
+ for (var/i in 1 to length(smoke_effects))
var/obj/effect/supplypod_smoke/smoke_part = new (drop_location())
if (i == 1)
smoke_part.layer = FLY_LAYER
- SET_PLANE(smoke_part, ABOVE_GAME_PLANE, our_turf)
smoke_part.icon_state = "smoke_start"
smoke_part.transform = matrix().Turn(rotation)
smoke_effects[i] = smoke_part
@@ -741,7 +740,7 @@
QDEL_IN(smoke_part, pod.delays[POD_FALLING] + 35)
/obj/effect/pod_landingzone/proc/drawSmoke()
- if (pod.style == STYLE_INVISIBLE || pod.style == STYLE_SEETHROUGH)
+ if (ispath(pod.style, /datum/pod_style/invisible) || ispath(pod.style, /datum/pod_style/seethrough))
return
for (var/obj/effect/supplypod_smoke/smoke_part in smoke_effects)
animate(smoke_part, alpha = 0, time = 20, flags = ANIMATION_PARALLEL)
diff --git a/code/modules/cargo/universal_scanner.dm b/code/modules/cargo/universal_scanner.dm
index fdcbc9ba2bb9b..484f4a7a03201 100644
--- a/code/modules/cargo/universal_scanner.dm
+++ b/code/modules/cargo/universal_scanner.dm
@@ -165,7 +165,7 @@
context[SCREENTIP_CONTEXT_LMB] = "Scan for export value"
return CONTEXTUAL_SCREENTIP_SET
/**
- * Scans an object, target, and provides it's export value based on selling to the cargo shuttle, to mob/user.
+ * Scans an object, target, and provides its export value based on selling to the cargo shuttle, to mob/user.
*/
/obj/item/universal_scanner/proc/export_scan(obj/target, mob/user)
var/datum/export_report/report = export_item_and_contents(target, dry_run = TRUE)
@@ -240,7 +240,7 @@
to_chat(user, span_warning("Bank account not detected. Handling tip not registered."))
/**
- * Scans an object, target, and sets it's custom_price variable to new_custom_price, presenting it to the user.
+ * Scans an object, target, and sets its custom_price variable to new_custom_price, presenting it to the user.
*/
/obj/item/universal_scanner/proc/price_tag(obj/target, mob/user)
if(isitem(target))
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index 2b60b3bdbb2e4..48ec55cd8e99b 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -45,8 +45,6 @@
var/datum/click_intercept = null
///Time when the click was intercepted
var/click_intercept_time = 0
- ///Used for admin AI interaction
- var/AI_Interact = FALSE
///Used to cache this client's bans to save on DB queries
var/ban_cache = null
@@ -168,9 +166,9 @@
var/middragtime = 0
//Middle-mouse-button clicked object control for aimbot exploit detection. Weakref
var/datum/weakref/middle_drag_atom_ref
- //When we started the currently active drag
+ ///When we started the currently active drag
var/drag_start = 0
- //The params we were passed at the start of the drag, in list form
+ ///The params we were passed at the start of the drag, in list form
var/list/drag_details
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 66fde8c2f1a72..6030aeaf48b6c 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -481,7 +481,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
display_unread_notes(src, time_stamp)
qdel(query_last_connected)
- var/cached_player_age = set_client_age_from_db(tdata) //we have to cache this because other shit may change it and we need it's current value now down below.
+ var/cached_player_age = set_client_age_from_db(tdata) //we have to cache this because other shit may change it and we need its current value now down below.
if (isnum(cached_player_age) && cached_player_age == -1) //first connection
player_age = 0
var/nnpa = CONFIG_GET(number/notify_new_player_age)
@@ -837,6 +837,23 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(dragged && button_clicked != dragged)
return
+ if(mob.turf_click_type != TURF_CLICK_FLAT && isturf(object) && length(modifiers))
+ //Split screen-loc up into X+Pixel_X and Y+Pixel_Y
+ var/list/screen_loc_params = splittext(modifiers[SCREEN_LOC], ",")
+ //Split Y+Pixel_Y up into list(Y, Pixel_Y)
+ var/list/screen_loc_Y = splittext(screen_loc_params[2],":")
+ var/pixel_offset = text2num(screen_loc_Y[2])
+ var/turf/below = get_step(object, SOUTH)
+ if(pixel_offset <= DEPTH_OFFSET && below)
+ object = below // Swap our target, cube click moment
+ if(mob.turf_click_type != TURF_CLICK_CUBE_KEEP_PARAMS)
+ if(modifiers[ICON_Y])
+ modifiers[ICON_Y] = WRAP(text2num(modifiers[ICON_Y]) - DEPTH_OFFSET, 0, world.icon_size)
+ screen_loc_Y[1] -= 1
+ screen_loc_Y[2] = WRAP(pixel_offset - DEPTH_OFFSET, 0, world.icon_size)
+ screen_loc_params[2] = screen_loc_Y.Join(":")
+ modifiers[SCREEN_LOC] = screen_loc_params.Join(",")
+
if (object && IS_WEAKREF_OF(object, middle_drag_atom_ref) && button_clicked == LEFT_CLICK)
ab = max(0, 5 SECONDS-(world.time-middragtime)*0.1)
@@ -1206,6 +1223,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
winset(usr, "mainwindow", "can-resize=true")
winset(usr, "mainwindow", "is-maximized=false")
winset(usr, "mainwindow", "on-size=attempt_auto_fit_viewport")
+ attempt_auto_fit_viewport()
/client/verb/toggle_status_bar()
set name = "Toggle Status Bar"
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 7cd3965718e60..c078a23e89c24 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -33,7 +33,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/list/key_bindings_by_key = list()
var/toggles = TOGGLES_DEFAULT
- var/db_flags
+ var/db_flags = NONE
var/chat_toggles = TOGGLES_DEFAULT_CHAT
var/ghost_form = "ghost"
diff --git a/code/modules/client/preferences/_preference.dm b/code/modules/client/preferences/_preference.dm
index 1ba43cf3d8a63..2ed2405f37687 100644
--- a/code/modules/client/preferences/_preference.dm
+++ b/code/modules/client/preferences/_preference.dm
@@ -111,10 +111,6 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
/// DOES have random body on, will this already be randomized?
var/randomize_by_default = TRUE
- /// If the selected species has this in its /datum/species/mutant_bodyparts,
- /// will show the feature as selectable.
- var/relevant_mutant_bodypart = null
-
/// If the selected species has this in its /datum/species/body_markings,
/// will show the feature as selectable.
var/relevant_body_markings = null
@@ -336,8 +332,7 @@ GLOBAL_LIST_INIT(preference_entries_by_key, init_preference_entries_by_key())
SHOULD_NOT_SLEEP(TRUE)
if ( \
- !isnull(relevant_mutant_bodypart) \
- || !isnull(relevant_inherent_trait) \
+ !isnull(relevant_inherent_trait) \
|| !isnull(relevant_external_organ) \
|| !isnull(relevant_head_flag) \
|| !isnull(relevant_body_markings) \
diff --git a/code/modules/client/preferences/clothing.dm b/code/modules/client/preferences/clothing.dm
index e0539e7b619c0..074ed4e041ac6 100644
--- a/code/modules/client/preferences/clothing.dm
+++ b/code/modules/client/preferences/clothing.dm
@@ -34,7 +34,7 @@
)
/datum/preference/choiced/backpack/create_default_value()
- return GBACKPACK
+ return DBACKPACK
/datum/preference/choiced/backpack/icon_for(value)
switch (value)
@@ -80,6 +80,9 @@
PREF_SKIRT,
)
+/datum/preference/choiced/jumpsuit/create_default_value()
+ return PREF_SUIT
+
/datum/preference/choiced/jumpsuit/icon_for(value)
switch (value)
if (PREF_SUIT)
@@ -90,15 +93,6 @@
/datum/preference/choiced/jumpsuit/apply_to_human(mob/living/carbon/human/target, value)
target.jumpsuit_style = value
-/datum/preference/choiced/jumpsuit/create_informed_default_value(datum/preferences/preferences)
- switch(preferences.read_preference(/datum/preference/choiced/gender))
- if(MALE)
- return PREF_SUIT
- if(FEMALE)
- return PREF_SKIRT
-
- return ..()
-
/// Socks preference
/datum/preference/choiced/socks
savefile_key = "socks"
diff --git a/code/modules/client/preferences/frill_transparency.dm b/code/modules/client/preferences/frill_transparency.dm
new file mode 100644
index 0000000000000..d0bc47de367f6
--- /dev/null
+++ b/code/modules/client/preferences/frill_transparency.dm
@@ -0,0 +1,12 @@
+/datum/preference/toggle/frill_transparency
+ category = PREFERENCE_CATEGORY_GAME_PREFERENCES
+ savefile_key = "frill_transparent_pref"
+ savefile_identifier = PREFERENCE_PLAYER
+ default_value = FALSE
+
+/datum/preference/toggle/frill_transparency/apply_to_client(client/client, value)
+ var/datum/hud/working_hud = client?.mob?.hud_used
+ if(!working_hud)
+ return
+ for(var/atom/movable/screen/plane_master/frill/frill as anything in working_hud.get_true_plane_masters(RENDER_PLANE_FRILL))
+ frill.show_to(client.mob)
diff --git a/code/modules/client/preferences/paraplegic.dm b/code/modules/client/preferences/paraplegic.dm
new file mode 100644
index 0000000000000..1ffa704c77d0b
--- /dev/null
+++ b/code/modules/client/preferences/paraplegic.dm
@@ -0,0 +1,20 @@
+/datum/preference/choiced/paraplegic
+ category = PREFERENCE_CATEGORY_MANUALLY_RENDERED
+ savefile_key = "paraplegic"
+ savefile_identifier = PREFERENCE_CHARACTER
+
+/datum/preference/choiced/paraplegic/init_possible_values()
+ return GLOB.paraplegic_choice
+
+/datum/preference/choiced/paraplegic/create_default_value()
+ return "Default"
+
+/datum/preference/choiced/paraplegic/is_accessible(datum/preferences/preferences)
+ . = ..()
+ if (!.)
+ return FALSE
+
+ return "Paraplegic" in preferences.all_quirks
+
+/datum/preference/choiced/paraplegic/apply_to_human(mob/living/carbon/human/target, value)
+ return
diff --git a/code/modules/client/preferences/sounds.dm b/code/modules/client/preferences/sounds.dm
index f1778405665ad..4a0298132c268 100644
--- a/code/modules/client/preferences/sounds.dm
+++ b/code/modules/client/preferences/sounds.dm
@@ -116,3 +116,9 @@
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
savefile_key = "sound_elevator"
savefile_identifier = PREFERENCE_PLAYER
+
+/// Controls hearing radio noise
+/datum/preference/toggle/radio_noise
+ category = PREFERENCE_CATEGORY_GAME_PREFERENCES
+ savefile_key = "sound_radio_noise"
+ savefile_identifier = PREFERENCE_PLAYER
diff --git a/code/modules/client/preferences/species_features/felinid.dm b/code/modules/client/preferences/species_features/felinid.dm
index 4c874ea7df750..be90d806323d3 100644
--- a/code/modules/client/preferences/species_features/felinid.dm
+++ b/code/modules/client/preferences/species_features/felinid.dm
@@ -20,7 +20,7 @@
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
can_randomize = FALSE
- relevant_mutant_bodypart = "ears"
+ relevant_external_organ = /obj/item/organ/internal/ears/cat
/datum/preference/choiced/ears/init_possible_values()
return assoc_to_keys_features(SSaccessories.ears_list)
diff --git a/code/modules/client/preferences/species_features/lizard.dm b/code/modules/client/preferences/species_features/lizard.dm
index 66c107153305e..11fefc17b8b9e 100644
--- a/code/modules/client/preferences/species_features/lizard.dm
+++ b/code/modules/client/preferences/species_features/lizard.dm
@@ -93,13 +93,51 @@
savefile_key = "feature_lizard_legs"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
- relevant_mutant_bodypart = "legs"
/datum/preference/choiced/lizard_legs/init_possible_values()
- return assoc_to_keys_features(SSaccessories.legs_list)
+ return list(NORMAL_LEGS, DIGITIGRADE_LEGS)
/datum/preference/choiced/lizard_legs/apply_to_human(mob/living/carbon/human/target, value)
target.dna.features["legs"] = value
+ // Hack to update the dummy in the preference menu
+ // (Because digi legs are ONLY handled on species change)
+ if(!isdummy(target) || target.dna.species.digitigrade_customization == DIGITIGRADE_NEVER)
+ return
+
+ var/list/correct_legs = target.dna.species.bodypart_overrides.Copy() & list(BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)
+
+ if(value == DIGITIGRADE_LEGS)
+ correct_legs[BODY_ZONE_R_LEG] = /obj/item/bodypart/leg/right/digitigrade
+ correct_legs[BODY_ZONE_L_LEG] = /obj/item/bodypart/leg/left/digitigrade
+
+ for(var/obj/item/bodypart/old_part as anything in target.bodyparts)
+ if(old_part.change_exempt_flags & BP_BLOCK_CHANGE_SPECIES)
+ continue
+
+ var/path = correct_legs[old_part.body_zone]
+ if(!path)
+ continue
+ var/obj/item/bodypart/new_part = new path()
+ new_part.replace_limb(target, TRUE)
+ new_part.update_limb(is_creating = TRUE)
+ qdel(old_part)
+
+/datum/preference/choiced/lizard_legs/is_accessible(datum/preferences/preferences)
+ if(!..())
+ return FALSE
+ var/datum/species/species_type = preferences.read_preference(/datum/preference/choiced/species)
+ return initial(species_type.digitigrade_customization) == DIGITIGRADE_OPTIONAL
+
+
+/datum/preference/choiced/lizard_legs/is_accessible(datum/preferences/preferences)
+ . = ..()
+
+ if(!.)
+ return
+
+ var/datum/species/species_type = preferences.read_preference(/datum/preference/choiced/species)
+
+ return initial(species_type.digitigrade_customization) & DIGITIGRADE_OPTIONAL
/datum/preference/choiced/lizard_snout
savefile_key = "feature_lizard_snout"
@@ -121,7 +159,7 @@
savefile_key = "feature_lizard_spines"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
- relevant_mutant_bodypart = "spines"
+ relevant_external_organ = /obj/item/organ/external/spines
/datum/preference/choiced/lizard_spines/init_possible_values()
return assoc_to_keys_features(SSaccessories.spines_list)
diff --git a/code/modules/client/preferences/species_features/mushperson.dm b/code/modules/client/preferences/species_features/mushperson.dm
index 45bd9c4b72620..4b624e9c02b4f 100644
--- a/code/modules/client/preferences/species_features/mushperson.dm
+++ b/code/modules/client/preferences/species_features/mushperson.dm
@@ -2,7 +2,7 @@
savefile_key = "feature_mushperson_cap"
savefile_identifier = PREFERENCE_CHARACTER
category = PREFERENCE_CATEGORY_SECONDARY_FEATURES
- relevant_mutant_bodypart = "cap"
+ relevant_external_organ = /obj/item/organ/external/mushroom_cap
/datum/preference/choiced/mushroom_cap/init_possible_values()
return assoc_to_keys_features(SSaccessories.caps_list)
diff --git a/code/modules/clothing/chameleon/generic_chameleon_clothing.dm b/code/modules/clothing/chameleon/generic_chameleon_clothing.dm
index 47b6f4db185a5..69800031f73a3 100644
--- a/code/modules/clothing/chameleon/generic_chameleon_clothing.dm
+++ b/code/modules/clothing/chameleon/generic_chameleon_clothing.dm
@@ -107,6 +107,7 @@ do { \
greyscale_colors = null
resistance_flags = NONE
+ body_parts_covered = HANDS|ARMS
armor_type = /datum/armor/gloves_chameleon
actions_types = list(/datum/action/item_action/chameleon/change/gloves)
clothing_traits = list(TRAIT_FAST_CUFFING)
@@ -218,6 +219,7 @@ do { \
desc = "A pair of black shoes."
icon_state = "sneakers"
inhand_icon_state = "sneakers_back"
+ body_parts_covered = FEET|LEGS
greyscale_colors = "#545454#ffffff"
greyscale_config = /datum/greyscale_config/sneakers
greyscale_config_worn = /datum/greyscale_config/sneakers/worn
diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm
index e2632dd394e0c..4ed35997680c0 100644
--- a/code/modules/clothing/glasses/_glasses.dm
+++ b/code/modules/clothing/glasses/_glasses.dm
@@ -611,7 +611,7 @@
glass_colour_type = FALSE
vision_flags = SEE_TURFS
clothing_traits = list(TRAIT_REAGENT_SCANNER, TRAIT_MADNESS_IMMUNE)
- var/list/hudlist = list(DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC_ADVANCED, DATA_HUD_SECURITY_ADVANCED)
+ var/list/hudlist = list(DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC, DATA_HUD_SECURITY_ADVANCED, DATA_HUD_BOT_PATH)
var/xray = FALSE
/obj/item/clothing/glasses/debug/equipped(mob/user, slot)
diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm
index 4f5668f7bcce7..dc35ab1dbf4c0 100644
--- a/code/modules/clothing/glasses/hud.dm
+++ b/code/modules/clothing/glasses/hud.dm
@@ -2,25 +2,6 @@
name = "HUD"
desc = "A heads-up display that provides important info in (almost) real time."
flags_1 = null //doesn't protect eyes because it's a monocle, duh
- var/hud_type = null
-
- // NOTE: Just because you have a HUD display doesn't mean you should be able to interact with stuff on examine, that's where the associated trait (TRAIT_MEDICAL_HUD, TRAIT_SECURITY_HUD, etc) is necessary.
-
-/obj/item/clothing/glasses/hud/equipped(mob/living/carbon/human/user, slot)
- ..()
- if(!(slot & ITEM_SLOT_EYES))
- return
- if(hud_type)
- var/datum/atom_hud/our_hud = GLOB.huds[hud_type]
- our_hud.show_to(user)
-
-/obj/item/clothing/glasses/hud/dropped(mob/living/carbon/human/user)
- ..()
- if(!istype(user) || user.glasses != src)
- return
- if(hud_type)
- var/datum/atom_hud/our_hud = GLOB.huds[hud_type]
- our_hud.hide_from(user)
/obj/item/clothing/glasses/hud/emp_act(severity)
. = ..()
@@ -55,10 +36,15 @@
name = "health scanner HUD"
desc = "A heads-up display that scans the humanoids in view and provides accurate data about their health status."
icon_state = "healthhud"
- hud_type = DATA_HUD_MEDICAL_ADVANCED
clothing_traits = list(TRAIT_MEDICAL_HUD)
glass_colour_type = /datum/client_colour/glass_colour/lightblue
+/obj/item/clothing/glasses/hud/medsechud
+ name = "health scanner security HUD"
+ desc = "A heads-up display that scans the humanoids in view and provides accurate data about their health status, ID status and security records."
+ icon_state = "medsechud"
+ clothing_traits = list(TRAIT_MEDICAL_HUD, TRAIT_SECURITY_HUD)
+
/obj/item/clothing/glasses/hud/health/night
name = "night vision health scanner HUD"
desc = "An advanced medical heads-up display that allows doctors to find patients in complete darkness."
@@ -110,7 +96,6 @@
name = "diagnostic HUD"
desc = "A heads-up display capable of analyzing the integrity and status of robotics and exosuits."
icon_state = "diagnostichud"
- hud_type = DATA_HUD_DIAGNOSTIC_BASIC
clothing_traits = list(TRAIT_DIAGNOSTIC_HUD)
glass_colour_type = /datum/client_colour/glass_colour/lightorange
@@ -153,7 +138,6 @@
name = "security HUD"
desc = "A heads-up display that scans the humanoids in view and provides accurate data about their ID status and security records."
icon_state = "securityhud"
- hud_type = DATA_HUD_SECURITY_ADVANCED
clothing_traits = list(TRAIT_SECURITY_HUD)
glass_colour_type = /datum/client_colour/glass_colour/red
@@ -243,20 +227,18 @@
if (wearer.glasses != src)
return
- if (hud_type)
- var/datum/atom_hud/our_hud = GLOB.huds[hud_type]
- our_hud.hide_from(user)
+ for(var/trait in clothing_traits)
+ REMOVE_CLOTHING_TRAIT(user, trait)
- if (hud_type == DATA_HUD_MEDICAL_ADVANCED)
- hud_type = null
- else if (hud_type == DATA_HUD_SECURITY_ADVANCED)
- hud_type = DATA_HUD_MEDICAL_ADVANCED
+ if (TRAIT_MEDICAL_HUD in clothing_traits)
+ clothing_traits = null
+ else if (TRAIT_SECURITY_HUD in clothing_traits)
+ clothing_traits = list(TRAIT_MEDICAL_HUD)
else
- hud_type = DATA_HUD_SECURITY_ADVANCED
+ clothing_traits = list(TRAIT_SECURITY_HUD)
- if (hud_type)
- var/datum/atom_hud/our_hud = GLOB.huds[hud_type]
- our_hud.show_to(user)
+ for(var/trait in clothing_traits)
+ ADD_CLOTHING_TRAIT(user, trait)
/datum/action/item_action/switch_hud
name = "Switch HUD"
@@ -265,19 +247,22 @@
name = "thermal HUD scanner"
desc = "Thermal imaging HUD in the shape of glasses."
icon_state = "thermal"
- hud_type = DATA_HUD_SECURITY_ADVANCED
vision_flags = SEE_MOBS
color_cutoffs = list(25, 8, 5)
glass_colour_type = /datum/client_colour/glass_colour/red
+ clothing_traits = list(TRAIT_SECURITY_HUD)
/obj/item/clothing/glasses/hud/toggle/thermal/attack_self(mob/user)
..()
+ var/hud_type
+ if (!isnull(clothing_traits) && clothing_traits.len)
+ hud_type = clothing_traits[1]
switch (hud_type)
- if (DATA_HUD_MEDICAL_ADVANCED)
+ if (TRAIT_MEDICAL_HUD)
icon_state = "meson"
color_cutoffs = list(5, 15, 5)
change_glass_color(/datum/client_colour/glass_colour/green)
- if (DATA_HUD_SECURITY_ADVANCED)
+ if (TRAIT_SECURITY_HUD)
icon_state = "thermal"
color_cutoffs = list(25, 8, 5)
change_glass_color(/datum/client_colour/glass_colour/red)
diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm
index bb0e12809955e..82b1eb8f37954 100644
--- a/code/modules/clothing/gloves/color.dm
+++ b/code/modules/clothing/gloves/color.dm
@@ -9,7 +9,6 @@
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
resistance_flags = NONE
cut_type = /obj/item/clothing/gloves/fingerless
- clothing_traits = list(TRAIT_FAST_CUFFING)
/obj/item/clothing/gloves/color/black/Initialize(mapload)
. = ..()
@@ -20,6 +19,15 @@
slapcraft_recipes = slapcraft_recipe_list,\
)
+/obj/item/clothing/gloves/color/black/security
+ name = "security gloves"
+ desc = "These security gloves come with microchips that help the user quickly restrain suspects."
+ icon_state = "sec"
+ clothing_traits = list(TRAIT_FAST_CUFFING)
+
+/obj/item/clothing/gloves/color/black/security/blu
+ icon_state = "sec_blu"
+
/obj/item/clothing/gloves/fingerless
name = "fingerless gloves"
desc = "Plain black gloves without fingertips for the hard-working."
diff --git a/code/modules/clothing/gloves/tacklers.dm b/code/modules/clothing/gloves/tacklers.dm
index 4adb374d92bd7..bbe7f5dba18b4 100644
--- a/code/modules/clothing/gloves/tacklers.dm
+++ b/code/modules/clothing/gloves/tacklers.dm
@@ -59,9 +59,8 @@
/obj/item/clothing/gloves/tackler/combat
name = "gorilla gloves"
desc = "Premium quality combative gloves, heavily reinforced to give the user an edge in close combat tackles, though they are more taxing to use than normal gripper gloves. Fireproof to boot!"
- icon_state = "black"
- inhand_icon_state = "greyscale_gloves"
- greyscale_colors = "#2f2e31"
+ icon_state = "gorilla"
+ inhand_icon_state = null
tackle_stam_cost = 30
base_knockdown = 1.25 SECONDS
@@ -77,6 +76,7 @@
/obj/item/clothing/gloves/tackler/combat/insulated
name = "guerrilla gloves"
desc = "Superior quality combative gloves, good for performing tackle takedowns as well as absorbing electrical shocks."
+ icon_state = "guerrilla"
siemens_coefficient = 0
armor_type = /datum/armor/combat_insulated
@@ -101,7 +101,7 @@
desc = "Ratty looking fingerless gloves wrapped with sticky tape. Beware anyone wearing these, for they clearly have no shame and nothing to lose."
icon_state = "fingerless"
inhand_icon_state = null
-
+ clothing_traits = list(TRAIT_FINGERPRINT_PASSTHROUGH)
tackle_stam_cost = 30
base_knockdown = 1.75 SECONDS
min_distance = 2
diff --git a/code/modules/clothing/head/costume.dm b/code/modules/clothing/head/costume.dm
index 2768656d5e6f9..5442210aecd73 100644
--- a/code/modules/clothing/head/costume.dm
+++ b/code/modules/clothing/head/costume.dm
@@ -87,6 +87,10 @@
clothing_flags = SNUG_FIT
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
+/obj/item/clothing/head/costume/lobsterhat/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/speechmod, replacements = strings("crustacean_replacement.json", "crustacean"))
+
/obj/item/clothing/head/costume/drfreezehat
name = "doctor freeze's wig"
desc = "A cool wig for cool people."
@@ -199,3 +203,9 @@
It's only a replica, and probably wouldn't protect you from anything."
icon_state = "allies_helmet"
inhand_icon_state = null
+
+/obj/item/clothing/head/costume/hairpin
+ name = "fancy hairpin"
+ desc = "A delicate hairpin normally paired with traditional clothing"
+ icon_state = "hairpin_fancy"
+ inhand_icon_state = "hairpin_fancy"
diff --git a/code/modules/clothing/head/hat.dm b/code/modules/clothing/head/hat.dm
index 0fc2de1375a48..a8247a55603e2 100644
--- a/code/modules/clothing/head/hat.dm
+++ b/code/modules/clothing/head/hat.dm
@@ -53,11 +53,12 @@
/obj/item/clothing/head/bio_hood/plague
name = "plague doctor's hat"
- desc = "These were once used by plague doctors. Will protect you from exposure to the Pestilence."
+ desc = "These were once used by plague doctors. This hat will only slightly protect you from exposure to the Pestilence."
icon_state = "plaguedoctor"
- clothing_flags = THICKMATERIAL | BLOCK_GAS_SMOKE_EFFECT | SNUG_FIT | STACKABLE_HELMET_EXEMPT
armor_type = /datum/armor/bio_hood_plague
flags_inv = NONE
+ clothing_flags = SNUG_FIT
+ flags_cover = NONE
/datum/armor/bio_hood_plague
bio = 100
@@ -209,6 +210,14 @@
name = "rice hat"
desc = "Welcome to the rice fields, motherfucker."
icon_state = "rice_hat"
+ base_icon_state = "rice_hat"
+ var/reversed = FALSE
+
+/obj/item/clothing/head/costume/rice_hat/click_alt(mob/user)
+ reversed = !reversed
+ worn_icon_state = "[base_icon_state][reversed ? "_kim" : ""]"
+ to_chat(user, span_notice("You [reversed ? "lower" : "raise"] the hat."))
+ update_appearance()
/obj/item/clothing/head/costume/lizard
name = "lizardskin cloche hat"
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index 4d41d9daa5c76..76c230ca504d2 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -137,7 +137,7 @@
/obj/item/clothing/head/helmet/marine/pmc
icon_state = "marine"
- desc = "A tactical black helmet, designed to protect one's head from various injuries sustained in operations. Its stellar survivability making up is for it's lack of space worthiness"
+ desc = "A tactical black helmet, designed to protect one's head from various injuries sustained in operations. Its stellar survivability making up is for its lack of space worthiness"
min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT
max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT
clothing_flags = null
@@ -568,3 +568,106 @@
fire = 50
acid = 50
wound = 30
+
+/obj/item/clothing/head/helmet/durability/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)
+ take_damage(1, BRUTE, 0, 0)
+
+/obj/item/clothing/head/helmet/durability/watermelon
+ name = "Watermelon Helmet"
+ desc = "A helmet cut out from a watermelon. Might take a few hits, but don't expect it whitstand much."
+ icon_state = "watermelon"
+ inhand_icon_state = "watermelon"
+ flags_inv = HIDEEARS
+ dog_fashion = /datum/dog_fashion/head/watermelon
+ armor_type = /datum/armor/helmet_watermelon
+ max_integrity = 15
+
+/obj/item/clothing/head/helmet/durability/watermelon/fire_resist
+ resistance_flags = FIRE_PROOF
+ armor_type = /datum/armor/helmet_watermelon_fr
+
+/datum/armor/helmet_watermelon
+ melee = 15
+ bullet = 10
+ energy = 10
+ bomb = 10
+ fire = 0
+ acid = 25
+ wound = 5
+
+/datum/armor/helmet_watermelon_fr
+ melee = 15
+ bullet = 10
+ energy = 10
+ bomb = 10
+ fire = 15
+ acid = 30
+ wound = 5
+
+/obj/item/clothing/head/helmet/durability/holymelon
+ name = "Holymelon Helmet"
+ desc = "A helmet from a hollowed out holymelon. Might take a few hits, but don't expect it whitstand much."
+ icon_state = "holymelon"
+ inhand_icon_state = "holymelon"
+ flags_inv = HIDEEARS
+ dog_fashion = /datum/dog_fashion/head/holymelon
+ armor_type = /datum/armor/helmet_watermelon
+ max_integrity = 15
+ var/decayed = FALSE
+
+/obj/item/clothing/head/helmet/durability/holymelon/fire_resist
+ resistance_flags = FIRE_PROOF
+ armor_type = /datum/armor/helmet_watermelon_fr
+
+/obj/item/clothing/head/helmet/durability/holymelon/Initialize(mapload)
+ . = ..()
+ if(decayed)
+ decay()
+ return
+
+ AddComponent(
+ /datum/component/anti_magic, \
+ antimagic_flags = MAGIC_RESISTANCE_HOLY, \
+ inventory_flags = ITEM_SLOT_OCLOTHING, \
+ charges = 1, \
+ drain_antimagic = CALLBACK(src, PROC_REF(drain_antimagic)), \
+ expiration = CALLBACK(src, PROC_REF(decay)) \
+ )
+
+/obj/item/clothing/head/helmet/durability/holymelon/proc/drain_antimagic(mob/user)
+ to_chat(user, span_warning("[src] looses a bit of its shimmer and glossiness..."))
+
+/obj/item/clothing/head/helmet/durability/holymelon/proc/decay()
+ take_damage(8, BRUTE, 0, 0)
+
+/obj/item/clothing/head/helmet/durability/barrelmelon
+ name = "Barrelmelon Helmet"
+ desc = "A helmet from hollowed out barrelmelon. As sturdy as if made from actual wood, though its rigid structure makes it break up quicker."
+ icon_state = "barrelmelon"
+ inhand_icon_state = "barrelmelon"
+ flags_inv = HIDEEARS
+ dog_fashion = /datum/dog_fashion/head/barrelmelon
+ armor_type = /datum/armor/helmet_barrelmelon
+ max_integrity = 10
+
+/obj/item/clothing/head/helmet/durability/barrelmelon/fire_resist
+ resistance_flags = FIRE_PROOF
+ armor_type = /datum/armor/helmet_barrelmelon_fr
+
+/datum/armor/helmet_barrelmelon
+ melee = 25
+ bullet = 20
+ energy = 15
+ bomb = 10
+ fire = 0
+ acid = 35
+ wound = 10
+
+/datum/armor/helmet_barrelmelon_fr
+ melee = 25
+ bullet = 20
+ energy = 15
+ bomb = 10
+ fire = 20
+ acid = 40
+ wound = 10
diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm
index 3003e9a76ee15..4744296cdb8ee 100644
--- a/code/modules/clothing/head/soft_caps.dm
+++ b/code/modules/clothing/head/soft_caps.dm
@@ -169,9 +169,11 @@
clothing_flags = SNUG_FIT
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE
dog_fashion = null
+ clothing_traits = list(TRAIT_SCARY_FISHERMAN) //Fish, carps, lobstrosities and frogs fear me.
/obj/item/clothing/head/soft/fishing_hat/Initialize(mapload)
. = ..()
+ AddComponent(/datum/component/speechmod, replacements = strings("crustacean_replacement.json", "crustacean")) //you asked for this.
AddElement(/datum/element/skill_reward, /datum/skill/fishing)
#define PROPHAT_MOOD "prophat"
diff --git a/code/modules/clothing/head/tophat.dm b/code/modules/clothing/head/tophat.dm
index 2affc4c63da18..612f02ce692e9 100644
--- a/code/modules/clothing/head/tophat.dm
+++ b/code/modules/clothing/head/tophat.dm
@@ -38,7 +38,7 @@
/obj/item/clothing/head/hats/tophat/balloon
name = "balloon top-hat"
- desc = "It's an colourful looking top-hat to match yout colourful personality."
+ desc = "It's a colourful looking top-hat to match your colourful personality."
icon_state = "balloon_tophat"
inhand_icon_state = "balloon_that"
throwforce = 0
diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm
index 36fe591a63edb..9d859fec700b6 100644
--- a/code/modules/clothing/masks/gasmask.dm
+++ b/code/modules/clothing/masks/gasmask.dm
@@ -24,10 +24,10 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
var/list/gas_filters
///Type of filter that spawns on roundstart
var/starting_filter_type = /obj/item/gas_filter
- ///Does the mask have an FOV?
- var/has_fov = TRUE
///Cigarette in the mask
var/obj/item/cigarette/cig
+ ///Does the mask have an FOV?
+ var/has_fov = TRUE
voice_filter = "lowpass=f=750,volume=2"
/datum/armor/mask_gas
@@ -250,12 +250,12 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
//Plague Dr suit can be found in clothing/suits/bio.dm
/obj/item/clothing/mask/gas/plaguedoctor
name = "plague doctor mask"
- desc = "A modernised version of the classic design, this mask will not only filter out toxins but it can also be connected to an air supply."
+ desc = "A modernised version of the classic design, this mask will not only protect you from exposure to the Pestilence but it can also be connected to an air supply."
icon_state = "plaguedoctor"
flags_inv = HIDEEARS|HIDEEYES|HIDEFACE|HIDEFACIALHAIR|HIDESNOUT|HIDEHAIR
inhand_icon_state = "gas_mask"
has_fov = FALSE
- flags_cover = MASKCOVERSEYES
+ clothing_flags = BLOCK_GAS_SMOKE_EFFECT|MASKINTERNALS
/obj/item/clothing/mask/gas/syndicate
name = "syndicate mask"
diff --git a/code/modules/clothing/outfits/ert.dm b/code/modules/clothing/outfits/ert.dm
index 2aa61b0046070..263f831cff25d 100644
--- a/code/modules/clothing/outfits/ert.dm
+++ b/code/modules/clothing/outfits/ert.dm
@@ -104,6 +104,8 @@
l_pocket = /obj/item/healthanalyzer/advanced
additional_radio = /obj/item/encryptionkey/heads/cmo
+ skillchips = list(/obj/item/skillchip/entrails_reader)
+
/datum/outfit/centcom/ert/medic/alert
name = "ERT Medic - High Alert"
@@ -133,6 +135,8 @@
l_pocket = /obj/item/rcd_ammo/large
additional_radio = /obj/item/encryptionkey/heads/ce
+ skillchips = list(/obj/item/skillchip/job/engineer)
+
/datum/outfit/centcom/ert/engineer/alert
name = "ERT Engineer - High Alert"
@@ -502,6 +506,8 @@
head = /obj/item/clothing/head/helmet/marine/security
additional_radio = /obj/item/encryptionkey/heads/hos
+ skillchips = null
+
/datum/outfit/centcom/ert/marine/medic
name = "Marine Medic"
@@ -520,6 +526,8 @@
glasses = /obj/item/clothing/glasses/hud/health/sunglasses
additional_radio = /obj/item/encryptionkey/heads/cmo
+ skillchips = list(/obj/item/skillchip/entrails_reader)
+
/datum/outfit/centcom/ert/marine/engineer
name = "Marine Engineer"
@@ -533,6 +541,8 @@
glasses = /obj/item/clothing/glasses/hud/diagnostic/sunglasses
additional_radio = /obj/item/encryptionkey/heads/ce
+ skillchips = list(/obj/item/skillchip/job/engineer)
+
/datum/outfit/centcom/militia
name = "Militia Man"
diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm
index 4470a398168c5..61298e970cbf9 100644
--- a/code/modules/clothing/shoes/_shoes.dm
+++ b/code/modules/clothing/shoes/_shoes.dm
@@ -115,9 +115,10 @@
* *
* * state: SHOES_UNTIED, SHOES_TIED, or SHOES_KNOTTED, depending on what you want them to become
* * user: used to check to see if we're the ones unknotting our own laces
+ * * force_lacing: boolean. if TRUE, ignores can_be_tied
*/
-/obj/item/clothing/shoes/proc/adjust_laces(state, mob/user)
- if(!can_be_tied)
+/obj/item/clothing/shoes/proc/adjust_laces(state, mob/user, force_lacing = FALSE)
+ if(!can_be_tied && !force_lacing)
return
var/mob/living/carbon/human/our_guy
diff --git a/code/modules/clothing/shoes/boots.dm b/code/modules/clothing/shoes/boots.dm
index dbfa4f8b3c40d..03f174aa43c31 100644
--- a/code/modules/clothing/shoes/boots.dm
+++ b/code/modules/clothing/shoes/boots.dm
@@ -3,6 +3,7 @@
desc = "High speed, low drag combat boots."
icon_state = "jackboots"
inhand_icon_state = "jackboots"
+ body_parts_covered = FEET|LEGS
armor_type = /datum/armor/shoes_combat
strip_delay = 40
resistance_flags = NONE
@@ -49,6 +50,7 @@
resistance_flags = NONE
armor_type = /datum/armor/shoes_jackboots
can_be_tied = FALSE
+ body_parts_covered = FEET|LEGS
/datum/armor/shoes_jackboots
bio = 90
@@ -108,6 +110,7 @@
strip_delay = 4 SECONDS
equip_delay_other = 4 SECONDS
clothing_flags = THICKMATERIAL
+ body_parts_covered = FEET|LEGS
resistance_flags = NONE
/datum/armor/ice_boots_eva
@@ -177,6 +180,7 @@
strip_delay = 40
resistance_flags = NONE
lace_time = 12 SECONDS
+ body_parts_covered = FEET|LEGS
/datum/armor/shoes_pirate
melee = 25
diff --git a/code/modules/clothing/shoes/cowboy.dm b/code/modules/clothing/shoes/cowboy.dm
index 4295b91cad2f5..ab8ef30c99b60 100644
--- a/code/modules/clothing/shoes/cowboy.dm
+++ b/code/modules/clothing/shoes/cowboy.dm
@@ -109,3 +109,4 @@
desc = "And they sing, oh, ain't you glad you're single? And that song ain't so very far from wrong."
armor_type = /datum/armor/shoes_combat
has_spurs = TRUE
+ body_parts_covered = FEET|LEGS
diff --git a/code/modules/clothing/shoes/cult.dm b/code/modules/clothing/shoes/cult.dm
index 80d03d3a09e25..f1a856b42688b 100644
--- a/code/modules/clothing/shoes/cult.dm
+++ b/code/modules/clothing/shoes/cult.dm
@@ -1,5 +1,5 @@
/obj/item/clothing/shoes/cult
- name = "\improper Nar'Sien invoker boots"
+ name = "\improper Nar'Sian boots"
desc = "A pair of boots worn by the followers of Nar'Sie."
icon_state = "cult"
inhand_icon_state = null
@@ -10,7 +10,7 @@
lace_time = 10 SECONDS
/obj/item/clothing/shoes/cult/alt
- name = "cultist boots"
+ name = "\improper Nar'Sian invoker boots"
icon_state = "cultalt"
/obj/item/clothing/shoes/cult/alt/ghost
diff --git a/code/modules/clothing/shoes/sandals.dm b/code/modules/clothing/shoes/sandals.dm
index 9c948e2fac88e..92c90a90348ce 100644
--- a/code/modules/clothing/shoes/sandals.dm
+++ b/code/modules/clothing/shoes/sandals.dm
@@ -11,6 +11,12 @@
can_be_tied = FALSE
species_exception = list(/datum/species/golem)
+/obj/item/clothing/shoes/sandal/alt
+ desc = "A pair of shiny black wooden sandals."
+ name = "black sandals"
+ icon_state = "blacksandals"
+ inhand_icon_state = "blacksandals"
+
/datum/armor/shoes_sandal
bio = 10
diff --git a/code/modules/clothing/shoes/sneakers.dm b/code/modules/clothing/shoes/sneakers.dm
index 953d5bd9a1cb8..0ae1e6e9caad9 100644
--- a/code/modules/clothing/shoes/sneakers.dm
+++ b/code/modules/clothing/shoes/sneakers.dm
@@ -12,6 +12,11 @@
flags_1 = IS_PLAYER_COLORABLE_1
interaction_flags_mouse_drop = NEED_HANDS
+/obj/item/clothing/shoes/sneakers/random/Initialize(mapload)
+ . = ..()
+ greyscale_colors = "#" + random_color() + "#" + random_color()
+ update_greyscale()
+
/obj/item/clothing/shoes/sneakers/black
name = "black shoes"
desc = "A pair of black shoes."
diff --git a/code/modules/clothing/spacesuits/_spacesuits.dm b/code/modules/clothing/spacesuits/_spacesuits.dm
index 703000a1f9133..caf84d9562dcb 100644
--- a/code/modules/clothing/spacesuits/_spacesuits.dm
+++ b/code/modules/clothing/spacesuits/_spacesuits.dm
@@ -24,6 +24,7 @@
flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF
resistance_flags = NONE
dog_fashion = null
+ slowdown = 0.5
/datum/armor/helmet_space
bio = 100
@@ -47,7 +48,7 @@
/obj/item/tank/internals,
/obj/item/tank/jetpack/oxygen/captain,
)
- slowdown = 1
+ slowdown = 0.5
armor_type = /datum/armor/suit_space
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
cold_protection = CHEST | GROIN | LEGS | FEET | ARMS | HANDS
diff --git a/code/modules/clothing/spacesuits/pirate.dm b/code/modules/clothing/spacesuits/pirate.dm
index ca041d68d036f..73feec525c548 100644
--- a/code/modules/clothing/spacesuits/pirate.dm
+++ b/code/modules/clothing/spacesuits/pirate.dm
@@ -3,6 +3,7 @@
desc = "A modified helmet to allow space pirates to intimidate their customers whilst staying safe from the void. Comes with some additional protection."
icon_state = "spacepirate"
inhand_icon_state = "space_pirate_helmet"
+ slowdown = 0
armor_type = /datum/armor/space_pirate
strip_delay = 40
equip_delay_other = 20
diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm
index 63a0dd515c3f6..380cd0cf3fb53 100644
--- a/code/modules/clothing/spacesuits/plasmamen.dm
+++ b/code/modules/clothing/spacesuits/plasmamen.dm
@@ -118,33 +118,45 @@
. = ..()
if(!up)
. += visor_icon
-
-/obj/item/clothing/head/helmet/space/plasmaman/attackby(obj/item/hitting_item, mob/living/user)
- . = ..()
- if(istype(hitting_item, /obj/item/toy/crayon))
- if(smile == FALSE)
- var/obj/item/toy/crayon/CR = hitting_item
- to_chat(user, span_notice("You start drawing a smiley face on the helmet's visor.."))
- if(do_after(user, 2.5 SECONDS, target = src))
- smile = TRUE
- smile_color = CR.paint_color
- to_chat(user, "You draw a smiley on the helmet visor.")
- update_appearance()
- else
- to_chat(user, span_warning("Seems like someone already drew something on this helmet's visor!"))
- return
- if(istype(hitting_item, /obj/item/clothing/head))
- var/obj/item/clothing/hitting_clothing = hitting_item
- if(hitting_clothing.clothing_flags & STACKABLE_HELMET_EXEMPT)
- to_chat(user, span_notice("You cannot place [hitting_clothing.name] on helmet!"))
- return
- if(attached_hat)
- to_chat(user, span_notice("There's already something placed on helmet!"))
- return
- attached_hat = hitting_clothing
- to_chat(user, span_notice("You placed [hitting_clothing.name] on helmet!"))
- hitting_clothing.forceMove(src)
+ if(smile)
+ var/mutable_appearance/smiley = mutable_appearance(icon, smile_state)
+ smiley.color = smile_color
+ . += smiley
+
+/obj/item/clothing/head/helmet/space/plasmaman/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(istype(tool, /obj/item/toy/crayon))
+ if(smile)
+ to_chat(user, span_warning("Seems like someone already drew something on [src]'s visor!"))
+ return ITEM_INTERACT_BLOCKING
+
+ var/obj/item/toy/crayon/crayon = tool
+ to_chat(user, span_notice("You start drawing a smiley face on [src]'s visor..."))
+ if(!do_after(user, 2.5 SECONDS, target = src))
+ return ITEM_INTERACT_BLOCKING
+
+ smile = TRUE
+ smile_color = crayon.paint_color
+ to_chat(user, "You draw a smiley on [src] visor.")
update_appearance()
+ return ITEM_INTERACT_SUCCESS
+
+ if(!istype(tool, /obj/item/clothing/head))
+ return NONE
+
+ var/obj/item/clothing/hitting_clothing = tool
+ if(hitting_clothing.clothing_flags & STACKABLE_HELMET_EXEMPT)
+ to_chat(user, span_notice("You cannot place [hitting_clothing.name] on [src]!"))
+ return ITEM_INTERACT_BLOCKING
+
+ if(attached_hat)
+ to_chat(user, span_notice("There's already something placed on [src]!"))
+ return ITEM_INTERACT_BLOCKING
+
+ attached_hat = hitting_clothing
+ to_chat(user, span_notice("You placed [hitting_clothing.name] on [src]!"))
+ hitting_clothing.forceMove(src)
+ update_appearance()
+ return ITEM_INTERACT_SUCCESS
///By the by, helmets have the update_icon_updates_onmob element, so we don't have to call mob.update_worn_head()
/obj/item/clothing/head/helmet/space/plasmaman/worn_overlays(mutable_appearance/standing, isinhands)
diff --git a/code/modules/clothing/spacesuits/santa.dm b/code/modules/clothing/spacesuits/santa.dm
index 138f52e046e47..f6bd1657606c5 100644
--- a/code/modules/clothing/spacesuits/santa.dm
+++ b/code/modules/clothing/spacesuits/santa.dm
@@ -7,6 +7,7 @@
inhand_icon_state = "santahat"
flags_cover = HEADCOVERSEYES
dog_fashion = /datum/dog_fashion/head/santa
+ slowdown = 0
/obj/item/clothing/head/helmet/space/santahat/beardless
icon = 'icons/obj/clothing/head/costume.dmi'
@@ -14,6 +15,7 @@
icon_state = "santahatnorm"
inhand_icon_state = "that"
flags_inv = NONE
+ slowdown = 0
/obj/item/clothing/suit/space/santa
name = "Santa's suit"
diff --git a/code/modules/clothing/spacesuits/softsuit.dm b/code/modules/clothing/spacesuits/softsuit.dm
index 510c9e7056f2f..0b644286063ec 100644
--- a/code/modules/clothing/spacesuits/softsuit.dm
+++ b/code/modules/clothing/spacesuits/softsuit.dm
@@ -16,13 +16,14 @@
name = "Engineering Void Helmet"
desc = "A CentCom engineering dark red space suit helmet. While old and dusty, it still gets the job done."
icon_state = "void"
+ slowdown = 2
/obj/item/clothing/suit/space/nasavoid/old
name = "Engineering Voidsuit"
icon_state = "void"
inhand_icon_state = "void_suit"
desc = "A CentCom engineering dark red space suit. Age has degraded the suit making it difficult to move around in."
- slowdown = 4
+ slowdown = 2
allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/multitool)
//EVA suit
@@ -72,6 +73,7 @@
inhand_icon_state = "syndicate-helm-orange" //resprite?
armor_type = /datum/armor/space_fragile
strip_delay = 65
+ slowdown = 1
/obj/item/clothing/suit/space/fragile
name = "emergency space suit"
@@ -79,7 +81,7 @@
var/torn = FALSE
icon_state = "syndicate-orange"
inhand_icon_state = "syndicate-orange"
- slowdown = 2
+ slowdown = 1
armor_type = /datum/armor/space_fragile
strip_delay = 65
diff --git a/code/modules/clothing/spacesuits/specialops.dm b/code/modules/clothing/spacesuits/specialops.dm
index cf8fc2a475cc6..caaa32cc24be2 100644
--- a/code/modules/clothing/spacesuits/specialops.dm
+++ b/code/modules/clothing/spacesuits/specialops.dm
@@ -7,6 +7,7 @@
inhand_icon_state = null
greyscale_colors = "#397F3F#FFCE5B"
clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL | SNUG_FIT
+ slowdown = 0
flags_inv = 0
armor_type = /datum/armor/space_beret
strip_delay = 130
diff --git a/code/modules/clothing/suits/ablativecoat.dm b/code/modules/clothing/suits/ablativecoat.dm
index 8bc37aaba22b7..32233f23400cc 100644
--- a/code/modules/clothing/suits/ablativecoat.dm
+++ b/code/modules/clothing/suits/ablativecoat.dm
@@ -50,15 +50,11 @@
/obj/item/clothing/suit/hooded/ablative/on_hood_up(obj/item/clothing/head/hooded/hood)
. = ..()
var/mob/living/carbon/user = loc
- var/datum/atom_hud/hud = GLOB.huds[DATA_HUD_SECURITY_ADVANCED]
ADD_TRAIT(user, TRAIT_SECURITY_HUD, HELMET_TRAIT)
- hud.show_to(user)
balloon_alert(user, "hud enabled")
/obj/item/clothing/suit/hooded/ablative/on_hood_down(obj/item/clothing/head/hooded/hood)
var/mob/living/carbon/user = loc
- var/datum/atom_hud/sec_hud = GLOB.huds[DATA_HUD_SECURITY_ADVANCED]
REMOVE_TRAIT(user, TRAIT_SECURITY_HUD, HELMET_TRAIT)
- sec_hud.hide_from(user)
balloon_alert(user, "hud disabled")
return ..()
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 2502850bd700d..3c7fa506b208d 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -107,7 +107,7 @@
body_parts_covered = CHEST|GROIN
/obj/item/clothing/suit/armor/vest/marine/pmc
- desc = "A set of the finest mass produced, stamped plasteel armor plates, for an all-around door-kicking and ass-smashing. Its stellar survivability making up is for it's lack of space worthiness"
+ desc = "A set of the finest mass produced, stamped plasteel armor plates, for an all-around door-kicking and ass-smashing. Its stellar survivability making up is for its lack of space worthiness"
min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT
max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT
clothing_flags = THICKMATERIAL
@@ -652,7 +652,7 @@
/obj/item/clothing/suit/armor/militia
name = "station defender's coat"
- desc = "A well worn uniform used by militia across the frontier, it's thick padding useful for cushioning blows."
+ desc = "A well worn uniform used by militia across the frontier, its thick padding useful for cushioning blows."
icon_state = "militia"
inhand_icon_state = "b_suit"
body_parts_covered = CHEST|GROIN|ARMS
@@ -714,3 +714,113 @@
fire = 50
acid = 50
wound = 30
+
+/obj/item/clothing/suit/armor/durability/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)
+ take_damage(1, BRUTE, 0, 0)
+
+/obj/item/clothing/suit/armor/durability/watermelon
+ name = "watermelon"
+ desc = "An armour, made from watermelons. Propably won't take too many hits, but at least it looks serious... As serious as worn watermelon can be."
+ icon_state = "watermelon"
+ inhand_icon_state = null
+ body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
+ armor_type = /datum/armor/watermelon
+ strip_delay = 60
+ equip_delay_other = 40
+ clothing_traits = list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED)
+ max_integrity = 15
+
+/obj/item/clothing/suit/armor/durability/watermelon/fire_resist
+ resistance_flags = FIRE_PROOF
+ armor_type = /datum/armor/watermelon_fr
+
+/datum/armor/watermelon
+ melee = 15
+ bullet = 10
+ energy = 10
+ bomb = 10
+ fire = 0
+ acid = 25
+ wound = 5
+
+/datum/armor/watermelon_fr
+ melee = 15
+ bullet = 10
+ energy = 10
+ bomb = 10
+ fire = 15
+ acid = 30
+ wound = 5
+
+/obj/item/clothing/suit/armor/durability/holymelon
+ name = "holymelon"
+ desc = "An armour, made from holymelons. Inspires you to go on some sort of a crusade... Perhaps spreading spinach to children?"
+ icon_state = "holymelon"
+ inhand_icon_state = null
+ body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
+ armor_type = /datum/armor/watermelon
+ strip_delay = 60
+ equip_delay_other = 40
+ clothing_traits = list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED)
+ max_integrity = 15
+ var/decayed = FALSE
+
+/obj/item/clothing/suit/armor/durability/holymelon/fire_resist
+ resistance_flags = FIRE_PROOF
+ armor_type = /datum/armor/watermelon_fr
+
+/obj/item/clothing/suit/armor/durability/holymelon/Initialize(mapload)
+ . = ..()
+ if(decayed)
+ decay()
+ return
+
+ AddComponent(
+ /datum/component/anti_magic, \
+ antimagic_flags = MAGIC_RESISTANCE_HOLY, \
+ inventory_flags = ITEM_SLOT_OCLOTHING, \
+ charges = 1, \
+ drain_antimagic = CALLBACK(src, PROC_REF(drain_antimagic)), \
+ expiration = CALLBACK(src, PROC_REF(decay)) \
+ )
+
+/obj/item/clothing/suit/armor/durability/holymelon/proc/drain_antimagic(mob/user)
+ to_chat(user, span_warning("[src] looses a bit of its shimmer and glossiness..."))
+
+/obj/item/clothing/suit/armor/durability/holymelon/proc/decay()
+ take_damage(8, BRUTE, 0, 0)
+
+
+/obj/item/clothing/suit/armor/durability/barrelmelon
+ name = "barrelmelon"
+ desc = "An armour, made from barrelmelons. Reeks of ale, inspiring to courageous deeds. Or, perhaps, a bar brawl."
+ icon_state = "barrelmelon"
+ inhand_icon_state = null
+ body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
+ armor_type = /datum/armor/barrelmelon
+ strip_delay = 60
+ equip_delay_other = 40
+ clothing_traits = list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED)
+ max_integrity = 10
+
+/obj/item/clothing/suit/armor/durability/barrelmelon/fire_resist
+ resistance_flags = FIRE_PROOF
+ armor_type = /datum/armor/barrelmelon_fr
+
+/datum/armor/barrelmelon
+ melee = 25
+ bullet = 20
+ energy = 15
+ bomb = 10
+ fire = 0
+ acid = 35
+ wound = 10
+
+/datum/armor/barrelmelon_fr
+ melee = 25
+ bullet = 20
+ energy = 15
+ bomb = 10
+ fire = 20
+ acid = 40
+ wound = 10
diff --git a/code/modules/clothing/suits/bio.dm b/code/modules/clothing/suits/bio.dm
index 4ddfd31631d5c..d094cc5e528be 100644
--- a/code/modules/clothing/suits/bio.dm
+++ b/code/modules/clothing/suits/bio.dm
@@ -16,7 +16,7 @@
. = ..()
if(flags_inv & HIDEFACE)
AddComponent(/datum/component/clothing_fov_visor, FOV_90_DEGREES)
-
+
/datum/armor/head_bio_hood
bio = 100
fire = 30
diff --git a/code/modules/clothing/suits/costume.dm b/code/modules/clothing/suits/costume.dm
index 929e8d931d5ca..7fb3ff362cdee 100644
--- a/code/modules/clothing/suits/costume.dm
+++ b/code/modules/clothing/suits/costume.dm
@@ -511,6 +511,14 @@
name = "bronze suit"
desc = "A big and clanky suit made of bronze that offers no protection and looks very unfashionable. Nice."
icon_state = "clockwork_cuirass_old"
+ allowed = list(
+ /obj/item/tank/internals/emergency_oxygen,
+ /obj/item/tank/internals/plasmaman,
+ /obj/item/tank/jetpack/oxygen/captain,
+ /obj/item/storage/belt/holster,
+ //new
+ /obj/item/toy/clockwork_watch,
+ )
armor_type = /datum/armor/costume_bronze
/obj/item/clothing/suit/hooded/mysticrobe
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index 2b8fef5bb5b46..cc94b200969ba 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -225,6 +225,10 @@
blood_overlay_type = "coat"
body_parts_covered = CHEST|ARMS
allowed = list(
+ /obj/item/tank/internals/emergency_oxygen,
+ /obj/item/tank/internals/plasmaman,
+ /obj/item/boxcutter,
+ /obj/item/dest_tagger,
/obj/item/stamp,
/obj/item/storage/bag/mail,
/obj/item/universal_scanner,
@@ -234,7 +238,7 @@
/obj/item/clothing/suit/jacket/quartermaster
name = "quartermaster's overcoat"
- desc = "A luxury, brown double-breasted overcoat made from kangaroo skin. It's gold cuffs are linked and styled on the credits symbol. It makes you feel more important than you probably are."
+ desc = "A luxury, brown double-breasted overcoat made from kangaroo skin. Its gold cuffs are linked and styled on the credits symbol. It makes you feel more important than you probably are."
icon_state = "qm_coat"
blood_overlay_type = "coat"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
diff --git a/code/modules/clothing/suits/reactive_armour_dimensional_themes.dm b/code/modules/clothing/suits/reactive_armour_dimensional_themes.dm
index f66b23b1261f2..d41a30c293f6b 100644
--- a/code/modules/clothing/suits/reactive_armour_dimensional_themes.dm
+++ b/code/modules/clothing/suits/reactive_armour_dimensional_themes.dm
@@ -155,21 +155,25 @@
barricade = /obj/structure/holosign/barrier
/datum/armour_dimensional_theme/safe/meat
+ replace_wall = /turf/closed/wall/mineral/meat
material = /datum/material/meat
/// Dangerous themes can potentially impede the user as much as people pursuing them
/datum/armour_dimensional_theme/dangerous
/datum/armour_dimensional_theme/dangerous/clown
+ replace_wall = /turf/closed/wall/mineral/bananium
material = /datum/material/bananium
barricade = /obj/item/restraints/legcuffs/beartrap/prearmed
barricade_anchored = FALSE
/datum/armour_dimensional_theme/dangerous/radioactive
+ replace_wall = /turf/closed/wall/mineral/uranium
material = /datum/material/uranium
barricade = /obj/structure/statue/uranium/nuke
/datum/armour_dimensional_theme/dangerous/plasma
+ replace_wall = /turf/closed/wall/mineral/plasma
material = /datum/material/plasma
barricade = /obj/structure/statue/plasma/xeno
diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm
index eb1ccb3c51092..53026e974bf63 100644
--- a/code/modules/clothing/suits/wiz_robe.dm
+++ b/code/modules/clothing/suits/wiz_robe.dm
@@ -49,6 +49,17 @@
resistance_flags = FLAMMABLE
dog_fashion = /datum/dog_fashion/head/blue_wizard
+/obj/item/clothing/head/wizard/chanterelle
+ name = "chanterelle hat"
+ desc = "An oversized chanterelle with hollow out space to fit a head in. Kinda looks like wizard's hat."
+ icon_state = "chanterelle"
+ inhand_icon_state = "chanterellehat"
+ armor_type = /datum/armor/none
+ resistance_flags = FLAMMABLE
+
+/obj/item/clothing/head/wizard/chanterelle/fr
+ resistance_flags = FIRE_PROOF
+
/obj/item/clothing/head/wizard/marisa
name = "witch hat"
desc = "Strange-looking hat-wear. Makes you want to cast fireballs."
@@ -205,6 +216,63 @@
actions_types = list(/datum/action/item_action/stickmen)
+/obj/item/clothing/suit/wizrobe/durathread
+ name = "durathread robe"
+ desc = "A rather dull durathread robe; not quite as protective as a proper piece of armour, but much stylish."
+ icon_state = "durathread-fake"
+ inhand_icon_state = null
+ armor_type = /datum/armor/robe_durathread
+ allowed = list(
+ /obj/item/cultivator,
+ /obj/item/geneshears,
+ /obj/item/graft,
+ /obj/item/hatchet,
+ /obj/item/plant_analyzer,
+ /obj/item/reagent_containers/cup/beaker,
+ /obj/item/reagent_containers/cup/bottle,
+ /obj/item/reagent_containers/cup/tube,
+ /obj/item/reagent_containers/spray/pestspray,
+ /obj/item/reagent_containers/spray/plantbgone,
+ /obj/item/secateurs,
+ /obj/item/seeds,
+ /obj/item/storage/bag/plants,
+ )
+
+/datum/armor/robe_durathread
+ melee = 15
+ bullet = 5
+ laser = 25
+ energy = 30
+ bomb = 10
+ fire = 30
+ acid = 40
+
+/obj/item/clothing/suit/wizrobe/durathread/fire
+ name = "pyromancer robe"
+ desc = "A rather dull durathread robe; not quite as protective as an woven armour, but much stylish."
+ icon_state = "durathread-fire"
+
+/obj/item/clothing/suit/wizrobe/durathread/ice
+ name = "pyromancer robe"
+ desc = "A rather dull durathread robe; not quite as protective as an woven armour, but much stylish."
+ icon_state = "durathread-ice"
+
+/obj/item/clothing/suit/wizrobe/durathread/electric
+ name = "electromancer robe"
+ desc = "Doesn't actually conduit or isolate from electricity. Though it does have some durability on account of being made from durathread."
+ icon_state = "durathread-electric"
+
+/obj/item/clothing/suit/wizrobe/durathread/earth
+ name = "geomancer robe"
+ desc = "A rather dull durathread robe; not quite as protective as an woven armour, but much stylish."
+ icon_state = "durathread-earth"
+
+/obj/item/clothing/suit/wizrobe/durathread/necro
+ name = "necromancer robe"
+ desc = "A rather dull durathread robe; not quite as protective as an woven armour, but much stylish."
+ icon_state = "durathread-necro"
+
+
/obj/item/clothing/suit/wizrobe/paper/ui_action_click(mob/user, action)
stickmen()
diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm
index 2168177abcba1..6c6f9608e4b76 100644
--- a/code/modules/clothing/under/_under.dm
+++ b/code/modules/clothing/under/_under.dm
@@ -8,6 +8,8 @@
slot_flags = ITEM_SLOT_ICLOTHING
interaction_flags_click = NEED_DEXTERITY
armor_type = /datum/armor/clothing_under
+ supports_variations_flags = CLOTHING_DIGITIGRADE_MASK
+ digitigrade_greyscale_config_worn = /datum/greyscale_config/jumpsuit/worn_digi
equip_sound = 'sound/items/equip/jumpsuit_equip.ogg'
drop_sound = 'sound/items/handling/cloth_drop.ogg'
pickup_sound = 'sound/items/handling/cloth_pickup.ogg'
diff --git a/code/modules/clothing/under/accessories/_accessories.dm b/code/modules/clothing/under/accessories/_accessories.dm
index 91854bc386bf0..5b25418838ab6 100644
--- a/code/modules/clothing/under/accessories/_accessories.dm
+++ b/code/modules/clothing/under/accessories/_accessories.dm
@@ -89,7 +89,7 @@
atom_storage.close_all()
attach_to.clone_storage(atom_storage)
attach_to.atom_storage.set_real_location(src)
- attach_to.atom_storage.rustle_sound = TRUE // it's on the suit now
+ attach_to.atom_storage.do_rustle = TRUE // it's on the suit now
var/num_other_accessories = LAZYLEN(attach_to.attached_accessories)
layer = FLOAT_LAYER + clamp(attach_to.max_number_of_accessories - num_other_accessories, 0, 10)
diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm
index 748e697415c54..9ae1c7d63e366 100644
--- a/code/modules/clothing/under/color.dm
+++ b/code/modules/clothing/under/color.dm
@@ -224,6 +224,7 @@
greyscale_config_inhand_left = null
greyscale_config_inhand_right = null
greyscale_config_worn = null
+ digitigrade_greyscale_colors = "#3f3f3f"
can_adjust = FALSE
flags_1 = NONE
diff --git a/code/modules/clothing/under/costume.dm b/code/modules/clothing/under/costume.dm
index 080d1afe70ad5..ddf325a2201ab 100644
--- a/code/modules/clothing/under/costume.dm
+++ b/code/modules/clothing/under/costume.dm
@@ -129,6 +129,45 @@
body_parts_covered = CHEST|GROIN|ARMS
can_adjust = FALSE
+/obj/item/clothing/under/costume/yukata
+ name = "black yukata"
+ desc = "A comfortable black cotton yukata inspired by traditional designs, perfect for a non-formal setting."
+ icon_state = "yukata1"
+ body_parts_covered = CHEST|GROIN|ARMS
+ can_adjust = FALSE
+ supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
+
+/obj/item/clothing/under/costume/yukata/green
+ name = "green yukata"
+ desc = "A comfortable green cotton yukata inspired by traditional designs, perfect for a non-formal setting."
+ icon_state = "yukata2"
+
+/obj/item/clothing/under/costume/yukata/white
+ name = "white yukata"
+ desc = "A comfortable white cotton yukata inspired by traditional designs, perfect for a non-formal setting."
+ icon_state = "yukata3"
+
+/obj/item/clothing/under/costume/kimono
+ name = "black kimono"
+ desc = "A luxurious black silk kimono with traditional flair, ideal for elegant festive occasions."
+ icon_state = "kimono1"
+ inhand_icon_state = "yukata1"
+ body_parts_covered = CHEST|GROIN|ARMS
+ can_adjust = FALSE
+ supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
+
+/obj/item/clothing/under/costume/kimono/red
+ name = "red kimono"
+ desc = "A luxurious red silk kimono with traditional flair, ideal for elegant festive occasions."
+ icon_state = "kimono2"
+ inhand_icon_state = "kimono2"
+
+/obj/item/clothing/under/costume/kimono/purple
+ name = "purple kimono"
+ desc = "A luxurious purple silk kimono with traditional flair, ideal for elegant festive occasions."
+ icon_state = "kimono3"
+ inhand_icon_state = "kimono3"
+
/obj/item/clothing/under/costume/villain
name = "villain suit"
desc = "A change of wardrobe is necessary if you ever want to catch a real superhero."
diff --git a/code/modules/clothing/under/jobs/civilian/clown_mime.dm b/code/modules/clothing/under/jobs/civilian/clown_mime.dm
index 98571182f2928..55f0da88918b5 100644
--- a/code/modules/clothing/under/jobs/civilian/clown_mime.dm
+++ b/code/modules/clothing/under/jobs/civilian/clown_mime.dm
@@ -31,6 +31,7 @@
inhand_icon_state = "clown"
female_sprite_flags = FEMALE_UNIFORM_TOP_ONLY
can_adjust = FALSE
+ supports_variations_flags = CLOTHING_NO_VARIATION
/obj/item/clothing/under/rank/civilian/clown/Initialize(mapload)
. = ..()
diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm
index 8f1263fa3e2b2..81002bd8a9e2d 100644
--- a/code/modules/clothing/under/miscellaneous.dm
+++ b/code/modules/clothing/under/miscellaneous.dm
@@ -33,6 +33,7 @@
desc = "Groovy!"
icon_state = "psyche"
inhand_icon_state = "p_suit"
+ digitigrade_greyscale_colors = "#3f3f3f"
/obj/item/clothing/under/misc/vice_officer
name = "vice officer's jumpsuit"
diff --git a/code/modules/deathmatch/deathmatch_controller.dm b/code/modules/deathmatch/deathmatch_controller.dm
index 45b5f087c5bb5..0b098871624dc 100644
--- a/code/modules/deathmatch/deathmatch_controller.dm
+++ b/code/modules/deathmatch/deathmatch_controller.dm
@@ -76,6 +76,9 @@
return
switch (action)
if ("host")
+ if(!(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME))
+ tgui_alert(usr, "Deathmatch has been temporarily disabled by admins.")
+ return
if (lobbies[usr.ckey])
return
if(!SSticker.HasRoundStarted())
@@ -84,6 +87,9 @@
ui.close()
create_new_lobby(usr)
if ("join")
+ if(!(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME))
+ tgui_alert(usr, "Deathmatch has been temporarily disabled by admins.")
+ return
if (!lobbies[params["id"]])
return
var/datum/deathmatch_lobby/playing_lobby = find_lobby_by_user(usr.ckey)
diff --git a/code/modules/deathmatch/deathmatch_loadouts.dm b/code/modules/deathmatch/deathmatch_loadouts.dm
index 911e5bf6e1b15..3d49f9909c46c 100644
--- a/code/modules/deathmatch/deathmatch_loadouts.dm
+++ b/code/modules/deathmatch/deathmatch_loadouts.dm
@@ -8,7 +8,9 @@
/// If defined, using this outfit sets the targets species to it
var/datum/species/species_override
/// This outfit will grant these spells if applied
- var/list/granted_spells = list()
+ var/list/spells_to_add = list()
+ /// This outfit will grant these mutations if applied
+ var/list/mutations_to_add = list()
/datum/outfit/deathmatch_loadout/pre_equip(mob/living/carbon/human/user, visualsOnly = FALSE)
. = ..()
@@ -17,15 +19,20 @@
if(!isnull(species_override))
user.set_species(species_override)
+
else if (!isnull(user.dna.species.outfit_important_for_life)) //plasmamen get lit on fire and die
user.set_species(/datum/species/human)
- for(var/datum/action/act as anything in granted_spells)
+
+ for(var/datum/action/act as anything in spells_to_add)
var/datum/action/new_ability = new act(user)
if(istype(new_ability, /datum/action/cooldown/spell))
var/datum/action/cooldown/spell/new_spell = new_ability
- new_spell.spell_requirements = SPELL_REQUIRES_NO_ANTIMAGIC
+ new_spell.spell_requirements = NONE
new_ability.Grant(user)
+ for(var/mutation in mutations_to_add)
+ user.dna.add_mutation(mutation)
+
/datum/outfit/deathmatch_loadout/naked
name = "Deathmatch: Naked"
display_name = "Unarmed, Butt-naked"
@@ -349,7 +356,7 @@
suit = /obj/item/clothing/suit/hooded/explorer
shoes = /obj/item/clothing/shoes/workboots/mining
mask = /obj/item/clothing/mask/gas/explorer
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/mob_cooldown/dash,
)
@@ -392,7 +399,7 @@
suit = /datum/outfit/wizard::suit
head = /datum/outfit/wizard::head
shoes = /datum/outfit/wizard::shoes
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/spell/aoe/magic_missile,
/datum/action/cooldown/spell/forcewall,
/datum/action/cooldown/spell/jaunt/ethereal_jaunt,
@@ -406,7 +413,7 @@
suit = /obj/item/clothing/suit/wizrobe/red
head = /obj/item/clothing/head/wizard/red
mask = /obj/item/cigarette
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/spell/pointed/projectile/fireball,
/datum/action/cooldown/spell/smoke,
)
@@ -418,7 +425,7 @@
suit = /obj/item/clothing/suit/wizrobe/magusred
head = /obj/item/clothing/head/wizard/magus
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/spell/pointed/projectile/lightningbolt,
/datum/action/cooldown/spell/charged/beam/tesla,
)
@@ -431,7 +438,7 @@
species_override = /datum/species/skeleton
suit = /obj/item/clothing/suit/wizrobe/black
head = /obj/item/clothing/head/wizard/black
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/spell/touch/scream_for_me,
/datum/action/cooldown/spell/teleport/radius_turf/blink,
)
@@ -445,7 +452,7 @@
suit = /obj/item/clothing/suit/wizrobe/fake
head = /obj/item/clothing/head/wizard/fake
shoes = /obj/item/clothing/shoes/sandal
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/spell/conjure_item/spellpacket,
/datum/action/cooldown/spell/aoe/repulse/wizard,
)
@@ -459,7 +466,7 @@
suit = /obj/item/clothing/suit/wizrobe/marisa
head = /obj/item/clothing/head/wizard/marisa
shoes = /obj/item/clothing/shoes/sneakers/marisa
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/spell/chuuni_invocations,
/datum/action/cooldown/spell/pointed/projectile/spell_cards,
)
@@ -472,7 +479,7 @@
l_hand = /obj/item/mjollnir
suit = /obj/item/clothing/suit/wizrobe/magusblue
head = /obj/item/clothing/head/wizard/magus
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/spell/summonitem,
)
@@ -482,7 +489,7 @@
desc = "You feel severely under-leveled for this encounter..."
l_hand = null
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/spell/charge,
)
@@ -495,7 +502,7 @@
suit = /obj/item/clothing/suit/wizrobe/tape
head = /obj/item/clothing/head/wizard/tape
shoes = /obj/item/clothing/shoes/jackboots
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/spell/conjure_item/infinite_guns/gun,
/datum/action/cooldown/spell/aoe/knock,
)
@@ -510,7 +517,7 @@
suit = /obj/item/clothing/suit/costume/hawaiian
head = /obj/item/clothing/head/wizard/red
shoes = /obj/item/clothing/shoes/sneakers/marisa
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/spell/rod_form,
/datum/action/cooldown/spell/conjure/the_traps,
)
@@ -527,7 +534,7 @@
mask = /obj/item/clothing/mask/gas/clown_hat
back = /obj/item/storage/backpack/clown
shoes = /obj/item/clothing/shoes/clown_shoes
- granted_spells = null
+ spells_to_add = null
/datum/outfit/deathmatch_loadout/wizard/monkey
name = "Deathmatch: Monkey"
@@ -540,7 +547,7 @@
suit = null
head = /obj/item/clothing/head/wizard
shoes = null
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/spell/conjure/simian,
)
@@ -716,7 +723,7 @@
/obj/item/food/croissant/throwing = 2,
)
- granted_spells = list(
+ spells_to_add = list(
/datum/action/cooldown/spell/vow_of_silence,
/datum/action/cooldown/spell/conjure_item/invisible_box,
/datum/action/cooldown/spell/conjure/invisible_chair,
@@ -744,3 +751,414 @@
/obj/item/knife/butcher,
/obj/item/sharpener,
)
+
+//species
+
+/datum/outfit/deathmatch_loadout/humanity
+ name = "Deathmatch: Human Species"
+ display_name = "Humanity"
+ desc = "The most ambitious and successful species. Or just the most rapacious, depending on who you ask."
+ species_override = /datum/species/human
+
+ head = /obj/item/clothing/head/soft/black
+ glasses = /obj/item/clothing/glasses/sunglasses
+ ears = /obj/item/radio/headset/headset_com
+ neck = /obj/item/clothing/neck/large_scarf/blue
+ //suit
+ id_trim = /datum/id_trim/job/bridge_assistant // half tider half command
+ id = /obj/item/card/id/advanced/chameleon
+ uniform = /obj/item/clothing/under/trek/command/next
+ l_pocket = /obj/item/gun/energy/e_gun/mini
+ r_pocket = /obj/item/extinguisher/mini
+ gloves = /obj/item/clothing/gloves/fingerless
+ belt = /obj/item/storage/belt/utility/full/inducer
+ shoes = /obj/item/clothing/shoes/sneakers/black
+
+// Lizard: Desert, Soldier, Trash
+
+/datum/outfit/deathmatch_loadout/lizardkind
+ name = "Deathmatch: Lizard Species"
+ display_name = "Lizardfolk"
+ desc = "They may be heavily discrimated against, they may be most often seen doing menial activities, but at least they, uh, uhh..."
+ species_override = /datum/species/lizard
+
+ head = /obj/item/clothing/head/soft/purple
+ id_trim = /datum/id_trim/job/janitor
+ id = /obj/item/card/id/advanced/chameleon
+ uniform = /obj/item/clothing/under/rank/civilian/janitor
+ gloves = /obj/item/clothing/gloves/color/black
+ belt = /obj/item/storage/belt/janitor/full
+ shoes = /obj/item/clothing/shoes/chameleon/noslip
+ r_hand = /obj/item/mop/advanced
+ back = /obj/item/storage/backpack
+ backpack_contents = list(
+ /obj/item/toy/plush/lizard_plushie/green,
+ // reclaiming lizard racism
+ /obj/item/reagent_containers/cup/glass/bottle/lizardwine,
+ /obj/item/tailclub,
+ /obj/item/melee/chainofcommand/tailwhip,
+ /obj/item/reagent_containers/cup/glass/coffee
+ )
+
+/datum/outfit/deathmatch_loadout/mothman
+ name = "Deathmatch: Moth Species"
+ display_name = "Mothmen"
+ desc = "An innocent and fluffy visage hides the combat ability of a particularly hairy kitten."
+ species_override = /datum/species/moth
+
+ head = /obj/item/clothing/head/utility/head_mirror
+ glasses = /obj/item/clothing/glasses/hud/health
+ suit = /obj/item/clothing/suit/hooded/wintercoat/medical
+ suit_store = /obj/item/flashlight/pen/paramedic
+ id_trim = /datum/id_trim/job/medical_doctor
+ id = /obj/item/card/id/advanced/chameleon
+ uniform = /obj/item/clothing/under/rank/medical/scrubs/blue
+ belt = /obj/item/storage/belt/medical/paramedic
+ shoes = /obj/item/clothing/shoes/sneakers/white
+ l_hand = /obj/item/circular_saw
+
+ back = /obj/item/storage/backpack/medic
+
+ backpack_contents = list(
+ /obj/item/toy/plush/moth,
+ /obj/item/storage/medkit/brute,
+ /obj/item/storage/medkit/fire,
+ /obj/item/statuebust/hippocratic
+ )
+
+// Roboticist??
+/datum/outfit/deathmatch_loadout/ethereal
+ name = "Deathmatch: Ethereal Species"
+ display_name = "Etherealkind"
+ desc = "Prepare to be SHOCKED as you are reminded of this species's existence."
+ species_override = /datum/species/ethereal
+
+ head = /obj/item/clothing/head/soft/black
+ id_trim = /datum/id_trim/job/roboticist
+ id = /obj/item/card/id/advanced/chameleon
+ suit = /obj/item/clothing/suit/toggle/labcoat/roboticist
+ suit_store = /datum/id_trim/job/roboticist
+ uniform = /obj/item/clothing/under/rank/rnd/roboticist
+ l_pocket = /obj/item/assembly/flash
+ belt = /obj/item/storage/belt/utility/full
+ shoes = /obj/item/clothing/shoes/sneakers/black
+
+ back = /obj/item/storage/backpack/science
+
+ backpack_contents = list(
+ /obj/item/dnainjector/shock,
+ /obj/item/etherealballdeployer,
+ )
+
+/datum/outfit/deathmatch_loadout/plasmamen
+ name = "Deathmatch: Plasmaman Species"
+ display_name = "Plasmamen"
+ desc = "Burn baby burn!"
+ species_override = /datum/species/plasmaman
+
+ head = /obj/item/clothing/head/helmet/space/plasmaman/atmospherics
+ suit = /obj/item/clothing/suit/hazardvest
+ suit_store = /obj/item/flashlight
+ uniform = /obj/item/clothing/under/plasmaman/atmospherics
+ id_trim = /datum/id_trim/job/atmospheric_technician
+ id = /obj/item/card/id/advanced/chameleon
+ belt = /obj/item/storage/belt/utility/atmostech
+ gloves = /obj/item/clothing/gloves/color/plasmaman/atmos
+ shoes = /obj/item/clothing/shoes/workboots
+ r_pocket = /obj/item/tank/internals/plasmaman/belt/full
+
+ back = /obj/item/storage/backpack/industrial
+
+ backpack_contents = list(
+ /obj/item/toy/plush/plasmamanplushie,
+ /obj/item/tank/internals/plasma,
+ /obj/item/tank/internals/plasmaman,
+ /obj/item/stack/sheet/mineral/uranium/half,
+ /obj/item/stack/sheet/mineral/plasma/thirty,
+ /obj/item/reagent_containers/condiment/milk,
+ /obj/item/storage/medkit/fire,
+ /obj/item/reagent_containers/syringe/plasma
+ )
+
+/datum/outfit/deathmatch_loadout/felinid
+ name = "Deathmatch: Felinid Species"
+ display_name = "Felinids"
+ desc = "Strictly inferior to humans in every way."
+ species_override = /datum/species/human/felinid
+
+ head = /obj/item/clothing/head/soft/rainbow
+ glasses = null
+ ears = /obj/item/radio/headset
+ neck = /obj/item/clothing/neck/petcollar
+ //suit
+ uniform = /obj/item/clothing/under/color/rainbow
+ l_pocket = /obj/item/toy/cattoy
+ r_pocket = /obj/item/restraints/handcuffs/fake
+ gloves = /obj/item/clothing/gloves/color/rainbow
+ belt = /obj/item/melee/curator_whip
+ shoes = /obj/item/clothing/shoes/sneakers/rainbow
+
+//spleef
+
+/datum/outfit/deathmatch_loadout/lattice_battles
+ name = "Deathmatch: Lattice loadout"
+ display_name = "Lattice Battler"
+ desc = "Snip the catwalks under everyone else and win! You're pacifist, so no punching."
+
+ uniform = /obj/item/clothing/under/pants/jeans
+ suit = /obj/item/clothing/suit/costume/wellworn_shirt/graphic
+ back = /obj/item/storage/backpack
+ r_hand = /obj/item/wirecutters
+
+ backpack_contents = list(
+ /obj/item/stack/rods/fifty,
+ /obj/item/stack/rods/fifty,
+ /obj/item/stack/rods/fifty,
+ /obj/item/stack/rods/fifty,
+ )
+
+// We don't want them to just punch each other to death
+
+/datum/outfit/deathmatch_loadout/lattice_battles/pre_equip(mob/living/carbon/human/user, visualsOnly)
+ . = ..()
+ ADD_TRAIT(user, TRAIT_PACIFISM, REF(src))
+
+// Ragnarok: Fight between religions!
+
+/datum/outfit/deathmatch_loadout/cultish/pre_equip(mob/living/carbon/human/user, visualsOnly)
+ . = ..()
+ ADD_TRAIT(user, TRAIT_ACT_AS_CULTIST, REF(src))
+ user.AddElement(/datum/element/cult_halo, initial_delay = 0 SECONDS)
+ user.AddElement(/datum/element/cult_eyes, initial_delay = 0 SECONDS)
+
+// Cultist Invoker, has all the balanced cult gear
+
+/datum/outfit/deathmatch_loadout/cultish/invoker
+ name = "Deathmatch: Cultist Invoker"
+ display_name = "Cultist Invoker"
+ desc = "Prove Nar'sie's superiority with your well-balanced set of equipment."
+ //species_override = /datum/species/plasmaman
+
+ head = /obj/item/clothing/head/hooded/cult_hoodie/cult_shield
+ glasses = /obj/item/clothing/glasses/hud/health/night/cultblind
+ suit = /obj/item/clothing/suit/hooded/cultrobes/cult_shield // the dreaded return!
+ suit_store = /obj/item/melee/cultblade
+ uniform = /obj/item/clothing/under/color/black
+ id_trim = null
+ belt = /obj/item/melee/cultblade/dagger
+ l_pocket = /obj/item/flashlight/flare/culttorch
+ r_pocket = /obj/item/flashlight/flare/culttorch
+ gloves = /obj/item/clothing/gloves/color/black
+ shoes = /obj/item/clothing/shoes/cult/alt
+ l_hand = /obj/item/shield/mirror // the dreaded return!!
+
+ back = /obj/item/storage/backpack/cultpack
+
+ backpack_contents = list(
+ /obj/item/restraints/legcuffs/bola/cult,
+ /obj/item/reagent_containers/cup/beaker/unholywater,
+ )
+
+// Cultist Artificer, gets all the balanced cult magicks
+
+/datum/outfit/deathmatch_loadout/cultish/artificer
+ name = "Deathmatch: Cultist Artificer"
+ display_name = "Cultist Artificer"
+ desc = "Prove Nar'sie's superiority with your well-balanced blood magicks."
+ //species_override = /datum/species/plasmaman
+
+ head = /obj/item/clothing/head/hooded/cult_hoodie/berserkerhood
+ neck = /obj/item/clothing/neck/heretic_focus/crimson_medallion
+ suit = /obj/item/clothing/suit/hooded/cultrobes/berserker
+ suit_store = /obj/item/melee/sickly_blade/cursed
+ uniform = /obj/item/clothing/under/color/red
+ id_trim = null
+ belt = /obj/item/melee/cultblade/dagger
+ l_pocket = /obj/item/flashlight/flare/culttorch
+ r_pocket = /obj/item/flashlight/flare/culttorch
+ gloves = /obj/item/clothing/gloves/color/red
+ shoes = /obj/item/clothing/shoes/cult
+ l_hand = null
+
+ back = /obj/item/storage/backpack/cultpack
+
+ backpack_contents = list(
+ /obj/item/cult_shift,
+ /obj/item/reagent_containers/cup/beaker/unholywater,
+ /obj/item/reagent_containers/cup/beaker/unholywater,
+ /obj/item/reagent_containers/cup/beaker/unholywater,
+ )
+
+ spells_to_add = list(
+ /datum/action/innate/cult/blood_spell/horror,
+ /datum/action/innate/cult/blood_spell/horror,
+ /datum/action/innate/cult/blood_spell/stun,
+ /datum/action/innate/cult/blood_spell/stun,
+ /datum/action/innate/cult/blood_spell/manipulation,
+ )
+
+/datum/outfit/deathmatch_loadout/cultish/artificer/post_equip(mob/living/carbon/human/user, visualsOnly)
+ . = ..()
+ var/datum/action/innate/cult/blood_spell/manipulation/magick = locate() in user
+ magick.charges = 300
+
+/datum/outfit/deathmatch_loadout/heresy
+ /// Grants the effects of these knowledges to the DMer
+ var/list/knowledge_to_grant
+
+/datum/outfit/deathmatch_loadout/heresy/pre_equip(mob/living/carbon/human/user, visualsOnly)
+ . = ..()
+ ADD_TRAIT(user, TRAIT_ACT_AS_HERETIC, REF(src))
+ user.AddElement(/datum/element/leeching_walk)
+
+ // Creates the knowledge as an isolated datum inside the target, allowing passive knowledges to work still.
+ for(var/datum/heretic_knowledge/knowhow as anything in knowledge_to_grant)
+ knowhow = new knowhow(user)
+ knowhow.on_gain(user, null)
+
+// Heretic Warrior
+
+// Has spells of Ash, Blade, and Rust. Overall aggressive
+
+/datum/outfit/deathmatch_loadout/heresy/warrior
+ name = "Deathmatch: Heretic Warrior"
+ display_name = "Heretic Warrior"
+ desc = "Prove the furious strength of the Mansus."
+ //species_override = /datum/species/plasmaman
+
+ head = /obj/item/clothing/head/hooded/cult_hoodie/eldritch
+ neck = /obj/item/clothing/neck/heretic_focus
+ suit = /obj/item/clothing/suit/hooded/cultrobes/eldritch
+ suit_store = /obj/item/melee/sickly_blade/dark
+ uniform = /obj/item/clothing/under/color/darkgreen
+ id_trim = null
+ belt = /obj/item/melee/sickly_blade/ash
+ gloves = null
+ shoes = /obj/item/clothing/shoes/sandal
+ l_pocket = /obj/item/flashlight/lantern/jade/on
+ r_pocket = /obj/item/melee/rune_carver
+ l_hand = null
+
+ back = /obj/item/storage/backpack
+
+ backpack_contents = list(
+ /obj/item/reagent_containers/cup/beaker/eldritch,
+ /obj/item/reagent_containers/cup/beaker/eldritch,
+ /obj/item/eldritch_potion/wounded,
+ /obj/item/eldritch_potion/wounded,
+ )
+
+ // I mean is it really that bad if they don't even know half this stuff is added to them.
+ // It's like, forbidden knowledge. It fits with the mansus theme - great excuse for poor design!
+ knowledge_to_grant = list(
+ /datum/heretic_knowledge/duel_stance,
+ /datum/heretic_knowledge/blade_grasp,
+ /datum/heretic_knowledge/blade_dance,
+ /datum/heretic_knowledge/blade_upgrade/blade,
+ )
+
+ spells_to_add = list(
+ /datum/action/cooldown/spell/touch/mansus_grasp,
+ /datum/action/cooldown/spell/realignment,
+ /datum/action/cooldown/spell/pointed/projectile/furious_steel,
+ /datum/action/cooldown/spell/charged/beam/fire_blast,
+ /datum/action/cooldown/spell/aoe/fiery_rebirth,
+ /datum/action/cooldown/spell/cone/staggered/entropic_plume,
+ /datum/action/cooldown/spell/pointed/rust_construction,
+ )
+
+// Heretic Scribe
+
+// Has spells of Void, Moon, and Cosmos. Overall defensive/mobile
+
+/datum/outfit/deathmatch_loadout/heresy/scribe
+ name = "Deathmatch: Heretic Scribe"
+ display_name = "Heretic Scribe"
+ desc = "Reveal the forgotten knowledge of the Mansus."
+
+ head = /obj/item/clothing/head/helmet/chaplain/witchunter_hat
+ mask = /obj/item/clothing/mask/madness_mask
+ neck = /obj/item/clothing/neck/eldritch_amulet
+ suit = /obj/item/clothing/suit/hooded/cultrobes/void
+ suit_store = /obj/item/melee/sickly_blade
+ uniform = /obj/item/clothing/under/costume/gamberson/military
+ id_trim = null
+ belt = /obj/item/storage/belt/unfathomable_curio
+ gloves = null
+ shoes = /obj/item/clothing/shoes/winterboots/ice_boots
+ l_pocket = /obj/item/ammo_box/strilka310/lionhunter
+ r_pocket = /obj/item/codex_cicatrix
+
+ back = /obj/item/gun/ballistic/rifle/lionhunter // for his neutral b, he wields a gun
+
+ belt_contents = list(
+ /obj/item/heretic_labyrinth_handbook,
+ /obj/item/heretic_labyrinth_handbook,
+ /obj/item/eldritch_potion/crucible_soul,
+ /obj/item/clothing/neck/heretic_focus/moon_amulet = 3,
+ )
+
+ knowledge_to_grant = list(
+ /datum/heretic_knowledge/cosmic_grasp,
+ /datum/heretic_knowledge/moon_grasp,
+ /datum/heretic_knowledge/mark/moon_mark,
+ )
+
+ spells_to_add = list(
+ /datum/action/cooldown/spell/touch/mansus_grasp,
+ /datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash,
+ /datum/action/cooldown/spell/pointed/projectile/star_blast,
+ /datum/action/cooldown/spell/touch/star_touch,
+ /datum/action/cooldown/spell/pointed/void_phase,
+ /datum/action/cooldown/spell/aoe/void_pull,
+ )
+
+// Chaplain! No spells (other than smoke), but strong armor and weapons, and immune to others' spells
+
+/datum/outfit/deathmatch_loadout/holy_crusader
+ name = "Deathmatch: Holy Crusader"
+ display_name = "Holy Crusader"
+ desc = "Smite the heathens!!"
+ //species_override = /datum/species/plasmaman
+
+ head = /obj/item/clothing/head/helmet/chaplain
+ neck = /obj/item/camera/spooky
+ suit = /obj/item/clothing/suit/chaplainsuit/armor/templar
+ suit_store = /obj/item/book/bible/booze
+ uniform = /obj/item/clothing/under/rank/civilian/chaplain
+ id_trim = null
+ belt = /obj/item/nullrod // choose any!
+ gloves = /obj/item/clothing/gloves/plate
+ shoes = /obj/item/clothing/shoes/plate
+ l_pocket = /obj/item/flashlight/lantern/on
+ r_pocket = /obj/item/reagent_containers/cup/glass/bottle/holywater
+ l_hand = /obj/item/shield/buckler
+
+ back = /obj/item/claymore/weak // or don't
+
+ spells_to_add = list(
+ /datum/action/cooldown/spell/smoke/lesser
+ )
+ mutations_to_add = list(
+ /datum/mutation/human/medieval,
+ /datum/mutation/human/lay_on_hands, // useless, but fun
+ )
+
+// Rat'var Apostate
+
+/datum/outfit/deathmatch_loadout/clock_cult
+ name = "Deathmatch: Clock Cultist"
+ display_name = "Rat'var Apostate"
+ desc = "You're in a fight between the servants of gods, and yours is dead. Good luck?"
+ //species_override = /datum/species/plasmaman
+
+ head = /obj/item/clothing/head/costume/bronze
+ suit = /obj/item/clothing/suit/costume/bronze
+ suit_store = /obj/item/toy/clockwork_watch
+ uniform = /obj/item/clothing/under/chameleon
+ id_trim = null
+ belt = /obj/item/brass_spear
+ gloves = /obj/item/clothing/gloves/tinkerer
+ shoes = /obj/item/clothing/shoes/bronze
+ l_pocket = /obj/item/reagent_containers/cup/beaker/synthflesh // they used to turn their dmg into tox with a spell. close enough
+ r_pocket = /obj/item/reagent_containers/cup/beaker/synthflesh
diff --git a/code/modules/deathmatch/deathmatch_mapping.dm b/code/modules/deathmatch/deathmatch_mapping.dm
index 9f006e1524295..c517d4d2449a4 100644
--- a/code/modules/deathmatch/deathmatch_mapping.dm
+++ b/code/modules/deathmatch/deathmatch_mapping.dm
@@ -14,6 +14,10 @@
/area/deathmatch/teleport //Prevent access to cross-z teleportation in the map itself (no wands of safety/teleportation scrolls). Cordons should prevent same-z teleportations outside of the arena.
area_flags = /area/deathmatch::area_flags & ~NOTELEPORT
+/area/deathmatch/teleport/fullbright
+ static_lighting = FALSE
+ base_lighting_alpha = 255
+
// for the illusion of a moving train
/turf/open/chasm/true/no_smooth/fake_motion_sand
name = "air"
@@ -25,3 +29,12 @@
/turf/open/chasm/true/no_smooth/fake_motion_sand/fast
icon_state = "sandmovingfast"
base_icon_state = "sandmovingfast"
+
+// fakeout
+
+/turf/open/chasm/true/fakeout
+ name = /turf/open/floor/wood::name
+ // desc kept the same
+ icon_state = /turf/open/floor/wood::icon_state
+ base_icon_state = /turf/open/floor/wood::base_icon_state
+ icon = /turf/open/floor/wood::icon
diff --git a/code/modules/deathmatch/deathmatch_maps.dm b/code/modules/deathmatch/deathmatch_maps.dm
index 6a8a245abb795..243172ea9dadb 100644
--- a/code/modules/deathmatch/deathmatch_maps.dm
+++ b/code/modules/deathmatch/deathmatch_maps.dm
@@ -1,6 +1,7 @@
-/datum/lazy_template/deathmatch //deathmatch maps that have any possibility of the walls being destroyed should use indestructible walls, because baseturf moment
+/datum/lazy_template/deathmatch
map_dir = "_maps/deathmatch"
place_on_top = TRUE
+ turf_reservation_type = /datum/turf_reservation/turf_not_baseturf
/// Map UI Name
var/name
/// Map Description
@@ -16,16 +17,6 @@
/// whether we are currently being loaded by a lobby
var/template_in_use = FALSE
-
-/datum/lazy_template/deathmatch/ragecage
- name = "Ragecage"
- desc = "Fun for the whole family, the classic ragecage."
- max_players = 4
- automatic_gameend_time = 4 MINUTES // its a 10x10 cage what are you guys doing in there
- allowed_loadouts = list(/datum/outfit/deathmatch_loadout/assistant)
- map_name = "ragecage"
- key = "ragecage"
-
/datum/lazy_template/deathmatch/maintenance
name = "Maint Mania"
desc = "Dark maintenance tunnels, floor pills, improvised weaponry and a bloody beatdown. Welcome to assistant utopia."
@@ -50,49 +41,6 @@
map_name = "meta_brig"
key = "meta_brig"
-/datum/lazy_template/deathmatch/shooting_range
- name = "Shooting Range"
- desc = "A simple room with a bunch of wooden barricades."
- max_players = 6
- allowed_loadouts = list(
- /datum/outfit/deathmatch_loadout/operative/ranged,
- /datum/outfit/deathmatch_loadout/operative/melee,
- )
- map_name = "shooting_range"
- key = "shooting_range"
-
-/datum/lazy_template/deathmatch/securing
- name = "SecuRing"
- desc = "Presenting the Security Ring, ever wanted to shoot people with disablers? Well now you can."
- max_players = 4
- allowed_loadouts = list(/datum/outfit/deathmatch_loadout/securing_sec)
- map_name = "secu_ring"
- key = "secu_ring"
-
-/datum/lazy_template/deathmatch/instagib
- name = "Instagib"
- desc = "EVERYONE GETS AN INSTAKILL RIFLE!"
- max_players = 8
- allowed_loadouts = list(/datum/outfit/deathmatch_loadout/assistant/instagib)
- map_name = "instagib"
- key = "instagib"
-
-/datum/lazy_template/deathmatch/mech_madness
- name = "Mech Madness"
- desc = "Do you hate mechs? Yeah? Dont care! Go fight eachother!"
- max_players = 4
- allowed_loadouts = list(/datum/outfit/deathmatch_loadout/operative)
- map_name = "mech_madness"
- key = "mech_madness"
-
-/datum/lazy_template/deathmatch/sniper_elite
- name = "Sniper Elite"
- desc = "Sound of gunfire and screaming people make my day"
- max_players = 8
- allowed_loadouts = list(/datum/outfit/deathmatch_loadout/operative/sniper)
- map_name = "sniper_elite"
- key = "sniper_elite"
-
/datum/lazy_template/deathmatch/meatower
name = "Meat Tower"
desc = "There can only be one chef in this kitchen"
@@ -204,5 +152,45 @@
map_name = "finaldestination"
key = "finaldestination"
+/datum/lazy_template/deathmatch/species_warfare
+ name = "Species Warfare"
+ desc = "Choose your favorite species and prove its superiority against all the other, lamer species. And also anyone else of your own."
+ max_players = 8
+ allowed_loadouts = list(
+ /datum/outfit/deathmatch_loadout/humanity,
+ /datum/outfit/deathmatch_loadout/lizardkind,
+ /datum/outfit/deathmatch_loadout/mothman,
+ /datum/outfit/deathmatch_loadout/ethereal,
+ /datum/outfit/deathmatch_loadout/plasmamen,
+ /datum/outfit/deathmatch_loadout/felinid,
+ )
+ map_name = "species_warfare"
+ key = "species_warfare"
+
+/datum/lazy_template/deathmatch/lattice_battles
+ name = "Lattice Battles"
+ desc = "Tired of fisticuffs all the time? Just snip the catwalk underneath instead!"
+ max_players = 8
+ allowed_loadouts = list(
+ /datum/outfit/deathmatch_loadout/lattice_battles,
+ )
+ map_name = "lattice_battles"
+ key = "lattice_battles"
+
+/datum/lazy_template/deathmatch/ragnarok
+ name = "Ragnarok"
+ desc = "Cultists, heretics, and chaplains all duking it out in the jungle to retrieve the McGuffin."
+ max_players = 8
+ allowed_loadouts = list(
+ /datum/outfit/deathmatch_loadout/cultish/invoker,
+ /datum/outfit/deathmatch_loadout/cultish/artificer,
+ /datum/outfit/deathmatch_loadout/heresy/warrior,
+ /datum/outfit/deathmatch_loadout/heresy/scribe,
+ /datum/outfit/deathmatch_loadout/holy_crusader,
+ /datum/outfit/deathmatch_loadout/clock_cult,
+ )
+ map_name = "ragnarok"
+ key = "ragnarok"
+
/datum/turf_reservation/indestructible_plating
turf_type = /turf/open/indestructible/plating //a little hacky but i guess it has to be done
diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm
index a6841d3975444..1cf617ce4513e 100644
--- a/code/modules/error_handler/error_handler.dm
+++ b/code/modules/error_handler/error_handler.dm
@@ -101,6 +101,12 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
// The proceeding mess will almost definitely break if error messages are ever changed
var/list/splitlines = splittext(E.desc, "\n")
var/list/desclines = list()
+ var/list/state_stack = GLOB.lua_state_stack
+ var/is_lua_call = length(state_stack)
+ var/list/lua_stacks = list()
+ if(is_lua_call)
+ for(var/level in 1 to state_stack.len)
+ lua_stacks += list(splittext(DREAMLUAU_GET_TRACEBACK(level), "\n"))
if(LAZYLEN(splitlines) > ERROR_USEFUL_LEN) // If there aren't at least three lines, there's no info
for(var/line in splitlines)
if(LAZYLEN(line) < 3 || findtext(line, "source file:") || findtext(line, "usr.loc:"))
@@ -110,13 +116,14 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
desclines.Add(usrinfo)
usrinfo = null
continue // Our usr info is better, replace it
-
if(copytext(line, 1, 3) != " ")//3 == length(" ") + 1
desclines += (" " + line) // Pad any unpadded lines, so they look pretty
else
desclines += line
if(usrinfo) //If this info isn't null, it hasn't been added yet
desclines.Add(usrinfo)
+ if(is_lua_call)
+ SSlua.log_involved_runtime(E, desclines, lua_stacks)
if(silencing)
desclines += " (This error will now be silenced for [DisplayTimeText(configured_error_silence_time)])"
if(GLOB.error_cache)
diff --git a/code/modules/events/anomaly/anomaly_ectoplasm.dm b/code/modules/events/anomaly/anomaly_ectoplasm.dm
index 6e45752b02a77..8c37f8f1017c3 100644
--- a/code/modules/events/anomaly/anomaly_ectoplasm.dm
+++ b/code/modules/events/anomaly/anomaly_ectoplasm.dm
@@ -8,7 +8,7 @@
typepath = /datum/round_event/anomaly/anomaly_ectoplasm
min_players = 30
max_occurrences = 2
- weight = 4 //Rare because of it's wacky and silly nature
+ weight = 4 //Rare because of its wacky and silly nature
category = EVENT_CATEGORY_ANOMALIES
min_wizard_trigger_potency = 0
max_wizard_trigger_potency = 3
diff --git a/code/modules/events/aurora_caelus.dm b/code/modules/events/aurora_caelus.dm
index 0acb0ad9781a7..875b8c0dcf23a 100644
--- a/code/modules/events/aurora_caelus.dm
+++ b/code/modules/events/aurora_caelus.dm
@@ -17,10 +17,12 @@
start_when = 21
end_when = 80
-/datum/round_event/aurora_caelus/announce()
+/datum/round_event/aurora_caelus/announce(fake)
priority_announce("[station_name()]: A harmless cloud of ions is approaching your station, and will exhaust their energy battering the hull. Nanotrasen has approved a short break for all employees to relax and observe this very rare event. During this time, starlight will be bright but gentle, shifting between quiet green and blue colors. Any staff who would like to view these lights for themselves may proceed to the area nearest to them with viewing ports to open space. We hope you enjoy the lights.",
sound = 'sound/misc/notice2.ogg',
sender_override = "Nanotrasen Meteorology Division")
+ if (fake)
+ return
for(var/V in GLOB.player_list)
var/mob/M = V
if((M.client.prefs.read_preference(/datum/preference/toggle/sound_midi)) && is_station_level(M.z))
@@ -31,6 +33,8 @@
/datum/round_event/aurora_caelus/start()
if(!prob(1) && !check_holidays(APRIL_FOOLS))
return
+
+ var/list/human_blacklist = list()
for(var/area/station/service/kitchen/affected_area in GLOB.areas)
var/obj/machinery/oven/roast_ruiner = locate() in affected_area
if(roast_ruiner)
@@ -40,10 +44,13 @@
message_admins("Aurora Caelus event caused an oven to ignite at [ADMIN_VERBOSEJMP(ruined_roast)].")
log_game("Aurora Caelus event caused an oven to ignite at [loc_name(ruined_roast)].")
announce_to_ghosts(roast_ruiner)
- for(var/mob/living/carbon/human/seymour as anything in GLOB.human_list)
- if(seymour.mind && istype(seymour.mind.assigned_role, /datum/job/cook))
- seymour.say("My roast is ruined!!!", forced = "ruined roast")
- seymour.emote("scream")
+ for(var/mob/living/carbon/human/seymour in viewers(roast_ruiner, 7))
+ if (seymour in human_blacklist)
+ continue
+ human_blacklist += seymour
+ if(seymour.mind && istype(seymour.mind.assigned_role, /datum/job/cook))
+ seymour.say("My roast is ruined!!!", forced = "ruined roast")
+ seymour.emote("scream")
/datum/round_event/aurora_caelus/tick()
if(activeFor % 8 != 0)
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index 5e53e22261ee8..fdbf46d48c6d0 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -60,7 +60,7 @@
announce_to_ghosts(origin_machine)
/datum/round_event/brand_intelligence/tick()
- if(!origin_machine || QDELETED(origin_machine) || origin_machine.shut_up || origin_machine.wires.is_all_cut()) //if the original vending machine is missing or has it's voice switch flipped
+ if(!origin_machine || QDELETED(origin_machine) || origin_machine.shut_up || origin_machine.wires.is_all_cut()) //if the original vending machine is missing or has its voice switch flipped
for(var/obj/machinery/vending/saved in infected_machines)
saved.shoot_inventory = FALSE
if(origin_machine)
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index 8fcc05f345bfc..3856bf550b4a3 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -102,7 +102,7 @@
var/list/afflicted = list()
/datum/round_event/disease_outbreak/announce(fake)
- if(isnull(illness_type))
+ if(!illness_type)
var/list/virus_candidates = list(
/datum/disease/anxiety,
/datum/disease/beesease,
diff --git a/code/modules/events/ghost_role/blob.dm b/code/modules/events/ghost_role/blob.dm
index 8e83351f5c045..469a222e3fa13 100644
--- a/code/modules/events/ghost_role/blob.dm
+++ b/code/modules/events/ghost_role/blob.dm
@@ -29,9 +29,9 @@
/datum/round_event/ghost_role/blob/spawn_role()
if(!GLOB.blobstart.len)
return MAP_ERROR
- var/icon/blob_icon = icon('icons/mob/nonhuman-player/blob.dmi', icon_state = "blob_core")
+ var/icon/blob_icon = icon('icons/mob/nonhuman-player/blob_tall.dmi', icon_state = "blob_core")
blob_icon.Blend("#9ACD32", ICON_MULTIPLY)
- blob_icon.Blend(icon('icons/mob/nonhuman-player/blob.dmi', "blob_core_overlay"), ICON_OVERLAY)
+ blob_icon.Blend(icon('icons/mob/nonhuman-player/blob_tall.dmi', "blob_core_overlay"), ICON_OVERLAY)
var/image/blob_image = image(blob_icon)
var/mob/chosen_one = SSpolling.poll_ghost_candidates(check_jobban = ROLE_BLOB, role = ROLE_BLOB, alert_pic = blob_image, role_name_text = role_name, amount_to_pick = 1, chat_text_border_icon = blob_image)
if(isnull(chosen_one))
diff --git a/code/modules/events/ghost_role/sentience.dm b/code/modules/events/ghost_role/sentience.dm
index 4017361dba51a..092813008458a 100644
--- a/code/modules/events/ghost_role/sentience.dm
+++ b/code/modules/events/ghost_role/sentience.dm
@@ -3,6 +3,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list(
/mob/living/basic/butterfly,
/mob/living/basic/carp/pet/cayenne,
/mob/living/basic/chicken,
+ /mob/living/basic/crab,
/mob/living/basic/cow,
/mob/living/basic/goat,
/mob/living/basic/lizard,
@@ -18,6 +19,7 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list(
/mob/living/simple_animal/bot/secbot/beepsky,
/mob/living/simple_animal/hostile/retaliate/goose/vomit,
/mob/living/basic/bear/snow/misha,
+ /mob/living/basic/mining/lobstrosity/juvenile,
)))
/datum/round_event_control/sentience
diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm
index eeed75d2299eb..17d976a6351fe 100644
--- a/code/modules/events/holiday/xmas.dm
+++ b/code/modules/events/holiday/xmas.dm
@@ -48,7 +48,6 @@
icon = 'icons/effects/landmarks_static.dmi'
icon_state = "x2"
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
/// Christmas tree, no presents included.
var/festive_tree = /obj/structure/flora/tree/pine/xmas
diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm
index 7afc67266102c..686adf1e5d3f6 100644
--- a/code/modules/events/ion_storm.dm
+++ b/code/modules/events/ion_storm.dm
@@ -106,7 +106,7 @@
var/ionallergysev = pick_list(ION_FILE, "ionallergysev")
//Species, for when the AI has to commit genocide. Plural.
var/ionspecies = pick_list(ION_FILE, "ionspecies")
- //Abstract concepts for the AI to decide on it's own definition of.
+ //Abstract concepts for the AI to decide on its own definition of.
var/ionabstract = pick_list(ION_FILE, "ionabstract")
//Foods. Drinks aren't included due to grammar; if you want to add drinks, make a new set
//of possible laws for best effect. Unless you want the crew having to drink hamburgers.
diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm
index 0ca7800ee22d1..472a74e5443b0 100644
--- a/code/modules/events/portal_storm.dm
+++ b/code/modules/events/portal_storm.dm
@@ -50,7 +50,7 @@
storm_appearances = list()
for(var/offset in 0 to SSmapping.max_plane_offset)
var/mutable_appearance/storm = mutable_appearance('icons/obj/machines/engine/energy_ball.dmi', "energy_ball_fast", FLY_LAYER)
- SET_PLANE_W_SCALAR(storm, ABOVE_GAME_PLANE, offset)
+ SET_PLANE_W_SCALAR(storm, GAME_PLANE, offset)
storm.color = COLOR_VIBRANT_LIME
storm_appearances += storm
diff --git a/code/modules/events/space_vines/vine_event.dm b/code/modules/events/space_vines/vine_event.dm
index c4c805b1667e2..ce9881c990797 100644
--- a/code/modules/events/space_vines/vine_event.dm
+++ b/code/modules/events/space_vines/vine_event.dm
@@ -39,7 +39,7 @@
for(var/area/station/hallway/area in GLOB.areas)
for(var/turf/open/floor in area.get_turfs_from_all_zlevels())
- if(floor.Enter(vine))
+ if(!isopenspaceturf(floor) && floor.Enter(vine))
turfs += floor
qdel(vine)
@@ -64,7 +64,7 @@
/datum/event_admin_setup/set_location/spacevine/apply_to_event(datum/round_event/spacevine/event)
event.override_turf = chosen_turf
-
+
/datum/event_admin_setup/multiple_choice/spacevine
input_text = "Select starting mutations."
min_choices = 0
@@ -88,7 +88,7 @@
type_choices += text2path(choice)
event.mutations_overridden = TRUE
event.override_mutations = type_choices
-
+
/datum/event_admin_setup/input_number/spacevine_potency
input_text = "Set vine's potency (effects mutation frequency + max severity)"
max_value = 100
diff --git a/code/modules/events/stray_cargo.dm b/code/modules/events/stray_cargo.dm
index 274d45aa7ca8c..0be1138f59a59 100644
--- a/code/modules/events/stray_cargo.dm
+++ b/code/modules/events/stray_cargo.dm
@@ -183,5 +183,5 @@
///Apply the syndicate pod skin
/datum/round_event/stray_cargo/syndicate/make_pod()
var/obj/structure/closet/supplypod/S = new
- S.setStyle(STYLE_SYNDICATE)
+ S.setStyle(/datum/pod_style/syndicate)
return S
diff --git a/code/modules/events/supermatter_surge.dm b/code/modules/events/supermatter_surge.dm
index 6c790c84f8d7e..c7fb6f969d82b 100644
--- a/code/modules/events/supermatter_surge.dm
+++ b/code/modules/events/supermatter_surge.dm
@@ -91,7 +91,8 @@
end_when = rand(SURGE_DURATION_MIN, SURGE_DURATION_MAX)
/datum/round_event/supermatter_surge/announce(fake)
- priority_announce("The Crystal Integrity Monitoring System has detected unusual atmospheric properties in the supermatter chamber, energy output from the supermatter crystal has increased significantly. Engineering intervention is required to stabilize the engine.", "Class [surge_class] Supermatter Surge Alert", 'sound/machines/engine_alert3.ogg')
+ var/class_to_announce = fake ? pick(1, 2, 3, 4) : surge_class
+ priority_announce("The Crystal Integrity Monitoring System has detected unusual atmospheric properties in the supermatter chamber, energy output from the supermatter crystal has increased significantly. Engineering intervention is required to stabilize the engine.", "Class [class_to_announce] Supermatter Surge Alert", 'sound/machines/engine_alert3.ogg')
/datum/round_event/supermatter_surge/start()
engine.bullet_energy = surge_class + SURGE_BULLET_ENERGY_ADDITION
@@ -99,7 +100,6 @@
sm_gas.heat_power_generation = (surge_class * SURGE_POWER_GENERATION_MODIFIER) - 1
sm_gas.heat_modifier = (surge_class * SURGE_HEAT_MODIFIER) - 1
-
/datum/round_event/supermatter_surge/end()
engine.bullet_energy = initial(engine.bullet_energy)
sm_gas.powerloss_inhibition = initial(sm_gas.powerloss_inhibition)
diff --git a/code/modules/experisci/experiment/experiments.dm b/code/modules/experisci/experiment/experiments.dm
index 9587b6209e271..c9f4f1b3d1bd7 100644
--- a/code/modules/experisci/experiment/experiments.dm
+++ b/code/modules/experisci/experiment/experiments.dm
@@ -82,7 +82,7 @@
/datum/experiment/ordnance/explosive/hydrogenbomb
name = "Hydrogen Explosives"
- description = "Combustion of Hydrogen and it's derivatives can be very powerful. Capture any tank explosion with a Doppler Array and publish the data in a paper. Only Hydrogen or Tritium Fires are allowed."
+ description = "Combustion of Hydrogen and its derivatives can be very powerful. Capture any tank explosion with a Doppler Array and publish the data in a paper. Only Hydrogen or Tritium Fires are allowed."
gain = list(15,40,60)
target_amount = list(50,75,150)
experiment_proper = TRUE
@@ -387,7 +387,7 @@
if (organ.type == target_species.get_mutant_organ_type_for_slot(organ.slot))
continue
else
- if ((organ.type in target_species.mutant_organs) || (organ.type in target_species.external_organs))
+ if ((organ.type in target_species.mutant_organs))
continue
return TRUE
return FALSE
diff --git a/code/modules/experisci/experiment/handlers/experiment_handler.dm b/code/modules/experisci/experiment/handlers/experiment_handler.dm
index 622d84551a285..bb0cc2fb0a5e3 100644
--- a/code/modules/experisci/experiment/handlers/experiment_handler.dm
+++ b/code/modules/experisci/experiment/handlers/experiment_handler.dm
@@ -355,7 +355,7 @@
)
.["experiments"] += list(data)
-/datum/component/experiment_handler/ui_act(action, params)
+/datum/component/experiment_handler/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if (.)
return
diff --git a/code/modules/explorer_drone/control_console.dm b/code/modules/explorer_drone/control_console.dm
index 8cc8854f27dc8..78451dd71a168 100644
--- a/code/modules/explorer_drone/control_console.dm
+++ b/code/modules/explorer_drone/control_console.dm
@@ -97,7 +97,7 @@
icon_screen = initial(icon_screen)
. = ..()
-/obj/machinery/computer/exodrone_control_console/ui_act(action, list/params)
+/obj/machinery/computer/exodrone_control_console/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/explorer_drone/exodrone.dm b/code/modules/explorer_drone/exodrone.dm
index 5754ccc4a5e18..e34bb581e01b1 100644
--- a/code/modules/explorer_drone/exodrone.dm
+++ b/code/modules/explorer_drone/exodrone.dm
@@ -342,6 +342,7 @@ GLOBAL_LIST_EMPTY(exodrone_launchers)
/// Exploration drone launcher
/obj/machinery/exodrone_launcher
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "exploration drone launcher"
desc = "A launch pad designed to send exploration drones into the great beyond."
icon = 'icons/obj/exploration.dmi'
diff --git a/code/modules/explorer_drone/scanner_array.dm b/code/modules/explorer_drone/scanner_array.dm
index 6317ee273bed4..0cbeb684a8c6e 100644
--- a/code/modules/explorer_drone/scanner_array.dm
+++ b/code/modules/explorer_drone/scanner_array.dm
@@ -121,7 +121,7 @@ GLOBAL_LIST_INIT(scan_conditions,init_scan_conditions())
. = ..()
.["all_bands"] = GLOB.exoscanner_bands
-/obj/machinery/computer/exoscanner_control/ui_act(action, list/params)
+/obj/machinery/computer/exoscanner_control/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/fishing/admin.dm b/code/modules/fishing/admin.dm
index 0a9cfc9d51dd9..ba5c29a7fd172 100644
--- a/code/modules/fishing/admin.dm
+++ b/code/modules/fishing/admin.dm
@@ -48,7 +48,7 @@ ADMIN_VERB(fishing_calculator, R_DEBUG, "Fishing Calculator", "A calculator... f
if(hook_type)
temporary_rod.set_slot(new hook_type(temporary_rod), ROD_SLOT_HOOK)
if(line_type)
- temporary_rod.set_slot(new line_type(temporary_rod), ROD_SLOT_HOOK)
+ temporary_rod.set_slot(new line_type(temporary_rod), ROD_SLOT_LINE)
var/result_table = list()
var/modified_table = spot.get_modified_fish_table(temporary_rod,user)
diff --git a/code/modules/fishing/aquarium/aquarium.dm b/code/modules/fishing/aquarium/aquarium.dm
index cd05d0bd34baf..f7698a80b43fb 100644
--- a/code/modules/fishing/aquarium/aquarium.dm
+++ b/code/modules/fishing/aquarium/aquarium.dm
@@ -13,9 +13,9 @@
name = "aquarium"
desc = "A vivarium in which aquatic fauna and flora are usually kept and displayed."
density = TRUE
- anchored = TRUE
+ anchored = FALSE
- icon = 'icons/obj/aquarium/tanks.dmi'
+ icon = 'icons/obj/structures/aquarium/tanks.dmi'
icon_state = "aquarium_map"
integrity_failure = 0.3
@@ -35,17 +35,17 @@
var/last_feeding
/// Can fish reproduce in this quarium.
- var/allow_breeding = FALSE
+ var/allow_breeding = TRUE
//This is the area where fish can swim
var/aquarium_zone_min_px = 2
var/aquarium_zone_max_px = 31
var/aquarium_zone_min_py = 10
- var/aquarium_zone_max_py = 24
+ var/aquarium_zone_max_py = 28
var/list/fluid_types = list(AQUARIUM_FLUID_SALTWATER, AQUARIUM_FLUID_FRESHWATER, AQUARIUM_FLUID_SULPHWATEVER, AQUARIUM_FLUID_AIR)
- var/panel_open = TRUE
+ var/panel_open = FALSE
///Current layers in use by aquarium contents
var/list/used_layers = list()
@@ -64,7 +64,7 @@
RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(on_attacked))
create_reagents(6, SEALED_CONTAINER)
RegisterSignal(reagents, COMSIG_REAGENTS_NEW_REAGENT, PROC_REF(start_autofeed))
- AddComponent(/datum/component/plumbing/aquarium)
+ AddComponent(/datum/component/plumbing/aquarium, start = anchored)
if(current_beauty)
AddElement(/datum/element/beauty, current_beauty)
ADD_KEEP_TOGETHER(src, INNATE_TRAIT)
@@ -323,7 +323,7 @@
.["maxTemperature"] = max_fluid_temp
.["fluidTypes"] = fluid_types
-/obj/structure/aquarium/ui_act(action, params)
+/obj/structure/aquarium/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -390,15 +390,25 @@
#undef AQUARIUM_BORDERS_LAYER
#undef AQUARIUM_BELOW_GLASS_LAYER
+/obj/structure/aquarium/lawyer
+ anchored = TRUE
+
/obj/structure/aquarium/lawyer/Initialize(mapload)
. = ..()
new /obj/item/aquarium_prop/sand(src)
new /obj/item/aquarium_prop/seaweed(src)
- new /obj/item/fish/goldfish/gill(src)
+ if(prob(85))
+ new /obj/item/fish/goldfish/gill(src)
+ reagents.add_reagent(/datum/reagent/consumable/nutriment, 2)
+ else
+ new /obj/item/fish/three_eyes/gill(src)
+ reagents.add_reagent(/datum/reagent/toxin/mutagen, 2) //three eyes goldfish feed on mutagen.
+
- reagents.add_reagent(/datum/reagent/consumable/nutriment, 2)
+/obj/structure/aquarium/prefilled
+ anchored = TRUE
/obj/structure/aquarium/prefilled/Initialize(mapload)
. = ..()
diff --git a/code/modules/fishing/aquarium/aquarium_kit.dm b/code/modules/fishing/aquarium/aquarium_kit.dm
index 1161648f7d15f..e8bf5e9104090 100644
--- a/code/modules/fishing/aquarium/aquarium_kit.dm
+++ b/code/modules/fishing/aquarium/aquarium_kit.dm
@@ -2,7 +2,7 @@
/obj/item/fish_feed
name = "fish feed can"
desc = "A refillable can that dispenses nutritious fish feed."
- icon = 'icons/obj/aquarium/supplies.dmi'
+ icon = 'icons/obj/structures/aquarium/supplies.dmi'
icon_state = "fish_feed"
w_class = WEIGHT_CLASS_TINY
@@ -21,15 +21,15 @@
desc = "A resizable case keeping the fish inside in stasis."
icon = 'icons/obj/storage/case.dmi'
icon_state = "fishbox"
-
+ w_class = WEIGHT_CLASS_SMALL
inhand_icon_state = "syringe_kit"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
- storage_type = /datum/storage/fish_case
+ storage_type = /datum/storage/fish_case/adjust_size
/obj/item/storage/fish_case/Initialize(mapload)
- ADD_TRAIT(src, TRAIT_FISH_SAFE_STORAGE, TRAIT_GENERIC) // Before populate so fish instatiates in ready container already
- return ..()
+ . = ..()
+ AddElement(/datum/element/fish_safe_storage)
/obj/item/storage/fish_case/PopulateContents()
var/fish_type = get_fish_type()
@@ -58,7 +58,7 @@
name = "ominous fish case"
/obj/item/storage/fish_case/syndicate/get_fish_type()
- return pick(/obj/item/fish/donkfish, /obj/item/fish/emulsijack, /obj/item/fish/jumpercable)
+ return pick(/obj/item/fish/donkfish, /obj/item/fish/emulsijack, /obj/item/fish/jumpercable, /obj/item/fish/chainsawfish)
/obj/item/storage/fish_case/tiziran
name = "imported fish case"
@@ -91,10 +91,17 @@
for(var/obj/item/fish/fish as anything in contents)
fish.set_status(FISH_DEAD)
+/obj/item/storage/fish_case/bluespace
+ name = "bluespace fish case"
+ icon_state = "fishbox_bluespace"
+ desc = "An improved fish case to keep large fish in stasis in a compact little space."
+ w_class = WEIGHT_CLASS_NORMAL
+ storage_type = /datum/storage/fish_case
+
/obj/item/aquarium_kit
name = "DIY Aquarium Construction Kit"
desc = "Everything you need to build your own aquarium. Raw materials sold separately."
- icon = 'icons/obj/aquarium/supplies.dmi'
+ icon = 'icons/obj/structures/aquarium/supplies.dmi'
icon_state = "construction_kit"
w_class = WEIGHT_CLASS_TINY
@@ -106,7 +113,7 @@
/obj/item/aquarium_prop
name = "generic aquarium prop"
desc = "very boring"
- icon = 'icons/obj/aquarium/supplies.dmi'
+ icon = 'icons/obj/structures/aquarium/supplies.dmi'
w_class = WEIGHT_CLASS_TINY
var/layer_mode = AQUARIUM_LAYER_MODE_BOTTOM
@@ -143,6 +150,7 @@
/obj/item/storage/box/aquarium_props
name = "aquarium props box"
desc = "All you need to make your aquarium look good."
+ illustration = "fish"
/obj/item/storage/box/aquarium_props/PopulateContents()
for(var/prop_type in subtypesof(/obj/item/aquarium_prop))
diff --git a/code/modules/fishing/aquarium/aquarium_upgrades.dm b/code/modules/fishing/aquarium/aquarium_upgrades.dm
index c73e6e9d230ae..df3f961ceed24 100644
--- a/code/modules/fishing/aquarium/aquarium_upgrades.dm
+++ b/code/modules/fishing/aquarium/aquarium_upgrades.dm
@@ -4,7 +4,7 @@
name = "Aquarium Upgrade"
desc = "An upgrade."
- icon = 'icons/obj/aquarium/supplies.dmi'
+ icon = 'icons/obj/structures/aquarium/supplies.dmi'
icon_state = "construction_kit"
/// What kind of aquarium can accept this upgrade. Strict type check, no subtypes.
var/upgrade_from_type = /obj/structure/aquarium
diff --git a/code/modules/fishing/aquarium/fish_analyzer.dm b/code/modules/fishing/aquarium/fish_analyzer.dm
index 2038f1960f903..3d01479ef5a2f 100644
--- a/code/modules/fishing/aquarium/fish_analyzer.dm
+++ b/code/modules/fishing/aquarium/fish_analyzer.dm
@@ -20,14 +20,8 @@
greyscale_config_worn = /datum/greyscale_config/fish_analyzer_worn
///The color of the case. Used by grayscale configs and update_overlays()
var/case_color
- /**
- * The radial menu shown when analyzing aquariums. Having a persistent one allows us
- * to update it whenever fish come and go, and is also required since we have a select callback
- * used to check right clicks for scanning traits instead of status.
- */
- var/datum/radial_menu/persistent/fish_menu
- /// A cached list of the current choices for the aforedefined radial menu.
- var/list/radial_choices
+ ///the item we have scanned
+ var/datum/weakref/scanned_item
/obj/item/fish_analyzer/Initialize(mapload)
case_color = rgb(rand(16, 255), rand(16, 255), rand(16, 255))
@@ -47,11 +41,6 @@
register_item_context()
update_appearance()
-/obj/item/fish_analyzer/Destroy()
- if(fish_menu)
- QDEL_NULL(fish_menu)
- radial_choices = null
- return ..()
/obj/item/fish_analyzer/examine(mob/user)
. = ..()
@@ -68,190 +57,83 @@
. += case
. += emissive_appearance(icon, "fish_analyzer_emissive", src)
-/obj/item/fish_analyzer/add_item_context(obj/item/source, list/context, atom/target)
- if (isfish(target))
- context[SCREENTIP_CONTEXT_LMB] = "Analyze status"
- context[SCREENTIP_CONTEXT_RMB] = "Analyze traits"
- return CONTEXTUAL_SCREENTIP_SET
- else if(isaquarium(target))
- context[SCREENTIP_CONTEXT_LMB] = "Open radial menu"
- return CONTEXTUAL_SCREENTIP_SET
- return NONE
-
/obj/item/fish_analyzer/interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(!isfish(target) && !isaquarium(target))
return NONE
if(!user.can_read(src) || user.is_blind())
return ITEM_INTERACT_BLOCKING
- if(isfish(target))
- balloon_alert(user, "analyzing stats")
- user.visible_message(span_notice("[user] analyzes [target]."), span_notice("You analyze [target]."))
- analyze_status(target, user)
- else if(istype(target, /obj/structure/aquarium))
- scan_aquarium(target, user)
- return ITEM_INTERACT_SUCCESS
-
-/obj/item/fish_analyzer/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
- if(!isfish(interacting_with))
- return NONE
- if(!user.can_read(src) || user.is_blind())
- return ITEM_INTERACT_BLOCKING
-
- balloon_alert(user, "analyzing traits")
- analyze_traits(interacting_with, user)
- return ITEM_INTERACT_SUCCESS
-
-///Instantiates the radial menu, populates the list of choices, shows it and register signals on the aquarium.
-/obj/item/fish_analyzer/proc/scan_aquarium(obj/structure/aquarium/aquarium, mob/user)
- if(fish_menu)
- balloon_alert(user, "already scanning")
- return
- var/list/fishes = aquarium.get_fishes()
- if(!length(fishes))
- balloon_alert(user, "no fish to scan")
- return
- radial_choices = list()
- for(var/obj/item/fish/fish as anything in fishes)
- radial_choices(fish)
- fish_menu = show_radial_menu_persistent(user, aquarium, radial_choices, select_proc = CALLBACK(src, PROC_REF(choice_selected), user, aquarium), tooltips = TRUE, custom_check = CALLBACK(src, PROC_REF(can_select_fish), user, aquarium))
- RegisterSignal(aquarium, COMSIG_ATOM_ABSTRACT_ENTERED, PROC_REF(on_aquarium_entered))
- RegisterSignal(aquarium, COMSIG_ATOM_ABSTRACT_EXITED, PROC_REF(on_aquarium_exited))
- RegisterSignal(aquarium, COMSIG_QDELETING, PROC_REF(delete_radial))
-
-///Instantiates a radial menu choice datum for the current fish and adds it to the list of choices.
-/obj/item/fish_analyzer/proc/radial_choices(obj/item/fish/fish)
- var/datum/radial_menu_choice/menu_choice = new
- menu_choice.name = fish.name
- menu_choice.info = "[fish.status == FISH_ALIVE ? "Alive" : "Dead"]\n[fish.size] cm\n[fish.weight] g\nProgenitors: [fish.progenitors]\nRight-click to analyze traits"
- var/mutable_appearance/fish_appearance = new(fish)
- fish_appearance.layer = FLOAT_LAYER
- fish_appearance.plane = FLOAT_PLANE
- menu_choice.image = fish_appearance
- radial_choices[fish] = menu_choice
-
-///Called when the user has selected a choice. If it's a right click, analyze the traits, else the status
-/obj/item/fish_analyzer/proc/choice_selected(mob/user, obj/structure/aquarium/aquarium, obj/item/fish/choice, params)
- if(!choice || !can_select_fish(user, aquarium))
- delete_radial(aquarium)
- return
- var/is_right_clicking = LAZYACCESS(params2list(params), RIGHT_CLICK)
- user.visible_message(span_notice("[user] analyzes [choice] inside [aquarium]."), span_notice("You analyze [choice] inside [aquarium]."))
- if(is_right_clicking)
- analyze_traits(choice, user)
- else
- analyze_status(choice, user)
-
-///Whether the item should continue to show its radial menu or delete it.
-/obj/item/fish_analyzer/proc/can_select_fish(mob/user, obj/structure/aquarium/aquarium)
- if(!user.is_holding(src) || !user?.CanReach(aquarium) || IS_DEAD_OR_INCAP(user))
- delete_radial(aquarium)
- return FALSE
- return TRUE
-
-///Called when something enters the aquarium. If it's a fish, update the choices.
-/obj/item/fish_analyzer/proc/on_aquarium_entered(obj/structure/aquarium/source, atom/movable/arrived)
- SIGNAL_HANDLER
- if(isfish(arrived))
- radial_choices(arrived)
- fish_menu.change_choices(radial_choices, tooltips = TRUE, animate = TRUE)
-
-///Called when something exits the aquarium. If it's a fish, update the choices.
-/obj/item/fish_analyzer/proc/on_aquarium_exited(obj/structure/aquarium/source, atom/movable/gone)
- SIGNAL_HANDLER
- if(!isfish(gone))
- return
- radial_choices -= gone
- if(!length(radial_choices))
- delete_radial(source)
- return
- fish_menu.change_choices(radial_choices, tooltips = TRUE, animate = TRUE)
+ if(isfish(target) || istype(target, /obj/structure/aquarium))
+ scanned_item = WEAKREF(target)
+ SEND_SIGNAL(src, COMSIG_FISH_ANALYZER_ANALYZE_STATUS, target, user)
+ ui_interact(user)
+ return ITEM_INTERACT_SUCCESS
-///Unregisters signals, delete the radial menu, unsets the choices.
-/obj/item/fish_analyzer/proc/delete_radial(obj/structure/aquarium/source)
- SIGNAL_HANDLER
- UnregisterSignal(source, list(COMSIG_ATOM_ABSTRACT_EXITED, COMSIG_ATOM_ABSTRACT_ENTERED, COMSIG_QDELETING))
- QDEL_NULL(fish_menu)
- radial_choices = null
-
-/**
- * Called when a fish or a menu choice is left-clicked.
- * This returns the fish's status, size, weight, feed type, hunger, breeding timeout.
- */
-/obj/item/fish_analyzer/proc/analyze_status(obj/item/fish/fish, mob/user)
-
- // the final list of strings to render
- var/render_list = list()
-
- var/fish_status = fish.status == FISH_DEAD ? span_alert("Deceased") : "[PERCENT(fish.health/initial(fish.health))]% healthy"
-
- render_list += "[span_info("Analyzing status for [fish]:")]\nOverrall status: [fish_status]\n"
- render_list += "Size: [fish.size] cm - Weight: [fish.weight] g\n"
- render_list += "Required feed type: [initial(fish.food.name)]\n"
- render_list += "Safe temperature: [fish.required_temperature_min] - [fish.required_temperature_max]K"
- if(isaquarium(fish.loc))
- var/obj/structure/aquarium/aquarium = fish.loc
- if(!ISINRANGE(aquarium.fluid_temp, fish.required_temperature_min, fish.required_temperature_max))
- render_list += span_alert("(OUT OF RANGE)")
- render_list += "\n"
- render_list += "Safe fluid type: [fish.required_fluid_type]"
- if(isaquarium(fish.loc))
- var/obj/structure/aquarium/aquarium = fish.loc
- if(!compatible_fluid_type(fish.required_fluid_type, aquarium.fluid_type))
- render_list += span_alert("(IN UNSAFE FLUID)")
- render_list += ""
-
- if(fish.status != FISH_DEAD)
- render_list += "\n"
- if(!HAS_TRAIT(fish, TRAIT_FISH_NO_HUNGER))
- var/hunger = PERCENT(min((world.time - fish.last_feeding) / fish.feeding_frequency, 1))
- var/hunger_string = "[hunger]%"
- switch(hunger)
- if(0 to 60)
- hunger_string = span_info(hunger_string)
- if(60 to 90)
- hunger_string = span_warning(hunger_string)
- if(90 to 100)
- hunger_string = span_alert(hunger_string)
- render_list += "Hunger: [hunger_string]\n"
- var/time_left = round(max(fish.breeding_wait - world.time, 0)/10)
- render_list += "Time until it can breed: [time_left] seconds"
-
- to_chat(user, examine_block(jointext(render_list, "")), type = MESSAGE_TYPE_INFO)
-
- SEND_SIGNAL(src, COMSIG_FISH_ANALYZER_ANALYZE_STATUS, fish, user)
-
-/**
- * Called when a fish or a menu choice is left-clicked.
- * This returns the fish's progenitors, traits and their inheritability.
- */
-/obj/item/fish_analyzer/proc/analyze_traits(obj/item/fish/fish, mob/user)
-
- // the final list of strings to render
- var/render_list = list()
-
- render_list += "[span_info("Analyzing traits for [fish]:")]\nProgenitor species: [fish.progenitors]\n"
-
- if(!length(fish.fish_traits))
- render_list += "This fish has no trait to speak of...\n"
- else
- render_list += "Traits:\n"
- for(var/trait_type in fish.fish_traits)
- var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
- var/tooltipped_trait = span_tooltip(trait.catalog_description, trait.name)
- render_list += "[tooltipped_trait] - Inheritabilities: [trait.inheritability]% - [trait.diff_traits_inheritability]%\n"
-
- var/evolution_len = length(fish.evolution_types)
- if(!evolution_len)
- render_list += "This fish has no evolution to speak of..."
- for(var/index in 1 to evolution_len)
- var/datum/fish_evolution/evolution = GLOB.fish_evolutions[fish.evolution_types[index]]
- var/evolution_name = evolution.name
- var/evolution_tooltip = evolution.get_evolution_tooltip()
- if(evolution_tooltip)
- evolution_name = span_tooltip(evolution_tooltip, evolution_name)
- render_list += "[evolution_name] - Base Probability: [evolution.probability]%"
- if(index != evolution_len)
- render_list += "\n"
+ return NONE
- to_chat(user, examine_block(jointext(render_list, "")), type = MESSAGE_TYPE_INFO)
+/obj/item/fish_analyzer/ui_interact(mob/user, datum/tgui/ui)
+ if(isnull(scanned_item?.resolve()))
+ balloon_alert(user, "no specimen data!")
+ return TRUE
+
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "FishAnalyzer")
+ ui.open()
+
+/obj/item/fish_analyzer/ui_static_data(mob/user)
+ var/list/data = list()
+ var/atom/scanned_object = scanned_item?.resolve()
+ data["fish_list"] = list()
+ data["fish_scanned"] = FALSE
+
+ if(isfish(scanned_object))
+ data["fish_scanned"] = TRUE
+ return extract_fish_info(data, scanned_object)
+
+ var/obj/structure/aquarium/aquarium = scanned_object
+ for(var/obj/item/fish/fishie in aquarium)
+ extract_fish_info(data, fishie, aquarium)
+
+ return data
+
+/obj/item/fish_analyzer/proc/extract_fish_info(list/data, obj/item/fish/fishie, obj/structure/aquarium/aquarium)
+ var/list/fish_traits = list()
+ var/list/fish_evolutions = list()
+
+ for(var/evolution_type in fishie.evolution_types)
+ var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evolution_type]
+ var/obj/item/evolution_fish = evolution.new_fish_type
+ fish_evolutions += list(list(
+ "evolution_name" = evolution.name,
+ "evolution_icon" = evolution_fish::icon,
+ "evolution_icon_state" = evolution_fish::icon_state,
+ "evolution_probability" = evolution.probability,
+ "evolution_conditions" = evolution.conditions_note,
+ ))
+
+ for(var/trait_type in fishie.fish_traits)
+ var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
+ fish_traits += list(list("trait_name" = trait.name, "trait_desc" = trait.catalog_description, "trait_inherit" = trait.diff_traits_inheritability))
+
+ data["fish_list"] += list(list(
+ "fish_name" = fishie.name,
+ "fish_icon" = fishie::icon,
+ "fish_icon_state" = fishie::icon_state,
+ "fish_health" = fishie.status == FISH_DEAD ? 0 : PERCENT(fishie.health/initial(fishie.health)),
+ "fish_size" = fishie.size,
+ "fish_weight" = fishie.weight,
+ "fish_food" = fishie.food::name,
+ "fish_food_color" = fishie.food::color,
+ "fish_min_temp" = fishie.required_temperature_min,
+ "fish_max_temp" = fishie.required_temperature_max,
+ "fish_hunger" = HAS_TRAIT(fishie, TRAIT_FISH_NO_HUNGER) ? 0 : PERCENT(min((world.time - fishie.last_feeding) / fishie.feeding_frequency, 1)),
+ "fish_fluid_compatible" = aquarium ? compatible_fluid_type(fishie.required_fluid_type, aquarium.fluid_type) : null,
+ "fish_fluid_type" = fishie.required_fluid_type,
+ "fish_breed_timer" = round(max(fishie.breeding_wait - world.time, 0) / 10),
+ "fish_traits" = fish_traits,
+ "fish_evolutions" = fish_evolutions,
+ "fish_suitable_temp" = aquarium ? ISINRANGE(aquarium.fluid_temp, fishie.required_temperature_min, fishie.required_temperature_max) : null
+ ))
+
+ return data
diff --git a/code/modules/fishing/bait.dm b/code/modules/fishing/bait.dm
index b67298fab9fea..8eb8911a864ed 100644
--- a/code/modules/fishing/bait.dm
+++ b/code/modules/fishing/bait.dm
@@ -3,13 +3,16 @@
desc = "there's a lot of them in there, getting them out takes a while though"
icon = 'icons/obj/fishing.dmi'
icon_state = "bait_can"
+ base_icon_state = "bait_can"
w_class = WEIGHT_CLASS_SMALL
/// Tracking until we can take out another bait item
COOLDOWN_DECLARE(bait_removal_cooldown)
/// What bait item it produces
- var/bait_type
+ var/obj/item/bait_type = /obj/item/food/bait
/// Time between bait retrievals
- var/cooldown_time = 10 SECONDS
+ var/cooldown_time = 5 SECONDS
+ /// How many uses does it have left.
+ var/uses_left = 20
/obj/item/bait_can/attack_self(mob/user, modifiers)
. = ..()
@@ -17,19 +20,62 @@
if(fresh_bait)
user.put_in_hands(fresh_bait)
+/obj/item/bait_can/examine(mob/user)
+ . = ..()
+ . += span_info("It[uses_left ? " has got [uses_left] [bait_type::name] left" : "'s empty"].")
+
+/obj/item/bait_can/update_icon_state()
+ . = ..()
+ icon_state = base_icon_state
+ if(uses_left <= initial(uses_left))
+ if(!uses_left)
+ icon_state = "[icon_state]_empty"
+ else
+ icon_state = "[icon_state]_open"
+
/obj/item/bait_can/proc/retrieve_bait(mob/user)
+ if(!uses_left)
+ user.balloon_alert(user, "empty")
+ return
if(!COOLDOWN_FINISHED(src, bait_removal_cooldown))
- user.balloon_alert(user, "wait a bit") //I can't think of generic ic reason.
+ user.balloon_alert(user, "wait a bit")
return
COOLDOWN_START(src, bait_removal_cooldown, cooldown_time)
+ update_appearance()
+ uses_left--
return new bait_type(src)
/obj/item/bait_can/worm
name = "can o' worm"
- desc = "this can got worms."
+ desc = "This can got worms."
bait_type = /obj/item/food/bait/worm
/obj/item/bait_can/worm/premium
name = "can o' worm deluxe"
- desc = "this can got fancy worms."
+ desc = "This can got fancy worms."
bait_type = /obj/item/food/bait/worm/premium
+
+/obj/item/bait_can/super_baits
+ name = "can o' super-baits"
+ desc = "This can got the nectar of god."
+ bait_type = /obj/item/food/bait/doughball/synthetic/super
+ uses_left = 12
+
+
+/// Helper proc that checks if a bait matches identifier from fav/disliked bait list
+/proc/is_matching_bait(obj/item/bait, identifier)
+ if(ispath(identifier)) //Just a path
+ return istype(bait, identifier)
+ if(islist(identifier))
+ var/list/special_identifier = identifier
+ switch(special_identifier["Type"])
+ if("Foodtype")
+ var/obj/item/food/food_bait = bait
+ return istype(food_bait) && food_bait.foodtypes & special_identifier["Value"]
+ if("Reagent")
+ return bait.reagents?.has_reagent(special_identifier["Value"], special_identifier["Amount"], check_subtypes = TRUE)
+ else
+ CRASH("Unknown bait identifier in fish favourite/disliked list")
+ else
+ return HAS_TRAIT(bait, identifier)
+
diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm
index c4be35817b7ac..6c6621634642b 100644
--- a/code/modules/fishing/fish/_fish.dm
+++ b/code/modules/fishing/fish/_fish.dm
@@ -2,21 +2,23 @@
/obj/item/fish
name = "generic looking aquarium fish"
desc = "very bland"
- icon = 'icons/obj/aquarium/fish.dmi'
+ icon = 'icons/obj/structures/aquarium/fish.dmi'
icon_state = "bugfish"
lefthand_file = 'icons/mob/inhands/fish_lefthand.dmi'
righthand_file = 'icons/mob/inhands/fish_righthand.dmi'
- inhand_icon_state = "fish_normal"
force = 6
+ throwforce = 6
+ throw_range = 8
attack_verb_continuous = list("slaps", "whacks")
attack_verb_simple = list("slap", "whack")
hitsound = 'sound/weapons/slap.ogg'
///The grind results of the fish. They scale with the weight of the fish.
- grind_results = list(/datum/reagent/blood = 20, /datum/reagent/consumable/liquidgibs = 5)
+ grind_results = list(/datum/reagent/blood = 5, /datum/reagent/consumable/liquidgibs = 5)
obj_flags = UNIQUE_RENAME
+ item_flags = IMMUTABLE_SLOW|SLOWS_WHILE_IN_HAND
/// Resulting width of aquarium visual icon - default size of "fish_greyscale" state
- var/sprite_width = 3
+ var/sprite_width = 5
/// Resulting height of aquarium visual icon - default size of "fish_greyscale" state
var/sprite_height = 3
@@ -85,10 +87,6 @@
/// The species' name(s) of the parents of the fish. Shown by the fish analyzer.
var/progenitors
- var/flopping = FALSE
-
- var/in_stasis = FALSE
-
// Fishing related properties
/**
@@ -97,8 +95,8 @@
*/
var/list/fish_traits = list()
- /// Fishing behaviour
- var/fish_ai_type = FISH_AI_DUMB
+ /// path to datums that dictate how the fish moves during the fishing minigame
+ var/fish_movement_type = /datum/fish_movement
/// Base additive modifier to fishing difficulty
var/fishing_difficulty_modifier = 0
@@ -125,6 +123,9 @@
/// Average weight for this fish type in grams
var/average_weight = 1000
+ ///The general deviation from the average weight and size this fish has in the wild
+ var/weight_size_deviation = 0.2
+
/// When outside of an aquarium, these gases that are checked (as well as pressure and temp) to assert if the environment is safe or not.
var/list/safe_air_limits = list(
/datum/gas/oxygen = list(12, 100),
@@ -190,12 +191,24 @@
/obj/item/fish/examine(mob/user)
. = ..()
- // All spacemen have magic eyes of fish weight perception until fish scale (get it?) is implemented.
- . += span_notice("It's [size] cm long.")
- . += span_notice("It weighs [weight] g.")
+ if(HAS_MIND_TRAIT(user, TRAIT_EXAMINE_DEEPER_FISH))
+ if(status == FISH_DEAD)
+ . += span_deadsay("it's dead.")
+ var/list/warnings = list()
+ if(is_hungry())
+ warnings += "starving"
+ if(!HAS_TRAIT(src, TRAIT_FISH_STASIS) && !proper_environment())
+ warnings += "drowning"
+ if(health < initial(health) * 0.6)
+ warnings += "sick"
+ if(length(warnings))
+ . += span_warning("it's [english_list(warnings)]")
+ if(HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISH))
+ . += span_notice("It's [size] cm long.")
+ . += span_notice("It weighs [weight] g.")
///Randomizes weight and size.
-/obj/item/fish/proc/randomize_size_and_weight(base_size = average_size, base_weight = average_weight, deviation = 0.2)
+/obj/item/fish/proc/randomize_size_and_weight(base_size = average_size, base_weight = average_weight, deviation = weight_size_deviation)
var/size_deviation = 0.2 * base_size
var/new_size = round(clamp(gaussian(base_size, size_deviation), average_size * 1/MAX_FISH_DEVIATION_COEFF, average_size * MAX_FISH_DEVIATION_COEFF))
@@ -206,38 +219,125 @@
///Updates weight and size, along with weight class, number of fillets you can get and grind results.
/obj/item/fish/proc/update_size_and_weight(new_size = average_size, new_weight = average_weight)
- if(size && fillet_type)
- RemoveElement(/datum/element/processable, TOOL_KNIFE, fillet_type, num_fillets, 0.5 SECONDS, screentip_verb = "Cut")
+ SEND_SIGNAL(src, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, new_size, new_weight)
+ if(size)
+ if(fillet_type)
+ RemoveElement(/datum/element/processable, TOOL_KNIFE, fillet_type, num_fillets, 0.5 SECONDS * num_fillets, screentip_verb = "Cut")
+ if(size > FISH_SIZE_TWO_HANDS_REQUIRED)
+ qdel(GetComponent(/datum/component/two_handed))
size = new_size
+ var/init_icon_state = initial(inhand_icon_state)
switch(size)
if(0 to FISH_SIZE_TINY_MAX)
update_weight_class(WEIGHT_CLASS_TINY)
- inhand_icon_state = "fish_small"
+ if(!init_icon_state)
+ inhand_icon_state = "fish_small"
if(FISH_SIZE_TINY_MAX to FISH_SIZE_SMALL_MAX)
- inhand_icon_state = "fish_small"
+ if(!init_icon_state)
+ inhand_icon_state = "fish_small"
update_weight_class(WEIGHT_CLASS_SMALL)
if(FISH_SIZE_SMALL_MAX to FISH_SIZE_NORMAL_MAX)
- inhand_icon_state = "fish_normal"
+ if(!init_icon_state)
+ inhand_icon_state = "fish_normal"
update_weight_class(WEIGHT_CLASS_NORMAL)
if(FISH_SIZE_NORMAL_MAX to FISH_SIZE_BULKY_MAX)
- inhand_icon_state = "fish_bulky"
+ if(!init_icon_state)
+ inhand_icon_state = "fish_bulky"
update_weight_class(WEIGHT_CLASS_BULKY)
- if(FISH_SIZE_BULKY_MAX to INFINITY)
- inhand_icon_state = "fish_huge"
+ if(FISH_SIZE_BULKY_MAX to FISH_SIZE_HUGE_MAX)
+ if(!init_icon_state)
+ inhand_icon_state = "fish_huge"
update_weight_class(WEIGHT_CLASS_HUGE)
+ if(FISH_SIZE_HUGE_MAX to INFINITY)
+ if(!init_icon_state)
+ inhand_icon_state = "fish_huge"
+ update_weight_class(WEIGHT_CLASS_GIGANTIC)
+
+ if(size > FISH_SIZE_TWO_HANDS_REQUIRED)
+ inhand_icon_state = "[inhand_icon_state]_wielded"
+ AddComponent(/datum/component/two_handed, require_twohands = TRUE)
+
if(fillet_type)
var/init_fillets = initial(num_fillets)
var/amount = max(round(init_fillets * size / FISH_FILLET_NUMBER_SIZE_DIVISOR, 1), 1)
num_fillets = amount
- AddElement(/datum/element/processable, TOOL_KNIFE, fillet_type, num_fillets, 0.5 SECONDS, screentip_verb = "Cut")
+ AddElement(/datum/element/processable, TOOL_KNIFE, fillet_type, num_fillets, 0.5 SECONDS * num_fillets, screentip_verb = "Cut")
if(weight)
for(var/reagent_type in grind_results)
grind_results[reagent_type] /= FLOOR(weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR, 0.1)
weight = new_weight
+
+ if(weight >= FISH_WEIGHT_SLOWDOWN)
+ slowdown = round(((weight/FISH_WEIGHT_SLOWDOWN_DIVISOR)**FISH_WEIGHT_SLOWDOWN_EXPONENT)-1.3, 0.1)
+ drag_slowdown = round(slowdown * 0.5, 1)
+ else
+ slowdown = 0
+ drag_slowdown = 0
+ if(ismob(loc))
+ var/mob/mob = loc
+ mob.update_equipment_speed_mods()
+
for(var/reagent_type in grind_results)
grind_results[reagent_type] *= FLOOR(weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR, 0.1)
+ update_fish_force()
+
+///Reset weapon-related variables of this items and recalculates those values based on the fish weight and size.
+/obj/item/fish/proc/update_fish_force()
+ force = initial(force)
+ throwforce = initial(throwforce)
+ throw_range = initial(throw_range)
+ demolition_mod = initial(demolition_mod)
+ attack_verb_continuous = initial(attack_verb_continuous)
+ attack_verb_simple = initial(attack_verb_simple)
+ hitsound = initial(hitsound)
+ damtype = initial(damtype)
+ attack_speed = initial(attack_speed)
+ block_chance = initial(block_chance)
+ armour_penetration = initial(armour_penetration)
+ wound_bonus = initial(wound_bonus)
+ bare_wound_bonus = initial(bare_wound_bonus)
+ toolspeed = initial(toolspeed)
+
+ var/weight_rank = max(round(1 + log(2, weight/FISH_WEIGHT_FORCE_DIVISOR), 1), 1)
+
+ throw_range -= weight_rank
+ get_force_rank()
+
+ var/bonus_malus = weight_rank - w_class
+ if(bonus_malus)
+ calculate_fish_force_bonus(bonus_malus)
+
+ throwforce = force
+
+ SEND_SIGNAL(src, COMSIG_FISH_FORCE_UPDATED, weight_rank, bonus_malus)
+
+///A proc that makes the fish slightly stronger or weaker if there's a noticeable discrepancy between size and weight.
+/obj/item/fish/proc/calculate_fish_force_bonus(bonus_malus)
+ demolition_mod += bonus_malus * 0.1
+ attack_speed += bonus_malus * 0.1
+ force = round(force * (1 + bonus_malus * 0.1), 0.1)
+
+/obj/item/fish/proc/get_force_rank()
+ switch(w_class)
+ if(WEIGHT_CLASS_TINY)
+ force -= 3
+ attack_speed -= 0.1 SECONDS
+ if(WEIGHT_CLASS_NORMAL)
+ force += 2
+ if(WEIGHT_CLASS_BULKY)
+ force += 5
+ attack_speed += 0.1 SECONDS
+ if(WEIGHT_CLASS_HUGE)
+ force += 9
+ attack_speed += 0.2 SECONDS
+ demolition_mod += 0.2
+ if(WEIGHT_CLASS_GIGANTIC)
+ force += 13
+ attack_speed += 0.4 SECONDS
+ demolition_mod += 0.4
+
/**
* This proc has fish_traits list populated with fish_traits paths from three different lists:
* traits from x_traits and y_traits are compared, and inserted if conditions are met;
@@ -253,18 +353,21 @@
var/list/same_traits = x_traits & y_traits
var/list/all_traits = (x_traits|y_traits)-removed_traits
- /**
- * Traits that the fish is guaranteed to inherit will be inherited,
- * with the assertion that they're compatible anyway.
- */
- for(var/trait_type in all_traits)
- var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
- if(type in trait.guaranteed_inheritance_types)
- fish_traits |= trait_type
- all_traits -= trait_type
- ///Build a list of incompatible traits. Don't let any such trait pass onto the fish.
+ /// a list of incompatible traits that'll be filled as it goes on. Don't let any such trait pass onto the fish.
var/list/incompatible_traits = list()
+
+ ///some traits can spontaneously manifest for some fishes. These have higher priorities than other traits
+ var/list/potential_spontaneous_traits = GLOB.spontaneous_fish_traits[type]
+ for(var/trait_type in potential_spontaneous_traits)
+ if(!prob(potential_spontaneous_traits[trait_type]))
+ continue
+ var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
+ if(length(fish_traits & trait.incompatible_traits))
+ continue
+ fish_traits |= trait_type
+ incompatible_traits |= trait.incompatible_traits
+
for(var/trait_type in fish_traits)
var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
incompatible_traits |= trait.incompatible_traits
@@ -278,6 +381,8 @@
if(trait_type in incompatible_traits)
continue
var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
+ if(!isnull(trait.fish_whitelist) && !(type in trait.fish_whitelist))
+ continue
if(length(fish_traits & trait.incompatible_traits))
continue
if((trait_type in same_traits) ? prob(trait.inheritability) : prob(trait.diff_traits_inheritability))
@@ -301,13 +406,13 @@
check_environment()
/obj/item/fish/proc/enter_stasis()
- in_stasis = TRUE
+ ADD_TRAIT(src, TRAIT_FISH_STASIS, INNATE_TRAIT)
// Stop processing until inserted into aquarium again.
stop_flopping()
STOP_PROCESSING(SSobj, src)
/obj/item/fish/proc/exit_stasis()
- in_stasis = FALSE
+ REMOVE_TRAIT(src, TRAIT_FISH_STASIS, INNATE_TRAIT)
if(status != FISH_DEAD)
START_PROCESSING(SSobj, src)
@@ -327,15 +432,9 @@
fed_reagents.remove_reagent(fed_reagent_type, 0.1)
SEND_SIGNAL(src, COMSIG_FISH_FED, fed_reagents, fed_reagent_type)
-/obj/item/fish/proc/check_environment(stasis_check = TRUE)
+/obj/item/fish/proc/check_environment()
if(QDELETED(src)) //we don't care anymore
return
- if(stasis_check)
- // Apply/remove stasis as needed
- if(loc && HAS_TRAIT(loc, TRAIT_FISH_SAFE_STORAGE))
- enter_stasis()
- else if(in_stasis)
- exit_stasis()
if(!do_flop_animation)
return
@@ -343,7 +442,7 @@
// Do additional stuff
var/in_aquarium = isaquarium(loc)
// Start flopping if outside of fish container
- var/should_be_flopping = status == FISH_ALIVE && loc && !HAS_TRAIT(loc,TRAIT_FISH_SAFE_STORAGE) && !in_aquarium
+ var/should_be_flopping = status == FISH_ALIVE && !HAS_TRAIT(src, TRAIT_FISH_STASIS) && !in_aquarium
if(should_be_flopping)
start_flopping()
@@ -351,7 +450,7 @@
stop_flopping()
/obj/item/fish/process(seconds_per_tick)
- if(in_stasis || status != FISH_ALIVE)
+ if(HAS_TRAIT(src, TRAIT_FISH_STASIS) || status != FISH_ALIVE)
return
process_health(seconds_per_tick)
@@ -363,7 +462,7 @@
SEND_SIGNAL(src, COMSIG_FISH_LIFE, seconds_per_tick)
-/obj/item/fish/proc/set_status(new_status)
+/obj/item/fish/proc/set_status(new_status, silent = FALSE)
if(status == new_status)
return
switch(new_status)
@@ -371,20 +470,36 @@
status = FISH_ALIVE
health = initial(health) // since the fishe has been revived
last_feeding = world.time //reset hunger
- check_environment(FALSE)
+ check_environment()
START_PROCESSING(SSobj, src)
if(FISH_DEAD)
status = FISH_DEAD
STOP_PROCESSING(SSobj, src)
stop_flopping()
- var/message = span_notice(replacetext(death_text, "%SRC", "[src]"))
- if(isaquarium(loc))
- loc.visible_message(message)
- else
- visible_message(message)
+ if(!silent)
+ var/message = span_notice(replacetext(death_text, "%SRC", "[src]"))
+ if(isaquarium(loc))
+ loc.visible_message(message)
+ else
+ visible_message(message)
update_appearance()
+ update_fish_force()
SEND_SIGNAL(src, COMSIG_FISH_STATUS_CHANGED)
+/obj/item/fish/expose_reagents(list/reagents, datum/reagents/source, methods = TOUCH, volume_modifier = 1, show_message = TRUE)
+ . = ..()
+ if(. & COMPONENT_NO_EXPOSE_REAGENTS || status != FISH_DEAD)
+ return
+ var/datum/reagent/medicine/strange_reagent/revival = locate() in reagents
+ if(!revival)
+ return
+ if(reagents[revival] >= 2 * w_class)
+ set_status(FISH_ALIVE)
+ else
+ balloon_alert_to_viewers("twitches for a moment!")
+ animate(src, pixel_x = 1, time = 0.1 SECONDS, loop = 2, flags = ANIMATION_RELATIVE|ANIMATION_PARALLEL)
+ animate(pixel_x = -1, flags = ANIMATION_RELATIVE)
+
/obj/item/fish/proc/use_lazarus(datum/source, obj/item/lazarus_injector/injector, mob/user)
SIGNAL_HANDLER
if(injector.revive_type != SENTIENCE_ORGANIC)
@@ -447,10 +562,6 @@
if(health <= 0)
set_status(FISH_DEAD)
-
-//Fish breeding stops if fish count exceeds this.
-#define AQUARIUM_MAX_BREEDING_POPULATION 20
-
/obj/item/fish/proc/ready_to_reproduce(being_targeted = FALSE)
var/obj/structure/aquarium/aquarium = loc
if(!istype(aquarium))
@@ -459,9 +570,7 @@
return FALSE
if(!being_targeted && length(aquarium.get_fishes()) >= AQUARIUM_MAX_BREEDING_POPULATION)
return FALSE
- return aquarium.allow_breeding && health >= initial(health) * 0.8 && stable_population > 1 && world.time >= breeding_wait
-
-#undef AQUARIUM_MAX_BREEDING_POPULATION
+ return aquarium.allow_breeding && health >= initial(health) * 0.8 && stable_population >= 1 && world.time >= breeding_wait
/obj/item/fish/proc/try_to_reproduce()
var/obj/structure/aquarium/aquarium = loc
@@ -496,36 +605,48 @@
second_fish = other_fish
break
- if(!second_fish && !HAS_TRAIT(src, TRAIT_FISH_SELF_REPRODUCE))
- return FALSE
+ if(!second_fish)
+ if(!HAS_TRAIT(src, TRAIT_FISH_SELF_REPRODUCE))
+ return FALSE
+ if(length(aquarium.tracked_fish_by_type[type]) >= stable_population)
+ return FALSE
+
+ if(PERFORM_ALL_TESTS(fish_breeding) && second_fish && !length(evolution_types))
+ return create_offspring(second_fish.type, second_fish)
var/chosen_type
var/datum/fish_evolution/chosen_evolution
- if(PERFORM_ALL_TESTS(fish_breeding) && second_fish && !length(evolution_types))
- chosen_type = second_fish.type
- else
- var/list/possible_evolutions = list()
- for(var/evolution_type in evolution_types)
+ var/list/possible_evolutions = list()
+ for(var/evolution_type in evolution_types)
+ var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evolution_type]
+ if(evolution.check_conditions(src, second_fish, aquarium))
+ possible_evolutions += evolution
+ if(second_fish?.evolution_types)
+ var/secondary_evolutions = (second_fish.evolution_types - evolution_types)
+ for(var/evolution_type in secondary_evolutions)
var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evolution_type]
- if(evolution.check_conditions(src, second_fish, aquarium))
+ if(evolution.check_conditions(second_fish, src, aquarium))
possible_evolutions += evolution
- if(second_fish?.evolution_types)
- var/secondary_evolutions = (second_fish.evolution_types - evolution_types)
- for(var/evolution_type in secondary_evolutions)
- var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evolution_type]
- if(evolution.check_conditions(second_fish, src, aquarium))
- possible_evolutions += evolution
-
- if(length(possible_evolutions))
- chosen_evolution = pick(possible_evolutions)
- chosen_type = chosen_evolution.new_fish_type
- else if(second_fish)
- if(length(aquarium.tracked_fish_by_type[type]) >= stable_population)
+
+ if(length(possible_evolutions))
+ chosen_evolution = pick(possible_evolutions)
+ chosen_type = chosen_evolution.new_fish_type
+ else if(second_fish)
+ var/recessive = HAS_TRAIT(src, TRAIT_FISH_RECESSIVE)
+ var/recessive_partner = HAS_TRAIT(second_fish, TRAIT_FISH_RECESSIVE)
+ if(length(aquarium.tracked_fish_by_type[type]) >= stable_population)
+ if(recessive_partner && !recessive)
+ return FALSE
+ chosen_type = second_fish.type
+ else
+ if(recessive && !recessive_partner)
chosen_type = second_fish.type
+ else if(recessive_partner && !recessive)
+ chosen_type = type
else
chosen_type = pick(second_fish.type, type)
- else
- chosen_type = type
+ else
+ chosen_type = type
return create_offspring(chosen_type, second_fish, chosen_evolution)
@@ -595,15 +716,15 @@
/// Starts flopping animation
/obj/item/fish/proc/start_flopping()
- if(flopping) //Requires update_transform/animate_wrappers to be less restrictive.
+ if(HAS_TRAIT(src, TRAIT_FISH_FLOPPING)) //Requires update_transform/animate_wrappers to be less restrictive.
return
- flopping = TRUE
+ ADD_TRAIT(src, TRAIT_FISH_FLOPPING, TRAIT_GENERIC)
flop_animation()
/// Stops flopping animation
/obj/item/fish/proc/stop_flopping()
- if(flopping)
- flopping = FALSE
+ if(HAS_TRAIT(src, TRAIT_FISH_FLOPPING))
+ REMOVE_TRAIT(src, TRAIT_FISH_FLOPPING, TRAIT_GENERIC)
animate(src, transform = matrix()) //stop animation
/// Refreshes flopping animation after temporary animation finishes
@@ -612,7 +733,7 @@
addtimer(CALLBACK(src, PROC_REF(refresh_flopping)), animation_duration)
/obj/item/fish/proc/refresh_flopping()
- if(flopping)
+ if(HAS_TRAIT(src, TRAIT_FISH_FLOPPING))
flop_animation()
/obj/item/fish/proc/try_electrogenesis()
@@ -628,6 +749,14 @@
fish_zap_flags |= (ZAP_GENERATES_POWER | ZAP_MOB_STUN)
tesla_zap(source = get_turf(src), zap_range = fish_zap_range, power = fish_zap_power, cutoff = 1 MEGA JOULES, zap_flags = fish_zap_flags)
+///Returns the price of this fish, for the fish export.
+/obj/item/fish/proc/get_export_price(price, percent)
+ var/size_weight_exponentation = (size * weight * 0.01)^0.85
+ var/calculated_price = price + size_weight_exponentation * percent
+ if(HAS_TRAIT(src, TRAIT_FISH_FROM_CASE)) //Avoid printing money by simply ordering fish and sending it back.
+ calculated_price *= 0.05
+ return round(calculated_price)
+
/// Returns random fish, using random_case_rarity probabilities.
/proc/random_fish_type(required_fluid)
var/static/probability_table
diff --git a/code/modules/fishing/fish/chasm_detritus.dm b/code/modules/fishing/fish/chasm_detritus.dm
index 8d6653781595f..ea9fcb4775770 100644
--- a/code/modules/fishing/fish/chasm_detritus.dm
+++ b/code/modules/fishing/fish/chasm_detritus.dm
@@ -24,27 +24,30 @@ GLOBAL_LIST_INIT_TYPED(chasm_detritus_types, /datum/chasm_detritus, init_chasm_d
/// Stuff which you can always fish up even if nothing fell into a hole. Associative by type.
var/static/list/default_contents = list(
NORMAL_CONTENTS = list(
- /obj/item/stack/sheet/bone = 3,
- /obj/item/stack/ore/slag = 2,
+ /obj/item/stack/sheet/bone = 6,
+ /obj/item/stack/ore/slag = 4,
+ /obj/effect/mob_spawn/corpse/human/skeleton = 2,
/mob/living/basic/mining/lobstrosity/lava = 1,
- /obj/effect/mob_spawn/corpse/human/skeleton = 1,
+ /mob/living/basic/mining/lobstrosity/juvenile/lava = 1,
),
BODIES_ONLY = list(
- /obj/effect/mob_spawn/corpse/human/skeleton = 3,
+ /obj/effect/mob_spawn/corpse/human/skeleton = 6,
/mob/living/basic/mining/lobstrosity/lava = 1,
+ /mob/living/basic/mining/lobstrosity/juvenile/lava = 1,
),
NO_CORPSES = list(
- /obj/item/stack/sheet/bone = 14,
- /obj/item/stack/ore/slag = 10,
+ /obj/item/stack/sheet/bone = 28,
+ /obj/item/stack/ore/slag = 20,
/mob/living/basic/mining/lobstrosity/lava = 1,
+ /mob/living/basic/mining/lobstrosity/juvenile/lava = 1,
),
)
-/datum/chasm_detritus/proc/dispense_detritus(mob/fisherman, turf/fishing_spot)
+/datum/chasm_detritus/proc/dispense_detritus(atom/spawn_location, turf/fishing_spot)
if(prob(default_contents_chance))
var/default_spawn = pick(default_contents[default_contents_key])
- return new default_spawn(get_turf(fisherman))
- return find_chasm_contents(fishing_spot, get_turf(fisherman))
+ return new default_spawn(spawn_location)
+ return find_chasm_contents(fishing_spot, spawn_location)
/// Returns the chosen detritus from the given list of things to choose from
/datum/chasm_detritus/proc/determine_detritus(list/chasm_stuff)
diff --git a/code/modules/fishing/fish/fish_evolution.dm b/code/modules/fishing/fish/fish_evolution.dm
index c04ef2c30796d..4fcc35109a2f8 100644
--- a/code/modules/fishing/fish/fish_evolution.dm
+++ b/code/modules/fishing/fish/fish_evolution.dm
@@ -78,6 +78,7 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio
/datum/fish_evolution/purple_sludgefish
probability = 5
new_fish_type = /obj/item/fish/sludgefish/purple
+ new_traits = list(/datum/fish_trait/recessive)
removed_traits = list(/datum/fish_trait/no_mating)
/datum/fish_evolution/mastodon
@@ -85,10 +86,10 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio
probability = 40
new_fish_type = /obj/item/fish/mastodon
new_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/amphibious, /datum/fish_trait/predator, /datum/fish_trait/aggressive)
- conditions_note = "The fish (and its mate) need to be unusually big both in size and weight."
+ conditions_note = "The fish (and its mate) needs to be unusually big both in size and weight."
/datum/fish_evolution/mastodon/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium)
- if((source.size < 144 || source.weight < 4000) || (mate && (mate.size < 144 || mate.weight < 4000)))
+ if((source.size < 120 || source.weight < 3000) || (mate && (mate.size < 120 || mate.weight < 3000)))
return FALSE
return ..()
@@ -103,3 +104,21 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio
new_fish_type = /obj/item/fish/chasm_crab/ice
required_temperature_min = MIN_AQUARIUM_TEMP+9
required_temperature_max = MIN_AQUARIUM_TEMP+10
+
+/datum/fish_evolution/three_eyes
+ name = "Three-eyed Goldfish"
+ probability = 3
+ new_fish_type = /obj/item/fish/three_eyes
+ new_traits = list(/datum/fish_trait/recessive)
+
+/datum/fish_evolution/chainsawfish
+ name = "Chainsawfish"
+ probability = 30
+ new_fish_type = /obj/item/fish/chainsawfish
+ new_traits = list(/datum/fish_trait/predator, /datum/fish_trait/aggressive)
+ conditions_note = "The fish needs to be unusually big and aggressive"
+
+/datum/fish_evolution/chainsawfish/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium)
+ if(source.size >= 60 && source.size >= 1000 && (/datum/fish_trait/aggressive in source.fish_traits))
+ return ..()
+ return FALSE
diff --git a/code/modules/fishing/fish/fish_traits.dm b/code/modules/fishing/fish/fish_traits.dm
index b03233e76a724..3667a038bff49 100644
--- a/code/modules/fishing/fish/fish_traits.dm
+++ b/code/modules/fishing/fish/fish_traits.dm
@@ -1,5 +1,26 @@
+///A global list of singleton fish traits by their paths
GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list()))
+/**
+ * A nested list of fish types and traits that they can spontaneously manifest with associated probabilities
+ * e.g. list(/obj/item/fish = list(/datum/fish_trait = 100), etc...)
+ */
+GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
+
+/proc/populate_spontaneous_fish_traits()
+ var/list/list = list()
+ for(var/trait_path as anything in GLOB.fish_traits)
+ var/datum/fish_trait/trait = GLOB.fish_traits[trait_path]
+ if(isnull(trait.spontaneous_manifest_types))
+ continue
+ var/list/trait_typecache = zebra_typecacheof(trait.spontaneous_manifest_types) - /obj/item/fish
+ for(var/fish_type in trait_typecache)
+ var/trait_prob = trait_typecache[fish_type]
+ if(!trait_prob)
+ continue
+ LAZYSET(list[fish_type], trait_path, trait_typecache[fish_type])
+ return list
+
/datum/fish_trait
var/name = "Unnamed Trait"
/// Description of the trait in the fishing catalog and scanner
@@ -10,10 +31,14 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
var/inheritability = 100
/// Same as above, but for when only one has it.
var/diff_traits_inheritability = 50
- /// fishes of types within this list are granted to have this trait, no matter the probability
- var/list/guaranteed_inheritance_types
+ /// A list of fish types and traits that they can spontaneously manifest with associated probabilities
+ var/list/spontaneous_manifest_types
+ /// An optional whitelist of fish that can get this trait
+ var/list/fish_whitelist
/// Depending on the value, fish with trait will be reported as more or less difficult in the catalog.
var/added_difficulty = 0
+ /// Reagents added to the fish when gained
+ var/list/reagents_to_add
/// Difficulty modifier from this mod, needs to return a list with two values
/datum/fish_trait/proc/difficulty_mod(obj/item/fishing_rod/rod, mob/fisherman)
@@ -31,7 +56,20 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
/// Applies some special qualities to the fish that has been spawned
/datum/fish_trait/proc/apply_to_fish(obj/item/fish/fish)
- return
+ SHOULD_CALL_PARENT(TRUE)
+ if(reagents_to_add)
+ for(var/reagent in reagents_to_add)
+ add_to_reagents(fish, reagent, reagents_to_add[reagent])
+ RegisterSignal(fish, COMSIG_ATOM_PROCESSED, PROC_REF(process_reagents))
+
+/// Applies some special qualities to basic mobs generated by fish (i.e. chasm chrab --> young lobstrosity --> lobstrosity).
+/datum/fish_trait/proc/apply_to_mob(mob/living/basic/mob)
+ SHOULD_CALL_PARENT(TRUE)
+ RegisterSignal(mob, COMSIG_MOB_CHANGED_TYPE, PROC_REF(on_transformed))
+
+/datum/fish_trait/proc/on_transformed(mob/source, mob/desired_mob)
+ SIGNAL_HANDLER
+ apply_to_mob(desired_mob)
/// Proc used by both the predator and necrophage traits.
/datum/fish_trait/proc/eat_fish(obj/item/fish/predator, obj/item/fish/prey)
@@ -41,6 +79,43 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
SEND_SIGNAL(prey, COMSIG_FISH_EATEN_BY_OTHER_FISH, predator)
qdel(prey)
+/// Proc that inserts a reagent to the grind_results list of the fish. You'll still have to set the processed comsig proc yourself.
+/datum/fish_trait/proc/add_to_reagents(obj/item/fish/fish, reagent_type, amount)
+ LAZYINITLIST(fish.grind_results)
+ fish.grind_results.Insert(1, reagent_type)
+ fish.grind_results[reagent_type] = amount
+
+/// Proc that handles adding reagents from the trait to the fillets from butchered fish.
+/datum/fish_trait/proc/process_reagents(obj/item/fish/source, mob/living/user, obj/item/process_item, list/results)
+ SIGNAL_HANDLER
+ var/results_with_reagents = 0
+ for(var/atom/result as anything in results)
+ if(result.reagents)
+ results_with_reagents++
+ if(!results_with_reagents)
+ return
+ for(var/reagent in reagents_to_add)
+ var/amount = round(source.grind_results[reagent] / results_with_reagents, 0.1)
+ for(var/atom/result as anything in results)
+ result.reagents?.add_reagent(reagent, amount)
+
+/// Proc that adds or changes the venomous when the fish size and/or weight are updated
+/datum/fish_trait/proc/add_venom(obj/item/fish/source, venom_path, new_weight, mult = 0.25)
+ if(source.size)
+ var/old_amount = max(round((source.weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR) * mult, 0.1), mult)
+ source.RemoveElement(/datum/element/venomous, venom_path, old_amount)
+
+ var/new_amount = max(round((new_weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR) * mult, 0.1), mult)
+ source.AddElement(/datum/element/venomous, venom_path, new_amount)
+
+/// Proc that changes the venomous element based on if the fish is alive or dead (basically dead fish are weaker).
+/datum/fish_trait/proc/change_venom_on_death(obj/item/fish/source, venom_path, live_mult, dead_mult)
+ var/live_amount = max(round((source.weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR) * live_mult, 0.1), live_mult)
+ var/dead_amount = max(round((source.weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR) * dead_mult, 0.1), dead_mult)
+ var/is_dead = source.status == FISH_DEAD
+ source.RemoveElement(/datum/element/venomous, venom_path, is_dead ? live_amount : dead_amount)
+ source.AddElement(/datum/element/venomous, venom_path, is_dead ? dead_amount : live_amount)
+
/datum/fish_trait/wary
name = "Wary"
catalog_description = "This fish will avoid visible fish lines, cloaked line recommended."
@@ -72,7 +147,7 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
return
if(HAS_TRAIT(rod.bait, TRAIT_OMNI_BAIT))
return
- if(HAS_TRAIT(rod.bait, TRAIT_GOOD_QUALITY_BAIT) || HAS_TRAIT(rod.bait, TRAIT_GREAT_QUALITY_BAIT))
+ if(!HAS_TRAIT(rod.bait, TRAIT_GOOD_QUALITY_BAIT) && !HAS_TRAIT(rod.bait, TRAIT_GREAT_QUALITY_BAIT))
.[MULTIPLICATIVE_FISHING_MOD] = 0
@@ -88,6 +163,7 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
.[MULTIPLICATIVE_FISHING_MOD] = 0
/datum/fish_trait/nocturnal/apply_to_fish(obj/item/fish/fish)
+ . = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(check_light))
/datum/fish_trait/nocturnal/proc/check_light(obj/item/fish/source, seconds_per_tick)
@@ -98,12 +174,41 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
if(light_amount > SHADOW_SPECIES_LIGHT_THRESHOLD)
source.adjust_health(source.health - 0.5 * seconds_per_tick)
+/datum/fish_trait/nocturnal/apply_to_mob(mob/living/basic/mob)
+ . = ..()
+ // Make sure the mob can also ee in the dark
+ mob.lighting_cutoff_red = min(mob.lighting_cutoff_red, 20)
+ mob.lighting_cutoff_green = min(mob.lighting_cutoff_green, 20)
+ mob.lighting_cutoff_blue = min(mob.lighting_cutoff_blue, 20)
+ mob.update_sight()
+
+ RegisterSignal(mob, COMSIG_LIVING_HANDLE_BREATHING, PROC_REF(on_non_stasis_life))
+
+/datum/fish_trait/nocturnal/proc/on_non_stasis_life(mob/living/basic/mob, seconds_per_tick = SSMOBS_DT)
+ SIGNAL_HANDLER
+ var/turf/our_turf = mob.loc
+ if(!isturf(our_turf))
+ return
+ var/light_amount = our_turf.get_lumcount()
+
+ if (light_amount < SHADOW_SPECIES_LIGHT_THRESHOLD) //heal in the dark
+ mob.apply_status_effect(/datum/status_effect/shadow_regeneration)
+
/datum/fish_trait/heavy
name = "Heavy"
- catalog_description = "This fish tends to stay near the waterbed.";
+ catalog_description = "This fish tends to stay near the waterbed."
+
+/datum/fish_trait/heavy/apply_to_mob(mob/living/basic/mob)
+ . = ..()
+ mob.add_movespeed_modifier(/datum/movespeed_modifier/heavy_fish)
+ mob.maxHealth *= 1.5
+ mob.health *= 1.5
+ mob.melee_damage_lower *= 1.3
+ mob.melee_damage_upper *= 1.3
+ mob.obj_damage *= 1.3
/datum/fish_trait/heavy/minigame_mod(obj/item/fishing_rod/rod, mob/fisherman, datum/fishing_challenge/minigame)
- minigame.fish_idle_velocity -= 10
+ minigame.mover.fish_idle_velocity -= 10
/datum/fish_trait/carnivore
name = "Carnivore"
@@ -144,6 +249,7 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
catalog_description = "This fish emits an invisible toxin that emulsifies other fish for it to feed on."
/datum/fish_trait/emulsijack/apply_to_fish(obj/item/fish/fish)
+ . = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(emulsify))
ADD_TRAIT(fish, TRAIT_RESIST_EMULSIFY, FISH_TRAIT_DATUM)
@@ -161,12 +267,29 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
source.adjust_health(source.health + 3 * seconds_per_tick)
source.last_feeding = world.time //it feeds on the emulsion!
+/datum/fish_trait/emulsijack/apply_to_mob(mob/living/basic/mob)
+ . = ..()
+ RegisterSignal(mob, COMSIG_LIVING_HANDLE_BREATHING, PROC_REF(on_non_stasis_life))
+
+/datum/fish_trait/emulsijack/proc/on_non_stasis_life(mob/living/basic/mob, seconds_per_tick = SSMOBS_DT)
+ SIGNAL_HANDLER
+ var/turf/open/our_turf = get_turf(mob)
+ if(our_turf.return_air().return_pressure() > ONE_ATMOSPHERE * 1.5) //put a cap otherwise closed spaces may overpressurize
+ return
+
+ var/datum/gas_mixture/stench = new
+ ADD_GAS(/datum/gas/miasma, stench.gases)
+ stench.gases[/datum/gas/miasma][MOLES] = MIASMA_CORPSE_MOLES * 2 * seconds_per_tick
+ stench.temperature = mob.bodytemperature
+ our_turf.assume_air(stench)
+
/datum/fish_trait/necrophage
name = "Necrophage"
catalog_description = "This fish will eat carcasses of dead fish when hungry."
incompatible_traits = list(/datum/fish_trait/vegan)
/datum/fish_trait/necrophage/apply_to_fish(obj/item/fish/fish)
+ . = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(eat_dead_fishes))
/datum/fish_trait/necrophage/proc/eat_dead_fishes(obj/item/fish/source, seconds_per_tick)
@@ -186,6 +309,7 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
diff_traits_inheritability = 25
/datum/fish_trait/parthenogenesis/apply_to_fish(obj/item/fish/fish)
+ . = ..()
ADD_TRAIT(fish, TRAIT_FISH_SELF_REPRODUCE, FISH_TRAIT_DATUM)
/**
@@ -199,15 +323,27 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
incompatible_traits = list(/datum/fish_trait/crossbreeder)
/datum/fish_trait/no_mating/apply_to_fish(obj/item/fish/fish)
+ . = ..()
ADD_TRAIT(fish, TRAIT_FISH_NO_MATING, FISH_TRAIT_DATUM)
+///Prevent offsprings of fish with this trait from being of the same type (unless self-mating or the partner also has the trait)
+/datum/fish_trait/recessive
+ name = "Recessive"
+ catalog_description = "If crossbred, offsprings will always be of the mate species, unless it also possess the trait."
+ diff_traits_inheritability = 0
+
+/datum/fish_trait/no_mating/apply_to_fish(obj/item/fish/fish)
+ . = ..()
+ ADD_TRAIT(fish, TRAIT_FISH_RECESSIVE, FISH_TRAIT_DATUM)
+
/datum/fish_trait/revival
diff_traits_inheritability = 15
name = "Self-Revival"
catalog_description = "This fish shows a peculiar ability of reviving itself a minute or two after death."
- guaranteed_inheritance_types = list(/obj/item/fish/boned, /obj/item/fish/mastodon)
+ spontaneous_manifest_types = list(/obj/item/fish/boned = 100, /obj/item/fish/mastodon = 100)
/datum/fish_trait/revival/apply_to_fish(obj/item/fish/fish)
+ . = ..()
RegisterSignal(fish, COMSIG_FISH_STATUS_CHANGED, PROC_REF(check_status))
/datum/fish_trait/revival/proc/check_status(obj/item/fish/source)
@@ -226,12 +362,17 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
else
source.visible_message(message)
+/datum/fish_trait/revival/apply_to_mob(mob/living/basic/mob)
+ . = ..()
+ mob.AddComponent(/datum/component/regenerator, regeneration_delay = 6 SECONDS, brute_per_second = 2 SECONDS, outline_colour = COLOR_BLUE)
+
/datum/fish_trait/predator
name = "Predator"
catalog_description = "It's a predatory fish. It'll hunt down and eat live fishes of smaller size when hungry."
incompatible_traits = list(/datum/fish_trait/vegan)
/datum/fish_trait/predator/apply_to_fish(obj/item/fish/fish)
+ . = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(eat_fishes))
/datum/fish_trait/predator/proc/eat_fishes(obj/item/fish/source, seconds_per_tick)
@@ -240,7 +381,7 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
return
var/obj/structure/aquarium/aquarium = source.loc
for(var/obj/item/fish/victim in aquarium.get_fishes(TRUE, source))
- if(victim.size < source.size * 0.75) // It's a big fish eat small fish world
+ if(victim.size < source.size * 0.7) // It's a big fish eat small fish world
continue
if(victim.status != FISH_ALIVE || victim == source || HAS_TRAIT(victim, TRAIT_YUCKY_FISH) || SPT_PROB(80, seconds_per_tick))
continue
@@ -250,31 +391,35 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
/datum/fish_trait/yucky
name = "Yucky"
catalog_description = "This fish tastes so repulsive, other fishes won't try to eat it."
+ reagents_to_add = list(/datum/reagent/yuck = 3)
/datum/fish_trait/yucky/apply_to_fish(obj/item/fish/fish)
- RegisterSignal(fish, COMSIG_ATOM_PROCESSED, PROC_REF(add_yuck))
+ . = ..()
ADD_TRAIT(fish, TRAIT_YUCKY_FISH, FISH_TRAIT_DATUM)
- LAZYSET(fish.grind_results, /datum/reagent/yuck, 3)
-
-/datum/fish_trait/yucky/proc/add_yuck(obj/item/fish/source, mob/living/user, obj/item/process_item, list/results)
- var/amount = source.grind_results[/datum/reagent/yuck] / length(results)
- for(var/atom/result as anything in results)
- result.reagents?.add_reagent(/datum/reagent/yuck, amount)
/datum/fish_trait/toxic
name = "Toxic"
- catalog_description = "This fish contains toxins in its liver. Feeding it to predatory fishes or people is not reccomended."
+ catalog_description = "This fish contains toxins. Feeding it to predatory fishes or people is not reccomended."
diff_traits_inheritability = 25
+ reagents_to_add = list(/datum/reagent/toxin/tetrodotoxin = 2.5)
/datum/fish_trait/toxic/apply_to_fish(obj/item/fish/fish)
- RegisterSignal(fish, COMSIG_ATOM_PROCESSED, PROC_REF(add_toxin))
+ . = ..()
+ RegisterSignal(fish, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, PROC_REF(make_venomous))
+ RegisterSignal(fish, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_status_change))
RegisterSignal(fish, COMSIG_FISH_EATEN_BY_OTHER_FISH, PROC_REF(on_eaten))
- LAZYSET(fish.grind_results, /datum/reagent/toxin/tetrodotoxin, 2.5)
-/datum/fish_trait/toxic/proc/add_toxin(obj/item/fish/source, mob/living/user, obj/item/process_item, list/results)
- var/amount = source.grind_results[ /datum/reagent/toxin/tetrodotoxin] / length(results)
- for(var/atom/result as anything in results)
- result.reagents?.add_reagent( /datum/reagent/toxin/tetrodotoxin, amount)
+/datum/fish_trait/toxic/proc/make_venomous(obj/item/fish/source, new_size, new_weight)
+ SIGNAL_HANDLER
+ if(!HAS_TRAIT(source, TRAIT_FISH_STINGER))
+ return
+ add_venom(source, /datum/reagent/toxin/tetrodotoxin, new_weight, mult = source.status == FISH_DEAD ? 0.1 : 0.25)
+
+/datum/fish_trait/toxic/proc/on_status_change(obj/item/fish/source)
+ SIGNAL_HANDLER
+ if(!HAS_TRAIT(source, TRAIT_FISH_STINGER))
+ return
+ change_venom_on_death(source, /datum/reagent/toxin/tetrodotoxin, 0.25, 0.1)
/datum/fish_trait/toxic/proc/on_eaten(obj/item/fish/source, obj/item/fish/predator)
if(HAS_TRAIT(predator, TRAIT_FISH_TOXIN_IMMUNE))
@@ -291,12 +436,17 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
if(source.status == FISH_DEAD)
UnregisterSignal(source, list(COMSIG_FISH_LIFE, COMSIG_FISH_STATUS_CHANGED))
+/datum/fish_trait/toxic/apply_to_mob(mob/living/basic/mob)
+ . = ..()
+ mob.AddElement(/datum/element/venomous, /datum/reagent/toxin/tetrodotoxin, 0.5 * mob.mob_size)
+
/datum/fish_trait/toxin_immunity
name = "Toxin Immunity"
catalog_description = "This fish has developed an ample-spected immunity to toxins."
diff_traits_inheritability = 40
/datum/fish_trait/toxin_immunity/apply_to_fish(obj/item/fish/fish)
+ . = ..()
ADD_TRAIT(fish, TRAIT_FISH_TOXIN_IMMUNE, FISH_TRAIT_DATUM)
/datum/fish_trait/crossbreeder
@@ -307,6 +457,7 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
incompatible_traits = list(/datum/fish_trait/no_mating)
/datum/fish_trait/crossbreeder/apply_to_fish(obj/item/fish/fish)
+ . = ..()
ADD_TRAIT(fish, TRAIT_FISH_CROSSBREEDER, FISH_TRAIT_DATUM)
/datum/fish_trait/aggressive
@@ -316,6 +467,7 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
catalog_description = "This fish is aggressively territorial, and may attack fish that come close to it."
/datum/fish_trait/aggressive/apply_to_fish(obj/item/fish/fish)
+ . = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(try_attack_fish))
/datum/fish_trait/aggressive/proc/try_attack_fish(obj/item/fish/source, seconds_per_tick)
@@ -335,13 +487,18 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
name = "Lubed"
inheritability = 90
diff_traits_inheritability = 45
- guaranteed_inheritance_types = list(/obj/item/fish/clownfish/lube)
- catalog_description = "This fish exudes a viscous, slippery lubrificant. It's reccomended not to step on it."
+ spontaneous_manifest_types = list(/obj/item/fish/clownfish/lube = 100)
+ catalog_description = "This fish exudes a viscous, slippery lubrificant. It's recommended not to step on it."
added_difficulty = 5
/datum/fish_trait/lubed/apply_to_fish(obj/item/fish/fish)
+ . = ..()
fish.AddComponent(/datum/component/slippery, 8 SECONDS, SLIDE|GALOSHES_DONT_HELP)
+/datum/fish_trait/lubed/apply_to_mob(mob/living/basic/mob)
+ . = ..()
+ mob.AddElement(/datum/element/lube_walking)
+
/datum/fish_trait/lubed/minigame_mod(obj/item/fishing_rod/rod, mob/fisherman, datum/fishing_challenge/minigame)
minigame.reeling_velocity *= 1.4
minigame.gravity_velocity *= 1.4
@@ -353,6 +510,7 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
catalog_description = "This fish has developed a primitive adaptation to life on both land and water."
/datum/fish_trait/amphibious/apply_to_fish(obj/item/fish/fish)
+ . = ..()
ADD_TRAIT(fish, TRAIT_FISH_AMPHIBIOUS, FISH_TRAIT_DATUM)
if(fish.required_fluid_type == AQUARIUM_FLUID_AIR)
fish.required_fluid_type = AQUARIUM_FLUID_FRESHWATER
@@ -365,6 +523,7 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
incompatible_traits = list(/datum/fish_trait/predator, /datum/fish_trait/necrophage)
/datum/fish_trait/mixotroph/apply_to_fish(obj/item/fish/fish)
+ . = ..()
ADD_TRAIT(fish, TRAIT_FISH_NO_HUNGER, FISH_TRAIT_DATUM)
/datum/fish_trait/antigrav
@@ -372,14 +531,20 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
inheritability = 75
diff_traits_inheritability = 25
catalog_description = "This fish will invert the gravity of the bait at random. May fall upward outside after being caught."
- added_difficulty = 15
+ added_difficulty = 20
/datum/fish_trait/antigrav/minigame_mod(obj/item/fishing_rod/rod, mob/fisherman, datum/fishing_challenge/minigame)
minigame.special_effects |= FISHING_MINIGAME_RULE_ANTIGRAV
/datum/fish_trait/antigrav/apply_to_fish(obj/item/fish/fish)
+ . = ..()
fish.AddElement(/datum/element/forced_gravity, NEGATIVE_GRAVITY)
+/datum/fish_trait/antigrav/apply_to_mob(mob/living/basic/mob)
+ . = ..()
+ mob.add_traits(list(TRAIT_FREE_HYPERSPACE_MOVEMENT, TRAIT_SPACEWALK), FISH_TRAIT_DATUM)
+ mob.AddElement(/datum/element/simple_flying)
+
///Anxiety means the fish will die if in a location with more than 3 fish (including itself)
///This is just barely enough to crossbreed out of anxiety, but it severely limits the potential of
/datum/fish_trait/anxiety
@@ -389,6 +554,7 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
catalog_description = "This fish tends to die of stress when forced to be around too many other fish."
/datum/fish_trait/anxiety/apply_to_fish(obj/item/fish/fish)
+ . = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(on_fish_life))
///signal sent when the anxiety fish is fed, killing it if sharing contents with too many fish.
@@ -411,21 +577,73 @@ GLOBAL_LIST_INIT(fish_traits, init_subtypes_w_path_keys(/datum/fish_trait, list(
catalog_description = "This fish is electroreceptive, and will generate electric fields. Can be harnessed inside a bioelectric generator."
/datum/fish_trait/electrogenesis/apply_to_fish(obj/item/fish/fish)
+ . = ..()
ADD_TRAIT(fish, TRAIT_FISH_ELECTROGENESIS, FISH_TRAIT_DATUM)
- RegisterSignal(fish, COMSIG_ITEM_ATTACK, PROC_REF(on_item_attack))
+ RegisterSignal(fish, COMSIG_FISH_FORCE_UPDATED, PROC_REF(on_force_updated))
-/datum/fish_trait/electrogenesis/proc/on_item_attack(obj/item/fish/fish, mob/living/target, mob/living/user)
+/datum/fish_trait/electrogenesis/proc/on_force_updated(obj/item/fish/fish, weight_rank, bonus_or_malus)
SIGNAL_HANDLER
-
if(fish.status == FISH_ALIVE)
- fish.force = 16
+ fish.force += 10 - fish.w_class
fish.damtype = BURN
fish.attack_verb_continuous = list("shocks", "zaps")
fish.attack_verb_simple = list("shock", "zap")
fish.hitsound = 'sound/effects/sparks4.ogg'
- else
- fish.force = fish::force
- fish.damtype = fish::damtype
- fish.attack_verb_continuous = fish::attack_verb_continuous
- fish.attack_verb_simple = fish::attack_verb_simple
- fish.hitsound = fish::hitsound
+
+/datum/fish_trait/electrogenesis/apply_to_mob(mob/living/basic/mob)
+ . = ..()
+ ADD_TRAIT(mob, TRAIT_SHOCKIMMUNE, FISH_TRAIT_DATUM)
+ mob.grant_actions_by_list(list(/datum/action/cooldown/mob_cooldown/charge_apc))
+ mob.AddElement(/datum/element/venomous, /datum/reagent/teslium, 3 * mob.mob_size)
+
+/datum/fish_trait/stunted
+ name = "Stunted Growth"
+ catalog_description = "This chrab's development is stunted, and will not properly reach adulthood."
+ spontaneous_manifest_types = list(/obj/item/fish/chasm_crab = 12, /obj/item/fish/chasm_crab/ice = 12)
+ fish_whitelist = list(/obj/item/fish/chasm_crab, /obj/item/fish/chasm_crab/ice)
+ diff_traits_inheritability = 40
+
+/datum/fish_trait/stunted/apply_to_mob(mob/living/basic/mob)
+ . = ..()
+ qdel(mob.GetComponent(/datum/component/growth_and_differentiation))
+
+/datum/fish_trait/stinger
+ name = "Stinger"
+ inheritability = 80
+ diff_traits_inheritability = 35
+ catalog_description = "This fish is equipped with a sharp stringer or bill capable of delivering damage and toxins."
+ spontaneous_manifest_types = list(/obj/item/fish/stingray = 100, /obj/item/fish/swordfish = 100, /obj/item/fish/chainsawfish = 100)
+
+/datum/fish_trait/stinger/apply_to_fish(obj/item/fish/fish)
+ . = ..()
+ ADD_TRAIT(fish, TRAIT_FISH_STINGER, FISH_TRAIT_DATUM)
+ RegisterSignal(fish, COMSIG_FISH_FORCE_UPDATED, PROC_REF(on_force_updated))
+
+/datum/fish_trait/stinger/proc/on_force_updated(obj/item/fish/fish, weight_rank, bonus_or_malus)
+ SIGNAL_HANDLER
+ fish.force += 1 + fish.w_class + bonus_or_malus
+
+/datum/fish_trait/toxic_barbs
+ name = "Toxic Barbs"
+ catalog_description = "This fish' stinger, bill or otherwise, is coated with simple, yet effetive venom."
+ spontaneous_manifest_types = list(/obj/item/fish/stingray = 35)
+
+/datum/fish_trait/toxic_barbs/apply_to_fish(obj/item/fish/fish)
+ . = ..()
+ RegisterSignal(fish, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, PROC_REF(make_venomous))
+ RegisterSignal(fish, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_status_change))
+
+/datum/fish_trait/toxic_barbs/proc/make_venomous(obj/item/fish/source, new_size, new_weight)
+ SIGNAL_HANDLER
+ if(!HAS_TRAIT(source, TRAIT_FISH_STINGER))
+ ///Remove the trait from the fish so it doesn't show on the analyzer as it doesn't do anything on stingerless ones.
+ source.fish_traits -= type
+ UnregisterSignal(source, list(COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, COMSIG_FISH_STATUS_CHANGED))
+ return
+ add_venom(source, /datum/reagent/toxin/venom, new_weight, mult = source.status == FISH_DEAD ? 0.3 : 0.7)
+
+/datum/fish_trait/toxic_barbs/proc/on_status_change(obj/item/fish/source)
+ SIGNAL_HANDLER
+ if(!HAS_TRAIT(source, TRAIT_FISH_STINGER))
+ return
+ change_venom_on_death(source, /datum/reagent/toxin/venom, 0.7, 0.3)
diff --git a/code/modules/fishing/fish/fish_types.dm b/code/modules/fishing/fish/fish_types.dm
index cc001560ee0a9..95027b75ea6b0 100644
--- a/code/modules/fishing/fish/fish_types.dm
+++ b/code/modules/fishing/fish/fish_types.dm
@@ -10,9 +10,12 @@
stable_population = 3
average_size = 30
average_weight = 500
+ weight_size_deviation = 0.35
favorite_bait = list(/obj/item/food/bait/worm)
required_temperature_min = MIN_AQUARIUM_TEMP+18
required_temperature_max = MIN_AQUARIUM_TEMP+26
+ evolution_types = list(/datum/fish_evolution/three_eyes, /datum/fish_evolution/chainsawfish)
+ compatible_types = list(/obj/item/fish/goldfish/gill, /obj/item/fish/three_eyes, /obj/item/fish/three_eyes/gill)
/obj/item/fish/goldfish/gill
name = "McGill"
@@ -21,6 +24,8 @@
random_case_rarity = FISH_RARITY_NOPE
show_in_catalog = FALSE
beauty = FISH_BEAUTY_GOOD
+ compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/three_eyes)
+ fish_traits = list(/datum/fish_trait/recessive)
/obj/item/fish/angelfish
name = "angelfish"
@@ -63,13 +68,14 @@
required_temperature_max = MIN_AQUARIUM_TEMP+28
/obj/item/fish/catfish
- name = "cory catfish"
+ name = "catfish"
desc = "A catfish has about 100,000 taste buds, and their bodies are covered with them to help detect chemicals present in the water and also to respond to touch."
icon_state = "catfish"
dedicated_in_aquarium_icon_state = "fish_greyscale"
aquarium_vc_color = "#907420"
- average_size = 100
- average_weight = 2000
+ average_size = 80
+ average_weight = 1600
+ weight_size_deviation = 0.35
stable_population = 3
favorite_bait = list(
list(
@@ -81,6 +87,55 @@
required_temperature_max = MIN_AQUARIUM_TEMP+30
beauty = FISH_BEAUTY_GOOD
+/obj/item/fish/tadpole
+ name = "tadpole"
+ desc = "The larval spawn of an amphibian. A very minuscle, round creature with a long tail it uses to swim around."
+ icon_state = "tadpole"
+ dedicated_in_aquarium_icon_state = "tadpole small"
+ average_size = 3
+ average_weight = 10
+ sprite_width = 3
+ sprite_height = 1
+ health = 50
+ feeding_frequency = 1.5 MINUTES
+ required_temperature_min = MIN_AQUARIUM_TEMP+15
+ required_temperature_max = MIN_AQUARIUM_TEMP+20
+ fillet_type = null
+ fish_traits = list(/datum/fish_trait/no_mating) //They grow into frogs and that's it.
+ beauty = FISH_BEAUTY_NULL
+ random_case_rarity = FISH_RARITY_NOPE //Why would you want generic frog tadpoles you get from ponds inside fish cases?
+ /// Once dead, tadpoles disappear after a dozen seconds, since you can get infinite tadpoles.
+ var/del_timerid
+
+/obj/item/fish/tadpole/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ AddComponent(/datum/component/fish_growth, /mob/living/basic/frog, 100 / rand(2.5, 3 MINUTES) * 10)
+ RegisterSignal(src, COMSIG_FISH_BEFORE_GROWING, PROC_REF(growth_checks))
+ RegisterSignal(src, COMSIG_FISH_FINISH_GROWING, PROC_REF(on_growth))
+
+/obj/item/fish/tadpole/set_status(new_status, silent = FALSE)
+ . = ..()
+ if(status == FISH_DEAD)
+ del_timerid = QDEL_IN_STOPPABLE(src, 12 SECONDS)
+ else
+ deltimer(del_timerid)
+
+/obj/item/fish/tadpole/proc/growth_checks(datum/source, seconds_per_tick)
+ SIGNAL_HANDLER
+ var/hunger = CLAMP01((world.time - last_feeding) / feeding_frequency)
+ if(hunger >= 0.7) //too hungry to grow
+ return COMPONENT_DONT_GROW
+ var/obj/structure/aquarium/aquarium = loc
+ if(!aquarium.allow_breeding) //the aquarium has breeding disabled
+ return COMPONENT_DONT_GROW
+
+/obj/item/fish/tadpole/proc/on_growth(datum/source, mob/living/basic/frog/result)
+ SIGNAL_HANDLER
+ playsound(result, result.attack_sound, 50, TRUE) // reeeeeeeeeeeeeee...
+
+/obj/item/fish/tadpole/get_export_price(price, percent)
+ return 2 //two credits. Tadpoles aren't really that valueable.
+
// Saltwater fish below
/obj/item/fish/clownfish
@@ -110,6 +165,7 @@
evolution_types = null
compatible_types = list(/obj/item/fish/clownfish)
food = /datum/reagent/lube
+ fishing_difficulty_modifier = 5
beauty = FISH_BEAUTY_GREAT
/obj/item/fish/cardinal
@@ -151,7 +207,7 @@
average_weight = 500
stable_population = 3
disliked_bait = list(/obj/item/food/bait/worm, /obj/item/food/bait/doughball)
- fish_ai_type = FISH_AI_ZIPPY
+ fish_movement_type = /datum/fish_movement/zippy
required_temperature_min = MIN_AQUARIUM_TEMP+23
required_temperature_max = MIN_AQUARIUM_TEMP+28
@@ -167,10 +223,10 @@
stable_population = 3
required_temperature_min = MIN_AQUARIUM_TEMP+23
required_temperature_max = MIN_AQUARIUM_TEMP+28
+ fillet_type = /obj/item/food/fishmeat/quality //Too bad they're poisonous
fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/toxic)
beauty = FISH_BEAUTY_GOOD
-
/obj/item/fish/lanternfish
name = "lanternfish"
desc = "Typically found in areas below 6600 feet below the surface of the ocean, they live in complete darkness."
@@ -181,8 +237,8 @@
source_height = 21
sprite_width = 8
sprite_height = 8
- average_size = 100
- average_weight = 1500
+ average_size = 50
+ average_weight = 1000
stable_population = 3
fish_traits = list(/datum/fish_trait/nocturnal)
required_temperature_min = MIN_AQUARIUM_TEMP+2 //My source is that the water at a depth 6600 feet is pretty darn cold.
@@ -197,8 +253,8 @@
required_fluid_type = AQUARIUM_FLUID_SALTWATER
stable_population = 2
fillet_type = /obj/item/food/fishmeat/moonfish
- average_size = 100
- average_weight = 2000
+ average_size = 60
+ average_weight = 1000
required_temperature_min = MIN_AQUARIUM_TEMP+20
required_temperature_max = MIN_AQUARIUM_TEMP+30
beauty = FISH_BEAUTY_GOOD
@@ -222,6 +278,7 @@
sprite_width = 7
required_fluid_type = AQUARIUM_FLUID_SALTWATER
stable_population = 12
+ breeding_timeout = 1 MINUTES
fillet_type = null
average_size = 20
average_weight = 300
@@ -236,14 +293,17 @@
dedicated_in_aquarium_icon_state = "armorfish_small"
sprite_height = 5
sprite_width = 6
+ average_size = 25
+ average_weight = 350
required_fluid_type = AQUARIUM_FLUID_SALTWATER
stable_population = 10
+ breeding_timeout = 1.25 MINUTES
fillet_type = /obj/item/food/fishmeat/armorfish
- fish_ai_type = FISH_AI_SLOW
+ fish_movement_type = /datum/fish_movement/slow
required_temperature_min = MIN_AQUARIUM_TEMP+10
required_temperature_max = MIN_AQUARIUM_TEMP+32
-//Chasm fish
+/// Commonly found on the mining fishing spots. Can be grown into lobstrosities
/obj/item/fish/chasm_crab
name = "chasm chrab"
desc = "The young of the lobstrosity mature in pools below the earth, eating what falls in until large enough to clamber out. Those found near the station are well-fed."
@@ -252,7 +312,7 @@
sprite_height = 9
sprite_width = 8
stable_population = 4
- feeding_frequency = 15 MINUTES
+ feeding_frequency = 10 MINUTES
random_case_rarity = FISH_RARITY_RARE
fillet_type = /obj/item/food/meat/slab/rawcrab
required_temperature_min = MIN_AQUARIUM_TEMP+9
@@ -270,17 +330,84 @@
evolution_types = list(/datum/fish_evolution/ice_chrab)
compatible_types = list(/obj/item/fish/chasm_crab/ice)
beauty = FISH_BEAUTY_GOOD
+ ///This value represents how much the crab needs aren't being met. Higher values translate to a more likely hostile lobstrosity.
+ var/anger = 0
+ ///The lobstrosity type this matures into
+ var/lob_type = /mob/living/basic/mining/lobstrosity/juvenile/lava
+ ///at which rate the crab gains maturation
+ var/growth_rate = 100 / (10 MINUTES) * 10
+
+/obj/item/fish/chasm_crab/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ RegisterSignal(src, COMSIG_FISH_BEFORE_GROWING, PROC_REF(growth_checks))
+ RegisterSignal(src, COMSIG_FISH_FINISH_GROWING, PROC_REF(on_growth))
+
+///A chasm crab growth speed is determined by its initial weight and size, ergo bigger crabs for faster lobstrosities
+/obj/item/fish/chasm_crab/update_size_and_weight(new_size = average_size, new_weight = average_weight)
+ . = ..()
+ var/multiplier = 1
+ switch(size)
+ if(0 to FISH_SIZE_TINY_MAX)
+ multiplier -= 0.2
+ if(FISH_SIZE_SMALL_MAX to FISH_SIZE_NORMAL_MAX)
+ multiplier += 0.2
+ if(FISH_SIZE_NORMAL_MAX to FISH_SIZE_BULKY_MAX)
+ multiplier += 0.5
+ if(FISH_SIZE_BULKY_MAX to INFINITY)
+ multiplier += 0.8
+
+ if(weight <= 800)
+ multiplier -= 0.1 * round((1000 - weight) / 200)
+ else if(weight >= 1500)
+ multiplier += min(0.1 * round((weight - 1000) / 500), 2)
+
+ AddComponent(/datum/component/fish_growth, lob_type, initial(growth_rate) * multiplier)
+
+/obj/item/fish/chasm_crab/proc/growth_checks(datum/source, seconds_per_tick)
+ SIGNAL_HANDLER
+ var/hunger = CLAMP01((world.time - last_feeding) / feeding_frequency)
+ if(health <= initial(health) * 0.6 || hunger >= 0.6) //if too hurt or hungry, don't grow.
+ anger += growth_rate * 2 * seconds_per_tick
+ return COMPONENT_DONT_GROW
+
+ if(hunger >= 0.4) //I'm hungry and angry
+ anger += growth_rate * 0.6 * seconds_per_tick
+
+ if(!isaquarium(loc))
+ return
+
+ var/obj/structure/aquarium/aquarium = loc
+ if(!aquarium.allow_breeding) //the aquarium has breeding disabled
+ return COMPONENT_DONT_GROW
+ if(!locate(/obj/item/aquarium_prop) in aquarium) //the aquarium deco is quite barren
+ anger += growth_rate * 0.25 * seconds_per_tick
+ var/fish_count = length(aquarium.get_fishes())
+ if(!ISINRANGE(fish_count, 3, AQUARIUM_MAX_BREEDING_POPULATION * 0.5)) //too lonely or overcrowded
+ anger += growth_rate * 0.3 * seconds_per_tick
+ if(fish_count > AQUARIUM_MAX_BREEDING_POPULATION * 0.5) //check if there's enough room to maturate.
+ return COMPONENT_DONT_GROW
+
+/obj/item/fish/chasm_crab/proc/on_growth(datum/source, mob/living/basic/mining/lobstrosity/juvenile/result)
+ SIGNAL_HANDLER
+ if(!prob(anger))
+ result.AddElement(/datum/element/ai_retaliate)
+ qdel(result.ai_controller)
+ result.ai_controller = new /datum/ai_controller/basic_controller/lobstrosity/juvenile/calm(result)
+ else if(anger < 30) //not really that mad, just a bit unstable.
+ qdel(result.ai_controller)
+ result.ai_controller = new /datum/ai_controller/basic_controller/lobstrosity/juvenile/capricious(result)
/obj/item/fish/chasm_crab/ice
name = "arctic chrab"
desc = "A subspecies of chasm chrabs that has adapted to the cold climate and lack of abysmal holes of the icemoon."
icon_state = "arctic_chrab"
- dedicated_in_aquarium_icon_state = "ice_chrab_small"
+ dedicated_in_aquarium_icon_state = "arctic_chrab_small"
required_temperature_min = ICEBOX_MIN_TEMPERATURE-20
required_temperature_max = MIN_AQUARIUM_TEMP+15
evolution_types = list(/datum/fish_evolution/chasm_chrab)
compatible_types = list(/obj/item/fish/chasm_crab)
beauty = FISH_BEAUTY_GREAT
+ lob_type = /mob/living/basic/mining/lobstrosity/juvenile
/obj/item/fish/donkfish
name = "donk co. company patent donkfish"
@@ -317,7 +444,7 @@
sprite_height = 5
stable_population = 12
average_size = 110
- average_weight = 10000
+ average_weight = 6000
random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
required_temperature_min = MIN_AQUARIUM_TEMP+10
required_temperature_max = MIN_AQUARIUM_TEMP+30
@@ -340,7 +467,7 @@
fish_traits = list(/datum/fish_trait/necrophage)
required_temperature_min = MIN_AQUARIUM_TEMP+15
required_temperature_max = MIN_AQUARIUM_TEMP+35
- fish_ai_type = FISH_AI_ZIPPY
+ fish_movement_type = /datum/fish_movement/zippy
favorite_bait = list(
list(
"Type" = "Foodtype",
@@ -349,7 +476,7 @@
)
beauty = FISH_BEAUTY_DISGUSTING
-/obj/item/fish/ratfish/Initialize(mapload)
+/obj/item/fish/ratfish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
//stable pop reflects the config for how many mice migrate. powerful...
stable_population = CONFIG_GET(number/mice_roundstart)
@@ -366,7 +493,7 @@
average_size = 20
average_weight = 400
health = 50
- breeding_timeout = 5 MINUTES
+ breeding_timeout = 2.5 MINUTES
fish_traits = list(/datum/fish_trait/parthenogenesis, /datum/fish_trait/no_mating)
required_temperature_min = MIN_AQUARIUM_TEMP+10
required_temperature_max = MIN_AQUARIUM_TEMP+40
@@ -418,13 +545,13 @@
dedicated_in_aquarium_icon_state = "bonemass_small"
sprite_width = 10
sprite_height = 7
- fish_ai_type = FISH_AI_ZIPPY
+ fish_movement_type = /datum/fish_movement/zippy
random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
required_fluid_type = AQUARIUM_FLUID_ANY_WATER
min_pressure = HAZARD_LOW_PRESSURE
health = 150
stable_population = 3
- grind_results = list(/datum/reagent/bone_dust = 20)
+ grind_results = list(/datum/reagent/bone_dust = 10)
fillet_type = /obj/item/stack/sheet/bone
num_fillets = 2
fish_traits = list(/datum/fish_trait/revival, /datum/fish_trait/carnivore)
@@ -437,9 +564,9 @@
/obj/item/fish/mastodon
name = "unmarine mastodon"
desc = "A monster of exposed muscles and innards, wrapped in a fish-like skeleton. You don't remember ever seeing it on the catalog."
- icon = 'icons/obj/aquarium/wide.dmi'
+ icon = 'icons/obj/structures/aquarium/wide.dmi'
icon_state = "mastodon"
- dedicated_in_aquarium_icon = 'icons/obj/aquarium/fish.dmi'
+ dedicated_in_aquarium_icon = 'icons/obj/structures/aquarium/fish.dmi'
dedicated_in_aquarium_icon_state = "mastodon_small"
base_pixel_x = -16
pixel_x = -16
@@ -447,16 +574,16 @@
sprite_height = 7
show_in_catalog = FALSE
random_case_rarity = FISH_RARITY_NOPE
- fishing_difficulty_modifier = 5
+ fishing_difficulty_modifier = 30
required_fluid_type = AQUARIUM_FLUID_ANY_WATER
min_pressure = HAZARD_LOW_PRESSURE
health = 300
- stable_population = 2 //This means they can only crossbreed.
- grind_results = list(/datum/reagent/bone_dust = 15, /datum/reagent/consumable/liquidgibs = 5)
+ stable_population = 1 //This means they can only crossbreed.
+ grind_results = list(/datum/reagent/bone_dust = 5, /datum/reagent/consumable/liquidgibs = 5)
fillet_type = /obj/item/stack/sheet/bone
num_fillets = 2
feeding_frequency = 2 MINUTES
- breeding_timeout = 10 MINUTES
+ breeding_timeout = 5 MINUTES
average_size = 180
average_weight = 5000
death_text = "%SRC stops moving."
@@ -481,7 +608,7 @@
fish_traits = list(/datum/fish_trait/no_mating) //just to be sure, these shouldn't reproduce
experisci_scannable = FALSE
-/obj/item/fish/holo/Initialize(mapload)
+/obj/item/fish/holo/Initialize(mapload, apply_qualities = TRUE)
. = ..()
var/area/station/holodeck/holo_area = get_area(src)
if(!istype(holo_area))
@@ -489,7 +616,7 @@
return
holo_area.linked.add_to_spawned(src)
-/obj/item/fish/holo/set_status(new_status)
+/obj/item/fish/holo/set_status(new_status, silent = FALSE)
. = ..()
if(status == FISH_DEAD)
animate(src, alpha = 0, 3 SECONDS, easing = SINE_EASING)
@@ -565,12 +692,12 @@
safe_air_limits = null
min_pressure = 0
max_pressure = INFINITY
- grind_results = list(/datum/reagent/bluespace = 10, /datum/reagent/consumable/liquidgibs = 5)
+ grind_results = list(/datum/reagent/bluespace = 10)
fillet_type = null
fish_traits = list(/datum/fish_trait/antigrav, /datum/fish_trait/mixotroph)
beauty = FISH_BEAUTY_GREAT
-/obj/item/fish/starfish/Initialize(mapload)
+/obj/item/fish/starfish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
update_appearance(UPDATE_OVERLAYS)
@@ -593,7 +720,7 @@
average_weight = 500
resistance_flags = FIRE_PROOF | LAVA_PROOF
required_fluid_type = AQUARIUM_FLUID_ANY_WATER //if we can survive hot lava and freezing plasrivers, we can survive anything
- fish_ai_type = FISH_AI_ZIPPY
+ fish_movement_type = /datum/fish_movement/zippy
min_pressure = HAZARD_LOW_PRESSURE
required_temperature_min = MIN_AQUARIUM_TEMP+30
required_temperature_max = MIN_AQUARIUM_TEMP+35
@@ -608,7 +735,7 @@
///maximum bonus damage when winded up
var/maximum_bonus = 25
-/obj/item/fish/lavaloop/Initialize(mapload)
+/obj/item/fish/lavaloop/Initialize(mapload, apply_qualities = TRUE)
. = ..()
ADD_TRAIT(src, TRAIT_BYPASS_RANGED_ARMOR, INNATE_TRAIT)
AddComponent(/datum/component/boomerang, throw_range, TRUE)
@@ -679,3 +806,404 @@
//anxiety naturally limits the amount of zipzaps per tank, so they are stronger alone
electrogenesis_power = 20 MEGA JOULES
beauty = FISH_BEAUTY_GOOD
+
+/obj/item/fish/sockeye_salmon
+ name = "sockeye salmon"
+ desc = "A fairly common and iconic salmon endemic of the Pacific Ocean. At some point imported into outer space, where we're now."
+ icon_state = "sockeye"
+ dedicated_in_aquarium_icon_state = "sockeye_small"
+ sprite_width = 6
+ sprite_height = 4
+ stable_population = 6
+ required_temperature_min = MIN_AQUARIUM_TEMP+3
+ required_temperature_max = MIN_AQUARIUM_TEMP+19
+ required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
+ fillet_type = /obj/item/food/fishmeat/salmon
+ beauty = FISH_BEAUTY_GOOD
+
+/obj/item/fish/arctic_char
+ name = "arctic char"
+ desc = "A cold-water anadromous fish widespread around the Northern Hemisphere of Earth, yet it has somehow found a way here."
+ icon_state = "arctic_char"
+ dedicated_in_aquarium_icon_state = "arctic_char"
+ sprite_width = 7
+ sprite_height = 4
+ stable_population = 6
+ average_size = 60
+ average_weight = 1200
+ weight_size_deviation = 0.5 // known for their size dismophism
+ required_temperature_min = MIN_AQUARIUM_TEMP+3
+ required_temperature_max = MIN_AQUARIUM_TEMP+19
+ required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
+
+/obj/item/fish/stingray
+ name = "stingray"
+ desc = "A type of ray, most known for its venomous stinger. Despite that, They're normally docile, if not a bit easily frightened."
+ icon_state = "stingray"
+ dedicated_in_aquarium_icon_state = "stingray_small"
+ stable_population = 4
+ sprite_height = 7
+ sprite_width = 8
+ average_size = 60
+ average_weight = 700
+ beauty = FISH_BEAUTY_GREAT
+ random_case_rarity = FISH_RARITY_RARE
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER //Someone ought to add river rays later I guess.
+ fish_traits = list(/datum/fish_trait/stinger, /datum/fish_trait/toxic_barbs, /datum/fish_trait/wary, /datum/fish_trait/carnivore, /datum/fish_trait/predator)
+
+/obj/item/fish/sand_surfer
+ name = "sand surfer"
+ desc = "A bronze alien \"fish\" living and swimming underneath faraway sandy places."
+ icon_state = "sand_surfer"
+ dedicated_in_aquarium_icon_state = "sand_surfer_small"
+ sprite_height = 6
+ sprite_width = 6
+ stable_population = 5
+ average_size = 65
+ average_weight = 1100
+ weight_size_deviation = 0.35
+ random_case_rarity = FISH_RARITY_RARE
+ required_fluid_type = AQUARIUM_FLUID_AIR
+ required_temperature_min = MIN_AQUARIUM_TEMP+25
+ required_temperature_max = MIN_AQUARIUM_TEMP+60
+ fish_movement_type = /datum/fish_movement/plunger
+ fishing_difficulty_modifier = 5
+ fish_traits = list(/datum/fish_trait/shiny_lover)
+ beauty = FISH_BEAUTY_GOOD
+
+/obj/item/fish/sand_crab
+ name = "burrower crab"
+ desc = "A sand-dwelling crustacean. It looks like a crab and tastes like a crab, but waddles like a fish."
+ icon_state = "crab"
+ dedicated_in_aquarium_icon_state = "crab_small"
+ sprite_height = 6
+ sprite_width = 10
+ average_size = 60
+ average_weight = 1000
+ weight_size_deviation = 0.1
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ required_temperature_min = MIN_AQUARIUM_TEMP+20
+ required_temperature_max = MIN_AQUARIUM_TEMP+40
+ fillet_type = /obj/item/food/meat/slab/rawcrab
+ fish_traits = list(/datum/fish_trait/amphibious, /datum/fish_trait/shiny_lover, /datum/fish_trait/carnivore)
+ fish_movement_type = /datum/fish_movement/slow
+ favorite_bait = list(
+ list(
+ "Type" = "Foodtype",
+ "Value" = SEAFOOD,
+ ),
+ )
+
+/obj/item/fish/bumpy
+ name = "bump-fish"
+ desc = "An misshapen fish-thing all covered in stubby little tendrils"
+ icon_state = "bumpy"
+ dedicated_in_aquarium_icon_state = "bumpy_small"
+ sprite_height = 4
+ sprite_width = 5
+ stable_population = 4
+ required_fluid_type = AQUARIUM_FLUID_ANY_WATER
+ required_temperature_min = MIN_AQUARIUM_TEMP+15
+ required_temperature_max = MIN_AQUARIUM_TEMP+40
+ beauty = FISH_BEAUTY_BAD
+ fish_traits = list(/datum/fish_trait/amphibious, /datum/fish_trait/vegan)
+ favorite_bait = list(
+ list(
+ "Type" = "Foodtype",
+ "Value" = VEGETABLES,
+ ),
+ )
+
+/obj/item/fish/three_eyes
+ name = "three-eyed goldfish"
+ desc = "A goldfish with an extra half a pair of eyes. You wonder what it's been feeding on lately..."
+ icon_state = "three_eyes"
+ sprite_width = 8
+ sprite_height = 8
+ average_size = 30
+ average_weight = 500
+ stable_population = 4
+ fish_traits = list(/datum/fish_trait/recessive, /datum/fish_trait/shiny_lover)
+ compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/goldfish/gill, /obj/item/fish/three_eyes/gill)
+ beauty = FISH_BEAUTY_GOOD
+ fishing_difficulty_modifier = 10
+ random_case_rarity = FISH_RARITY_VERY_RARE
+ food = /datum/reagent/toxin/mutagen
+ favorite_bait = list(
+ list(
+ "Type" = "Reagent",
+ "Value" = /datum/reagent/toxin/mutagen,
+ "Amount" = 3,
+ ),
+ )
+
+/obj/item/fish/three_eyes/gill
+ name = "McGill"
+ desc = "A great rubber duck tool for Lawyers who can't get a grasp over their case. It looks kinda different today..."
+ compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/three_eyes)
+ beauty = FISH_BEAUTY_GREAT
+ show_in_catalog = FALSE
+ stable_population = 1
+ random_case_rarity = FISH_RARITY_NOPE
+
+/obj/item/fish/swordfish
+ name = "swordfish"
+ desc = "A large billfish, most famous for its elongated bill, while also fairly popular for cooking, and as a fearsome weapon in the hands of a veteran spess-fisherman."
+ icon = 'icons/obj/structures/aquarium/wide.dmi'
+ icon_state = "swordfish"
+ inhand_icon_state = "swordfish"
+ dedicated_in_aquarium_icon = 'icons/obj/structures/aquarium/fish.dmi'
+ dedicated_in_aquarium_icon_state = "swordfish_small"
+ force = 18
+ sharpness = SHARP_EDGED
+ attack_verb_continuous = list("slashes", "cuts", "pierces")
+ attack_verb_simple = list("slash", "cut", "pierce")
+ block_sound = 'sound/weapons/parry.ogg'
+ hitsound = 'sound/weapons/rapierhit.ogg'
+ demolition_mod = 0.75
+ attack_speed = 1 SECONDS
+ block_chance = 50
+ wound_bonus = 10
+ bare_wound_bonus = 20
+ armour_penetration = 75
+ base_pixel_x = -18
+ pixel_x = -18
+ sprite_width = 13
+ sprite_height = 6
+ stable_population = 3
+ average_size = 140
+ average_weight = 4000
+ breeding_timeout = 4.5 MINUTES
+ feeding_frequency = 4 MINUTES
+ health = 180
+ beauty = FISH_BEAUTY_EXCELLENT
+ random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ fish_movement_type = /datum/fish_movement/plunger
+ fishing_difficulty_modifier = 25
+ fillet_type = /obj/item/food/fishmeat/quality
+ favorite_bait = list(
+ list(
+ "Type" = "Foodtype",
+ "Value" = SEAFOOD,
+ ),
+ )
+ fish_traits = list(/datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/stinger)
+
+/obj/item/fish/swordfish/get_force_rank()
+ switch(w_class)
+ if(WEIGHT_CLASS_TINY)
+ force -= 11
+ attack_speed -= 0.4 SECONDS
+ block_chance -= 45
+ armour_penetration -= 20
+ wound_bonus -= 15
+ bare_wound_bonus -= 20
+ if(WEIGHT_CLASS_SMALL)
+ force -= 8
+ attack_speed -= 0.3 SECONDS
+ block_chance -= 30
+ armour_penetration -= 15
+ wound_bonus -= 10
+ bare_wound_bonus -= 20
+ if(WEIGHT_CLASS_NORMAL)
+ force -= 5
+ attack_speed -= 0.2 SECONDS
+ block_chance -= 20
+ armour_penetration -= 10
+ wound_bonus -= 10
+ bare_wound_bonus -= 15
+ if(WEIGHT_CLASS_BULKY)
+ force -= 3
+ attack_speed -= 0.1 SECONDS
+ block_chance -= 10
+ armour_penetration -= 5
+ wound_bonus -= 5
+ bare_wound_bonus -= 10
+ if(WEIGHT_CLASS_GIGANTIC)
+ force += 5
+ attack_speed += 0.2 SECONDS
+ demolition_mod += 0.15
+ block_chance += 10
+ armour_penetration += 5
+ wound_bonus += 5
+ bare_wound_bonus += 10
+
+ if(status == FISH_DEAD)
+ force -= 4 + w_class
+ block_chance -= 25
+ armour_penetration -= 30
+ wound_bonus -= 10
+ bare_wound_bonus -= 10
+
+/obj/item/fish/swordfish/calculate_fish_force_bonus(bonus_malus)
+ . = ..()
+ armour_penetration += bonus_malus * 5
+ wound_bonus += bonus_malus * 3
+ bare_wound_bonus += bonus_malus * 5
+ block_chance += bonus_malus * 7
+
+/obj/item/fish/chainsawfish
+ name = "chainsawfish"
+ desc = "A very, very angry bioweapon, whose sole purpose is to rip and tear."
+ icon = 'icons/obj/structures/aquarium/wide.dmi'
+ icon_state = "chainsawfish"
+ inhand_icon_state = "chainsawfish"
+ icon_state_dead = "chainsawfish_dead"
+ dedicated_in_aquarium_icon = 'icons/obj/structures/aquarium/fish.dmi'
+ dedicated_in_aquarium_icon_state = "chainsaw_small"
+ force = 22
+ demolition_mod = 1.5
+ block_chance = 15
+ attack_verb_continuous = list("saws", "tears", "lacerates", "cuts", "chops", "dices")
+ attack_verb_simple = list("saw", "tear", "lacerate", "cut", "chop", "dice")
+ hitsound = 'sound/weapons/chainsawhit.ogg'
+ sharpness = SHARP_EDGED
+ tool_behaviour = TOOL_SAW
+ toolspeed = 0.5
+ base_pixel_x = -16
+ pixel_x = -16
+ sprite_width = 8
+ sprite_height = 5
+ stable_population = 3
+ average_size = 85
+ average_weight = 2500
+ breeding_timeout = 4.25 MINUTES
+ feeding_frequency = 3 MINUTES
+ health = 180
+ beauty = FISH_BEAUTY_GREAT
+ random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
+ required_fluid_type = AQUARIUM_FLUID_FRESHWATER
+ fish_movement_type = /datum/fish_movement/accelerando
+ fishing_difficulty_modifier = 30
+ favorite_bait = list(
+ list(
+ "Type" = "Foodtype",
+ "Value" = GORE
+ ),
+ )
+ fish_traits = list(/datum/fish_trait/aggressive, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/stinger)
+ required_temperature_min = MIN_AQUARIUM_TEMP+18
+ required_temperature_max = MIN_AQUARIUM_TEMP+26
+
+/obj/item/fish/chainsawfish/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+
+/obj/item/fish/chainsawfish/update_icon_state()
+ if(status == FISH_DEAD)
+ inhand_icon_state = "chainsawfish_dead"
+ else
+ inhand_icon_state = "chainsawfish"
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
+ inhand_icon_state = "[inhand_icon_state]_wielded"
+ return ..()
+
+/obj/item/fish/chainsawfish/get_force_rank()
+ switch(w_class)
+ if(WEIGHT_CLASS_TINY)
+ force -= 10
+ attack_speed -= 0.2 SECONDS
+ demolition_mod -= 0.4
+ block_chance -= 15
+ armour_penetration -= 10
+ wound_bonus -= 10
+ bare_wound_bonus -= 10
+ toolspeed += 0.6
+ if(WEIGHT_CLASS_SMALL)
+ force -= 8
+ attack_speed -= 0.1 SECONDS
+ demolition_mod -= 0.3
+ block_chance -= 10
+ armour_penetration -= 10
+ wound_bonus -= 10
+ bare_wound_bonus -= 10
+ toolspeed += 0.4
+ if(WEIGHT_CLASS_NORMAL)
+ force -= 5
+ demolition_mod -= 0.15
+ block_chance -= 5
+ armour_penetration -= 5
+ wound_bonus -= 5
+ bare_wound_bonus -= 5
+ toolspeed += 0.2
+ if(WEIGHT_CLASS_HUGE)
+ force += 2
+ attack_speed += 0.2 SECONDS
+ demolition_mod += 0.15
+ armour_penetration += 10
+ block_chance += 10
+ wound_bonus += 10
+ bare_wound_bonus += 5
+ if(WEIGHT_CLASS_GIGANTIC)
+ force += 4
+ attack_speed += 0.4 SECONDS
+ demolition_mod += 0.3
+ block_chance += 20
+ armour_penetration += 20
+ wound_bonus += 15
+ bare_wound_bonus += 10
+ toolspeed -= 0.1
+
+ if(status == FISH_DEAD)
+ force -= 8 + w_class
+ hitsound = SFX_SWING_HIT
+ block_chance -= 25
+ demolition_mod -= 0.3
+ armour_penetration -= 15
+ wound_bonus -= 5
+ bare_wound_bonus -= 5
+ toolspeed += 1
+
+/obj/item/fish/chainsawfish/calculate_fish_force_bonus(bonus_malus)
+ . = ..()
+ armour_penetration += bonus_malus * 3
+ wound_bonus += bonus_malus * 2
+ bare_wound_bonus += bonus_malus * 3
+ block_chance += bonus_malus * 2
+ toolspeed -= bonus_malus * 0.1
+
+/obj/item/fish/soul
+ name = "soulfish"
+ desc = "A distant yet vaguely close critter, like a long lost relative. You feel your soul rejuvenated just from looking at it... Also, what the fuck is this shit?!"
+ icon_state = "soulfish"
+ dedicated_in_aquarium_icon_state = "soul_small"
+ sprite_width = 7
+ sprite_height = 6
+ average_size = 60
+ average_weight = 1200
+ stable_population = 4
+ show_in_catalog = FALSE
+ beauty = FISH_BEAUTY_EXCELLENT
+ fish_movement_type = /datum/fish_movement/choppy //Glideless legacy movement? in my fishing minigame?
+ favorite_bait = list(
+ list(
+ "Type" = "Foodtype",
+ "Value" = FRIED
+ ),
+ )
+ fillet_type = /obj/item/food/meat/cutlet/plain/human
+ required_temperature_min = MIN_AQUARIUM_TEMP+3
+ required_temperature_max = MIN_AQUARIUM_TEMP+38
+ random_case_rarity = FISH_RARITY_NOPE
+
+/obj/item/fish/skin_crab
+ name = "skin crab"
+ desc = "\"And on the eighth day, a demential mockery of both humanity and crabity was made.\" Fascinating."
+ icon_state = "skin_crab"
+ dedicated_in_aquarium_icon_state = "skin_crab_small"
+ sprite_width = 7
+ sprite_height = 6
+ average_size = 40
+ average_weight = 750
+ stable_population = 5
+ show_in_catalog = FALSE
+ beauty = FISH_BEAUTY_GREAT
+ favorite_bait = list(
+ list(
+ "Type" = "Foodtype",
+ "Value" = FRIED
+ ),
+ )
+ fillet_type = /obj/item/food/meat/slab/rawcrab
+ random_case_rarity = FISH_RARITY_NOPE
diff --git a/code/modules/fishing/fish_movement.dm b/code/modules/fishing/fish_movement.dm
new file mode 100644
index 0000000000000..a328903617f62
--- /dev/null
+++ b/code/modules/fishing/fish_movement.dm
@@ -0,0 +1,214 @@
+/// Any lower than this, and the target position of the fish is considered null
+#define FISH_TARGET_MIN_DISTANCE 6
+/// The friction applied to fish jumps, so that it decelerates over time
+#define FISH_FRICTION_MULT 0.9
+/// Used to decide whether the fish can jump in a certain direction
+#define FISH_SHORT_JUMP_MIN_DISTANCE 100
+/// The maximum distance for a short jump
+#define FISH_SHORT_JUMP_MAX_DISTANCE 200
+
+///Fish movements are simple datums, generated by the fishing minigame, that represent how the fish moves suring the minigame.
+/datum/fish_movement
+ /// The minigame that spawned us
+ var/datum/fishing_challenge/master
+ /// How many times move_fish() has been called
+ var/times_fired = 0
+ /// How likely the fish is to perform a standard jump, then multiplied by difficulty
+ var/short_jump_chance = 2.25
+ /// How likely the fish is to perform a long jump, then multiplied by difficulty
+ var/long_jump_chance = 0.0625
+ /// The speed limit for the short jump
+ var/short_jump_velocity_limit = 400
+ /// The speed limit for the long jump
+ var/long_jump_velocity_limit = 200
+ /// The current speed limit used
+ var/current_velocity_limit
+ /// The base velocity of the fish, which may affect jump distances and falling speed.
+ var/fish_idle_velocity = 0
+ /// A position on the slider the fish wants to get to
+ var/target_position
+ /// If true, the fish can jump while a target position is set, thus overriding it
+ var/can_interrupt_move = TRUE
+ /// The current speed the fish is moving at
+ var/fish_velocity = 0
+
+/datum/fish_movement/New(datum/fishing_challenge/master)
+ src.master = master
+
+/**
+ * Proc that adjusts movement values to the difficulty of the minigame.
+ * The operations can be a tad complex, but basically it ensures that jump
+ * chances with a probability higher than 1% increase in a smooth curve so that
+ * they still reach 100% prob when the difficulty peakes.
+ */
+/datum/fish_movement/proc/adjust_to_difficulty()
+ var/square_angle_rad = TORADIANS(90)
+ var/zero_one_difficulty = master.difficulty/100
+ if(short_jump_chance > 1)
+ short_jump_chance = (zero_one_difficulty**(square_angle_rad-TORADIANS(arctan(short_jump_chance * 1/square_angle_rad))))*100
+ else
+ short_jump_chance *= master.difficulty
+ if(long_jump_chance > 1)
+ long_jump_chance = (zero_one_difficulty**(square_angle_rad-TORADIANS(arctan(long_jump_chance * 1/square_angle_rad))))*100
+ else
+ long_jump_chance *= master.difficulty
+
+///The main proc, called by minigame every SSfishing tick while it's in the 'active' phase.
+/datum/fish_movement/proc/move_fish(seconds_per_tick)
+ times_fired++
+ /**
+ * The jump chances are meant to run every odd tick (each every decisecond)
+ * We cannot do it every tick because the fish would be jumpier than intended
+ * and we cannot cut the chances in half to fit on each tick, because the maximum probability
+ * would go from 100% to 75%.
+ */
+ var/can_roll = times_fired % 2
+
+ var/long_chance = long_jump_chance * seconds_per_tick * (1/seconds_per_tick)
+ var/short_chance = short_jump_chance * seconds_per_tick * (1/seconds_per_tick)
+
+ // If we have the target but we're close enough, mark as target reached
+ if(abs(target_position - master.fish_position) < FISH_TARGET_MIN_DISTANCE)
+ target_position = null
+
+ // Switching to new long jump target can interrupt any other
+ if(can_roll && (can_interrupt_move || isnull(target_position)) && prob(long_chance))
+ /**
+ * Move at least 0.75 to full of the availible bar in given direction,
+ * and more likely to move in the direction where there's more space
+ */
+ var/distance_from_top = FISHING_MINIGAME_AREA - master.fish_position - master.fish_height
+ var/distance_from_bottom = master.fish_position
+ var/top_chance
+ if(distance_from_top < FISH_SHORT_JUMP_MIN_DISTANCE)
+ top_chance = 0
+ else
+ top_chance = (distance_from_top/max(distance_from_bottom, 1)) * 100
+ var/new_target = master.fish_position
+ if(prob(top_chance))
+ new_target += distance_from_top * rand(75, 100)/100
+ else
+ new_target -= distance_from_bottom * rand(75, 100)/100
+ target_position = round(new_target)
+ current_velocity_limit = long_jump_velocity_limit
+
+ // Move towards target
+ if(!isnull(target_position))
+ var/distance = target_position - master.fish_position
+ // about 5 at diff 15 , 10 at diff 30, 30 at diff 100
+ var/acceleration_mult = get_acceleration(seconds_per_tick)
+ var/target_acceleration = distance * acceleration_mult * seconds_per_tick
+
+ fish_velocity = fish_velocity * FISH_FRICTION_MULT + target_acceleration
+ else if(can_roll && prob(short_chance))
+ var/distance_from_top = FISHING_MINIGAME_AREA - master.fish_position - master.fish_height
+ var/distance_from_bottom = master.fish_position
+ var/jump_length
+ if(distance_from_top >= FISH_SHORT_JUMP_MIN_DISTANCE)
+ jump_length = rand(FISH_SHORT_JUMP_MIN_DISTANCE, FISH_SHORT_JUMP_MAX_DISTANCE)
+ if(distance_from_bottom >= FISH_SHORT_JUMP_MIN_DISTANCE && (!jump_length || prob(50)))
+ jump_length = -rand(FISH_SHORT_JUMP_MIN_DISTANCE, FISH_SHORT_JUMP_MAX_DISTANCE)
+ target_position = clamp(master.fish_position + jump_length, 0, FISHING_MINIGAME_AREA - master.fish_height)
+ current_velocity_limit = short_jump_velocity_limit
+
+ fish_velocity = clamp(fish_velocity + fish_idle_velocity, -current_velocity_limit, current_velocity_limit)
+ set_fish_position(seconds_per_tick)
+
+///Proc that returns the acceleration of the fish during the minigame.
+/datum/fish_movement/proc/get_acceleration(seconds_per_tick)
+ return 0.3 * master.difficulty + 0.5
+
+///Called at the end of move_fish(), for updating the position of the fish in the fishing minigame.
+/datum/fish_movement/proc/set_fish_position(seconds_per_tick)
+ master.fish_position = clamp(master.fish_position + fish_velocity * seconds_per_tick, 0, FISHING_MINIGAME_AREA - master.fish_height)
+
+///Generic fish movement datum that only performs slow, uninterrupted long jumps
+/datum/fish_movement/slow
+ short_jump_chance = 0
+ long_jump_chance = 1.5
+ long_jump_velocity_limit = 150
+ can_interrupt_move = FALSE
+
+///Generic fish movement datum with triple the short jump chance.
+/datum/fish_movement/zippy
+ short_jump_chance = parent_type::short_jump_chance * 3
+
+///fish movement datum that progressively gets faster until acceleration and velocity are double the starting ones.
+/datum/fish_movement/accelerando
+ ///The jump velocity to add each tick
+ var/short_jump_vel_add
+ ///The long jump velocity to add each tick
+ var/long_jump_vel_add
+ ///Time to reach full speed, in seconds.
+ var/accel_time_cap = 30
+
+/datum/fish_movement/accelerando/move_fish(seconds_per_tick)
+ var/seconds_elapsed = (times_fired * seconds_per_tick)
+ if(seconds_elapsed >= accel_time_cap)
+ return ..()
+ if(!times_fired) //First tick, cache the initial jump velocities
+ short_jump_vel_add = short_jump_velocity_limit/accel_time_cap
+ long_jump_vel_add = long_jump_velocity_limit/accel_time_cap
+ return ..()
+
+ if(current_velocity_limit)
+ var/vel_add = current_velocity_limit == short_jump_velocity_limit ? short_jump_vel_add : long_jump_vel_add
+ current_velocity_limit += round(vel_add * seconds_per_tick, 0.01)
+
+ short_jump_velocity_limit += round(short_jump_vel_add * seconds_per_tick, 0.01)
+ long_jump_velocity_limit += round(long_jump_vel_add * seconds_per_tick, 0.01)
+ return ..()
+
+/datum/fish_movement/accelerando/get_acceleration(seconds_per_tick)
+ var/acceleration = ..()
+ return acceleration + min(acceleration, acceleration * times_fired * seconds_per_tick / accel_time_cap)
+
+/datum/fish_movement/accelerando/set_fish_position(seconds_per_tick)
+ fish_velocity = round(fish_velocity)
+ return ..()
+
+///Fish movement datum that updates the fish position twice per second.
+/datum/fish_movement/choppy
+ ///We keep of the theorical fish position to eventually use
+ var/faux_position = 0
+
+/datum/fish_movement/choppy/set_fish_position(seconds_per_tick)
+ faux_position = clamp(faux_position + fish_velocity * seconds_per_tick, 0, FISHING_MINIGAME_AREA - master.fish_height)
+ if(!((times_fired * SSfishing.wait) % (0.5 SECONDS)))
+ master.fish_position = faux_position
+
+///Fish movement datum that weakly pushes the fish up and then down with greater force once it reaches the top of the minigame.
+/datum/fish_movement/plunger
+ ///Is the fish plunging to the bottom of the minigame area, or should it swim up?
+ var/is_plunging = TRUE
+ ///The added idle velocity when plunging
+ var/plunging_speed = -22
+
+/datum/fish_movement/plunger/adjust_to_difficulty()
+ . = ..()
+ //Adjust the fleeing velocity, up to five times the initial value.
+ plunging_speed += round(plunging_speed * master.difficulty * 0.03)
+ fish_idle_velocity += plunging_speed //so it can be safely subtracted if the fish starts at the bottom.
+
+/datum/fish_movement/plunger/move_fish(seconds_per_tick)
+ var/fish_area = FISHING_MINIGAME_AREA - master.fish_height
+ if(is_plunging)
+ if(target_position > master.fish_position) //nothing should stop us from plunging.
+ target_position = null
+ var/dist_bot_percent = master.fish_position/fish_area
+ if(dist_bot_percent <= 0.04)
+ fish_idle_velocity -= plunging_speed
+ is_plunging = FALSE
+ else
+ var/dist_top_percent = (fish_area - master.fish_position)/fish_area
+ if(dist_top_percent <= 0.04)
+ fish_idle_velocity += plunging_speed
+ is_plunging = TRUE
+
+ return ..()
+
+
+#undef FISH_TARGET_MIN_DISTANCE
+#undef FISH_FRICTION_MULT
+#undef FISH_SHORT_JUMP_MIN_DISTANCE
+#undef FISH_SHORT_JUMP_MAX_DISTANCE
diff --git a/code/modules/fishing/fishing_equipment.dm b/code/modules/fishing/fishing_equipment.dm
index f6b49a9b52314..b943f2bcdb708 100644
--- a/code/modules/fishing/fishing_equipment.dm
+++ b/code/modules/fishing/fishing_equipment.dm
@@ -260,7 +260,6 @@
// Can hold fishing rod despite the size
var/static/list/exception_cache = typecacheof(list(
/obj/item/fishing_rod,
- /obj/item/fishing_line,
))
atom_storage.exception_hold = exception_cache
@@ -286,30 +285,73 @@
new /obj/item/fishing_hook(src)
new /obj/item/fishing_line(src)
+/obj/item/storage/toolbox/fishing/master
+ name = "super fishing toolbox"
+ desc = "Contains EVERYTHING (almost) you need for your fishing trip."
+ icon_state = "gold"
+ inhand_icon_state = "toolbox_gold"
+
+/obj/item/storage/toolbox/fishing/master/PopulateContents()
+ new /obj/item/fishing_rod/telescopic/master(src)
+ new /obj/item/storage/box/fishing_hooks/master(src)
+ new /obj/item/storage/box/fishing_lines/master(src)
+ new /obj/item/bait_can/super_baits(src)
+ new /obj/item/fish_feed(src)
+ new /obj/item/aquarium_kit(src)
+ new /obj/item/fish_analyzer(src)
+ new /obj/item/experi_scanner(src)
+
/obj/item/storage/box/fishing_hooks
name = "fishing hook set"
+ illustration = "fish"
/obj/item/storage/box/fishing_hooks/PopulateContents()
- . = ..()
new /obj/item/fishing_hook/magnet(src)
new /obj/item/fishing_hook/shiny(src)
new /obj/item/fishing_hook/weighted(src)
+/obj/item/storage/box/fishing_hooks/master
+
+/obj/item/storage/box/fishing_hooks/master/PopulateContents()
+ . = ..()
+ new /obj/item/fishing_hook/stabilized(src)
+ new /obj/item/fishing_hook/jaws(src)
+
/obj/item/storage/box/fishing_lines
name = "fishing line set"
+ illustration = "fish"
/obj/item/storage/box/fishing_lines/PopulateContents()
- . = ..()
new /obj/item/fishing_line/bouncy(src)
new /obj/item/fishing_line/reinforced(src)
new /obj/item/fishing_line/cloaked(src)
+/obj/item/storage/box/fishing_lines/master
+
+/obj/item/storage/box/fishing_lines/master/PopulateContents()
+ . = ..()
+ new /obj/item/fishing_line/auto_reel(src)
+
/obj/item/storage/box/fish_debug
name = "box full of fish"
+ illustration = "fish"
/obj/item/storage/box/fish_debug/PopulateContents()
for(var/fish_type in subtypesof(/obj/item/fish))
new fish_type(src)
+///From the fishing mystery box. It's basically a lazarus and a few bottles of strange reagents.
+/obj/item/storage/box/fish_revival_kit
+ name = "fish revival kit"
+ desc = "Become a fish doctor today."
+ illustration = "fish"
+
+/obj/item/storage/box/fish_revival_kit/PopulateContents()
+ new /obj/item/lazarus_injector(src)
+ new /obj/item/reagent_containers/cup/bottle/strange_reagent(src)
+ new /obj/item/reagent_containers/cup(src) //to splash the reagents on the fish.
+ new /obj/item/storage/fish_case(src)
+ new /obj/item/storage/fish_case(src)
+
#undef MAGNET_HOOK_BONUS_MULTIPLIER
#undef RESCUE_HOOK_FISH_MULTIPLIER
diff --git a/code/modules/fishing/fishing_minigame.dm b/code/modules/fishing/fishing_minigame.dm
index 45739b79399e4..db01410d0e225 100644
--- a/code/modules/fishing/fishing_minigame.dm
+++ b/code/modules/fishing/fishing_minigame.dm
@@ -5,28 +5,20 @@
// UI minigame phase
#define MINIGAME_PHASE 3
-/// The height of the minigame slider. Not in pixels, but minigame units.
-#define FISHING_MINIGAME_AREA 1000
-/// Any lower than this, and the target position of the fish is considered null
-#define FISH_TARGET_MIN_DISTANCE 6
-/// The friction applied to fish jumps, so that it decelerates over time
-#define FISH_FRICTION_MULT 0.9
-/// Used to decide whether the fish can jump in a certain direction
-#define FISH_SHORT_JUMP_MIN_DISTANCE 100
-/// The maximum distance for a short jump
-#define FISH_SHORT_JUMP_MAX_DISTANCE 200
// Acceleration mod when bait is over fish
#define FISH_ON_BAIT_ACCELERATION_MULT 0.6
/// The minimum velocity required for the bait to bounce
#define BAIT_MIN_VELOCITY_BOUNCE 150
/// The extra deceleration of velocity that happens when the bait switches direction
-#define BAIT_DECELERATION_MULT 1.5
+#define BAIT_DECELERATION_MULT 1.8
/// Reduce initial completion rate depending on difficulty
#define MAX_FISH_COMPLETION_MALUS 15
/// The window of time between biting phase and back to baiting phase
#define BITING_TIME_WINDOW 4 SECONDS
+/// The multiplier of how much the difficulty negatively impacts the bait height
+#define BAIT_HEIGHT_DIFFICULTY_MALUS 1.3
///Defines to know how the bait is moving on the minigame slider.
#define REELING_STATE_IDLE 0
@@ -36,7 +28,7 @@
///The pixel height of the minigame bar
#define MINIGAME_SLIDER_HEIGHT 76
///The standard pixel height of the bait
-#define MINIGAME_BAIT_HEIGHT 24
+#define MINIGAME_BAIT_HEIGHT 27
///The standard pixel height of the fish (minus a pixel on each direction for the sake of a better looking sprite)
#define MINIGAME_FISH_HEIGHT 4
@@ -45,8 +37,6 @@
var/start_time
/// Is it finished (either by win/lose or window closing)
var/completed = FALSE
- /// Fish AI type to use
- var/fish_ai = FISH_AI_DUMB
/// Rule modifiers (eg weighted bait)
var/special_effects = NONE
/// A list of possible active minigame effects. If not empty, one will be picked from time to time.
@@ -82,7 +72,7 @@
/// How much space the fish takes on the minigame slider
var/fish_height = 50
/// How much space the bait takes on the minigame slider
- var/bait_height = 320
+ var/bait_height = 360
/// The height in pixels of the bait bar
var/bait_pixel_height = MINIGAME_BAIT_HEIGHT
/// The height in pixels of the fish
@@ -91,8 +81,6 @@
var/fish_position = 0
/// The position of the bait on the minigame slider
var/bait_position = 0
- /// The current speed the fish is moving at
- var/fish_velocity = 0
/// The current speed the bait is moving at
var/bait_velocity = 0
@@ -103,22 +91,7 @@
/// How much completion is gained per second when the bait area is intersecting with the fish's
var/completion_gain = 5
- /// How likely the fish is to perform a standard jump, then multiplied by difficulty
- var/short_jump_chance = 2.25
- /// How likely the fish is to perform a long jump, then multiplied by difficulty
- var/long_jump_chance = 0.0625
- /// The speed limit for the short jump
- var/short_jump_velocity_limit = 400
- /// The speed limit for the long jump
- var/long_jump_velocity_limit = 200
- /// The current speed limit used
- var/current_velocity_limit = 200
- /// The base velocity of the fish, which may affect jump distances and falling speed.
- var/fish_idle_velocity = 0
- /// A position on the slider the fish wants to get to
- var/target_position
- /// If true, the fish can jump while a target position is set, thus overriding it
- var/can_interrupt_move = TRUE
+ var/datum/fish_movement/mover
/// Whether the bait is idle or reeling up or down (left and right click)
var/reeling_state = REELING_STATE_IDLE
@@ -142,24 +115,21 @@
RegisterSignal(comp.fish_source, COMSIG_FISHING_SOURCE_INTERRUPT_CHALLENGE, PROC_REF(interrupt_challenge))
comp.fish_source.RegisterSignal(src, COMSIG_FISHING_CHALLENGE_COMPLETED, TYPE_PROC_REF(/datum/fish_source, on_challenge_completed))
background = comp.fish_source.background
+
/// Fish minigame properties
if(ispath(reward_path,/obj/item/fish))
var/obj/item/fish/fish = reward_path
- fish_ai = initial(fish.fish_ai_type)
- switch(fish_ai)
- if(FISH_AI_ZIPPY) // Keeps on jumping
- short_jump_chance *= 3
- if(FISH_AI_SLOW) // Only does long jump, and doesn't change direction until it gets there
- short_jump_chance = 0
- long_jump_chance = 1.5
- long_jump_velocity_limit = 150
- long_jump_velocity_limit = FALSE
+ var/movement_path = initial(fish.fish_movement_type)
+ mover = new movement_path(src)
// Apply fish trait modifiers
var/list/fish_list_properties = collect_fish_properties()
var/list/fish_traits = fish_list_properties[fish][NAMEOF(fish, fish_traits)]
for(var/fish_trait in fish_traits)
var/datum/fish_trait/trait = GLOB.fish_traits[fish_trait]
trait.minigame_mod(rod, user, src)
+ else
+ mover = new /datum/fish_movement(src)
+
/// Enable special parameters
if(rod.line)
completion_gain += 1 // Any fishing line will provide a small boost by default
@@ -182,36 +152,23 @@
if(rod.hook.fishing_hook_traits & FISHING_HOOK_KILL)
special_effects |= FISHING_MINIGAME_RULE_KILL
+ completion_loss += user.mind?.get_skill_modifier(/datum/skill/fishing, SKILL_VALUE_MODIFIER)/5
+
if(special_effects & FISHING_MINIGAME_RULE_KILL && ispath(reward_path,/obj/item/fish))
- RegisterSignal(user, COMSIG_MOB_FISHING_REWARD_DISPENSED, PROC_REF(hurt_fish))
+ RegisterSignal(comp.fish_source, COMSIG_FISH_SOURCE_REWARD_DISPENSED, PROC_REF(hurt_fish))
difficulty += comp.fish_source.calculate_difficulty(reward_path, rod, user, src)
- difficulty = clamp(round(difficulty), 1, 100)
+ difficulty = clamp(round(difficulty), FISHING_EASY_DIFFICULTY - 5, 100)
if(difficulty > FISHING_EASY_DIFFICULTY)
- completion -= round(MAX_FISH_COMPLETION_MALUS * (difficulty/100), 1)
+ completion -= MAX_FISH_COMPLETION_MALUS * (difficulty * 0.01)
if(HAS_MIND_TRAIT(user, TRAIT_REVEAL_FISH))
fish_icon = GLOB.specific_fish_icons[reward_path] || "fish"
- /**
- * If the chances are higher than 1% (100% at maximum difficulty), they'll scale
- * less than proportionally (exponent less than 1) instead.
- * This way we ensure fish with high jump chances won't get TOO jumpy until
- * they near the maximum difficulty, at which they hit 100%
- */
- var/square_angle_rad = TORADIANS(90)
- var/zero_one_difficulty = difficulty/100
- if(short_jump_chance > 1)
- short_jump_chance = (zero_one_difficulty**(square_angle_rad-TORADIANS(arctan(short_jump_chance * 1/square_angle_rad))))*100
- else
- short_jump_chance *= difficulty
- if(long_jump_chance > 1)
- long_jump_chance = (zero_one_difficulty**(square_angle_rad-TORADIANS(arctan(long_jump_chance * 1/square_angle_rad))))*100
- else
- long_jump_chance *= difficulty
+ mover.adjust_to_difficulty()
- bait_height -= difficulty
+ bait_height -= round(difficulty * BAIT_HEIGHT_DIFFICULTY_MALUS)
bait_pixel_height = round(MINIGAME_BAIT_HEIGHT * (bait_height/initial(bait_height)), 1)
/datum/fishing_challenge/Destroy(force)
@@ -226,6 +183,7 @@
SStgui.close_uis(src)
user = null
used_rod = null
+ QDEL_NULL(mover)
return ..()
/datum/fishing_challenge/proc/send_alert(message)
@@ -277,11 +235,11 @@
//You need to be holding the rod to use it.
if(LAZYACCESS(modifiers, SHIFT_CLICK) || LAZYACCESS(modifiers, CTRL_CLICK) || LAZYACCESS(modifiers, ALT_CLICK))
return
- if(!source.get_active_held_item(used_rod) && !HAS_TRAIT(source, TRAIT_PROFOUND_FISHER))
+ if(!HAS_TRAIT(source, TRAIT_PROFOUND_FISHER) && source.get_active_held_item() != used_rod)
return
- if(phase == WAIT_PHASE) //Reset wait
+ if(phase == WAIT_PHASE)
send_alert("miss!")
- start_baiting_phase(TRUE)
+ start_baiting_phase(TRUE) //Add in another 3 to 5 seconds for that blunder.
else if(phase == BITING_PHASE)
start_minigame_phase()
return COMSIG_MOB_CANCEL_CLICKON
@@ -329,7 +287,7 @@
if(penalty)
wait_time = min(timeleft(next_phase_timer) + rand(3 SECONDS, 5 SECONDS), 30 SECONDS)
else
- wait_time = rand(1 SECONDS, 30 SECONDS)
+ wait_time = rand(3 SECONDS, 25 SECONDS)
if(special_effects & FISHING_MINIGAME_AUTOREEL && wait_time >= 15 SECONDS)
wait_time = max(wait_time - 7.5 SECONDS, 15 SECONDS)
deltimer(next_phase_timer)
@@ -365,6 +323,14 @@
send_alert("crustacean!!!")
if(FISH_ICON_BONE)
send_alert("bones!!!")
+ if(FISH_ICON_ELECTRIC)
+ send_alert("zappy!!!")
+ if(FISH_ICON_WEAPON)
+ send_alert("weapon!!!")
+ if(FISH_ICON_CRITTER)
+ send_alert("critter!!!")
+ if(FISH_ICON_SEED)
+ send_alert("seed!!!")
else
send_alert("!!!")
animate(lure, pixel_y = 3, time = 5, loop = -1, flags = ANIMATION_RELATIVE)
@@ -409,7 +375,10 @@
completion *= 1.2
if(BITING_TIME_WINDOW - 0.5 SECONDS to BITING_TIME_WINDOW)
completion *= 1.4
- completion = round(completion, 1)
+ //randomize the position of the fish a little
+ fish_position = rand(0, (FISHING_MINIGAME_AREA - fish_height) * 0.8)
+ var/diff_dist = 100 + difficulty
+ bait_position = clamp(round(fish_position + rand(-diff_dist, diff_dist) - bait_height * 0.5), 0, FISHING_MINIGAME_AREA - bait_height)
if(!prepare_minigame_hud())
return
phase = MINIGAME_PHASE
@@ -462,7 +431,7 @@
/datum/fishing_challenge/process(seconds_per_tick)
if(length(active_effects) && COOLDOWN_FINISHED(src, active_effect_cd))
select_active_effect()
- move_fish(seconds_per_tick)
+ mover.move_fish(seconds_per_tick)
move_bait(seconds_per_tick)
if(!QDELETED(fishing_hud))
update_visuals()
@@ -498,58 +467,6 @@
fishing_hud.icon_state = background
current_active_effect = null
-///The proc that moves the fish around, just like in the old TGUI, mostly.
-/datum/fishing_challenge/proc/move_fish(seconds_per_tick)
- var/long_chance = long_jump_chance * seconds_per_tick * 10
- var/short_chance = short_jump_chance * seconds_per_tick * 10
-
- // If we have the target but we're close enough, mark as target reached
- if(abs(target_position - fish_position) < FISH_TARGET_MIN_DISTANCE)
- target_position = null
-
- // Switching to new long jump target can interrupt any other
- if((can_interrupt_move || isnull(target_position)) && prob(long_chance))
- /**
- * Move at least 0.75 to full of the availible bar in given direction,
- * and more likely to move in the direction where there's more space
- */
- var/distance_from_top = FISHING_MINIGAME_AREA - fish_position - fish_height
- var/distance_from_bottom = fish_position
- var/top_chance
- if(distance_from_top < FISH_SHORT_JUMP_MIN_DISTANCE)
- top_chance = 0
- else
- top_chance = (distance_from_top/max(distance_from_bottom, 1)) * 100
- var/new_target = fish_position
- if(prob(top_chance))
- new_target += distance_from_top * rand(75, 100)/100
- else
- new_target -= distance_from_bottom * rand(75, 100)/100
- target_position = round(new_target)
- current_velocity_limit = long_jump_velocity_limit
-
- // Move towards target
- if(!isnull(target_position))
- var/distance = target_position - fish_position
- // about 5 at diff 15 , 10 at diff 30, 30 at diff 100
- var/acceleration_mult = 0.3 * difficulty + 0.5
- var/target_acceleration = distance * acceleration_mult * seconds_per_tick
-
- fish_velocity = fish_velocity * FISH_FRICTION_MULT + target_acceleration
- else if(prob(short_chance))
- var/distance_from_top = FISHING_MINIGAME_AREA - fish_position - fish_height
- var/distance_from_bottom = fish_position
- var/jump_length
- if(distance_from_top >= FISH_SHORT_JUMP_MIN_DISTANCE)
- jump_length = rand(FISH_SHORT_JUMP_MIN_DISTANCE, FISH_SHORT_JUMP_MAX_DISTANCE)
- if(distance_from_bottom >= FISH_SHORT_JUMP_MIN_DISTANCE && (!jump_length || prob(50)))
- jump_length = -rand(FISH_SHORT_JUMP_MIN_DISTANCE, FISH_SHORT_JUMP_MAX_DISTANCE)
- target_position = clamp(fish_position + jump_length, 0, FISHING_MINIGAME_AREA - fish_height)
- current_velocity_limit = short_jump_velocity_limit
-
- fish_velocity = clamp(fish_velocity + fish_idle_velocity, -current_velocity_limit, current_velocity_limit)
- fish_position = clamp(fish_position + fish_velocity * seconds_per_tick, 0, FISHING_MINIGAME_AREA - fish_height)
-
///The proc that moves the bait around, just like in the old TGUI, mostly.
/datum/fishing_challenge/proc/move_bait(seconds_per_tick)
var/should_bounce = abs(bait_velocity) > BAIT_MIN_VELOCITY_BOUNCE
@@ -610,9 +527,7 @@
bait_velocity += velocity_change
//check that the fish area is still intersecting the bait now that it has moved
- fish_on_bait = (fish_position + fish_height >= bait_position) && (bait_position + bait_height >= fish_position)
-
- if(fish_on_bait)
+ if(is_fish_on_bait())
completion += completion_gain * seconds_per_tick
if(completion >= 100)
complete(TRUE)
@@ -624,6 +539,10 @@
completion = clamp(completion, 0, 100)
+///Returns TRUE if the fish and the bait are intersecting
+/datum/fishing_challenge/proc/is_fish_on_bait()
+ return (fish_position + fish_height >= bait_position) && (bait_position + bait_height >= fish_position)
+
///update the vertical pixel position of both fish and bait, and the icon state of the completion bar
/datum/fishing_challenge/proc/update_visuals()
var/bait_offset_mult = bait_position/FISHING_MINIGAME_AREA
@@ -723,22 +642,19 @@
#undef BITING_PHASE
#undef MINIGAME_PHASE
-#undef FISHING_MINIGAME_AREA
-#undef FISH_TARGET_MIN_DISTANCE
-#undef FISH_FRICTION_MULT
-#undef FISH_SHORT_JUMP_MIN_DISTANCE
-#undef FISH_SHORT_JUMP_MAX_DISTANCE
-#undef FISH_ON_BAIT_ACCELERATION_MULT
-#undef BAIT_MIN_VELOCITY_BOUNCE
-#undef BAIT_DECELERATION_MULT
-
#undef MINIGAME_SLIDER_HEIGHT
#undef MINIGAME_BAIT_HEIGHT
#undef MINIGAME_FISH_HEIGHT
+#undef BAIT_HEIGHT_DIFFICULTY_MALUS
+
#undef REELING_STATE_IDLE
#undef REELING_STATE_UP
#undef REELING_STATE_DOWN
+#undef FISH_ON_BAIT_ACCELERATION_MULT
+#undef BAIT_MIN_VELOCITY_BOUNCE
+#undef BAIT_DECELERATION_MULT
+
#undef MAX_FISH_COMPLETION_MALUS
#undef BITING_TIME_WINDOW
diff --git a/code/modules/fishing/fishing_portal_machine.dm b/code/modules/fishing/fishing_portal_machine.dm
index 494b29b4183ee..8b157cbebfff3 100644
--- a/code/modules/fishing/fishing_portal_machine.dm
+++ b/code/modules/fishing/fishing_portal_machine.dm
@@ -100,3 +100,7 @@
if(!choice || !can_interact(user))
return
activate(available_fish_sources[choice])
+
+/obj/machinery/fishing_portal_generator/emagged
+ obj_flags = parent_type::obj_flags | EMAGGED
+ circuit = /obj/item/circuitboard/machine/fishing_portal_generator/emagged
diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm
index 6ee02d6d9de4d..ece2f9d84af6b 100644
--- a/code/modules/fishing/fishing_rod.dm
+++ b/code/modules/fishing/fishing_rod.dm
@@ -71,6 +71,7 @@
. = ..()
if(currently_hooked)
context[SCREENTIP_CONTEXT_LMB] = "Reel in"
+ context[SCREENTIP_CONTEXT_RMB] = "Unhook"
return CONTEXTUAL_SCREENTIP_SET
return NONE
@@ -100,14 +101,25 @@
/obj/item/fishing_rod/proc/consume_bait(atom/movable/reward)
// catching things that aren't fish or alive mobs doesn't consume baits.
- if(isnull(reward) || isnull(bait))
+ if(isnull(reward) || isnull(bait) || HAS_TRAIT(bait, TRAIT_BAIT_UNCONSUMABLE))
return
if(isliving(reward))
var/mob/living/caught_mob = reward
if(caught_mob.stat == DEAD)
return
- else if(!isfish(reward))
- return
+ else
+ if(!isfish(reward))
+ return
+ var/obj/item/fish/fish = reward
+ if(HAS_TRAIT(bait, TRAIT_POISONOUS_BAIT) && !HAS_TRAIT(fish, TRAIT_FISH_TOXIN_IMMUNE))
+ var/kill_fish = TRUE
+ for(var/bait_identifer in fish.favorite_bait)
+ if(is_matching_bait(bait, bait_identifer))
+ kill_fish = FALSE
+ break
+ if(kill_fish)
+ fish.set_status(FISH_DEAD, silent = TRUE)
+
QDEL_NULL(bait)
update_icon()
@@ -212,6 +224,16 @@
cast_line(interacting_with, user)
return ITEM_INTERACT_SUCCESS
+/obj/item/fishing_rod/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
+ return ranged_interact_with_atom_secondary(interacting_with, user, modifiers)
+
+/obj/item/fishing_rod/ranged_interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
+ //Stop reeling, delete the fishing line
+ if(currently_hooked)
+ QDEL_NULL(fishing_line)
+ return ITEM_INTERACT_BLOCKING
+ return ..()
+
/// If the line to whatever that is is clear and we're not already busy, try fishing in it
/obj/item/fishing_rod/proc/cast_line(atom/target, mob/user)
if(casting || currently_hooked)
@@ -219,9 +241,6 @@
if(!hook)
balloon_alert(user, "install a hook first!")
return
- if(!CheckToolReach(user, target, cast_range))
- balloon_alert(user, "cannot reach there!")
- return
if(!COOLDOWN_FINISHED(src, casting_cd))
return
casting = TRUE
@@ -357,7 +376,7 @@
return FALSE
return TRUE
-/obj/item/fishing_rod/ui_act(action, list/params)
+/obj/item/fishing_rod/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return .
@@ -528,7 +547,7 @@
ui_description = "This rod has an infinite supply of synth-bait. Also doubles as an Experi-Scanner for fish."
icon_state = "fishing_rod_science"
reel_overlay = "reel_science"
- bait = /obj/item/food/bait/doughball/synthetic
+ bait = /obj/item/food/bait/doughball/synthetic/unconsumable
/obj/item/fishing_rod/tech/Initialize(mapload)
. = ..()
@@ -550,9 +569,6 @@
. = ..()
. += span_notice("Alt-Click to access the Experiment Configuration UI")
-/obj/item/fishing_rod/tech/consume_bait(atom/movable/reward)
- return
-
/obj/item/fishing_rod/tech/use_slot(slot, mob/user, obj/item/new_item)
if(slot == ROD_SLOT_BAIT)
return
@@ -578,23 +594,21 @@
if(owner.hook)
icon_state = owner.hook.icon_state
transform = transform.Scale(1, -1)
- return ..()
-
-/obj/projectile/fishing_cast/Impact(atom/hit_atom)
. = ..()
- owner.hook_hit(hit_atom)
- qdel(src)
+ if(!QDELETED(src))
+ our_line = owner.create_fishing_line(src)
-/obj/projectile/fishing_cast/fire(angle, atom/direct_target)
+/obj/projectile/fishing_cast/on_hit(atom/target, blocked = 0, pierce_hit)
. = ..()
- our_line = owner.create_fishing_line(src)
+ if(blocked < 100)
+ QDEL_NULL(our_line) //we need to delete the old beam datum, otherwise it won't let you fish.
+ owner.hook_hit(target)
/obj/projectile/fishing_cast/Destroy()
- . = ..()
QDEL_NULL(our_line)
owner?.casting = FALSE
-
-
+ owner = null
+ return ..()
/datum/beam/fishing_line
// Is the fishing rod held in left side hand
diff --git a/code/modules/fishing/sources/_fish_source.dm b/code/modules/fishing/sources/_fish_source.dm
index 059a532072204..dc9b910ad8f2d 100644
--- a/code/modules/fishing/sources/_fish_source.dm
+++ b/code/modules/fishing/sources/_fish_source.dm
@@ -8,30 +8,48 @@ GLOBAL_LIST_INIT(preset_fish_sources, init_subtypes_w_path_keys(/datum/fish_sour
* A lot of the icons here may be a tad inaccurate, but since we're limited to the free font awesome icons we
* have access to, we got to make do.
*/
-GLOBAL_LIST_INIT(specific_fish_icons, zebra_typecacheof(list(
- /mob/living/basic/carp = FISH_ICON_DEF,
- /mob/living/basic/mining = FISH_ICON_HOSTILE,
- /obj/effect/decal/remains = FISH_ICON_BONE,
- /obj/effect/mob_spawn/corpse = FISH_ICON_BONE,
- /obj/item/coin = FISH_ICON_COIN,
- /obj/item/fish = FISH_ICON_DEF,
- /obj/item/fish/armorfish = FISH_ICON_CRAB,
- /obj/item/fish/boned = FISH_ICON_BONE,
- /obj/item/fish/chasm_crab = FISH_ICON_CRAB,
- /obj/item/fish/gunner_jellyfish = FISH_ICON_JELLYFISH,
- /obj/item/fish/holo/crab = FISH_ICON_CRAB,
- /obj/item/fish/holo/puffer = FISH_ICON_CHUNKY,
- /obj/item/fish/mastodon = FISH_ICON_BONE,
- /obj/item/fish/pufferfish = FISH_ICON_CHUNKY,
- /obj/item/fish/slimefish = FISH_ICON_SLIME,
- /obj/item/fish/sludgefish = FISH_ICON_SLIME,
- /obj/item/fish/starfish = FISH_ICON_STAR,
- /obj/item/storage/wallet = FISH_ICON_COIN,
- /obj/item/stack/sheet/bone = FISH_ICON_BONE,
- /obj/item/stack/sheet/mineral = FISH_ICON_GEM,
- /obj/item/stack/ore = FISH_ICON_GEM,
- /obj/structure/closet/crate = FISH_ICON_COIN,
-)))
+GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
+
+/proc/generate_specific_fish_icons()
+ var/list/return_list = zebra_typecacheof(list(
+ /mob/living/basic/axolotl = FISH_ICON_CRITTER,
+ /mob/living/basic/frog = FISH_ICON_CRITTER,
+ /mob/living/basic/carp = FISH_ICON_DEF,
+ /mob/living/basic/mining = FISH_ICON_HOSTILE,
+ /obj/effect/decal/remains = FISH_ICON_BONE,
+ /obj/effect/mob_spawn/corpse = FISH_ICON_BONE,
+ /obj/item/coin = FISH_ICON_COIN,
+ /obj/item/fish = FISH_ICON_DEF,
+ /obj/item/fish/armorfish = FISH_ICON_CRAB,
+ /obj/item/fish/boned = FISH_ICON_BONE,
+ /obj/item/fish/chainsawfish = FISH_ICON_WEAPON,
+ /obj/item/fish/chasm_crab = FISH_ICON_CRAB,
+ /obj/item/fish/gunner_jellyfish = FISH_ICON_JELLYFISH,
+ /obj/item/fish/holo/crab = FISH_ICON_CRAB,
+ /obj/item/fish/holo/puffer = FISH_ICON_CHUNKY,
+ /obj/item/fish/jumpercable = FISH_ICON_ELECTRIC,
+ /obj/item/fish/lavaloop = FISH_ICON_WEAPON,
+ /obj/item/fish/mastodon = FISH_ICON_BONE,
+ /obj/item/fish/pufferfish = FISH_ICON_CHUNKY,
+ /obj/item/fish/sand_crab = FISH_ICON_CRAB,
+ /obj/item/fish/skin_crab = FISH_ICON_CRAB,
+ /obj/item/fish/slimefish = FISH_ICON_SLIME,
+ /obj/item/fish/sludgefish = FISH_ICON_SLIME,
+ /obj/item/fish/starfish = FISH_ICON_STAR,
+ /obj/item/fish/stingray = FISH_ICON_WEAPON,
+ /obj/item/fish/swordfish = FISH_ICON_WEAPON,
+ /obj/item/fish/zipzap = FISH_ICON_ELECTRIC,
+ /obj/item/seeds/grass = FISH_ICON_SEED,
+ /obj/item/seeds/random = FISH_ICON_SEED,
+ /obj/item/storage/wallet = FISH_ICON_COIN,
+ /obj/item/stack/sheet/bone = FISH_ICON_BONE,
+ /obj/item/stack/sheet/mineral = FISH_ICON_GEM,
+ /obj/item/stack/ore = FISH_ICON_GEM,
+ /obj/structure/closet/crate = FISH_ICON_COIN,
+ ))
+
+ return_list[FISHING_RANDOM_SEED] = FISH_ICON_SEED
+ return return_list
/**
* Where the fish actually come from - every fishing spot has one assigned but multiple fishing holes
@@ -45,6 +63,10 @@ GLOBAL_LIST_INIT(specific_fish_icons, zebra_typecacheof(list(
var/list/fish_table = list()
/// If a key from fish_table is present here, that fish is availible in limited quantity and is reduced by one on successful fishing
var/list/fish_counts = list()
+ /// Any limited quantity stuff in this list will be readded to the counts after a while
+ var/list/fish_count_regen
+ /// A list of stuff that's currently waiting to be readded to fish_counts
+ var/list/currently_on_regen
/// Text shown as baloon alert when you roll a dud in the table
var/duds = list("it was nothing", "the hook is empty")
/// Baseline difficulty for fishing in this spot
@@ -53,6 +75,15 @@ GLOBAL_LIST_INIT(specific_fish_icons, zebra_typecacheof(list(
var/catalog_description
/// Background image name from /datum/asset/simple/fishing_minigame
var/background = "background_default"
+ /// It true, repeated and large explosions won't be as efficient. This is usually meant for global fish sources.
+ var/explosive_malus = FALSE
+ /// If explosive_malus is true, this will be used to keep track of the turfs where an explosion happened for when we'll spawn the loot.
+ var/list/exploded_turfs
+ /// Mindless mobs that can fish will never pull up items on this list
+ var/static/list/profound_fisher_blacklist = typecacheof(list(
+ /mob/living/basic/mining/lobstrosity,
+ /obj/structure/closet/crate/necropolis/tendril,
+ ))
/datum/fish_source/New()
if(!PERFORM_ALL_TESTS(focus_only/fish_sources_tables))
@@ -61,6 +92,10 @@ GLOBAL_LIST_INIT(specific_fish_icons, zebra_typecacheof(list(
if(!(path in fish_table))
stack_trace("path [path] found in the 'fish_counts' list but not in the fish_table one of [type]")
+/datum/fish_source/Destroy()
+ exploded_turfs = null
+ return ..()
+
///Called when src is set as the fish source of a fishing spot component
/datum/fish_source/proc/on_fishing_spot_init(/datum/component/fishing_spot/spot)
return
@@ -89,7 +124,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, zebra_typecacheof(list(
. += EXPERT_FISHER_DIFFICULTY_MOD
// Difficulty modifier added by the fisher's skill level
- if(!challenge || !(challenge.special_effects & FISHING_MINIGAME_RULE_NO_EXP))
+ if(!(challenge?.special_effects & FISHING_MINIGAME_RULE_NO_EXP))
. += fisherman.mind?.get_skill_modifier(/datum/skill/fishing, SKILL_VALUE_MODIFIER)
// Difficulty modifier added by the rod
@@ -166,13 +201,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, zebra_typecacheof(list(
/// Gives out the reward if possible
/datum/fish_source/proc/dispense_reward(reward_path, mob/fisherman, turf/fishing_spot)
- if((reward_path in fish_counts)) // This is limited count result
- fish_counts[reward_path] -= 1
- if(!fish_counts[reward_path])
- fish_counts -= reward_path //Ran out of these since rolling (multiple fishermen on same source most likely)
- fish_table -= reward_path
-
- var/atom/movable/reward = spawn_reward(reward_path, fisherman, fishing_spot)
+ var/atom/movable/reward = simple_dispense_reward(reward_path, get_turf(fisherman), fishing_spot)
if(!reward) //balloon alert instead
fisherman.balloon_alert(fisherman, pick(duds))
return
@@ -185,18 +214,41 @@ GLOBAL_LIST_INIT(specific_fish_icons, zebra_typecacheof(list(
INVOKE_ASYNC(reward, TYPE_PROC_REF(/atom/movable, forceMove), get_turf(fisherman))
fisherman.balloon_alert(fisherman, "caught [reward]!")
- SEND_SIGNAL(fisherman, COMSIG_MOB_FISHING_REWARD_DISPENSED, reward)
return reward
+///Simplified version of dispense_reward that doesn't need a fisherman.
+/datum/fish_source/proc/simple_dispense_reward(reward_path, atom/spawn_location, turf/fishing_spot)
+ if(isnull(reward_path))
+ return null
+ if(reward_path in fish_counts) // This is limited count result
+ fish_counts[reward_path] -= 1
+ var/regen_time = fish_count_regen?[reward_path]
+ if(regen_time)
+ LAZYADDASSOC(currently_on_regen, reward_path, 1)
+ if(currently_on_regen[reward_path] == 1)
+ addtimer(CALLBACK(src, PROC_REF(regen_count), reward_path), regen_time)
+
+ var/atom/movable/reward = spawn_reward(reward_path, spawn_location, fishing_spot)
+ SEND_SIGNAL(src, COMSIG_FISH_SOURCE_REWARD_DISPENSED, reward)
+ return reward
+
+/datum/fish_source/proc/regen_count(reward_path, regen_time)
+ fish_counts[reward_path] += 1
+ currently_on_regen[reward_path] -= 1
+ if(!currently_on_regen[reward_path])
+ LAZYREMOVE(currently_on_regen, reward_path)
+ else
+ addtimer(CALLBACK(src, PROC_REF(regen_count), reward_path), regen_time)
+
/// Spawns a reward from a atom path right where the fisherman is. Part of the dispense_reward() logic.
-/datum/fish_source/proc/spawn_reward(reward_path, mob/fisherman, turf/fishing_spot)
+/datum/fish_source/proc/spawn_reward(reward_path, atom/spawn_location, turf/fishing_spot)
if(reward_path == FISHING_DUD)
return
if(ispath(reward_path, /datum/chasm_detritus))
- return GLOB.chasm_detritus_types[reward_path].dispense_detritus(fisherman, fishing_spot)
+ return GLOB.chasm_detritus_types[reward_path].dispense_detritus(spawn_location, fishing_spot)
if(!ispath(reward_path, /atom/movable))
CRASH("Unsupported /datum path [reward_path] passed to fish_source/proc/spawn_reward()")
- var/atom/movable/reward = new reward_path(get_turf(fisherman))
+ var/atom/movable/reward = new reward_path(spawn_location)
if(isfish(reward))
var/obj/item/fish/caught_fish = reward
caught_fish.randomize_size_and_weight()
@@ -219,22 +271,13 @@ GLOBAL_LIST(fishing_property_cache)
GLOB.fishing_property_cache = fish_property_table
return GLOB.fishing_property_cache
-/// Checks if bait matches identifier from fav/disliked bait list
-/datum/fish_source/proc/is_matching_bait(obj/item/bait, identifier)
- if(ispath(identifier)) //Just a path
- return istype(bait, identifier)
- if(islist(identifier))
- var/list/special_identifier = identifier
- switch(special_identifier["Type"])
- if("Foodtype")
- var/obj/item/food/food_bait = bait
- return istype(food_bait) && food_bait.foodtypes & special_identifier["Value"]
- if("Reagent")
- return bait.reagents?.has_reagent(special_identifier["Value"], special_identifier["Amount"], check_subtypes = TRUE)
- else
- CRASH("Unknown bait identifier in fish favourite/disliked list")
- else
- return HAS_TRAIT(bait, identifier)
+/// Returns the fish table, with with the unavailable items from fish_counts removed.
+/datum/fish_source/proc/get_fish_table()
+ var/list/table = fish_table.Copy()
+ for(var/result in table)
+ if(fish_counts[result] == 0)
+ table -= result
+ return table
/// Builds a fish weights table modified by bait/rod/user properties
/datum/fish_source/proc/get_modified_fish_table(obj/item/fishing_rod/rod, mob/fisherman)
@@ -244,6 +287,9 @@ GLOBAL_LIST(fishing_property_cache)
///Multiplier used to make fishes more common compared to everything else.
var/result_multiplier = 1
+
+ var/list/final_table = fish_table.Copy()
+
if(bait)
if(HAS_TRAIT(bait, TRAIT_GREAT_QUALITY_BAIT))
result_multiplier = 9
@@ -254,10 +300,13 @@ GLOBAL_LIST(fishing_property_cache)
else if(HAS_TRAIT(bait, TRAIT_BASIC_QUALITY_BAIT))
result_multiplier = 2
leveling_exponent = 0.1
+ final_table -= FISHING_DUD
var/list/fish_list_properties = collect_fish_properties()
- var/list/final_table = fish_table.Copy()
+
+ if(HAS_TRAIT(fisherman, TRAIT_PROFOUND_FISHER) && !fisherman.client)
+ final_table -= profound_fisher_blacklist
for(var/result in final_table)
final_table[result] *= rod.hook?.get_hook_bonus_multiplicative(result)
final_table[result] += rod.hook?.get_hook_bonus_additive(result)//Decide on order here so it can be multiplicative
@@ -316,3 +365,37 @@ GLOBAL_LIST(fishing_property_cache)
final_table[fish] += round(difference**leveling_exponent, 1)
return final_table
+
+/datum/fish_source/proc/spawn_reward_from_explosion(atom/location, severity)
+ if(!explosive_malus)
+ explosive_spawn(location, severity)
+ return
+ if(isnull(exploded_turfs))
+ exploded_turfs = list()
+ addtimer(CALLBACK(src, PROC_REF(post_explosion_spawn)), 1) //run this the next tick.
+ var/turf/turf = get_turf(location)
+ var/peak_severity = max(exploded_turfs[turf], severity)
+ exploded_turfs[turf] = peak_severity
+
+/datum/fish_source/proc/post_explosion_spawn()
+ var/multiplier = 1/(length(exploded_turfs)**0.5)
+ for(var/turf/turf as anything in exploded_turfs)
+ explosive_spawn(turf, exploded_turfs[turf], multiplier)
+ exploded_turfs = null
+
+/datum/fish_source/proc/explosive_spawn(location, severity, multiplier = 1)
+ for(var/i in 1 to (severity + 2))
+ if(!prob((100 + 100 * severity)/i * multiplier))
+ continue
+ var/reward_loot = pick_weight(get_fish_table())
+ var/atom/movable/reward = simple_dispense_reward(reward_loot, location, location)
+ if(isnull(reward))
+ continue
+ if(isfish(reward))
+ var/obj/item/fish/fish = reward
+ fish.set_status(FISH_DEAD, silent = TRUE)
+ if(isitem(reward))
+ reward.pixel_x = rand(-9, 9)
+ reward.pixel_y = rand(-9, 9)
+ if(severity >= EXPLODE_DEVASTATE)
+ reward.ex_act(EXPLODE_LIGHT)
diff --git a/code/modules/fishing/sources/source_types.dm b/code/modules/fishing/sources/source_types.dm
index 9328f87be6905..5b4b6fe3624f9 100644
--- a/code/modules/fishing/sources/source_types.dm
+++ b/code/modules/fishing/sources/source_types.dm
@@ -1,29 +1,90 @@
/datum/fish_source/ocean
fish_table = list(
FISHING_DUD = 15,
- /obj/item/coin/gold = 5,
+ /obj/item/coin/gold = 7,
/obj/item/fish/clownfish = 15,
/obj/item/fish/pufferfish = 15,
/obj/item/fish/cardinal = 15,
/obj/item/fish/greenchromis = 15,
- /obj/item/fish/lanternfish = 5,
- /obj/item/fish/zipzap = 5,
- /obj/item/fish/clownfish/lube = 3,
+ /obj/item/fish/stingray = 10,
+ /obj/item/fish/lanternfish = 7,
+ /obj/item/fish/zipzap = 7,
+ /obj/item/fish/clownfish/lube = 5,
+ /obj/item/fish/swordfish = 5,
+ /obj/structure/mystery_box/fishing = 1,
)
fish_counts = list(
/obj/item/fish/clownfish/lube = 2,
+ /obj/item/fish/swordfish = 2,
+ /obj/structure/mystery_box/fishing = 1,
+ )
+ fish_count_regen = list(
+ /obj/item/fish/clownfish/lube = 3 MINUTES,
+ /obj/item/fish/swordfish = 5 MINUTES,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 5
+ explosive_malus = TRUE
/datum/fish_source/ocean/beach
catalog_description = "Beach shore water"
+/datum/fish_source/ice_fishing
+ catalog_description = "Ice-covered water"
+ fish_table = list(
+ FISHING_DUD = 4,
+ /obj/item/fish/arctic_char = 5,
+ /obj/item/fish/sockeye_salmon = 5,
+ /obj/item/fish/chasm_crab/ice = 2,
+ /obj/item/fish/boned = 1,
+ )
+ fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 20
+
+/datum/fish_source/river
+ catalog_description = "River water"
+ fish_table = list(
+ FISHING_DUD = 4,
+ /obj/item/fish/goldfish = 5,
+ /obj/item/fish/guppy = 5,
+ /obj/item/fish/angelfish = 4,
+ /obj/item/fish/catfish = 4,
+ /obj/item/fish/slimefish = 2,
+ /obj/item/fish/sockeye_salmon = 1,
+ /obj/item/fish/arctic_char = 1,
+ /obj/item/fish/three_eyes = 1,
+ )
+ fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 5
+
+/datum/fish_source/sand
+ catalog_description = "Sand"
+ fish_table = list(
+ FISHING_DUD = 8,
+ /obj/item/fish/sand_crab = 10,
+ /obj/item/fish/sand_surfer = 10,
+ /obj/item/fish/bumpy = 10,
+ /obj/item/coin/gold = 3,
+ )
+ fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 20
+
+/datum/fish_source/cursed_spring
+ catalog_description = null //it's a secret (sorta, I know you're reading this)
+ fish_table = list(
+ FISHING_DUD = 2,
+ /obj/item/fish/soul = 3,
+ /obj/item/fish/skin_crab = 3,
+ /obj/item/fishing_rod/telescopic/master = 1,
+ )
+ fish_counts = list(
+ /obj/item/fishing_rod/telescopic/master = 1,
+ )
+ fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 25
+
/datum/fish_source/portal
fish_table = list(
FISHING_DUD = 7,
/obj/item/fish/goldfish = 10,
/obj/item/fish/guppy = 10,
/obj/item/fish/angelfish = 10,
+ /obj/item/fish/three_eyes = 3,
)
catalog_description = "Aquarium dimension (Fishing portal generator)"
///The name of this option shown in the radial menu on the fishing portal generator
@@ -44,6 +105,7 @@
catalog_description = "Beach dimension (Fishing portal generator)"
radial_name = "Beach"
radial_state = "palm_beach"
+ overlay_state = "portal_beach"
/datum/fish_source/portal/chasm
background = "background_lavaland"
@@ -69,6 +131,14 @@
/obj/item/fish/needlefish = 5,
/obj/item/fish/armorfish = 5,
/obj/item/fish/zipzap = 5,
+ /obj/item/fish/stingray = 4,
+ /obj/item/fish/swordfish = 3,
+ )
+ fish_counts = list(
+ /obj/item/fish/swordfish = 2,
+ )
+ fish_count_regen = list(
+ /obj/item/fish/swordfish = 5 MINUTES,
)
catalog_description = "Ocean dimension (Fishing portal generator)"
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 10
@@ -100,6 +170,13 @@
/obj/item/fish/donkfish = 5,
/obj/item/fish/emulsijack = 5,
/obj/item/fish/jumpercable = 5,
+ /obj/item/fish/chainsawfish = 3,
+ )
+ fish_counts = list(
+ /obj/item/fish/chainsawfish = 1,
+ )
+ fish_count_regen = list(
+ /obj/item/fish/chainsawfish = 7 MINUTES,
)
catalog_description = "Syndicate dimension (Fishing portal generator)"
radial_name = "Syndicate"
@@ -126,6 +203,7 @@
///rewards not found in other fishing portals
fish_table = list(
+ /obj/item/fish/three_eyes = 3,
/obj/item/fish/holo/checkered = 1,
)
@@ -151,15 +229,15 @@
challenge.bait_bounce_mult = clamp(challenge.bait_bounce_mult + (rand(-3, 3) * 0.1), 0.1, 1)
challenge.completion_loss = max(challenge.completion_loss + rand(-2, 2), 0)
challenge.completion_gain = max(challenge.completion_gain + rand(-1, 1), 2)
- challenge.short_jump_velocity_limit += rand(-100, 100)
- challenge.long_jump_velocity_limit += rand(-100, 100)
+ challenge.mover.short_jump_velocity_limit += rand(-100, 100)
+ challenge.mover.long_jump_velocity_limit += rand(-100, 100)
var/static/list/active_effects = bitfield_to_list(FISHING_MINIGAME_ACTIVE_EFFECTS)
for(var/effect in active_effects)
if(prob(30))
challenge.special_effects |= effect
///Cherry on top, fish caught from the randomizer portal also have (almost completely) random traits
-/datum/fish_source/portal/random/spawn_reward(reward_path, mob/fisherman, turf/fishing_spot)
+/datum/fish_source/portal/random/spawn_reward(reward_path, atom/movable/spawn_location, turf/fishing_spot)
if(!ispath(reward_path, /obj/item/fish))
return ..()
@@ -170,16 +248,11 @@
var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
weighted_traits[trait.type] = round(trait.inheritability**2/100)
- var/obj/item/fish/caught_fish = new reward_path(get_turf(fisherman), FALSE)
- var/list/fixed_traits = list()
- for(var/trait_type in caught_fish.fish_traits)
- var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
- if(caught_fish.type in trait.guaranteed_inheritance_types)
- fixed_traits += trait_type
+ var/obj/item/fish/caught_fish = new reward_path(spawn_location, FALSE)
var/list/new_traits = list()
for(var/iteration in rand(1, 4))
new_traits |= pick_weight(weighted_traits)
- caught_fish.inherit_traits(new_traits, fixed_traits = fixed_traits)
+ caught_fish.inherit_traits(new_traits)
caught_fish.randomize_size_and_weight(deviation = 0.3)
caught_fish.progenitors = full_capitalize(caught_fish.name)
return caught_fish
@@ -211,7 +284,8 @@
return rod.hook.chasm_detritus_type
-/datum/fish_source/chasm
+/datum/fish_source/chasm/spawn_reward_from_explosion(atom/location, severity)
+ return //Spawned content would immediately fall back into the chasm, so it wouldn't matter.
/datum/fish_source/lavaland
catalog_description = "Lava vents"
@@ -228,6 +302,7 @@
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 10
+ explosive_malus = TRUE
/datum/fish_source/lavaland/reason_we_cant_fish(obj/item/fishing_rod/rod, mob/fisherman, atom/parent)
. = ..()
@@ -241,14 +316,15 @@
catalog_description = "Liquid plasma vents"
fish_table = list(
FISHING_DUD = 5,
- /obj/item/fish/chasm_crab/ice = 15,
- /obj/item/fish/lavaloop/plasma_river = 15,
- /obj/item/coin/plasma = 3,
- /obj/item/stack/ore/plasma = 3,
+ /obj/item/fish/chasm_crab/ice = 30,
+ /obj/item/fish/lavaloop/plasma_river = 30,
+ /obj/item/coin/plasma = 6,
+ /obj/item/stack/ore/plasma = 6,
+ /obj/effect/decal/remains/plasma = 2,
+ /obj/item/stack/sheet/mineral/runite = 2,
+ /obj/item/stack/sheet/mineral/adamantine = 2,
/mob/living/basic/mining/lobstrosity = 1,
- /obj/effect/decal/remains/plasma = 1,
- /obj/item/stack/sheet/mineral/runite = 1,
- /obj/item/stack/sheet/mineral/adamantine = 1,
+ /mob/living/basic/mining/lobstrosity/juvenile = 1,
)
fish_counts = list(
/obj/item/stack/sheet/mineral/adamantine = 3,
@@ -317,16 +393,19 @@
/obj/item/clothing/gloves/bracer = 2,
/obj/effect/decal/remains/human = 2,
/obj/item/fish/mastodon = 1,
+ /obj/item/fishing_rod/telescopic/master = 1,
)
fish_counts = list(
/obj/item/clothing/gloves/bracer = 1,
/obj/effect/decal/remains/human = 1,
/obj/item/fish/mastodon = 1,
+ /obj/item/fishing_rod/telescopic/master = 1,
+ )
+ fish_count_regen = list(
+ /obj/item/fish/mastodon = 8 MINUTES,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 15
-#define RANDOM_SEED "Random seed"
-
/datum/fish_source/hydro_tray
catalog_description = "Hydroponics trays"
fish_table = list(
@@ -360,8 +439,17 @@
return ..()
+/datum/fish_source/hydro_tray/spawn_reward_from_explosion(atom/location, severity)
+ if(!istype(location, /obj/machinery/hydroponics/constructable))
+ return ..()
+
+ var/obj/machinery/hydroponics/constructable/basin = location
+ if(basin.myseed || basin.waterlevel <= 0)
+ return
+ return ..()
+
/datum/fish_source/hydro_tray/spawn_reward(reward_path, mob/fisherman, turf/fishing_spot)
- if(reward_path != RANDOM_SEED)
+ if(reward_path != FISHING_RANDOM_SEED)
var/mob/living/created_reward = ..()
if(istype(created_reward))
created_reward.name = "small [created_reward.name]"
@@ -382,5 +470,3 @@
var/picked_path = pick(seeds_to_draw_from)
return new picked_path(get_turf(fishing_spot))
-
-#undef RANDOM_SEED
diff --git a/code/modules/food_and_drinks/machinery/coffeemaker.dm b/code/modules/food_and_drinks/machinery/coffeemaker.dm
index 5fdd296d7fc69..bb532b3162483 100644
--- a/code/modules/food_and_drinks/machinery/coffeemaker.dm
+++ b/code/modules/food_and_drinks/machinery/coffeemaker.dm
@@ -731,7 +731,7 @@
coffeepot.reagents.add_reagent_list(reagent_delta)
qdel(reference_bean)
-
+
// remove the coffee beans from the machine
coffee.Cut(1,2)
coffee_amount--
diff --git a/code/modules/food_and_drinks/machinery/deep_fryer.dm b/code/modules/food_and_drinks/machinery/deep_fryer.dm
index 75d894ee00c38..62536ef8d1320 100644
--- a/code/modules/food_and_drinks/machinery/deep_fryer.dm
+++ b/code/modules/food_and_drinks/machinery/deep_fryer.dm
@@ -16,6 +16,7 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
)))
/obj/machinery/deepfryer
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "deep fryer"
desc = "Deep fried everything."
icon = 'icons/obj/machines/kitchen.dmi'
@@ -38,6 +39,12 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
var/frying_fried = FALSE
/// Has our currently frying object been burnt?
var/frying_burnt = FALSE
+ /// How dirty the fryer is - show overlay at 1
+ var/grease_level = 0
+ /// The chance (%) of grease_level increase on process()
+ var/grease_increase_chance = 50
+ /// The amount of grease_level increase on process()
+ var/grease_Increase_amount = 0.1
/// Our sound loop for the frying sounde effect.
var/datum/looping_sound/deep_fryer/fry_loop
@@ -56,6 +63,7 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
create_reagents(50, OPENCONTAINER)
reagents.add_reagent(/datum/reagent/consumable/nutriment/fat/oil, 25)
fry_loop = new(src, FALSE)
+ RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_cleaned))
/obj/machinery/deepfryer/Destroy()
QDEL_NULL(fry_loop)
@@ -75,6 +83,11 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
oil_use = initial(oil_use) - (oil_efficiency * 0.00475)
fry_speed = oil_efficiency
+/obj/machinery/deepfryer/update_overlays()
+ . = ..()
+ if(grease_level >= 1)
+ . += "fryer_greasy"
+
/obj/machinery/deepfryer/examine(mob/user)
. = ..()
if(frying)
@@ -140,6 +153,8 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
return
reagents.trans_to(frying, oil_use * seconds_per_tick, multiplier = fry_speed * 3) //Fried foods gain more of the reagent thanks to space magic
+ grease_level += prob(grease_increase_chance) * grease_Increase_amount
+
cook_time += fry_speed * seconds_per_tick
if(cook_time >= DEEPFRYER_COOKTIME && !frying_fried)
frying_fried = TRUE //frying... frying... fried
@@ -169,7 +184,9 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
frying_burnt = FALSE
fry_loop.stop()
cook_time = 0
+ flick("fryer_stop", src)
icon_state = "fryer_off"
+ update_appearance(UPDATE_OVERLAYS)
/obj/machinery/deepfryer/proc/start_fry(obj/item/frying_item, mob/user)
to_chat(user, span_notice("You put [frying_item] into [src]."))
@@ -188,6 +205,7 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
ADD_TRAIT(frying, TRAIT_FOOD_CHEF_MADE, REF(user.mind))
SEND_SIGNAL(frying, COMSIG_ITEM_ENTERED_FRYER)
+ flick("fryer_start", src)
icon_state = "fryer_on"
fry_loop.start()
@@ -234,5 +252,9 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
user.changeNext_move(CLICK_CD_MELEE)
return ..()
+/obj/machinery/deepfryer/proc/on_cleaned(obj/source_component, obj/source)
+ grease_level = 0
+ update_appearance(UPDATE_OVERLAYS)
+
#undef DEEPFRYER_COOKTIME
#undef DEEPFRYER_BURNTIME
diff --git a/code/modules/food_and_drinks/machinery/gibber.dm b/code/modules/food_and_drinks/machinery/gibber.dm
index cd50f29ffe478..cc5a0ece3a08a 100644
--- a/code/modules/food_and_drinks/machinery/gibber.dm
+++ b/code/modules/food_and_drinks/machinery/gibber.dm
@@ -1,4 +1,5 @@
/obj/machinery/gibber
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "gibber"
desc = "The name isn't descriptive enough?"
icon = 'icons/obj/machines/kitchen.dmi'
diff --git a/code/modules/food_and_drinks/machinery/griddle.dm b/code/modules/food_and_drinks/machinery/griddle.dm
index e0c45e6c9af10..44e86a1755a9e 100644
--- a/code/modules/food_and_drinks/machinery/griddle.dm
+++ b/code/modules/food_and_drinks/machinery/griddle.dm
@@ -1,4 +1,5 @@
/obj/machinery/griddle
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "griddle"
desc = "Because using pans is for pansies."
icon = 'icons/obj/machines/kitchen.dmi'
diff --git a/code/modules/food_and_drinks/machinery/microwave.dm b/code/modules/food_and_drinks/machinery/microwave.dm
index bc5adfbd0f951..4fa586401ff56 100644
--- a/code/modules/food_and_drinks/machinery/microwave.dm
+++ b/code/modules/food_and_drinks/machinery/microwave.dm
@@ -112,7 +112,6 @@
/obj/machinery/microwave/Destroy()
QDEL_LIST(ingredients)
- QDEL_NULL(wires)
QDEL_NULL(soundloop)
QDEL_NULL(particles)
if(!isnull(cell))
@@ -382,6 +381,9 @@
if(operating)
return NONE
+ if (item.item_flags & ABSTRACT)
+ return NONE
+
if(broken > NOT_BROKEN)
balloon_alert(user, "it's broken!")
return ITEM_INTERACT_BLOCKING
@@ -414,32 +416,7 @@
balloon_alert(user, "max 1 device!")
return ITEM_INTERACT_BLOCKING
- if(istype(item, /obj/item/storage))
- var/obj/item/storage/tray = item
- var/loaded = 0
-
- if(!istype(item, /obj/item/storage/bag/tray))
- // Non-tray dumping requires a do_after
- to_chat(user, span_notice("You start dumping out the contents of [item] into [src]..."))
- if(!do_after(user, 2 SECONDS, target = tray))
- return ITEM_INTERACT_BLOCKING
-
- for(var/obj/tray_item in tray.contents)
- if(!IS_EDIBLE(tray_item))
- continue
- if(ingredients.len >= max_n_of_items)
- balloon_alert(user, "it's full!")
- return ITEM_INTERACT_BLOCKING
- if(tray.atom_storage.attempt_remove(tray_item, src))
- loaded++
- ingredients += tray_item
- if(loaded)
- open(autoclose = 0.6 SECONDS)
- to_chat(user, span_notice("You insert [loaded] items into \the [src]."))
- update_appearance()
- return ITEM_INTERACT_SUCCESS
-
- if(item.w_class <= WEIGHT_CLASS_NORMAL && !user.combat_mode)
+ if(item.w_class <= WEIGHT_CLASS_NORMAL && !user.combat_mode && isnull(item.atom_storage))
if(ingredients.len >= max_n_of_items)
balloon_alert(user, "it's full!")
return ITEM_INTERACT_BLOCKING
@@ -453,6 +430,43 @@
update_appearance()
return ITEM_INTERACT_SUCCESS
+/obj/machinery/microwave/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers)
+ if (isnull(tool.atom_storage))
+ return
+ handle_dumping(user, tool)
+ return ITEM_INTERACT_BLOCKING
+
+/obj/machinery/microwave/proc/handle_dumping(mob/living/user, obj/item/tool)
+ if(isnull(tool.atom_storage))
+ return
+
+ var/loaded = 0
+ if(!istype(tool, /obj/item/storage/bag/tray))
+ // Non-tray dumping requires a do_after
+ to_chat(user, span_notice("You start dumping out the contents of [tool] into [src]..."))
+ if(!do_after(user, 2 SECONDS, target = tool))
+ return
+
+ for(var/obj/tray_item in tool.contents)
+ if(!IS_EDIBLE(tray_item))
+ continue
+ if(ingredients.len >= max_n_of_items)
+ balloon_alert(user, "it's full!")
+ return
+ if(tool.atom_storage.attempt_remove(tray_item, src))
+ loaded++
+ ingredients += tray_item
+
+ if(loaded)
+ open(autoclose = 0.6 SECONDS)
+ to_chat(user, span_notice("You insert [loaded] items into \the [src]."))
+ update_appearance()
+
+/obj/machinery/microwave/mouse_drop_receive(obj/item/tool, mob/user, params)
+ if (!istype(tool) || isnull(tool.atom_storage))
+ return
+ handle_dumping(user, tool)
+
/obj/machinery/microwave/attack_hand_secondary(mob/user, list/modifiers)
if(user.can_perform_action(src, ALLOW_SILICON_REACH))
if(!length(ingredients))
diff --git a/code/modules/food_and_drinks/machinery/oven.dm b/code/modules/food_and_drinks/machinery/oven.dm
index c997f34924145..a3cf89e5917d9 100644
--- a/code/modules/food_and_drinks/machinery/oven.dm
+++ b/code/modules/food_and_drinks/machinery/oven.dm
@@ -9,6 +9,7 @@
#define OVEN_TRAY_X_OFFSET -2
/obj/machinery/oven
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "oven"
desc = "Why do they call it oven when you of in the cold food of out hot eat the food?"
icon = 'icons/obj/machines/kitchen.dmi'
diff --git a/code/modules/food_and_drinks/machinery/processor.dm b/code/modules/food_and_drinks/machinery/processor.dm
index 21d49808beda1..383a7c34e2756 100644
--- a/code/modules/food_and_drinks/machinery/processor.dm
+++ b/code/modules/food_and_drinks/machinery/processor.dm
@@ -159,7 +159,9 @@
var/duration = (total_time / rating_speed)
INVOKE_ASYNC(src, TYPE_PROC_REF(/atom, Shake), 1, 0, duration)
- sleep(duration)
+ addtimer(CALLBACK(src, PROC_REF(complete_processing)), duration)
+
+/obj/machinery/processor/proc/complete_processing()
for(var/atom/movable/content_item in processor_contents)
var/datum/food_processor_process/recipe = PROCESSOR_SELECT_RECIPE(content_item)
if (!recipe)
diff --git a/code/modules/food_and_drinks/machinery/smartfridge.dm b/code/modules/food_and_drinks/machinery/smartfridge.dm
index 114d7d020f6c9..8c210822b754b 100644
--- a/code/modules/food_and_drinks/machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/machinery/smartfridge.dm
@@ -5,8 +5,8 @@
name = "smartfridge"
desc = "Keeps cold things cold and hot things cold."
icon = 'icons/obj/machines/smartfridge.dmi'
- icon_state = "smartfridge"
- base_icon_state = "plant"
+ icon_state = "smartfridge-icon"
+ base_icon_state = "smartfridge"
layer = BELOW_OBJ_LAYER
density = TRUE
circuit = /obj/item/circuitboard/machine/smartfridge
@@ -14,9 +14,11 @@
light_range = MINIMUM_USEFUL_LIGHT_RANGE
integrity_failure = 0.5
can_atmos_pass = ATMOS_PASS_NO
+ /// Icon state part for contents display
+ var/contents_overlay_icon = "plant"
/// What path boards used to construct it should build into when dropped. Needed so we don't accidentally have them build variants with items preloaded in them.
var/base_build_path = /obj/machinery/smartfridge
- /// Maximum number of items that can be loaded into the machine
+ /// Maximum number of items that can be loaded into the machine per matter bin tier
var/max_n_of_items = 1500
/// List of items that the machine starts with upon spawn
var/list/initial_contents
@@ -25,14 +27,20 @@
/// Is this smartfridge going to have a glowing screen? (Drying Racks are not)
var/has_emissive = TRUE
/// Whether the smartfridge is welded down to the floor disabling unwrenching
+ var/can_be_welded_down = TRUE
+ /// Whether the smartfridge is welded down to the floor disabling unwrenching
var/welded_down = FALSE
+ /// The sound of item retrieval
+ var/vend_sound = 'sound/machines/machine_vend.ogg'
+ /// Whether the UI should be set to list view by default
+ var/default_list_view = FALSE
/obj/machinery/smartfridge/Initialize(mapload)
. = ..()
create_reagents(100, NO_REACT)
air_update_turf(TRUE, TRUE)
register_context()
- if(mapload)
+ if(mapload && can_be_welded_down)
welded_down = TRUE
if(islist(initial_contents))
@@ -49,6 +57,8 @@
move_update_air(old_loc)
/obj/machinery/smartfridge/welder_act(mob/living/user, obj/item/tool)
+ if(!can_be_welded_down)
+ return ..()
if(welded_down)
if(!tool.tool_start_check(user, amount=2))
return ITEM_INTERACT_BLOCKING
@@ -112,9 +122,9 @@
/obj/machinery/smartfridge/screwdriver_act(mob/living/user, obj/item/tool)
if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
if(panel_open)
- add_overlay("[initial(icon_state)]-panel")
+ add_overlay("[base_icon_state]-panel")
else
- cut_overlay("[initial(icon_state)]-panel")
+ cut_overlay("[base_icon_state]-panel")
SStgui.update_uis(src)
return ITEM_INTERACT_SUCCESS
return ITEM_INTERACT_BLOCKING
@@ -156,7 +166,7 @@
if(welded_down)
context[SCREENTIP_CONTEXT_LMB] = "Unweld"
tool_tip_set = TRUE
- else if (!welded_down && anchored)
+ else if (!welded_down && anchored && can_be_welded_down)
context[SCREENTIP_CONTEXT_LMB] = "Weld down"
tool_tip_set = TRUE
if(machine_stat & BROKEN)
@@ -181,7 +191,7 @@
/obj/machinery/smartfridge/RefreshParts()
. = ..()
for(var/datum/stock_part/matter_bin/matter_bin in component_parts)
- max_n_of_items = 1500 * matter_bin.tier
+ max_n_of_items = initial(max_n_of_items) * matter_bin.tier
/obj/machinery/smartfridge/examine(mob/user)
. = ..()
@@ -197,7 +207,7 @@
if(welded_down)
. += span_info("It's moorings are firmly [EXAMINE_HINT("welded")] to the floor.")
- else
+ else if (can_be_welded_down)
. += span_info("It's moorings are loose and can be [EXAMINE_HINT("welded")] down.")
if(anchored)
@@ -211,24 +221,21 @@
set_light((!(machine_stat & BROKEN) && powered()) ? MINIMUM_USEFUL_LIGHT_RANGE : 0)
/obj/machinery/smartfridge/update_icon_state()
- icon_state = "[initial(icon_state)]"
+ icon_state = "[base_icon_state]"
if(machine_stat & BROKEN)
icon_state += "-broken"
- else if(!powered())
- icon_state += "-off"
return ..()
/// Returns the number of items visible in the fridge. Faster than subtracting 2 lists
/obj/machinery/smartfridge/proc/visible_items()
- return contents.len - 1 // Circuitboard
+ return contents.len - 1 // Exclude circuitboard
/obj/machinery/smartfridge/update_overlays()
. = ..()
- var/initial_icon_state = initial(icon_state)
var/shown_contents_length = visible_items()
if(visible_contents && shown_contents_length)
- var/content_level = "[initial_icon_state]-[base_icon_state]"
+ var/content_level = "[base_icon_state]-[contents_overlay_icon]"
switch(shown_contents_length)
if(1 to 25)
content_level += "-1"
@@ -238,10 +245,10 @@
content_level += "-3"
. += mutable_appearance(icon, content_level)
- . += mutable_appearance(icon, "[initial_icon_state]-glass[(machine_stat & BROKEN) ? "-broken" : ""]")
-
- if(!machine_stat && has_emissive)
- . += emissive_appearance(icon, "[initial_icon_state]-light-mask", src, alpha = src.alpha)
+ . += mutable_appearance(icon, "[base_icon_state]-glass[(machine_stat & BROKEN) ? "-broken" : ""]")
+ if(has_emissive && powered() && !(machine_stat & BROKEN))
+ . += mutable_appearance(icon, "[base_icon_state]-powered")
+ . += emissive_appearance(icon, "[base_icon_state]-light-mask", src, alpha = src.alpha)
/obj/machinery/smartfridge/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
@@ -336,7 +343,7 @@
if(ismob(weapon.loc))
var/mob/owner = weapon.loc
if(!owner.transferItemToLoc(weapon, src))
- to_chat(usr, span_warning("\the [weapon] is stuck to your hand, you cannot put it in \the [src]!"))
+ to_chat(owner, span_warning("\the [weapon] is stuck to your hand, you cannot put it in \the [src]!"))
return FALSE
return TRUE
else
@@ -364,16 +371,21 @@
var/atom/movable/atom = item
if (!QDELETED(atom))
- var/md5name = md5(atom.name) // This needs to happen because of a bug in a TGUI component, https://github.com/ractivejs/ractive/issues/744
- if (listofitems[md5name]) // which is fixed in a version we cannot use due to ie8 incompatibility
- listofitems[md5name]["amount"]++ // The good news is, #30519 made smartfridge UIs non-auto-updating
+ var/key = "[atom.type]-[atom.name]"
+ if (listofitems[key])
+ listofitems[key]["amount"]++
else
- listofitems[md5name] = list("name" = atom.name, "amount" = 1)
- sort_list(listofitems)
-
- .["contents"] = listofitems
+ listofitems[key] = list(
+ "path" = key,
+ "name" = full_capitalize(atom.name),
+ "icon" = atom.icon,
+ "icon_state" = atom.icon_state,
+ "amount" = 1
+ )
+ .["contents"] = sort_list(listofitems)
.["name"] = name
.["isdryer"] = FALSE
+ .["default_list_view"] = default_list_view
/obj/machinery/smartfridge/Exited(atom/movable/gone, direction) // Update the UIs in case something inside is removed
. = ..()
@@ -389,26 +401,24 @@
switch(action)
if("Release")
- var/desired = 0
+ var/amount = text2num(params["amount"])
+ var/desired = 1
+ var/dispensed_amount = 0
if(isAI(living_mob))
to_chat(living_mob, span_warning("[src] does not respect your authority!"))
return
- if (params["amount"])
- desired = text2num(params["amount"])
- else
- desired = tgui_input_number(living_mob, "How many items would you like to take out?", "Release", max_value = 50)
+ if (amount > 1)
+ desired = tgui_input_number(living_mob, "How many items would you like to take out?", "Release", default = min(amount, 50), max_value = min(amount, 50))
if(!desired)
return
for(var/obj/item/dispensed_item in src)
if(desired <= 0)
break
- // Grab the first item in contents which name matches our passed name.
- // format_text() is used here to strip \improper and \proper from both names,
- // which is required for correct string comparison between them.
- if(format_text(dispensed_item.name) == format_text(params["name"]))
+ var/item_name = "[dispensed_item.type]-[replacetext(replacetext(dispensed_item.name, "\proper", ""), "\improper", "")]"
+ if(params["path"] == item_name)
if(dispensed_item in component_parts)
CRASH("Attempted removal of [dispensed_item] component_part from smartfridge via smartfridge interface.")
//dispense the item
@@ -416,8 +426,10 @@
dispensed_item.forceMove(drop_location())
adjust_item_drop_location(dispensed_item)
use_energy(active_power_usage)
+ dispensed_amount++
desired--
-
+ if(dispensed_amount && vend_sound)
+ playsound(src, vend_sound, 50, TRUE, extrarange = -3)
if (visible_contents)
update_appearance()
return
@@ -425,108 +437,71 @@
return FALSE
// ----------------------------
-// Drying Rack 'smartfridge'
+// Drying 'smartfridge'
// ----------------------------
-/obj/machinery/smartfridge/drying_rack
- name = "drying rack"
- desc = "A wooden contraption, used to dry plant products, food and hide."
- icon = 'icons/obj/service/hydroponics/equipment.dmi'
- icon_state = "drying_rack"
- resistance_flags = FLAMMABLE
- visible_contents = FALSE
- base_build_path = /obj/machinery/smartfridge/drying_rack //should really be seeing this without admin fuckery.
- use_power = NO_POWER_USE
- idle_power_usage = 0
+/obj/machinery/smartfridge/drying
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
+ name = "dehydrator"
+ desc = "A machine meant to remove moisture from various food."
+ icon_state = "dehydrator-icon"
+ base_icon_state = "dehydrator"
+ contents_overlay_icon = "contents"
+ circuit = /obj/item/circuitboard/machine/dehydrator
+ light_power = 0.5
+ base_build_path = /obj/machinery/smartfridge/drying //should really be seeing this without admin fuckery.
has_emissive = FALSE
can_atmos_pass = ATMOS_PASS_YES
+ can_be_welded_down = FALSE
+ max_n_of_items = 25
+ vend_sound = null
/// Is the rack currently drying stuff
var/drying = FALSE
/// The reference to the last user's mind. Needed for the chef made trait to be properly applied correctly to dried food.
var/datum/weakref/current_user
-/obj/machinery/smartfridge/drying_rack/Initialize(mapload)
- . = ..()
-
- //you can't weld down wood
- welded_down = FALSE
-
- //so we don't drop any of the parent smart fridge parts upon deconstruction
- clear_components()
-
-/obj/machinery/smartfridge/drying_rack/Destroy()
+/obj/machinery/smartfridge/drying/Destroy()
current_user = null
return ..()
-/// We cleared out the components in initialize so we can optimize this
-/obj/machinery/smartfridge/drying_rack/visible_items()
- return contents.len
-
-/obj/machinery/smartfridge/drying_rack/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
- if(isnull(held_item))
- return NONE
-
- var/tool_tip_set = FALSE
- if(held_item.tool_behaviour == TOOL_CROWBAR)
- context[SCREENTIP_CONTEXT_LMB] = "Deconstruct"
- tool_tip_set = TRUE
- else if(held_item.tool_behaviour == TOOL_WRENCH)
- context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""]anchore"
- tool_tip_set = TRUE
-
- return tool_tip_set ? CONTEXTUAL_SCREENTIP_SET : NONE
-
-/obj/machinery/smartfridge/drying_rack/structure_examine()
- . = ""
- if(anchored)
- . += span_info("It's currently anchored to the floor. It can be [EXAMINE_HINT("wrenched")] loose.")
- else
- . += span_info("It's not anchored to the floor. It can be [EXAMINE_HINT("wrenched")] down.")
- . += span_info("The whole rack can be [EXAMINE_HINT("pried")] apart.")
-
-/obj/machinery/smartfridge/drying_rack/welder_act(mob/living/user, obj/item/tool)
- return NONE
-
-/obj/machinery/smartfridge/drying_rack/welder_act_secondary(mob/living/user, obj/item/tool)
- return NONE
-
-/obj/machinery/smartfridge/drying_rack/default_deconstruction_screwdriver()
- return NONE
-
-/obj/machinery/smartfridge/drying_rack/exchange_parts()
- return
+/obj/machinery/smartfridge/drying/AllowDrop()
+ return TRUE // Allow drying results to stay inside
-/obj/machinery/smartfridge/drying_rack/on_deconstruction(disassembled)
- new /obj/item/stack/sheet/mineral/wood(drop_location(), 10)
+/obj/machinery/smartfridge/drying/update_overlays()
+ . = ..()
+ if(visible_contents && powered() && !(machine_stat & BROKEN))
+ var/suffix = drying ? "on" : "off"
+ . += mutable_appearance(icon, "[base_icon_state]-[suffix]")
+ . += emissive_appearance(icon, "[base_icon_state]-[suffix]", src, alpha = src.alpha)
-/obj/machinery/smartfridge/drying_rack/crowbar_act(mob/living/user, obj/item/tool)
- if(default_deconstruction_crowbar(tool, ignore_panel = TRUE))
- return ITEM_INTERACT_SUCCESS
+/obj/machinery/smartfridge/drying/visible_items()
+ return min(1, (contents.len - 1)) // Return one if has any, as there's only one icon for overlay
-/obj/machinery/smartfridge/drying_rack/ui_data(mob/user)
+/obj/machinery/smartfridge/drying/ui_data(mob/user)
. = ..()
.["isdryer"] = TRUE
.["drying"] = drying
-/obj/machinery/smartfridge/drying_rack/ui_act(action, params)
+/obj/machinery/smartfridge/drying/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
update_appearance() // This is to handle a case where the last item is taken out manually instead of through drying pop-out
return
+ var/mob/user = ui.user
switch(action)
if("Dry")
- toggle_drying(FALSE, usr)
+ toggle_drying(FALSE, user)
return TRUE
-/obj/machinery/smartfridge/drying_rack/powered()
+/obj/machinery/smartfridge/drying/powered()
return !anchored ? FALSE : ..()
-/obj/machinery/smartfridge/drying_rack/power_change()
+/obj/machinery/smartfridge/drying/power_change()
. = ..()
if(!powered())
toggle_drying(TRUE)
-/obj/machinery/smartfridge/drying_rack/load(obj/item/dried_object, mob/user) //For updating the filled overlay
+/obj/machinery/smartfridge/drying/load(obj/item/dried_object, mob/user) //For updating the filled overlay
. = ..()
if(!.)
return
@@ -534,25 +509,18 @@
if(drying && user?.mind)
current_user = WEAKREF(user.mind)
-/obj/machinery/smartfridge/drying_rack/update_overlays()
- . = ..()
- if(drying)
- . += "drying_rack_drying"
- if(contents.len)
- . += "drying_rack_filled"
-
-/obj/machinery/smartfridge/drying_rack/process()
+/obj/machinery/smartfridge/drying/process(seconds_per_tick)
if(drying)
for(var/obj/item/item_iterator in src)
if(!accept_check(item_iterator))
continue
- rack_dry(item_iterator)
+ SEND_SIGNAL(item_iterator, COMSIG_ITEM_DRIED, current_user, seconds_per_tick)
SStgui.update_uis(src)
update_appearance()
use_energy(active_power_usage)
-/obj/machinery/smartfridge/drying_rack/accept_check(obj/item/O)
+/obj/machinery/smartfridge/drying/accept_check(obj/item/O)
return HAS_TRAIT(O, TRAIT_DRYABLE)
/**
@@ -560,7 +528,7 @@
* Arguments
* * forceoff - if TRUE will force the dryer off always
*/
-/obj/machinery/smartfridge/drying_rack/proc/toggle_drying(forceoff, mob/user)
+/obj/machinery/smartfridge/drying/proc/toggle_drying(forceoff, mob/user)
if(drying || forceoff)
drying = FALSE
current_user = FALSE
@@ -572,15 +540,70 @@
update_use_power(ACTIVE_POWER_USE)
update_appearance()
-/obj/machinery/smartfridge/drying_rack/proc/rack_dry(obj/item/target)
- SEND_SIGNAL(target, COMSIG_ITEM_DRIED, current_user)
-
-/obj/machinery/smartfridge/drying_rack/emp_act(severity)
+/obj/machinery/smartfridge/drying/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
return
atmos_spawn_air("[TURF_TEMPERATURE(1000)]")
+/// Wooden version
+/obj/machinery/smartfridge/drying/rack
+ name = "drying rack"
+ desc = "A wooden contraption, used to dry plant products, food and hide."
+ icon_state = "drying-rack"
+ base_icon_state = "drying-rack"
+ resistance_flags = FLAMMABLE
+ visible_contents = FALSE
+ base_build_path = /obj/machinery/smartfridge/drying/rack
+ use_power = NO_POWER_USE
+ idle_power_usage = 0
+
+/obj/machinery/smartfridge/drying/rack/Initialize(mapload)
+ . = ..()
+ //so we don't drop any of the parent smart fridge parts upon deconstruction
+ clear_components()
+
+/obj/machinery/smartfridge/drying/rack/welder_act_secondary(mob/living/user, obj/item/tool)
+ return NONE // Can't repair wood with welder
+
+/obj/machinery/smartfridge/drying/rack/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
+ if(isnull(held_item))
+ return NONE
+
+ var/tool_tip_set = FALSE
+ if(held_item.tool_behaviour == TOOL_CROWBAR)
+ context[SCREENTIP_CONTEXT_LMB] = "Deconstruct"
+ tool_tip_set = TRUE
+ else if(held_item.tool_behaviour == TOOL_WRENCH)
+ context[SCREENTIP_CONTEXT_LMB] = "[anchored ? "Un" : ""]anchore"
+ tool_tip_set = TRUE
+
+ return tool_tip_set ? CONTEXTUAL_SCREENTIP_SET : NONE
+
+/obj/machinery/smartfridge/drying/rack/structure_examine()
+ . = ..()
+ . += span_info("The whole rack can be [EXAMINE_HINT("pried")] apart.")
+
+/obj/machinery/smartfridge/drying/rack/default_deconstruction_screwdriver()
+ return NONE
+
+/obj/machinery/smartfridge/drying/rack/exchange_parts()
+ return
+
+/obj/machinery/smartfridge/drying/rack/on_deconstruction(disassembled)
+ new /obj/item/stack/sheet/mineral/wood(drop_location(), 10)
+
+/obj/machinery/smartfridge/drying/rack/crowbar_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_crowbar(tool, ignore_panel = TRUE))
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/smartfridge/drying/rack/update_overlays()
+ . = ..()
+ if(drying)
+ . += "[base_icon_state]-drying"
+ if(contents.len)
+ . += "[base_icon_state]-filled"
+
// ----------------------------
// Bar drink smartfridge
// ----------------------------
@@ -588,7 +611,7 @@
name = "drink showcase"
desc = "A refrigerated storage unit for tasty tasty alcohol."
base_build_path = /obj/machinery/smartfridge/drinks
- base_icon_state = "drink"
+ contents_overlay_icon = "drink"
/obj/machinery/smartfridge/drinks/accept_check(obj/item/weapon)
//not an item or valid container
@@ -608,7 +631,7 @@
/obj/machinery/smartfridge/food
desc = "A refrigerated storage unit for food."
base_build_path = /obj/machinery/smartfridge/food
- base_icon_state = "food"
+ contents_overlay_icon = "food"
/obj/machinery/smartfridge/food/accept_check(obj/item/weapon)
if(weapon.w_class >= WEIGHT_CLASS_BULKY)
@@ -626,7 +649,7 @@
name = "smart slime extract storage"
desc = "A refrigerated storage unit for slime extracts."
base_build_path = /obj/machinery/smartfridge/extract
- base_icon_state = "slime"
+ contents_overlay_icon = "slime"
/obj/machinery/smartfridge/extract/accept_check(obj/item/weapon)
return (istype(weapon, /obj/item/slime_extract) || istype(weapon, /obj/item/slime_scanner))
@@ -641,7 +664,7 @@
name = "smart petri dish storage"
desc = "A refrigerated storage unit for petri dishes."
base_build_path = /obj/machinery/smartfridge/petri
- base_icon_state = "petri"
+ contents_overlay_icon = "petri"
/obj/machinery/smartfridge/petri/accept_check(obj/item/weapon)
return istype(weapon, /obj/item/petri_dish)
@@ -657,7 +680,7 @@
desc = "A refrigerated storage unit for organ storage."
max_n_of_items = 20 //vastly lower to prevent processing too long
base_build_path = /obj/machinery/smartfridge/organ
- base_icon_state = "organ"
+ contents_overlay_icon = "organ"
/// The rate at which this fridge will repair damaged organs
var/repair_rate = 0
@@ -710,7 +733,8 @@
name = "smart chemical storage"
desc = "A refrigerated storage unit for medicine storage."
base_build_path = /obj/machinery/smartfridge/chemistry
- base_icon_state = "chem"
+ contents_overlay_icon = "chem"
+ default_list_view = TRUE
/obj/machinery/smartfridge/chemistry/accept_check(obj/item/weapon)
// not an item or reagent container
@@ -760,7 +784,8 @@
name = "smart virus storage"
desc = "A refrigerated storage unit for volatile sample storage."
base_build_path = /obj/machinery/smartfridge/chemistry/virology
- base_icon_state = "viro"
+ contents_overlay_icon = "viro"
+ default_list_view = TRUE
/obj/machinery/smartfridge/chemistry/virology/preloaded
initial_contents = list(
@@ -781,10 +806,12 @@
name = "disk compartmentalizer"
desc = "A machine capable of storing a variety of disks. Denoted by most as the DSU (disk storage unit)."
icon_state = "disktoaster"
- icon = 'icons/obj/machines/vending.dmi'
+ base_icon_state = "disktoaster"
+ has_emissive = TRUE
pass_flags = PASSTABLE
can_atmos_pass = ATMOS_PASS_YES
visible_contents = FALSE
+ has_emissive = FALSE
base_build_path = /obj/machinery/smartfridge/disks
/obj/machinery/smartfridge/disks/accept_check(obj/item/weapon)
diff --git a/code/modules/food_and_drinks/recipes/soup_mixtures.dm b/code/modules/food_and_drinks/recipes/soup_mixtures.dm
index ef75de8db8672..446782d00cbf4 100644
--- a/code/modules/food_and_drinks/recipes/soup_mixtures.dm
+++ b/code/modules/food_and_drinks/recipes/soup_mixtures.dm
@@ -1469,6 +1469,20 @@
/datum/reagent/water = 5,
)
+//Fresh Jellyfish fillet soup!
+/datum/chemical_reaction/food/soup/jellyfish_stew_two
+ required_reagents = list(/datum/reagent/water = 50)
+ required_ingredients = list(
+ /obj/item/food/fishmeat/gunner_jellyfish = 1,
+ /obj/item/food/grown/soybeans = 1,
+ /obj/item/food/grown/redbeet = 1,
+ /obj/item/food/grown/potato = 1
+ )
+ results = list(
+ /datum/reagent/consumable/nutriment/soup/jellyfish = 50,
+ )
+
+
// Rootbread Soup
/datum/reagent/consumable/nutriment/soup/rootbread
name = "Rootbread Soup"
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_egg.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_egg.dm
index ec8eda8d3cfff..7b8c071b3a539 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_egg.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_egg.dm
@@ -13,7 +13,7 @@
category = CAT_EGG
/datum/crafting_recipe/food/omelette
- name = "Omelette"
+ name = "Omelette du fromage"
reqs = list(
/obj/item/food/egg = 2,
/obj/item/food/cheese/wedge = 2
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_guide.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_guide.dm
index c02a7243368bd..4d028de06fb22 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_guide.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_guide.dm
@@ -827,7 +827,7 @@
// Machinery: Drying rack
/datum/crafting_recipe/food/drying
- machinery = list(/obj/machinery/smartfridge/drying_rack)
+ machinery = list(/obj/machinery/smartfridge/drying)
steps = list("Put into the rack and dry")
category = CAT_MISCFOOD
non_craftable = TRUE
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_lizard.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_lizard.dm
index 100e74fd91682..7b253f32ce17d 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_lizard.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_lizard.dm
@@ -74,7 +74,8 @@
/obj/item/food/grown/garlic = 1,
/datum/reagent/consumable/lemonjuice = 3,
/datum/reagent/consumable/blackpepper = 2,
- /datum/reagent/consumable/nutriment/fat/oil/olive = 3
+ /datum/reagent/consumable/nutriment/fat/oil/olive = 3,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/lizard_escargot
category = CAT_LIZARD
@@ -94,7 +95,8 @@
reqs = list(
/obj/item/food/fries = 1,
/obj/item/food/meat/cutlet = 2,
- /datum/reagent/consumable/bbqsauce = 5
+ /datum/reagent/consumable/bbqsauce = 5,
+ /obj/item/plate = 1,
)
result = /obj/item/food/lizard_fries
category = CAT_LIZARD
@@ -480,6 +482,10 @@
reaction = /datum/chemical_reaction/food/soup/jellyfish_stew
category = CAT_LIZARD
+/datum/crafting_recipe/food/reaction/soup/jellyfish_stew_two
+ reaction = /datum/chemical_reaction/food/soup/jellyfish_stew_two
+ category = CAT_LIZARD
+
/datum/crafting_recipe/food/reaction/soup/rootbread_soup
reaction = /datum/chemical_reaction/food/soup/rootbread_soup
category = CAT_LIZARD
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_martian.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_martian.dm
index 02bb9ae7bf82c..9048964df1c7c 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_martian.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_martian.dm
@@ -45,6 +45,7 @@
/obj/item/food/grown/onion = 1,
/datum/reagent/consumable/sugar = 3,
/datum/reagent/consumable/limejuice = 3,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/sambal
category = CAT_MARTIAN
@@ -78,6 +79,7 @@
/obj/item/food/meat/cutlet = 1,
/obj/item/food/pineappleslice = 1,
/datum/reagent/consumable/soysauce = 3,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/hurricane_rice
category = CAT_MARTIAN
@@ -91,6 +93,7 @@
/obj/item/food/onion_slice = 1,
/obj/item/food/sausage = 1,
/obj/item/food/grown/chili = 1,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/ikareis
category = CAT_MARTIAN
@@ -103,7 +106,8 @@
/obj/item/food/grown/bell_pepper = 1,
/obj/item/food/pineappleslice = 1,
/obj/item/food/onion_slice = 1,
- /datum/reagent/consumable/soysauce = 5
+ /datum/reagent/consumable/soysauce = 5,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/hawaiian_fried_rice
category = CAT_MARTIAN
@@ -118,6 +122,7 @@
/obj/item/food/grown/peas = 1,
/datum/reagent/consumable/ketchup = 5,
/datum/reagent/consumable/worcestershire = 2,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/ketchup_fried_rice
category = CAT_MARTIAN
@@ -131,6 +136,7 @@
/obj/item/food/cheese/firm_cheese_slice = 1,
/obj/item/food/grown/olive = 1,
/obj/item/food/meatball = 1,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/mediterranean_fried_rice
category = CAT_MARTIAN
@@ -141,6 +147,7 @@
/obj/item/food/boiledrice = 1,
/obj/item/food/egg = 1,
/datum/reagent/consumable/soysauce = 3,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/egg_fried_rice
category = CAT_MARTIAN
@@ -154,6 +161,7 @@
/obj/item/food/meat/cutlet = 1,
/obj/item/food/kimchi = 1,
/obj/item/food/egg = 1,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/bibimbap
category = CAT_MARTIAN
@@ -167,6 +175,7 @@
/obj/item/food/grown/garlic = 1,
/obj/item/food/onion_slice = 1,
/datum/reagent/consumable/nutriment/soup/teriyaki = 4,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/bulgogi_noodles
category = CAT_MARTIAN
@@ -180,6 +189,7 @@
/obj/item/food/onion_slice = 1,
/obj/item/food/katsu_fillet = 1,
/datum/reagent/consumable/worcestershire = 3,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/yakisoba_katsu
category = CAT_MARTIAN
@@ -194,6 +204,7 @@
/obj/item/food/egg = 1,
/datum/reagent/consumable/soysauce = 3,
/datum/reagent/consumable/red_bay = 3,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/martian_fried_noodles
category = CAT_MARTIAN
@@ -203,6 +214,7 @@
reqs = list(
/obj/item/food/spaghetti/boilednoodles = 1,
/datum/reagent/consumable/soysauce = 3,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/simple_fried_noodles
category = CAT_MARTIAN
@@ -222,6 +234,7 @@
/obj/item/food/grown/onion = 1,
/obj/item/food/grown/carrot = 1,
/obj/item/food/grown/potato = 1,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/setagaya_curry
category = CAT_MARTIAN
@@ -545,6 +558,7 @@
/obj/item/food/meat/slab/chicken = 1,
/datum/reagent/consumable/coconut_milk = 5,
/datum/reagent/consumable/curry_powder = 3,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/po_kok_gai
category = CAT_MARTIAN
@@ -579,6 +593,7 @@
/obj/item/food/grown/tomato = 1,
/obj/item/food/uncooked_rice = 1,
/datum/reagent/blood = 5,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/galinha_de_cabidela
category = CAT_MARTIAN
@@ -589,6 +604,7 @@
/obj/item/food/katsu_fillet = 1,
/obj/item/food/boiledrice = 1,
/datum/reagent/consumable/nutriment/soup/curry_sauce = 5,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/katsu_curry
category = CAT_MARTIAN
@@ -600,6 +616,7 @@
/obj/item/food/onion_slice = 1,
/obj/item/food/boiledrice = 1,
/datum/reagent/consumable/nutriment/soup/dashi = 5,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/beef_bowl
category = CAT_MARTIAN
@@ -613,6 +630,7 @@
/obj/item/food/boiledrice = 1,
/datum/reagent/consumable/salt = 2,
/datum/reagent/consumable/nutriment/soup/curry_sauce = 5,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/salt_chilli_bowl
category = CAT_MARTIAN
@@ -625,6 +643,7 @@
/obj/item/food/grown/onion = 1,
/obj/item/food/boiledrice = 1,
/datum/reagent/consumable/nutriment/soup/dashi = 5,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/kansai_bowl
category = CAT_MARTIAN
@@ -637,6 +656,7 @@
/obj/item/food/fishmeat = 1,
/obj/item/food/boiledrice = 1,
/datum/reagent/consumable/cafe_latte = 5,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/eigamudo_curry
category = CAT_MARTIAN
@@ -681,6 +701,7 @@
/datum/reagent/consumable/caramel = 2,
/obj/item/food/icecream = 1,
/datum/reagent/consumable/ethanol/rum = 2,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/pineapple_foster
category = CAT_MARTIAN
@@ -848,6 +869,7 @@
/obj/item/food/fishmeat = 1,
/obj/item/food/boiledrice = 1,
/datum/reagent/consumable/nutriment/soup/dashi = 5,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/salad/sprout_bowl
category = CAT_MARTIAN
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm
index 98eafb66823df..804956cbeed5b 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_meat.dm
@@ -243,6 +243,7 @@
/obj/item/food/grown/onion = 1,
/obj/item/food/grown/tomato = 1,
/obj/item/food/meat/steak = 1,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/beef_stroganoff
category = CAT_MEAT
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_mexican.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_mexican.dm
index 8778ee976ef25..840d3a8c08b1a 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_mexican.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_mexican.dm
@@ -120,7 +120,8 @@
/obj/item/food/cornchips = 1,
/obj/item/food/grown/chili = 1,
/obj/item/food/grown/onion = 1,
- /obj/item/food/grown/tomato = 1
+ /obj/item/food/grown/tomato = 1,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/chipsandsalsa
category = CAT_MEXICAN
@@ -198,6 +199,7 @@
/obj/item/food/grown/tomato = 1,
/obj/item/food/grown/onion = 1,
/obj/item/food/grown/chili = 1,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/pineapple_salsa
category = CAT_MEXICAN
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_moth.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_moth.dm
index bb20f4ab0b370..aeb27f026b251 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_moth.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_moth.dm
@@ -2,7 +2,8 @@
name = "Herby cheese"
reqs = list(
/obj/item/food/cheese/curd_cheese = 1,
- /obj/item/food/grown/herbs = 4
+ /obj/item/food/grown/herbs = 4,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/herby_cheese
category = CAT_MOTH
@@ -187,7 +188,8 @@
reqs = list(
/datum/reagent/consumable/nutriment/soup/rice_porridge = 10,
/obj/item/food/meat/bacon = 1,
- /obj/item/food/friedegg = 2
+ /obj/item/food/friedegg = 2,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/bowled/hua_mulan_congee
category = CAT_MOTH
@@ -199,7 +201,8 @@
/obj/item/food/grown/eggplant = 1,
/obj/item/food/breadslice/plain = 2,
/obj/item/food/tomato_sauce = 1,
- /obj/item/food/cheese/mozzarella = 1
+ /obj/item/food/cheese/mozzarella = 1,
+ /obj/item/reagent_containers/cup/bowl = 1,
)
result = /obj/item/food/bowled/fried_eggplant_polenta
category = CAT_MOTH
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
index 3ce319b1985a2..c965526bcb1c6 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
@@ -399,6 +399,42 @@
result = /obj/item/food/donkpocket/gondola
category = CAT_PASTRY
+/datum/crafting_recipe/food/donkpocket/deluxe
+ time = 15
+ name = "Deluxe Donk-pocket"
+ reqs = list(
+ /obj/item/food/pastrybase = 1,
+ /obj/item/food/meatball = 1,
+ /obj/item/food/meat/bacon = 1,
+ /obj/item/food/onion_slice/red = 1
+ )
+ result = /obj/item/food/donkpocket/deluxe
+ category = CAT_PASTRY
+
+/datum/crafting_recipe/food/donkpocket/deluxe/nocarb
+ time = 15
+ name = "Deluxe Meat-pocket"
+ reqs = list(
+ /obj/item/organ/internal/heart = 1,
+ /obj/item/food/meatball = 1,
+ /obj/item/food/meat/slab = 1,
+ /obj/item/food/grown/herbs = 1
+ )
+ result = /obj/item/food/donkpocket/deluxe/nocarb
+ category = CAT_PASTRY
+
+/datum/crafting_recipe/food/donkpocket/deluxe/vegan
+ time = 15
+ name = "Deluxe Donk-roll"
+ reqs = list(
+ /obj/item/food/pastrybase = 1,
+ /obj/item/food/boiledrice = 1,
+ /obj/item/food/grown/bell_pepper = 1,
+ /obj/item/food/tofu = 2
+ )
+ result = /obj/item/food/donkpocket/deluxe/vegan
+ category = CAT_PASTRY
+
////////////////////////////////////////////////MUFFINS////////////////////////////////////////////////
/datum/crafting_recipe/food/muffin
@@ -568,6 +604,16 @@
result = /obj/item/food/cherrycupcake/blue
category = CAT_PASTRY
+/datum/crafting_recipe/food/jupitercupcake
+ name = "Jupiter-cup-cake"
+ reqs = list(
+ /obj/item/food/pastrybase = 1,
+ /obj/item/food/grown/mushroom/jupitercup = 1,
+ /datum/reagent/consumable/caramel = 3,
+ )
+ result = /obj/item/food/jupitercupcake
+ category = CAT_PASTRY
+
/datum/crafting_recipe/food/honeybun
name = "Honey bun"
reqs = list(
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_spaghetti.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_spaghetti.dm
index 589235eacb70c..edf82287373f0 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_spaghetti.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_spaghetti.dm
@@ -44,7 +44,7 @@
/obj/item/reagent_containers/cup/bowl = 1,
/obj/item/food/spaghetti/boiledspaghetti = 1,
/obj/item/food/meat/cutlet = 2,
- /obj/item/food/grown/cabbage = 1
+ /obj/item/food/grown/cabbage = 1,
)
result = /obj/item/food/spaghetti/beefnoodle
category = CAT_SPAGHETTI
@@ -157,3 +157,15 @@
)
result = /obj/item/food/spaghetti/pad_thai
category = CAT_SPAGHETTI
+
+/datum/crafting_recipe/food/carbonara
+ name = "Spaghetti Carbonara"
+ reqs = list(
+ /obj/item/food/spaghetti/boiledspaghetti = 1,
+ /obj/item/food/cheese/firm_cheese_slice = 1,
+ /obj/item/food/meat/bacon = 1,
+ /obj/item/food/egg = 1,
+ /datum/reagent/consumable/blackpepper = 2,
+ )
+ result = /obj/item/food/spaghetti/carbonara
+ category = CAT_SPAGHETTI
diff --git a/code/modules/hallucination/fake_plasmaflood.dm b/code/modules/hallucination/fake_plasmaflood.dm
index 5b3a67f53125f..4e0860da23bb9 100644
--- a/code/modules/hallucination/fake_plasmaflood.dm
+++ b/code/modules/hallucination/fake_plasmaflood.dm
@@ -7,10 +7,12 @@
random_hallucination_weight = 7
var/list/image/flood_images = list()
- var/list/obj/effect/plasma_image_holder/flood_image_holders = list()
- var/list/turf/flood_turfs = list()
- var/image_icon = 'icons/effects/atmospherics.dmi'
- var/image_state = "plasma"
+ // Assoc list of turfs we have flooded -> the /obj/effect/plasma_image_holder living on them
+ var/list/turf_to_flood = list()
+ // List of turfs that have not yet been completely flooded out
+ var/list/half_baked_turfs = list()
+ var/image_icon = 'icons/effects/atmos/plasma.dmi'
+ var/base_image_state = ""
var/radius = 0
var/next_expand = 0
@@ -50,7 +52,7 @@
expand_flood()
- if(get_turf(hallucinator) in flood_turfs)
+ if(turf_to_flood[get_turf(hallucinator)])
var/mob/living/carbon/carbon_hallucinator = hallucinator
if(istype(carbon_hallucinator) && !carbon_hallucinator.internal)
hallucinator.cause_hallucination(/datum/hallucination/fake_alert/bad_plasma, "fake plasmaflood hallucination")
@@ -61,34 +63,52 @@
for(var/image/flood_image in flood_images)
flood_image.alpha = min(flood_image.alpha + 50, 255)
- for(var/turf/flooded_turf in flood_turfs)
+ for(var/turf/flooded_turf in half_baked_turfs)
+ var/potential_hole = FALSE
for(var/dir in GLOB.cardinals)
var/turf/nearby_turf = get_step(flooded_turf, dir)
- if((nearby_turf in flood_turfs) || !TURFS_CAN_SHARE(nearby_turf, flooded_turf) || isspaceturf(nearby_turf))
+ if(turf_to_flood[nearby_turf])
+ continue
+ if(!TURFS_CAN_SHARE(nearby_turf, flooded_turf) || isspaceturf(nearby_turf))
+ potential_hole = TRUE
continue
create_new_plasma_image(nearby_turf)
+ if(!potential_hole)
+ half_baked_turfs -= flooded_turf
hallucinator.client?.images |= flood_images
/datum/hallucination/fake_flood/proc/create_new_plasma_image(turf/to_flood)
- flood_turfs += to_flood
-
+ half_baked_turfs += to_flood
var/obj/effect/plasma_image_holder/image_holder = new(to_flood)
- flood_image_holders += image_holder
+ turf_to_flood[to_flood] = image_holder
- var/image/plasma_image = image(image_icon, image_holder, image_state, FLY_LAYER)
+ var/image/plasma_image = image(image_icon, image_holder, "[base_image_state]-0", FLY_LAYER)
plasma_image.alpha = 50
- SET_PLANE_EXPLICIT(plasma_image, ABOVE_GAME_PLANE, to_flood)
+ SET_PLANE_EXPLICIT(plasma_image, GAME_PLANE, to_flood)
+ image_holder.overriding_image = plasma_image
flood_images += plasma_image
+ var/connected_dirs = NONE
+ for(var/dir in GLOB.cardinals)
+ var/turf/adjacent_turf = get_step(to_flood, dir)
+ var/obj/effect/plasma_image_holder/adjacent_holder = turf_to_flood[adjacent_turf]
+ if(!adjacent_holder)
+ continue
+ connected_dirs |= dir
+ adjacent_holder.smooth_directions |= REVERSE_DIR(dir)
+ adjacent_holder.overriding_image.icon_state = "[base_image_state]-[adjacent_holder.smooth_directions]"
+ image_holder.smooth_directions = connected_dirs
+ plasma_image.icon_state = "[base_image_state]-[image_holder.smooth_directions]"
+
/datum/hallucination/fake_flood/Destroy()
STOP_PROCESSING(SSobj, src)
hallucinator.client?.images -= flood_images
- flood_turfs.Cut() // We don't own these
- flood_images.Cut() // We also don't own these, kinda
- QDEL_LIST(flood_image_holders) // But we DO own these
+ flood_images.Cut() // We don't own these, kinda
+ half_baked_turfs.Cut() // We REALLY don't own these
+ QDEL_LIST_ASSOC_VAL(turf_to_flood) // But we DO own these (sorta)
return ..()
@@ -96,8 +116,16 @@
icon_state = "nothing"
anchored = TRUE
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
+ plane = GAME_PLANE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ var/datum/hallucination/fake_flood/owning_hallucination
+ var/image/overriding_image
+ var/smooth_directions = NONE
+
+/obj/effect/plasma_image_holder/Destroy(force)
+ . = ..()
+ owning_hallucination = null
+ overriding_image = null
#undef FAKE_FLOOD_EXPAND_TIME
#undef FAKE_FLOOD_MAX_RADIUS
diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm
index 24e5274f7fbfe..d67969fb3b9e1 100644
--- a/code/modules/holiday/holidays.dm
+++ b/code/modules/holiday/holidays.dm
@@ -849,15 +849,11 @@
)
/datum/holiday/xmas/proc/roundstart_celebrate()
- for(var/obj/machinery/computer/security/telescreen/entertainment/Monitor as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/computer/security/telescreen/entertainment))
- Monitor.icon_state_on = "entertainment_xmas"
-
for(var/mob/living/basic/pet/dog/corgi/ian/Ian in GLOB.mob_living_list)
Ian.place_on_head(new /obj/item/clothing/head/helmet/space/santahat(Ian))
-// EASTER (this having it's own spot should be understandable)
-
+// EASTER (this having its own spot should be understandable)
/datum/holiday/easter
name = EASTER
holiday_hat = /obj/item/clothing/head/costume/rabbitears
diff --git a/code/modules/holodeck/computer.dm b/code/modules/holodeck/computer.dm
index b98d66f6bd2c7..1107c8c25793a 100644
--- a/code/modules/holodeck/computer.dm
+++ b/code/modules/holodeck/computer.dm
@@ -146,10 +146,10 @@ GLOBAL_LIST_INIT(typecache_holodeck_linked_floorcheck_ok, typecacheof(list(/turf
data["emagged"] = TRUE
data["emag_programs"] = emag_programs
data["program"] = program
- data["can_toggle_safety"] = issilicon(user) || isAdminGhostAI(user)
+ data["can_toggle_safety"] = HAS_SILICON_ACCESS(user)
return data
-/obj/machinery/computer/holodeck/ui_act(action, params)
+/obj/machinery/computer/holodeck/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/holodeck/items.dm b/code/modules/holodeck/items.dm
index 7a27aecb4bcfe..d01788a066ec4 100644
--- a/code/modules/holodeck/items.dm
+++ b/code/modules/holodeck/items.dm
@@ -75,8 +75,8 @@
/obj/machinery/readybutton
name = "ready declaration device"
desc = "This device is used to declare ready. If all devices in an area are ready, the event will begin!"
- icon = 'icons/obj/machines/wallmounts.dmi'
- icon_state = "auth_off"
+ icon = 'icons/obj/machines/keycard.dmi'
+ icon_state = "auth_on"
var/ready = 0
var/area/currentarea = null
var/eventstarted = FALSE
diff --git a/code/modules/holodeck/turfs.dm b/code/modules/holodeck/turfs.dm
index ee62a12b3d0e1..0222fd41cdcf1 100644
--- a/code/modules/holodeck/turfs.dm
+++ b/code/modules/holodeck/turfs.dm
@@ -120,7 +120,11 @@
. = ..()
if(prob(15))
icon_state = "basalt[rand(0, 12)]"
- set_basalt_light(src)
+ switch(icon_state)
+ if("basalt1", "basalt2", "basalt3")
+ set_light(BASALT_LIGHT_RANGE_BRIGHT, BASALT_LIGHT_POWER, LIGHT_COLOR_LAVA)
+ if("basalt5", "basalt9")
+ set_light(BASALT_LIGHT_RANGE_DIM, BASALT_LIGHT_POWER, LIGHT_COLOR_LAVA)
/turf/open/floor/holofloor/space
name = "\proper space"
diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm
index 52df09c29cdf8..da79225de97ca 100644
--- a/code/modules/hydroponics/biogenerator.dm
+++ b/code/modules/hydroponics/biogenerator.dm
@@ -4,6 +4,7 @@
#define PROCESSED_ITEMS_PER_RATING 5
/obj/machinery/biogenerator
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "biogenerator"
desc = "Converts plants into biomass, which can be used to construct useful items."
icon = 'icons/obj/machines/biogenerator.dmi'
@@ -526,7 +527,7 @@
return data
-/obj/machinery/biogenerator/ui_act(action, list/params)
+/obj/machinery/biogenerator/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/hydroponics/fermenting_barrel.dm b/code/modules/hydroponics/fermenting_barrel.dm
index 49b7056c9e7e8..515946989293d 100644
--- a/code/modules/hydroponics/fermenting_barrel.dm
+++ b/code/modules/hydroponics/fermenting_barrel.dm
@@ -1,7 +1,7 @@
/obj/structure/fermenting_barrel
name = "wooden barrel"
desc = "A large wooden barrel. You can ferment fruits and such inside it, or just use it to hold reagents."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/service/hydroponics/equipment.dmi'
icon_state = "barrel"
base_icon_state = "barrel"
resistance_flags = FLAMMABLE
diff --git a/code/modules/hydroponics/grown/aloe.dm b/code/modules/hydroponics/grown/aloe.dm
index cfbefce6dba21..0548098732622 100644
--- a/code/modules/hydroponics/grown/aloe.dm
+++ b/code/modules/hydroponics/grown/aloe.dm
@@ -1,7 +1,7 @@
// aloe
/obj/item/seeds/aloe
- name = "pack of aloe seeds"
+ name = "aloe seed pack"
desc = "These seeds grow into aloe."
icon_state = "seed-aloe"
species = "aloe"
diff --git a/code/modules/hydroponics/grown/ambrosia.dm b/code/modules/hydroponics/grown/ambrosia.dm
index 2becc390f3980..92cdc2d62c8c1 100644
--- a/code/modules/hydroponics/grown/ambrosia.dm
+++ b/code/modules/hydroponics/grown/ambrosia.dm
@@ -11,7 +11,7 @@
// Ambrosia Vulgaris
/obj/item/seeds/ambrosia
- name = "pack of ambrosia vulgaris seeds"
+ name = "ambrosia vulgaris seed pack"
desc = "These seeds grow into common ambrosia, a plant grown by and from medicine."
icon_state = "seed-ambrosiavulgaris"
plant_icon_offset = 0
@@ -36,7 +36,7 @@
// Ambrosia Deus
/obj/item/seeds/ambrosia/deus
- name = "pack of ambrosia deus seeds"
+ name = "ambrosia deus seed pack"
desc = "These seeds grow into ambrosia deus. Could it be the food of the gods..?"
icon_state = "seed-ambrosiadeus"
species = "ambrosiadeus"
@@ -55,7 +55,7 @@
//Ambrosia Gaia
/obj/item/seeds/ambrosia/gaia
- name = "pack of ambrosia gaia seeds"
+ name = "ambrosia gaia seed pack"
desc = "These seeds grow into ambrosia gaia, filled with infinite potential."
icon_state = "seed-ambrosia_gaia"
species = "ambrosia_gaia"
diff --git a/code/modules/hydroponics/grown/apple.dm b/code/modules/hydroponics/grown/apple.dm
index 0079f63ec9023..c1739cc7dea82 100644
--- a/code/modules/hydroponics/grown/apple.dm
+++ b/code/modules/hydroponics/grown/apple.dm
@@ -1,6 +1,6 @@
// Apple
/obj/item/seeds/apple
- name = "pack of apple seeds"
+ name = "apple seed pack"
desc = "These seeds grow into apple trees."
icon_state = "seed-apple"
species = "apple"
@@ -31,7 +31,7 @@
// Gold Apple
/obj/item/seeds/apple/gold
- name = "pack of golden apple seeds"
+ name = "golden apple seed pack"
desc = "These seeds grow into golden apple trees. Good thing there are no firebirds in space."
icon_state = "seed-goldapple"
species = "goldapple"
diff --git a/code/modules/hydroponics/grown/banana.dm b/code/modules/hydroponics/grown/banana.dm
index ab38f6889907a..6e09ee3497cdd 100644
--- a/code/modules/hydroponics/grown/banana.dm
+++ b/code/modules/hydroponics/grown/banana.dm
@@ -1,6 +1,6 @@
// Banana
/obj/item/seeds/banana
- name = "pack of banana seeds"
+ name = "banana seed pack"
desc = "They're seeds that grow into banana trees. When grown, keep away from clown."
icon_state = "seed-banana"
species = "banana"
@@ -91,7 +91,7 @@
// Mimana - invisible sprites are totally a feature!
/obj/item/seeds/banana/mime
- name = "pack of mimana seeds"
+ name = "mimana seed pack"
desc = "They're seeds that grow into mimana trees. When grown, keep away from mime."
icon_state = "seed-mimana"
species = "mimana"
@@ -119,7 +119,7 @@
// Bluespace Banana
/obj/item/seeds/banana/bluespace
- name = "pack of bluespace banana seeds"
+ name = "bluespace banana seed pack"
desc = "They're seeds that grow into bluespace banana trees. When grown, keep away from bluespace clown."
icon_state = "seed-banana-blue"
species = "bluespacebanana"
diff --git a/code/modules/hydroponics/grown/beans.dm b/code/modules/hydroponics/grown/beans.dm
index fb82737711404..885eb77c0c695 100644
--- a/code/modules/hydroponics/grown/beans.dm
+++ b/code/modules/hydroponics/grown/beans.dm
@@ -1,6 +1,6 @@
// Soybeans
/obj/item/seeds/soya
- name = "pack of soybean seeds"
+ name = "soybean seed pack"
desc = "These seeds grow into soybean plants."
icon_state = "seed-soybean"
species = "soybean"
@@ -30,7 +30,7 @@
// Koibean
/obj/item/seeds/soya/koi
- name = "pack of koibean seeds"
+ name = "koibean seed pack"
desc = "These seeds grow into koibean plants."
icon_state = "seed-koibean"
species = "koibean"
@@ -44,16 +44,26 @@
/obj/item/food/grown/koibeans
seed = /obj/item/seeds/soya/koi
name = "koibean"
- desc = "Something about these seems fishy."
+ desc = "Something about these seems fishy, they seem really soft, almost squeezable!"
icon_state = "koibeans"
foodtypes = VEGETABLES
tastes = list("koi" = 1)
wine_power = 40
+//Now squeezable for imitation carpmeat
+/obj/item/food/grown/koibeans/attack_self(mob/living/user)
+ user.visible_message(span_notice("[user] crushes [src] into a slab of carplike meat."), span_notice("You crush [src] into something that resembles a slab of carplike meat."))
+ playsound(user, 'sound/effects/blobattack.ogg', 50, TRUE)
+ var/obj/item/food/fishmeat/carp/imitation/fishie = new(null)
+ fishie.reagents.set_all_reagents_purity(seed.get_reagent_purity())
+ qdel(src)
+ user.put_in_hands(fishie)
+ return TRUE
+
//Butterbeans, the beans wid da butta!
// Butterbeans! - Squeeze for a single butter slice!
/obj/item/seeds/soya/butter
- name = "pack of butterbean seeds"
+ name = "butterbean seed pack"
desc = "These seeds grow into butterbean plants."
icon_state = "seed-butterbean"
species = "butterbean"
@@ -84,7 +94,7 @@
// Green Beans
/obj/item/seeds/greenbean
- name = "pack of green bean seeds"
+ name = "green bean seed pack"
desc = "These seeds grow into green bean plants."
icon_state = "seed-greenbean"
species = "greenbean"
@@ -113,7 +123,7 @@
// Jumping Bean
/obj/item/seeds/greenbean/jump
- name = "pack of jumping bean seeds"
+ name = "jumping bean seed pack"
desc = "These seeds grow into jumping bean plants."
icon_state = "seed-jumpingbean"
species = "jumpingbean"
diff --git a/code/modules/hydroponics/grown/berries.dm b/code/modules/hydroponics/grown/berries.dm
index 317f8ef2ed87f..10631e4dcb0b6 100644
--- a/code/modules/hydroponics/grown/berries.dm
+++ b/code/modules/hydroponics/grown/berries.dm
@@ -1,6 +1,6 @@
// Berries
/obj/item/seeds/berry
- name = "pack of berry seeds"
+ name = "berry seed pack"
desc = "These seeds grow into berry bushes."
icon_state = "seed-berry"
species = "berry"
@@ -31,7 +31,7 @@
// Poison Berries
/obj/item/seeds/berry/poison
- name = "pack of poison-berry seeds"
+ name = "poison-berry seed pack"
desc = "These seeds grow into poison-berry bushes."
icon_state = "seed-poisonberry"
species = "poisonberry"
@@ -55,7 +55,7 @@
// Death Berries
/obj/item/seeds/berry/death
- name = "pack of death-berry seeds"
+ name = "death-berry seed pack"
desc = "These seeds grow into death berries."
icon_state = "seed-deathberry"
species = "deathberry"
@@ -81,7 +81,7 @@
// Glow Berries
/obj/item/seeds/berry/glow
- name = "pack of glow-berry seeds"
+ name = "glow-berry seed pack"
desc = "These seeds grow into glow-berry bushes."
icon_state = "seed-glowberry"
species = "glowberry"
@@ -108,7 +108,7 @@
// Grapes
/obj/item/seeds/grape
- name = "pack of grape seeds"
+ name = "grape seed pack"
desc = "These seeds grow into grape vines."
icon_state = "seed-grapes"
species = "grape"
@@ -143,7 +143,7 @@
// Green Grapes
/obj/item/seeds/grape/green
- name = "pack of green grape seeds"
+ name = "green grape seed pack"
desc = "These seeds grow into green-grape vines."
icon_state = "seed-greengrapes"
species = "greengrape"
@@ -162,7 +162,7 @@
// Toechtauese Berries
/obj/item/seeds/toechtauese
- name = "pack of töchtaüse berry seeds"
+ name = "töchtaüse berry seed pack"
desc = "These seeds grow into töchtaüse bushes."
icon_state = "seed-toechtauese"
species = "toechtauese"
@@ -190,7 +190,7 @@
distill_reagent = /datum/reagent/toxin/itching_powder
/obj/item/seeds/lanternfruit
- name = "pack of lanternfruit seeds"
+ name = "lanternfruit seed pack"
desc = "These seeds grow into lanternfruit pods."
icon_state = "seed-lanternfruit"
species = "lanternfruit"
diff --git a/code/modules/hydroponics/grown/cannabis.dm b/code/modules/hydroponics/grown/cannabis.dm
index c336f0f4b2456..df2cda24b2d62 100644
--- a/code/modules/hydroponics/grown/cannabis.dm
+++ b/code/modules/hydroponics/grown/cannabis.dm
@@ -1,6 +1,6 @@
// Cannabis
/obj/item/seeds/cannabis
- name = "pack of cannabis seeds"
+ name = "cannabis seed pack"
desc = "Taxable."
icon_state = "seed-cannabis"
plant_icon_offset = 6
@@ -24,7 +24,7 @@
/obj/item/seeds/cannabis/rainbow
- name = "pack of rainbow weed seeds"
+ name = "rainbow weed seed pack"
desc = "These seeds grow into rainbow weed. Groovy... and also highly addictive."
icon_state = "seed-megacannabis"
icon_grow = "megacannabis-grow"
@@ -36,7 +36,7 @@
rarity = 40
/obj/item/seeds/cannabis/death
- name = "pack of deathweed seeds"
+ name = "deathweed seed pack"
desc = "These seeds grow into deathweed. Not groovy."
icon_state = "seed-blackcannabis"
icon_grow = "blackcannabis-grow"
@@ -48,7 +48,7 @@
rarity = 40
/obj/item/seeds/cannabis/white
- name = "pack of lifeweed seeds"
+ name = "lifeweed seed pack"
desc = "I will give unto him that is munchies of the fountain of the cravings of life, freely."
icon_state = "seed-whitecannabis"
icon_grow = "whitecannabis-grow"
@@ -62,7 +62,7 @@
/obj/item/seeds/cannabis/ultimate
- name = "pack of omega weed seeds"
+ name = "omega weed seed pack"
desc = "These seeds grow into omega weed."
icon_state = "seed-ocannabis"
plant_icon_offset = 0
diff --git a/code/modules/hydroponics/grown/cereals.dm b/code/modules/hydroponics/grown/cereals.dm
index 744c0dc5b023c..b304382d2dc47 100644
--- a/code/modules/hydroponics/grown/cereals.dm
+++ b/code/modules/hydroponics/grown/cereals.dm
@@ -1,6 +1,6 @@
// Wheat
/obj/item/seeds/wheat
- name = "pack of wheat seeds"
+ name = "wheat seed pack"
desc = "These may, or may not, grow into wheat."
icon_state = "seed-wheat"
species = "wheat"
@@ -30,7 +30,7 @@
// Oat
/obj/item/seeds/wheat/oat
- name = "pack of oat seeds"
+ name = "oat seed pack"
desc = "These may, or may not, grow into oat."
icon_state = "seed-oat"
species = "oat"
@@ -52,7 +52,7 @@
// Rice
/obj/item/seeds/wheat/rice
- name = "pack of rice seeds"
+ name = "rice seed pack"
desc = "These may, or may not, grow into rice."
icon_state = "seed-rice"
species = "rice"
@@ -76,7 +76,7 @@
//Meatwheat - grows into synthetic meat
/obj/item/seeds/wheat/meat
- name = "pack of meatwheat seeds"
+ name = "meatwheat seed pack"
desc = "If you ever wanted to drive a vegetarian to insanity, here's how."
icon_state = "seed-meatwheat"
species = "meatwheat"
diff --git a/code/modules/hydroponics/grown/cherries.dm b/code/modules/hydroponics/grown/cherries.dm
index 4ebd42489d96f..ad35bacf8fe71 100644
--- a/code/modules/hydroponics/grown/cherries.dm
+++ b/code/modules/hydroponics/grown/cherries.dm
@@ -1,6 +1,6 @@
// Cherries
/obj/item/seeds/cherry
- name = "pack of cherry pits"
+ name = "cherry pit pack"
desc = "Careful not to crack a tooth on one... That'd be the pits."
icon_state = "seed-cherry"
species = "cherry"
@@ -34,7 +34,7 @@
// Blue Cherries
/obj/item/seeds/cherry/blue
- name = "pack of blue cherry pits"
+ name = "blue cherry pit pack"
desc = "The blue kind of cherries."
icon_state = "seed-bluecherry"
species = "bluecherry"
@@ -57,7 +57,7 @@
//Cherry Bulbs
/obj/item/seeds/cherry/bulb
- name = "pack of cherry bulb pits"
+ name = "cherry bulb pit pack"
desc = "The glowy kind of cherries."
icon_state = "seed-cherrybulb"
species = "cherrybulb"
@@ -82,7 +82,7 @@
//Cherry Bombs
/obj/item/seeds/cherry/bomb
- name = "pack of cherry bomb pits"
+ name = "cherry bomb pit pack"
desc = "They give you vibes of dread and frustration."
icon_state = "seed-cherry_bomb"
species = "cherry_bomb"
diff --git a/code/modules/hydroponics/grown/chili.dm b/code/modules/hydroponics/grown/chili.dm
index 9f6d3bbd08ce0..1d9aaa8468ebf 100644
--- a/code/modules/hydroponics/grown/chili.dm
+++ b/code/modules/hydroponics/grown/chili.dm
@@ -1,6 +1,6 @@
// Chili
/obj/item/seeds/chili
- name = "pack of chili seeds"
+ name = "chili seed pack"
desc = "These seeds grow into chili plants. HOT! HOT! HOT!"
icon_state = "seed-chili"
species = "chili"
@@ -30,7 +30,7 @@
// Ice Chili
/obj/item/seeds/chili/ice
- name = "pack of chilly pepper seeds"
+ name = "chilly pepper seed pack"
desc = "These seeds grow into chilly pepper plants."
icon_state = "seed-icepepper"
species = "chiliice"
@@ -56,7 +56,7 @@
// Ghost Chili
/obj/item/seeds/chili/ghost
- name = "pack of ghost chili seeds"
+ name = "ghost chili seed pack"
desc = "These seeds grow into a chili said to be the hottest in the galaxy."
icon_state = "seed-chilighost"
species = "chilighost"
@@ -83,7 +83,7 @@
// Bell Pepper
/obj/item/seeds/chili/bell_pepper
- name = "pack of bell pepper seeds"
+ name = "bell pepper seed pack"
desc = "These seeds grow into bell pepper plants. MILD! MILD! MILD!"
icon_state = "seed-bell-pepper"
species = "bellpepper"
diff --git a/code/modules/hydroponics/grown/citrus.dm b/code/modules/hydroponics/grown/citrus.dm
index c624722f82392..6bfc8bc203af5 100644
--- a/code/modules/hydroponics/grown/citrus.dm
+++ b/code/modules/hydroponics/grown/citrus.dm
@@ -9,7 +9,7 @@
// Lime
/obj/item/seeds/lime
- name = "pack of lime seeds"
+ name = "lime seed pack"
desc = "These are very sour seeds."
icon_state = "seed-lime"
species = "lime"
@@ -33,7 +33,7 @@
// Orange
/obj/item/seeds/orange
- name = "pack of orange seeds"
+ name = "orange seed pack"
desc = "Sour seeds."
icon_state = "seed-orange"
species = "orange"
@@ -61,7 +61,7 @@
// Lemon
/obj/item/seeds/lemon
- name = "pack of lemon seeds"
+ name = "lemon seed pack"
desc = "These are sour seeds."
icon_state = "seed-lemon"
species = "lemon"
@@ -86,7 +86,7 @@
// Combustible lemon
/obj/item/seeds/firelemon //combustible lemon is too long so firelemon
- name = "pack of combustible lemon seeds"
+ name = "combustible lemon seed pack"
desc = "When life gives you lemons, don't make lemonade. Make life take the lemons back! Get mad! I don't want your damn lemons!"
icon_state = "seed-firelemon"
species = "firelemon"
@@ -112,7 +112,7 @@
//3D Orange
/obj/item/seeds/orange_3d
- name = "pack of extradimensional orange seeds"
+ name = "extradimensional orange seed pack"
desc = "Polygonal seeds."
icon_state = "seed-orange"
species = "orange"
diff --git a/code/modules/hydroponics/grown/cocoa_vanilla.dm b/code/modules/hydroponics/grown/cocoa_vanilla.dm
index 4e9a9810b4280..e4b6c916f7cc9 100644
--- a/code/modules/hydroponics/grown/cocoa_vanilla.dm
+++ b/code/modules/hydroponics/grown/cocoa_vanilla.dm
@@ -1,6 +1,6 @@
// Cocoa Pod
/obj/item/seeds/cocoapod
- name = "pack of cocoa pod seeds"
+ name = "cocoa pod seed pack"
desc = "These seeds grow into cacao trees. They look fattening." //SIC: cocoa is the seeds. The trees are spelled cacao.
icon_state = "seed-cocoapod"
species = "cocoapod"
@@ -31,7 +31,7 @@
// Vanilla Pod
/obj/item/seeds/cocoapod/vanillapod
- name = "pack of vanilla pod seeds"
+ name = "vanilla pod seed pack"
desc = "These seeds grow into vanilla trees. They look fattening."
icon_state = "seed-vanillapod"
species = "vanillapod"
@@ -52,7 +52,7 @@
distill_reagent = /datum/reagent/consumable/vanilla //Takes longer, but you can get even more vanilla from it.
/obj/item/seeds/cocoapod/bungotree
- name = "pack of bungo tree seeds"
+ name = "bungo tree seed pack"
desc = "These seeds grow into bungo trees. They appear to be heavy and almost perfectly spherical."
icon_state = "seed-bungotree"
plant_icon_offset = 4
diff --git a/code/modules/hydroponics/grown/corn.dm b/code/modules/hydroponics/grown/corn.dm
index d5f9f94e1b2df..02f77280c5ab9 100644
--- a/code/modules/hydroponics/grown/corn.dm
+++ b/code/modules/hydroponics/grown/corn.dm
@@ -1,6 +1,6 @@
// Corn
/obj/item/seeds/corn
- name = "pack of corn seeds"
+ name = "corn seed pack"
desc = "I don't mean to sound corny..."
icon_state = "seed-corn"
species = "corn"
@@ -57,7 +57,7 @@
// Snapcorn
/obj/item/seeds/corn/snapcorn
- name = "pack of snapcorn seeds"
+ name = "snapcorn seed pack"
desc = "Oh snap!"
icon_state = "seed-snapcorn"
species = "snapcorn"
@@ -100,7 +100,7 @@
//Pepper-corn - Heh funny.
/obj/item/seeds/corn/pepper
- name = "pack of pepper-corn seeds"
+ name = "pepper-corn seed pack"
desc = "If Peter picked a pack of pepper-corn..."
icon_state = "seed-peppercorn"
species = "peppercorn"
diff --git a/code/modules/hydroponics/grown/cotton.dm b/code/modules/hydroponics/grown/cotton.dm
index 819d97f321f8d..c2149b7a33006 100644
--- a/code/modules/hydroponics/grown/cotton.dm
+++ b/code/modules/hydroponics/grown/cotton.dm
@@ -1,5 +1,5 @@
/obj/item/seeds/cotton
- name = "pack of cotton seeds"
+ name = "cotton seed pack"
desc = "A pack of seeds that'll grow into a cotton plant. Assistants make good free labor if neccesary."
icon_state = "seed-cotton"
species = "cotton"
@@ -45,7 +45,7 @@
//reinforced mutated variant
/obj/item/seeds/cotton/durathread
- name = "pack of durathread seeds"
+ name = "durathread seed pack"
desc = "A pack of seeds that'll grow into an extremely durable thread that could easily rival plasteel if woven properly."
icon_state = "seed-durathread"
species = "durathread"
diff --git a/code/modules/hydroponics/grown/cucumber.dm b/code/modules/hydroponics/grown/cucumber.dm
index f3712c6a5c90a..a8487a0cac9bc 100644
--- a/code/modules/hydroponics/grown/cucumber.dm
+++ b/code/modules/hydroponics/grown/cucumber.dm
@@ -1,6 +1,6 @@
// CUCUMBERS YEAH
/obj/item/seeds/cucumber
- name = "pack of cucumber seeds"
+ name = "cucumber seed pack"
desc = "These seeds grow into cucumber plants."
icon_state = "seed-cucumber"
species = "cucumber"
diff --git a/code/modules/hydroponics/grown/eggplant.dm b/code/modules/hydroponics/grown/eggplant.dm
index d4b38c3438bdb..47cbc6d934cd3 100644
--- a/code/modules/hydroponics/grown/eggplant.dm
+++ b/code/modules/hydroponics/grown/eggplant.dm
@@ -1,6 +1,6 @@
// Eggplant
/obj/item/seeds/eggplant
- name = "pack of eggplant seeds"
+ name = "eggplant seed pack"
desc = "These seeds grow to produce berries that look nothing like eggs."
icon_state = "seed-eggplant"
species = "eggplant"
@@ -25,7 +25,7 @@
// Egg-Plant
/obj/item/seeds/eggplant/eggy
- name = "pack of egg-plant seeds"
+ name = "egg-plant seed pack"
desc = "These seeds grow to produce berries that look a lot like eggs."
icon_state = "seed-eggy"
species = "eggy"
diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm
index 84b1414335caf..d4ef53474fc97 100644
--- a/code/modules/hydroponics/grown/flowers.dm
+++ b/code/modules/hydroponics/grown/flowers.dm
@@ -1,6 +1,6 @@
// Poppy
/obj/item/seeds/poppy
- name = "pack of poppy seeds"
+ name = "poppy seed pack"
desc = "These seeds grow into poppies."
icon_state = "seed-poppy"
species = "poppy"
@@ -32,7 +32,7 @@
// Lily
/obj/item/seeds/poppy/lily
- name = "pack of lily seeds"
+ name = "lily seed pack"
desc = "These seeds grow into lilies."
icon_state = "seed-lily"
species = "lily"
@@ -52,7 +52,7 @@
//Spacemans's Trumpet
/obj/item/seeds/poppy/lily/trumpet
- name = "pack of spaceman's trumpet seeds"
+ name = "spaceman's trumpet seed pack"
desc = "A plant sculped by extensive genetic engineering. The spaceman's trumpet is said to bear no resemblance to its wild ancestors. Inside NT AgriSci circles it is better known as NTPW-0372."
icon_state = "seed-trumpet"
species = "spacemanstrumpet"
@@ -86,7 +86,7 @@
// Geranium
/obj/item/seeds/poppy/geranium
- name = "pack of geranium seeds"
+ name = "geranium seed pack"
desc = "These seeds grow into geranium."
icon_state = "seed-geranium"
species = "geranium"
@@ -106,7 +106,7 @@
///Fraxinella seeds.
/obj/item/seeds/poppy/geranium/fraxinella
- name = "pack of fraxinella seeds"
+ name = "fraxinella seed pack"
desc = "These seeds grow into fraxinella."
icon_state = "seed-fraxinella"
species = "fraxinella"
@@ -130,7 +130,7 @@
// Harebell
/obj/item/seeds/harebell
- name = "pack of harebell seeds"
+ name = "harebell seed pack"
desc = "These seeds grow into pretty little flowers."
icon_state = "seed-harebell"
plant_icon_offset = 1
@@ -162,7 +162,7 @@
// Sunflower
/obj/item/seeds/sunflower
- name = "pack of sunflower seeds"
+ name = "sunflower seed pack"
desc = "These seeds grow into sunflowers."
icon_state = "seed-sunflower"
species = "sunflower"
@@ -203,7 +203,7 @@
// Moonflower
/obj/item/seeds/sunflower/moonflower
- name = "pack of moonflower seeds"
+ name = "moonflower seed pack"
desc = "These seeds grow into moonflowers."
icon_state = "seed-moonflower"
lefthand_file = 'icons/mob/inhands/items/food_lefthand.dmi'
@@ -231,7 +231,7 @@
// Novaflower
/obj/item/seeds/sunflower/novaflower
- name = "pack of novaflower seeds"
+ name = "novaflower seed pack"
desc = "These seeds grow into novaflowers."
icon_state = "seed-novaflower"
species = "novaflower"
@@ -266,7 +266,7 @@
// Rose
/obj/item/seeds/rose
- name = "pack of rose seeds"
+ name = "rose seed pack"
desc = "These seeds grow into roses."
icon_state = "seed-rose"
species = "rose"
@@ -311,7 +311,7 @@
// Carbon Rose
/obj/item/seeds/carbon_rose
- name = "pack of carbon rose seeds"
+ name = "carbon rose seed pack"
desc = "These seeds grow into carbon roses."
icon_state = "seed-carbonrose"
species = "carbonrose"
diff --git a/code/modules/hydroponics/grown/garlic.dm b/code/modules/hydroponics/grown/garlic.dm
index e37d49fa93032..755d0c2920a3f 100644
--- a/code/modules/hydroponics/grown/garlic.dm
+++ b/code/modules/hydroponics/grown/garlic.dm
@@ -1,5 +1,5 @@
/obj/item/seeds/garlic
- name = "pack of garlic seeds"
+ name = "garlic seed pack"
desc = "A packet of extremely pungent seeds."
icon_state = "seed-garlic"
species = "garlic"
diff --git a/code/modules/hydroponics/grown/gatfruit.dm b/code/modules/hydroponics/grown/gatfruit.dm
index f64b6eb3bd481..c6de17d6eb684 100644
--- a/code/modules/hydroponics/grown/gatfruit.dm
+++ b/code/modules/hydroponics/grown/gatfruit.dm
@@ -1,7 +1,7 @@
// Gatfruit
/obj/item/seeds/gatfruit
- name = "pack of gatfruit seeds"
+ name = "gatfruit seed pack"
desc = "These seeds grow into .357 revolvers."
icon_state = "seed-gatfruit"
species = "gatfruit"
diff --git a/code/modules/hydroponics/grown/grass_carpet.dm b/code/modules/hydroponics/grown/grass_carpet.dm
index 732a6d32b881b..a5cdc08fb7658 100644
--- a/code/modules/hydroponics/grown/grass_carpet.dm
+++ b/code/modules/hydroponics/grown/grass_carpet.dm
@@ -1,6 +1,6 @@
// Grass
/obj/item/seeds/grass
- name = "pack of grass seeds"
+ name = "grass seed pack"
desc = "These seeds grow into grass. Yummy!"
icon_state = "seed-grass"
species = "grass"
@@ -42,7 +42,7 @@
//Fairygrass
/obj/item/seeds/grass/fairy
- name = "pack of fairygrass seeds"
+ name = "fairygrass seed pack"
desc = "These seeds grow into a more mystical grass."
icon_state = "seed-fairygrass"
species = "fairygrass"
@@ -65,7 +65,7 @@
// Carpet
/obj/item/seeds/grass/carpet
- name = "pack of carpet seeds"
+ name = "carpet seed pack"
desc = "These seeds grow into stylish carpet samples."
icon_state = "seed-carpet"
species = "carpet"
diff --git a/code/modules/hydroponics/grown/hedges.dm b/code/modules/hydroponics/grown/hedges.dm
index 9127f70f78fd4..1f8f792b32610 100644
--- a/code/modules/hydroponics/grown/hedges.dm
+++ b/code/modules/hydroponics/grown/hedges.dm
@@ -1,5 +1,5 @@
/obj/item/seeds/shrub
- name = "pack of shrub seeds"
+ name = "shrub seed pack"
desc = "These seeds grow into hedge shrubs."
icon_state = "seed-shrub"
species = "shrub"
@@ -34,7 +34,7 @@
/obj/structure/hedge
name = "hedge"
desc = "A large bushy hedge."
- icon = 'icons/obj/smooth_structures/hedge.dmi'
+ icon = 'icons/obj/structures/smooth/hedge.dmi'
icon_state = "hedge-0"
base_icon_state = "hedge"
smoothing_flags = SMOOTH_BITMASK
diff --git a/code/modules/hydroponics/grown/herbs.dm b/code/modules/hydroponics/grown/herbs.dm
index b22be06ec6aca..bc450d6857f18 100644
--- a/code/modules/hydroponics/grown/herbs.dm
+++ b/code/modules/hydroponics/grown/herbs.dm
@@ -1,6 +1,6 @@
// Herbs
/obj/item/seeds/herbs
- name = "pack of herb seeds"
+ name = "herb seed pack"
desc = "These seeds grow to produce an assortment of herbs and seasonings."
icon_state = "seed-herbs"
species = "herbs"
diff --git a/code/modules/hydroponics/grown/korta_nut.dm b/code/modules/hydroponics/grown/korta_nut.dm
index cfa6c1e5b51f3..457ebff07163e 100644
--- a/code/modules/hydroponics/grown/korta_nut.dm
+++ b/code/modules/hydroponics/grown/korta_nut.dm
@@ -1,6 +1,6 @@
//Korta Nut
/obj/item/seeds/korta_nut
- name = "pack of korta nut seeds"
+ name = "korta nut seed pack"
desc = "These seeds grow into korta nut bushes, native to Tizira."
icon_state = "seed-korta"
species = "kortanut"
@@ -29,7 +29,7 @@
//Sweet Korta Nut
/obj/item/seeds/korta_nut/sweet
- name = "pack of sweet korta nut seeds"
+ name = "sweet korta nut seed pack"
desc = "These seeds grow into sweet korta nuts, a mutation of the original species that produces a thick syrup that Tizirans use for desserts."
icon_state = "seed-sweetkorta"
species = "kortanut"
diff --git a/code/modules/hydroponics/grown/kronkus.dm b/code/modules/hydroponics/grown/kronkus.dm
index 90d264230e750..b4ba30a5eddbe 100644
--- a/code/modules/hydroponics/grown/kronkus.dm
+++ b/code/modules/hydroponics/grown/kronkus.dm
@@ -1,5 +1,5 @@
/obj/item/seeds/kronkus
- name = "pack of kronkus seeds"
+ name = "kronkus seed pack"
desc = "A pack of highly illegal kronkus seeds.\nPossession of these seeds carries the death penalty in 7 sectors."
icon_state = "seed-kronkus"
plant_icon_offset = 6
diff --git a/code/modules/hydroponics/grown/melon.dm b/code/modules/hydroponics/grown/melon.dm
index 0e59231141fc4..3aca50ae1be54 100644
--- a/code/modules/hydroponics/grown/melon.dm
+++ b/code/modules/hydroponics/grown/melon.dm
@@ -1,6 +1,6 @@
// Watermelon
/obj/item/seeds/watermelon
- name = "pack of watermelon seeds"
+ name = "watermelon seed pack"
desc = "These seeds grow into watermelon plants."
icon_state = "seed-watermelon"
species = "watermelon"
@@ -27,6 +27,7 @@
name = "watermelon"
desc = "It's full of watery goodness."
icon_state = "watermelon"
+ inhand_icon_state = "watermelon"
bite_consumption_mod = 2
w_class = WEIGHT_CLASS_NORMAL
foodtypes = FRUIT
@@ -39,9 +40,41 @@
/obj/item/food/grown/watermelon/make_dryable()
return //No drying
+/obj/item/food/grown/watermelon/attackby(obj/item/I, mob/user, params)
+ if(!istype(I, /obj/item/kitchen/spoon))
+ return ..()
+
+ var/melon_pulp_count = 1
+ if(seed)
+ melon_pulp_count += round(seed.potency / 25)
+
+ user.balloon_alert(user, "scooped out [melon_pulp_count] pulp(s)")
+ for(var/i in 1 to melon_pulp_count)
+ new /obj/item/food/watermelonmush(user.loc)
+
+ /// The piece of armour melon turns into; either chetsplate or helmet
+ var/obj/item/clothing/melon_armour
+ /// Chance for the armour to be a chestplate instead of the helmet
+ var/melon_chestplate_chance = (max(0, seed.potency - 50) / 50)
+ if (prob(melon_chestplate_chance))
+ if(seed.resistance_flags & FIRE_PROOF)
+ melon_armour = new /obj/item/clothing/suit/armor/durability/watermelon/fire_resist
+ else
+ melon_armour = new /obj/item/clothing/suit/armor/durability/watermelon
+ to_chat(user, span_notice("You hollow the melon into a helmet with [I]."))
+ else
+ if(seed.resistance_flags & FIRE_PROOF)
+ melon_armour = new /obj/item/clothing/head/helmet/durability/watermelon/fire_resist
+ else
+ melon_armour = new /obj/item/clothing/head/helmet/durability/watermelon
+ to_chat(user, span_notice("You hollow the melon into a chestplate with [I]."))
+ remove_item_from_storage(user)
+ qdel(src)
+ user.put_in_hands(melon_armour)
+
// Holymelon
/obj/item/seeds/watermelon/holy
- name = "pack of holymelon seeds"
+ name = "holymelon seed pack"
desc = "These seeds grow into holymelon plants."
icon_state = "seed-holymelon"
species = "holymelon"
@@ -66,6 +99,9 @@
wine_power = 70 //Water to wine, baby.
wine_flavor = "divinity"
+/obj/item/food/grown/holymelon/make_processable()
+ AddElement(/datum/element/processable, TOOL_KNIFE, /obj/item/food/holymelonslice, 5, 20, screentip_verb = "Slice")
+
/obj/item/food/grown/holymelon/make_dryable()
return //No drying
@@ -73,6 +109,39 @@
. = ..()
AddComponent(/datum/component/edible, check_liked = CALLBACK(src, PROC_REF(check_holyness)))
+
+/obj/item/food/grown/holymelon/attackby(obj/item/I, mob/user, params)
+ if(!istype(I, /obj/item/kitchen/spoon))
+ return ..()
+
+ var/holymelon_pulp_count = 1
+ if(seed)
+ holymelon_pulp_count += round(seed.potency / 25)
+
+ user.balloon_alert(user, "scooped out [holymelon_pulp_count] pulp(s)")
+ for(var/i in 1 to holymelon_pulp_count)
+ new /obj/item/food/holymelonmush(user.loc)
+
+ /// The piece of armour holymelon turns into; either chetsplate or helmet
+ var/obj/item/clothing/holymelon_armour
+ /// Chance for the armour to be a chestplate instead of the helmet
+ var/holymelon_chestplate_chance = (max(0, seed.potency - 50) / 50)
+ if (prob(holymelon_chestplate_chance))
+ if(seed.resistance_flags & FIRE_PROOF)
+ holymelon_armour = new /obj/item/clothing/suit/armor/durability/holymelon/fire_resist
+ else
+ holymelon_armour = new /obj/item/clothing/suit/armor/durability/holymelon
+ to_chat(user, span_notice("You hollow the holymelon into a helmet with [I]."))
+ else
+ if(seed.resistance_flags & FIRE_PROOF)
+ holymelon_armour = new /obj/item/clothing/head/helmet/durability/holymelon/fire_resist
+ else
+ holymelon_armour = new /obj/item/clothing/head/helmet/durability/holymelon
+ to_chat(user, span_notice("You hollow the holymelon into a chestplate with [I]."))
+ remove_item_from_storage(user)
+ qdel(src)
+ user.put_in_hands(holymelon_armour)
+
/*
* Callback to be used with the edible component.
* Checks whether or not the person eating the holymelon
@@ -90,7 +159,7 @@
/// Barrel melon Seeds
/obj/item/seeds/watermelon/barrel
- name = "pack of barrelmelon seeds"
+ name = "barrelmelon seed pack"
desc = "These seeds grow into barrelmelon plants."
icon_state = "seed-barrelmelon"
species = "barrelmelon"
@@ -108,4 +177,41 @@
name = "barrelmelon"
desc = "The nutriments within this melon have been compressed and fermented into rich alcohol."
icon_state = "barrelmelon"
+ inhand_icon_state = "barrelmelon"
distill_reagent = /datum/reagent/medicine/antihol //You can call it a integer overflow.
+
+/obj/item/food/grown/barrelmelon/make_processable()
+ AddElement(/datum/element/processable, TOOL_KNIFE, /obj/item/food/barrelmelonslice, 5, 20, screentip_verb = "Chop")
+
+/obj/item/food/grown/barrelmelon/attackby(obj/item/I, mob/user, params)
+ if(!istype(I, /obj/item/kitchen/spoon))
+ return ..()
+
+ var/barrelmelon_pulp_count = 1
+ if(seed)
+ barrelmelon_pulp_count += round(seed.potency / 25)
+
+ user.balloon_alert(user, "scooped out [barrelmelon_pulp_count] pulp(s)")
+ for(var/i in 1 to barrelmelon_pulp_count)
+ new /obj/item/food/barrelmelonmush(user.loc)
+
+ /// The piece of armour barrelmelon turns into; either chetsplate or helmet
+ var/obj/item/clothing/barrelmelon_armour
+ /// Chance for the armour to be a chestplate instead of the helmet
+ var/barrelmelon_chestplate_chance = (max(0, seed.potency - 50) / 50)
+ if (prob(barrelmelon_chestplate_chance))
+ if(seed.resistance_flags & FIRE_PROOF)
+ barrelmelon_armour = new /obj/item/clothing/suit/armor/durability/barrelmelon/fire_resist
+ else
+ barrelmelon_armour = new /obj/item/clothing/suit/armor/durability/barrelmelon
+ to_chat(user, span_notice("You hollow the barrelmelon into a helmet with [I]."))
+ else
+ if(seed.resistance_flags & FIRE_PROOF)
+ barrelmelon_armour = new /obj/item/clothing/head/helmet/durability/barrelmelon/fire_resist
+ else
+ barrelmelon_armour = new /obj/item/clothing/head/helmet/durability/barrelmelon
+ to_chat(user, span_notice("You hollow the barrelmelon into a chestplate with [I]."))
+
+ remove_item_from_storage(user)
+ qdel(src)
+ user.put_in_hands(barrelmelon_armour)
diff --git a/code/modules/hydroponics/grown/mushrooms.dm b/code/modules/hydroponics/grown/mushrooms.dm
index bfc50f0c483b3..9af495f6a6128 100644
--- a/code/modules/hydroponics/grown/mushrooms.dm
+++ b/code/modules/hydroponics/grown/mushrooms.dm
@@ -11,7 +11,7 @@
// Reishi
/obj/item/seeds/reishi
- name = "pack of reishi mycelium"
+ name = "reishi mycelium pack"
desc = "This mycelium grows into something medicinal and relaxing."
icon_state = "mycelium-reishi"
species = "reishi"
@@ -38,7 +38,7 @@
// Fly Amanita
/obj/item/seeds/amanita
- name = "pack of fly amanita mycelium"
+ name = "fly amanita mycelium pack"
desc = "This mycelium grows into something horrible."
icon_state = "mycelium-amanita"
species = "amanita"
@@ -65,7 +65,7 @@
// Destroying Angel
/obj/item/seeds/angel
- name = "pack of destroying angel mycelium"
+ name = "destroying angel mycelium pack"
desc = "This mycelium grows into something devastating."
icon_state = "mycelium-angel"
species = "angel"
@@ -93,7 +93,7 @@
// Liberty Cap
/obj/item/seeds/liberty
- name = "pack of liberty-cap mycelium"
+ name = "liberty-cap mycelium pack"
desc = "This mycelium grows into liberty-cap mushrooms."
icon_state = "mycelium-liberty"
species = "liberty"
@@ -119,7 +119,7 @@
// Plump Helmet
/obj/item/seeds/plump
- name = "pack of plump-helmet mycelium"
+ name = "plump-helmet mycelium pack"
desc = "This mycelium grows into helmets... maybe."
icon_state = "mycelium-plump"
species = "plump"
@@ -145,7 +145,7 @@
// Walking Mushroom
/obj/item/seeds/plump/walkingmushroom
- name = "pack of walking mushroom mycelium"
+ name = "walking mushroom mycelium pack"
desc = "This mycelium will grow into huge stuff!"
icon_state = "mycelium-walkingmushroom"
species = "walkingmushroom"
@@ -171,7 +171,7 @@
// Chanterelle
/obj/item/seeds/chanter
- name = "pack of chanterelle mycelium"
+ name = "chanterelle mycelium pack"
desc = "This mycelium grows into chanterelle mushrooms."
icon_state = "mycelium-chanter"
species = "chanter"
@@ -197,9 +197,23 @@
desc = "Cantharellus Cibarius: These jolly yellow little shrooms sure look tasty!"
icon_state = "chanterelle"
+/obj/item/food/grown/mushroom/chanterelle/attackby(obj/item/I, mob/user, params)
+ if(!istype(I, /obj/item/kitchen/spoon))
+ return ..()
+ if(seed.potency < 95)
+ return ..()
+
+ to_chat(user, span_notice("You hollow up the chanterelle with [I]."))
+ remove_item_from_storage(user)
+ if(seed.resistance_flags & FIRE_PROOF)
+ user.put_in_hands(new /obj/item/clothing/head/wizard/chanterelle/fr())
+ else
+ user.put_in_hands(new /obj/item/clothing/head/wizard/chanterelle())
+ qdel(src)
+
//Jupiter Cup
/obj/item/seeds/chanter/jupitercup
- name = "pack of jupiter cup mycelium"
+ name = "jupiter cup mycelium pack"
desc = "This mycelium grows into jupiter cups. Zeus would be envious at the power at your fingertips."
icon_state = "mycelium-jupitercup"
species = "jupitercup"
@@ -224,7 +238,7 @@
// Glowshroom
/obj/item/seeds/glowshroom
- name = "pack of glowshroom mycelium"
+ name = "glowshroom mycelium pack"
desc = "This mycelium -glows- into mushrooms!"
icon_state = "mycelium-glowshroom"
species = "glowshroom"
@@ -279,7 +293,7 @@
// Glowcap
/obj/item/seeds/glowshroom/glowcap
- name = "pack of glowcap mycelium"
+ name = "glowcap mycelium pack"
desc = "This mycelium -powers- into mushrooms!"
icon_state = "mycelium-glowcap"
species = "glowcap"
@@ -303,7 +317,7 @@
//Shadowshroom
/obj/item/seeds/glowshroom/shadowshroom
- name = "pack of shadowshroom mycelium"
+ name = "shadowshroom mycelium pack"
desc = "This mycelium will grow into something shadowy."
icon_state = "mycelium-shadowshroom"
species = "shadowshroom"
@@ -332,7 +346,7 @@
investigate_log("was planted by [key_name(user)] at [AREACOORD(user)]", INVESTIGATE_BOTANY)
/obj/item/seeds/odious_puffball
- name = "pack of odious pullball spores"
+ name = "odious pullball spore pack"
desc = "These spores reek! Disgusting."
icon_state = "seed-odiouspuffball"
species = "odiouspuffball"
diff --git a/code/modules/hydroponics/grown/olive.dm b/code/modules/hydroponics/grown/olive.dm
index 38102cacb24ea..0b2f52c7a5e7b 100644
--- a/code/modules/hydroponics/grown/olive.dm
+++ b/code/modules/hydroponics/grown/olive.dm
@@ -1,6 +1,6 @@
// Olive
/obj/item/seeds/olive
- name = "pack of olive seeds"
+ name = "olive seed pack"
desc = "These seeds grow into olive trees."
icon_state = "seed-olive"
species = "olive"
diff --git a/code/modules/hydroponics/grown/onion.dm b/code/modules/hydroponics/grown/onion.dm
index 8f948407a37b8..0d33c3e1f395d 100644
--- a/code/modules/hydroponics/grown/onion.dm
+++ b/code/modules/hydroponics/grown/onion.dm
@@ -1,5 +1,5 @@
/obj/item/seeds/onion
- name = "pack of onion seeds"
+ name = "onion seed pack"
desc = "These seeds grow into onions."
icon_state = "seed-onion"
species = "onion"
@@ -29,7 +29,7 @@
AddElement(/datum/element/processable, TOOL_KNIFE, /obj/item/food/onion_slice, 2, 15, screentip_verb = "Cut")
/obj/item/seeds/onion/red
- name = "pack of red onion seeds"
+ name = "red onion seed pack"
desc = "For growing exceptionally potent onions."
icon_state = "seed-onionred"
species = "onion_red"
diff --git a/code/modules/hydroponics/grown/peanut.dm b/code/modules/hydroponics/grown/peanut.dm
index 69cf6d9e0568d..6560ec196c918 100644
--- a/code/modules/hydroponics/grown/peanut.dm
+++ b/code/modules/hydroponics/grown/peanut.dm
@@ -1,6 +1,6 @@
// Peanuts!
/obj/item/seeds/peanut
- name = "pack of peanut seeds"
+ name = "peanut seed pack"
desc = "These seeds grow into peanut plants."
icon_state = "seed-peanut"
species = "peanut"
diff --git a/code/modules/hydroponics/grown/peas.dm b/code/modules/hydroponics/grown/peas.dm
index c232ed247c690..66eaec6ff1d54 100644
--- a/code/modules/hydroponics/grown/peas.dm
+++ b/code/modules/hydroponics/grown/peas.dm
@@ -1,6 +1,6 @@
// Finally, peas. Base plant.
/obj/item/seeds/peas
- name = "pack of pea pods"
+ name = "pea pod pack"
desc = "These seeds grows into vitamin rich peas!"
icon_state = "seed-peas"
species = "peas"
@@ -29,7 +29,7 @@
// Laughin' Peas
/obj/item/seeds/peas/laugh
- name = "pack of laughin' peas"
+ name = "laughin' pea pack"
desc = "These seeds give off a very soft purple glow.. they should grow into Laughin' Peas."
icon_state = "seed-laughpeas"
species = "laughpeas"
@@ -61,7 +61,7 @@
// World Peas - Peace at last, peace at last...
/obj/item/seeds/peas/laugh/peace
- name = "pack of world peas"
+ name = "world pea pack"
desc = "These rather large seeds give off a soothing blue glow..."
icon_state = "seed-worldpeas"
species = "worldpeas"
diff --git a/code/modules/hydroponics/grown/pineapple.dm b/code/modules/hydroponics/grown/pineapple.dm
index 3c0e462f38855..5de85e9168f1f 100644
--- a/code/modules/hydroponics/grown/pineapple.dm
+++ b/code/modules/hydroponics/grown/pineapple.dm
@@ -1,6 +1,6 @@
// Pineapple!
/obj/item/seeds/pineapple
- name = "pack of pineapple seeds"
+ name = "pineapple seed pack"
desc = "Oooooooooooooh!"
icon_state = "seed-pineapple"
species = "pineapple"
diff --git a/code/modules/hydroponics/grown/plum.dm b/code/modules/hydroponics/grown/plum.dm
index cac12bdb1eb5d..c11acdf2db553 100644
--- a/code/modules/hydroponics/grown/plum.dm
+++ b/code/modules/hydroponics/grown/plum.dm
@@ -1,6 +1,6 @@
// Plum
/obj/item/seeds/plum
- name = "pack of plum seeds"
+ name = "plum seed pack"
desc = "These seeds grow into plum trees."
icon_state = "seed-plum"
species = "plum"
@@ -28,7 +28,7 @@
// Plumb
/obj/item/seeds/plum/plumb
- name = "pack of plumb seeds"
+ name = "plumb seed pack"
desc = "These seeds grow into plumb trees."
icon_state = "seed-plumb"
species = "plumb"
diff --git a/code/modules/hydroponics/grown/potato.dm b/code/modules/hydroponics/grown/potato.dm
index 837937e41d128..c57111b86d0c0 100644
--- a/code/modules/hydroponics/grown/potato.dm
+++ b/code/modules/hydroponics/grown/potato.dm
@@ -1,6 +1,6 @@
// Potato
/obj/item/seeds/potato
- name = "pack of potato seeds"
+ name = "potato seed pack"
desc = "Boil 'em! Mash 'em! Stick 'em in a stew!"
icon_state = "seed-potato"
species = "potato"
@@ -50,7 +50,7 @@
// Sweet Potato
/obj/item/seeds/potato/sweet
- name = "pack of sweet potato seeds"
+ name = "sweet potato seed pack"
desc = "These seeds grow into sweet potato plants."
icon_state = "seed-sweetpotato"
species = "sweetpotato"
diff --git a/code/modules/hydroponics/grown/pumpkin.dm b/code/modules/hydroponics/grown/pumpkin.dm
index 11130c153344a..dce207302c306 100644
--- a/code/modules/hydroponics/grown/pumpkin.dm
+++ b/code/modules/hydroponics/grown/pumpkin.dm
@@ -1,6 +1,6 @@
// Pumpkin
/obj/item/seeds/pumpkin
- name = "pack of pumpkin seeds"
+ name = "pumpkin seed pack"
desc = "These seeds grow into pumpkin vines."
icon_state = "seed-pumpkin"
plant_icon_offset = 4
@@ -40,7 +40,7 @@
// Blumpkin
/obj/item/seeds/pumpkin/blumpkin
- name = "pack of blumpkin seeds"
+ name = "blumpkin seed pack"
desc = "These seeds grow into blumpkin vines."
icon_state = "seed-blumpkin"
species = "blumpkin"
diff --git a/code/modules/hydroponics/grown/rainbow_bunch.dm b/code/modules/hydroponics/grown/rainbow_bunch.dm
index 4ffad4583c705..c3d21347bf953 100644
--- a/code/modules/hydroponics/grown/rainbow_bunch.dm
+++ b/code/modules/hydroponics/grown/rainbow_bunch.dm
@@ -1,5 +1,5 @@
/obj/item/seeds/rainbow_bunch
- name = "pack of rainbow bunch seeds"
+ name = "rainbow bunch seed pack"
desc = "A pack of seeds that'll grow into a beautiful bush of various colored flowers."
icon_state = "seed-rainbowbunch"
species = "rainbowbunch"
diff --git a/code/modules/hydroponics/grown/random.dm b/code/modules/hydroponics/grown/random.dm
index 560e3c71e5ce6..3a97277f0d85c 100644
--- a/code/modules/hydroponics/grown/random.dm
+++ b/code/modules/hydroponics/grown/random.dm
@@ -1,7 +1,7 @@
//Random seeds; stats, traits, and plant type are randomized for each seed.
/obj/item/seeds/random
- name = "pack of strange seeds"
+ name = "strange seed pack"
desc = "Mysterious seeds as strange as their name implies. Spooky."
icon_state = "seed-x"
species = "?????"
diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm
index 31c0b7f81df64..602b90f4c3567 100644
--- a/code/modules/hydroponics/grown/replicapod.dm
+++ b/code/modules/hydroponics/grown/replicapod.dm
@@ -1,8 +1,8 @@
-// A very special plant, deserving it's own file.
+// A very special plant, deserving its own file.
// Yes, i'm talking about cabbage, baby! No, just kidding, but cabbages are the precursor to replica pods, so they are here as well.
/obj/item/seeds/cabbage
- name = "pack of cabbage seeds"
+ name = "cabbage seed pack"
desc = "These seeds grow into cabbages."
icon_state = "seed-cabbage"
species = "cabbage"
@@ -31,7 +31,7 @@
///The actual replica pods themselves!
/obj/item/seeds/replicapod
- name = "pack of replica pod seeds"
+ name = "replica pod seed pack"
desc = "These seeds grow into replica pods. They say these are used to harvest humans."
icon_state = "seed-replicapod"
plant_icon_offset = 2
diff --git a/code/modules/hydroponics/grown/root.dm b/code/modules/hydroponics/grown/root.dm
index c272b1e448462..4393d76a5fa22 100644
--- a/code/modules/hydroponics/grown/root.dm
+++ b/code/modules/hydroponics/grown/root.dm
@@ -1,6 +1,6 @@
// Carrot
/obj/item/seeds/carrot
- name = "pack of carrot seeds"
+ name = "carrot seed pack"
desc = "These seeds grow into carrots."
icon_state = "seed-carrot"
species = "carrot"
@@ -12,7 +12,7 @@
instability = 15
growthstages = 3
growing_icon = 'icons/obj/service/hydroponics/growing_vegetables.dmi'
- mutatelist = list(/obj/item/seeds/carrot/parsnip)
+ mutatelist = list(/obj/item/seeds/carrot/parsnip, /obj/item/seeds/carrot/cahnroot)
reagents_add = list(/datum/reagent/medicine/oculine = 0.1, /datum/reagent/consumable/nutriment/vitamin = 0.04, /datum/reagent/consumable/nutriment = 0.05)
/obj/item/food/grown/carrot
@@ -26,24 +26,26 @@
wine_power = 30
/obj/item/food/grown/carrot/attackby(obj/item/I, mob/user, params)
- if(I.get_sharpness())
- var/carrot_blade
- var/carrot_sword_chance = (max(0, seed.potency - 50) / 50)
- if (prob(carrot_sword_chance))
- carrot_blade = new /obj/item/claymore/carrot
- to_chat(user, span_notice("You sharpen the carrot into a sword with [I]."))
- else
- carrot_blade = new /obj/item/knife/shiv/carrot
- to_chat(user, span_notice("You sharpen the carrot into a shiv with [I]."))
- remove_item_from_storage(user)
- qdel(src)
- user.put_in_hands(carrot_blade)
- else
+ if(!I.get_sharpness())
return ..()
+ /// The blade carrot will turn into once sharpened
+ var/obj/item/carrot_blade
+ /// Chance for it to become a sword rather than a shiv
+ var/carrot_sword_chance = (max(0, seed.potency - 50) / 50)
+ if (prob(carrot_sword_chance))
+ carrot_blade = new /obj/item/claymore/carrot
+ to_chat(user, span_notice("You sharpen the carrot into a sword with [I]."))
+ else
+ carrot_blade = new /obj/item/knife/shiv/carrot
+ to_chat(user, span_notice("You sharpen the carrot into a shiv with [I]."))
+ remove_item_from_storage(user)
+ qdel(src)
+ user.put_in_hands(carrot_blade)
+
// Parsnip
/obj/item/seeds/carrot/parsnip
- name = "pack of parsnip seeds"
+ name = "parsnip seed pack"
desc = "These seeds grow into parsnips."
icon_state = "seed-parsnip"
species = "parsnip"
@@ -62,10 +64,73 @@
juice_typepath = /datum/reagent/consumable/parsnipjuice
wine_power = 35
+/obj/item/food/grown/parsnip/attackby(obj/item/I, mob/user, params)
+ if(!I.get_sharpness())
+ return ..()
+
+ /// The blade parsnip will turn into once sharpened
+ var/obj/item/parsnip_blade
+ /// Chance for it to become a sabre rather than a shiv
+ var/parsnip_sabre_chance = (max(0, seed.potency - 50) / 50)
+ if (prob(parsnip_sabre_chance))
+ parsnip_blade = new /obj/item/melee/parsnip_sabre
+ to_chat(user, span_notice("You sharpen the parsnip into a sabre with [I]."))
+ else
+ parsnip_blade = new /obj/item/knife/shiv/parsnip
+ to_chat(user, span_notice("You sharpen the parsnip into a shiv with [I]."))
+ remove_item_from_storage(user)
+ qdel(src)
+ user.put_in_hands(parsnip_blade)
+
+
+// Cahn'root
+/obj/item/seeds/carrot/cahnroot
+ name = "cahn'root seed pack"
+ desc = "These seeds grow into cahn'roots."
+ icon_state = "seed-cahn'root"
+ species = "cahn'root"
+ plantname = "Cahn'root"
+ product = /obj/item/food/grown/cahnroot
+ genes = list(/datum/plant_gene/trait/plant_type/weed_hardy)
+ endurance = 50
+ instability = 10
+ icon_dead = "cahn'root-dead"
+ mutatelist = null
+ reagents_add = list(/datum/reagent/consumable/nutriment/vitamin = 0.05, /datum/reagent/consumable/nutriment = 0.05, /datum/reagent/cellulose = 0.01, /datum/reagent/consumable/sugar = 0.01)
+ rarity = 10
+ graft_gene = /datum/plant_gene/trait/plant_type/weed_hardy
+
+/obj/item/food/grown/cahnroot
+ seed = /obj/item/seeds/carrot/cahnroot
+ name = "cahn'root"
+ desc = "Heavily modified version of terran carrot, originally made to survive the scarciest of environments by an enterprising scientist of Moth Flotilla, Cahn'Mang."
+ icon_state = "cahn'root"
+ foodtypes = VEGETABLES
+ juice_typepath = null
+ tastes = list("sweet dirt" = 1)
+ distill_reagent = /datum/reagent/consumable/rootbeer
+
+/obj/item/food/grown/cahnroot/attackby(obj/item/I, mob/user, params)
+ if(!I.get_sharpness())
+ return ..()
+
+ /// The blade cahn'root will turn into once sharpened
+ var/obj/item/knife/root_blade
+ /// Chance for it to become a dagger rather than a shiv
+ var/root_dagger_chance = (max(0, seed.potency - 25) / 50)
+ if (prob(root_dagger_chance))
+ root_blade = new /obj/item/knife/combat/root
+ to_chat(user, span_notice("You sharpen the cahn'root into a dagger with [I]."))
+ else
+ root_blade = new /obj/item/knife/shiv/root
+ to_chat(user, span_notice("You sharpen the cahn'root into a shiv with [I]."))
+ remove_item_from_storage(user)
+ qdel(src)
+ user.put_in_hands(root_blade)
// White-Beet
/obj/item/seeds/whitebeet
- name = "pack of white-beet seeds"
+ name = "white-beet seed pack"
desc = "These seeds grow into sugary beet producing plants."
icon_state = "seed-whitebeet"
species = "whitebeet"
@@ -91,7 +156,7 @@
// Red Beet
/obj/item/seeds/redbeet
- name = "pack of redbeet seeds"
+ name = "redbeet seed pack"
desc = "These seeds grow into red beet producing plants."
icon_state = "seed-redbeet"
species = "redbeet"
diff --git a/code/modules/hydroponics/grown/seedling.dm b/code/modules/hydroponics/grown/seedling.dm
index 57fd11280b6ed..9ce83acfa9e36 100644
--- a/code/modules/hydroponics/grown/seedling.dm
+++ b/code/modules/hydroponics/grown/seedling.dm
@@ -1,5 +1,5 @@
/obj/item/seeds/seedling
- name = "pack of seedling seeds"
+ name = "seedling seed pack"
desc = "These seeds grow into a floral assistant which can help look after other plants!"
icon_state = "seed-seedling"
growing_icon = 'icons/obj/service/hydroponics/growing_fruits.dmi'
diff --git a/code/modules/hydroponics/grown/sugarcane.dm b/code/modules/hydroponics/grown/sugarcane.dm
index 1c5c55fece08d..e7845f1e55607 100644
--- a/code/modules/hydroponics/grown/sugarcane.dm
+++ b/code/modules/hydroponics/grown/sugarcane.dm
@@ -1,7 +1,7 @@
// Sugarcane
/obj/item/seeds/sugarcane
- name = "pack of sugarcane seeds"
+ name = "sugarcane seed pack"
desc = "These seeds grow into sugarcane."
icon_state = "seed-sugarcane"
species = "sugarcane"
@@ -28,7 +28,7 @@
///and bamboo!
/obj/item/seeds/bamboo
- name = "pack of bamboo seeds"
+ name = "bamboo seed pack"
desc = "A plant known for its flexible and resistant logs."
icon_state = "seed-bamboo"
species = "bamboo"
@@ -59,7 +59,7 @@
//Saltcane - Gross, salty shafts!
/obj/item/seeds/sugarcane/saltcane
- name = "pack of saltcane seeds"
+ name = "saltcane seed pack"
desc = "These seeds grow into saltcane."
icon_state = "seed-saltcane"
species = "saltcane"
diff --git a/code/modules/hydroponics/grown/tea_coffee.dm b/code/modules/hydroponics/grown/tea_coffee.dm
index 366dd8b45237e..26a215be720b4 100644
--- a/code/modules/hydroponics/grown/tea_coffee.dm
+++ b/code/modules/hydroponics/grown/tea_coffee.dm
@@ -1,6 +1,6 @@
// Tea
/obj/item/seeds/tea
- name = "pack of tea aspera seeds"
+ name = "tea aspera seed pack"
desc = "These seeds grow into tea plants."
icon_state = "seed-teaaspera"
species = "teaaspera"
@@ -27,7 +27,7 @@
// Tea Astra
/obj/item/seeds/tea/astra
- name = "pack of tea astra seeds"
+ name = "tea astra seed pack"
icon_state = "seed-teaastra"
species = "teaastra"
plantname = "Tea Astra Plant"
@@ -46,7 +46,7 @@
// Coffee
/obj/item/seeds/coffee
- name = "pack of coffee arabica seeds"
+ name = "coffee arabica seed pack"
desc = "These seeds grow into coffee arabica bushes."
icon_state = "seed-coffeea"
species = "coffeea"
@@ -75,7 +75,7 @@
// Coffee Robusta
/obj/item/seeds/coffee/robusta
- name = "pack of coffee robusta seeds"
+ name = "coffee robusta seed pack"
desc = "These seeds grow into coffee robusta bushes."
icon_state = "seed-coffeer"
species = "coffeer"
diff --git a/code/modules/hydroponics/grown/tobacco.dm b/code/modules/hydroponics/grown/tobacco.dm
index 87f8253a4d96b..29e8fba6d6a8b 100644
--- a/code/modules/hydroponics/grown/tobacco.dm
+++ b/code/modules/hydroponics/grown/tobacco.dm
@@ -1,6 +1,6 @@
// Tobacco
/obj/item/seeds/tobacco
- name = "pack of tobacco seeds"
+ name = "tobacco seed pack"
desc = "These seeds grow into tobacco plants."
icon_state = "seed-tobacco"
species = "tobacco"
@@ -24,7 +24,7 @@
// Space Tobacco
/obj/item/seeds/tobacco/space
- name = "pack of space tobacco seeds"
+ name = "space tobacco seed pack"
desc = "These seeds grow into space tobacco plants."
icon_state = "seed-stobacco"
species = "stobacco"
diff --git a/code/modules/hydroponics/grown/tomato.dm b/code/modules/hydroponics/grown/tomato.dm
index e676d822bbf1a..1459887d6facb 100644
--- a/code/modules/hydroponics/grown/tomato.dm
+++ b/code/modules/hydroponics/grown/tomato.dm
@@ -1,6 +1,6 @@
// Tomato
/obj/item/seeds/tomato
- name = "pack of tomato seeds"
+ name = "tomato seed pack"
desc = "These seeds grow into tomato plants."
icon_state = "seed-tomato"
species = "tomato"
@@ -29,7 +29,7 @@
// Blood Tomato
/obj/item/seeds/tomato/blood
- name = "pack of blood-tomato seeds"
+ name = "blood-tomato seed pack"
desc = "These seeds grow into blood-tomato plants."
icon_state = "seed-bloodtomato"
species = "bloodtomato"
@@ -52,7 +52,7 @@
// Blue Tomato
/obj/item/seeds/tomato/blue
- name = "pack of blue-tomato seeds"
+ name = "blue-tomato seed pack"
desc = "These seeds grow into blue-tomato plants."
icon_state = "seed-bluetomato"
species = "bluetomato"
@@ -77,7 +77,7 @@
// Bluespace Tomato
/obj/item/seeds/tomato/blue/bluespace
- name = "pack of bluespace tomato seeds"
+ name = "bluespace tomato seed pack"
desc = "These seeds grow into bluespace tomato plants."
icon_state = "seed-bluespacetomato"
species = "bluespacetomato"
@@ -101,7 +101,7 @@
// Killer Tomato
/obj/item/seeds/tomato/killer
- name = "pack of killer-tomato seeds"
+ name = "killer-tomato seed pack"
desc = "These seeds grow into killer-tomato plants."
icon_state = "seed-killertomato"
species = "killertomato"
diff --git a/code/modules/hydroponics/grown/towercap.dm b/code/modules/hydroponics/grown/towercap.dm
index 1082b51665ff7..a09e4e61b4d15 100644
--- a/code/modules/hydroponics/grown/towercap.dm
+++ b/code/modules/hydroponics/grown/towercap.dm
@@ -1,5 +1,5 @@
/obj/item/seeds/tower
- name = "pack of tower-cap mycelium"
+ name = "tower-cap mycelium pack"
desc = "This mycelium grows into tower-cap mushrooms."
icon_state = "mycelium-tower"
species = "towercap"
@@ -20,7 +20,7 @@
graft_gene = /datum/plant_gene/trait/plant_type/fungal_metabolism
/obj/item/seeds/tower/steel
- name = "pack of steel-cap mycelium"
+ name = "steel-cap mycelium pack"
desc = "This mycelium grows into steel logs."
icon_state = "mycelium-steelcap"
species = "steelcap"
diff --git a/code/modules/hydroponics/grown/weeds/kudzu.dm b/code/modules/hydroponics/grown/weeds/kudzu.dm
index d8f1a66795b2b..a0a3c9405718c 100644
--- a/code/modules/hydroponics/grown/weeds/kudzu.dm
+++ b/code/modules/hydroponics/grown/weeds/kudzu.dm
@@ -1,7 +1,7 @@
-// A very special plant, deserving it's own file.
+// A very special plant, deserving its own file.
/obj/item/seeds/kudzu
- name = "pack of kudzu seeds"
+ name = "kudzu seed pack"
desc = "These seeds grow into a weed that grows incredibly fast."
icon_state = "seed-kudzu"
plant_icon_offset = 2
diff --git a/code/modules/hydroponics/grown/weeds/nettle.dm b/code/modules/hydroponics/grown/weeds/nettle.dm
index 98868b8d6e748..33a0f6288912d 100644
--- a/code/modules/hydroponics/grown/weeds/nettle.dm
+++ b/code/modules/hydroponics/grown/weeds/nettle.dm
@@ -1,5 +1,5 @@
/obj/item/seeds/nettle
- name = "pack of nettle seeds"
+ name = "nettle seed pack"
desc = "These seeds grow into nettles."
icon_state = "seed-nettle"
plant_icon_offset = 0
@@ -17,7 +17,7 @@
graft_gene = /datum/plant_gene/trait/plant_type/weed_hardy
/obj/item/seeds/nettle/death
- name = "pack of death-nettle seeds"
+ name = "death-nettle seed pack"
desc = "These seeds grow into death-nettles."
icon_state = "seed-deathnettle"
species = "deathnettle"
diff --git a/code/modules/hydroponics/grown/weeds/starthistle.dm b/code/modules/hydroponics/grown/weeds/starthistle.dm
index 74627a31f88e8..af94cf3d98508 100644
--- a/code/modules/hydroponics/grown/weeds/starthistle.dm
+++ b/code/modules/hydroponics/grown/weeds/starthistle.dm
@@ -1,6 +1,6 @@
// Starthistle
/obj/item/seeds/starthistle
- name = "pack of starthistle seeds"
+ name = "starthistle seed pack"
desc = "A robust species of weed that often springs up in-between the cracks of spaceship parking lots."
icon_state = "seed-starthistle"
plant_icon_offset = 3
@@ -33,7 +33,7 @@
// Corpse flower
/obj/item/seeds/starthistle/corpse_flower
- name = "pack of corpse flower seeds"
+ name = "corpse flower seed pack"
desc = "A species of plant that emits a horrible odor. The odor stops being produced in difficult atmospheric conditions."
icon_state = "seed-corpse-flower"
species = "corpse-flower"
@@ -46,7 +46,7 @@
//Galaxy Thistle
/obj/item/seeds/galaxythistle
- name = "pack of galaxythistle seeds"
+ name = "galaxythistle seed pack"
desc = "An impressive species of weed that is thought to have evolved from the simple milk thistle. Contains flavolignans that can help repair a damaged liver."
icon_state = "seed-galaxythistle"
species = "galaxythistle"
diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm
index 5f72806ca3635..9b2cbdfae40fb 100644
--- a/code/modules/hydroponics/hydroitemdefines.dm
+++ b/code/modules/hydroponics/hydroitemdefines.dm
@@ -160,7 +160,7 @@
/**
* This proc is called when we scan a hydroponics tray or soil on left click (stats mode)
- * It formats the plant name, it's age, the plant's stats, and the tray's stats.
+ * It formats the plant name, its age, the plant's stats, and the tray's stats.
*
* - scanned_tray - the tray or soil we are scanning.
*
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index 34fb9fc04622e..30b2eced5c760 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -1,12 +1,12 @@
/obj/machinery/hydroponics
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "hydroponics tray"
desc = "A basin used to grow plants in."
icon = 'icons/obj/service/hydroponics/equipment.dmi'
icon_state = "hydrotray"
density = TRUE
pass_flags_self = PASSMACHINE | LETPASSTHROW
- pixel_z = 8
obj_flags = CAN_BE_HIT | UNIQUE_RENAME
circuit = /obj/item/circuitboard/machine/hydroponics
interaction_flags_click = FORBID_TELEKINESIS_REACH
@@ -58,7 +58,7 @@
/obj/machinery/hydroponics/Initialize(mapload)
//ALRIGHT YOU DEGENERATES. YOU HAD REAGENT HOLDERS FOR AT LEAST 4 YEARS AND NONE OF YOU MADE HYDROPONICS TRAYS HOLD NUTRIENT CHEMS INSTEAD OF USING "Points".
- //SO HERE LIES THE "nutrilevel" VAR. IT'S DEAD AND I PUT IT OUT OF IT'S MISERY. USE "reagents" INSTEAD. ~ArcaneMusic, accept no substitutes.
+ //SO HERE LIES THE "nutrilevel" VAR. IT'S DEAD AND I PUT IT OUT OF ITS MISERY. USE "reagents" INSTEAD. ~ArcaneMusic, accept no substitutes.
create_reagents(maxnutri, INJECTABLE)
if(mapload)
reagents.add_reagent(/datum/reagent/plantnutriment/eznutriment, max(maxnutri / 2, 10)) //Half filled nutrient trays for dirt trays to have more to grow with in prison/lavaland.
@@ -157,6 +157,11 @@
icon = 'icons/obj/service/hydroponics/equipment.dmi'
icon_state = "hydrotray3"
+/obj/machinery/hydroponics/constructable/fullupgrade
+ name = "deluxe hydroponics tray"
+ desc = "A basin used to grown plants in, packed full of cutting-edge technology."
+ circuit = /obj/item/circuitboard/machine/hydroponics/fullupgrade
+
/obj/machinery/hydroponics/constructable/Initialize(mapload)
. = ..()
AddComponent(/datum/component/simple_rotation)
@@ -781,7 +786,7 @@
/**
* Plant Cross-Pollination.
* Checks all plants in the tray's oview range, then averages out the seed's potency, instability, and yield values.
- * If the seed's instability is >= 20, the seed donates one of it's reagents to that nearby plant.
+ * If the seed's instability is >= 20, the seed donates one of its reagents to that nearby plant.
* * Range - The Oview range of trays to which to look for plants to donate reagents.
*/
/obj/machinery/hydroponics/proc/pollinate(range = 1)
@@ -798,15 +803,7 @@
if(isnull(particles))
particles = new /particles/pollen()
if(myseed.instability >= 20 && prob(70) && length(T.myseed.reagents_add))
- var/list/datum/plant_gene/reagent/possible_reagents = list()
- for(var/datum/plant_gene/reagent/reag in T.myseed.genes)
- possible_reagents += reag
- var/datum/plant_gene/reagent/reagent_gene = pick(possible_reagents) //Let this serve as a lession to delete your WIP comments before merge.
- if(reagent_gene.can_add(myseed))
- if(!reagent_gene.try_upgrade_gene(myseed))
- myseed.genes += reagent_gene.Copy()
- myseed.reagents_from_genes()
- continue
+ myseed.perform_reagent_pollination(T.myseed)
if(!any_adjacent)
particles = null
@@ -1033,7 +1030,7 @@
to_chat(user, span_warning("[src] is empty!"))
return
if(myseed.endurance <= FLORA_GUN_MIN_ENDURANCE)
- to_chat(user, span_warning("[myseed.plantname] isn't hardy enough to sequence it's mutation!"))
+ to_chat(user, span_warning("[myseed.plantname] isn't hardy enough to sequence its mutation!"))
return
if(!LAZYLEN(myseed.mutatelist))
to_chat(user, span_warning("[myseed.plantname] has nothing else to mutate into!"))
diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm
index 6fc671ccd4c94..f59adad783968 100644
--- a/code/modules/hydroponics/plant_genes.dm
+++ b/code/modules/hydroponics/plant_genes.dm
@@ -487,7 +487,7 @@
/**
* A plant trait that causes the plant's capacity to double.
*
- * When harvested, the plant's individual capacity is set to double it's default.
+ * When harvested, the plant's individual capacity is set to double its default.
* However, the plant's maximum yield is also halved, only up to 5.
*/
/datum/plant_gene/trait/maxchem
@@ -763,7 +763,7 @@
/**
* A plant trait that causes the plant's food reagents to ferment instead.
*
- * In practice, it replaces the plant's nutriment and vitamins with half as much of it's fermented reagent.
+ * In practice, it replaces the plant's nutriment and vitamins with half as much of its fermented reagent.
* This exception is executed in seeds.dm under 'prepare_result'.
*
* Incompatible with auto-juicing composition.
diff --git a/code/modules/hydroponics/seed_extractor.dm b/code/modules/hydroponics/seed_extractor.dm
index c558cba8b3eab..32982c421df45 100644
--- a/code/modules/hydroponics/seed_extractor.dm
+++ b/code/modules/hydroponics/seed_extractor.dm
@@ -42,6 +42,7 @@
return seeds
/obj/machinery/seed_extractor
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "seed extractor"
desc = "Extracts and bags seeds from produce."
icon = 'icons/obj/service/hydroponics/equipment.dmi'
@@ -276,7 +277,7 @@
data["trait_db"] += trait_data
return data
-/obj/machinery/seed_extractor/ui_act(action, params)
+/obj/machinery/seed_extractor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm
index fefd4d999387f..18197c006b51e 100644
--- a/code/modules/hydroponics/seeds.dm
+++ b/code/modules/hydroponics/seeds.dm
@@ -40,7 +40,7 @@
var/potency = 10
/// Amount of growth sprites the plant has.
var/growthstages = 6
- // Chance that a plant will mutate in each stage of it's life.
+ // Chance that a plant will mutate in each stage of its life.
var/instability = 5
/// How rare the plant is. Used for giving points to cargo when shipping off to CentCom.
var/rarity = 0
@@ -616,3 +616,27 @@
/obj/item/grown/get_plant_seed()
return seed
+
+/obj/item/seeds/proc/perform_reagent_pollination(obj/item/seeds/donor)
+ var/list/datum/plant_gene/reagent/valid_reagents = list()
+ for(var/datum/plant_gene/reagent/donor_reagent in donor.genes)
+ var/repeated = FALSE
+ for(var/datum/plant_gene/reagent/receptor_reagent in genes)
+ if(donor_reagent.reagent_id == receptor_reagent.reagent_id)
+ if(receptor_reagent.rate < donor_reagent.rate)
+ receptor_reagent.rate = donor_reagent.rate
+ // sucessful pollination/upgrade, we stop here.
+ reagents_from_genes()
+ return
+ else
+ repeated = TRUE
+ break
+
+ if(!repeated)
+ valid_reagents += donor_reagent
+
+ if(length(valid_reagents))
+ // pick a valid reagent that our receptor seed don't have and add the gene to it
+ var/datum/plant_gene/reagent/selected_reagent = pick(valid_reagents)
+ genes += selected_reagent.Copy()
+ reagents_from_genes()
diff --git a/code/modules/instruments/items.dm b/code/modules/instruments/items.dm
index dcc4ef8daba3d..170000eb11557 100644
--- a/code/modules/instruments/items.dm
+++ b/code/modules/instruments/items.dm
@@ -17,7 +17,7 @@
/obj/item/instrument/Initialize(mapload)
. = ..()
song = new(src, allowed_instrument_ids, instrument_range)
- allowed_instrument_ids = null //We don't need this clogging memory after it's used.
+ allowed_instrument_ids = null //We don't need this clogging memory after its used.
/obj/item/instrument/Destroy()
QDEL_NULL(song)
diff --git a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm
index b2c051a4e1c7d..6b429b25f4831 100644
--- a/code/modules/jobs/job_exp.dm
+++ b/code/modules/jobs/job_exp.dm
@@ -203,6 +203,6 @@ GLOBAL_PROTECT(exp_to_update)
if(flags_read.NextRow())
prefs.db_flags = text2num(flags_read.item[1])
else if(isnull(prefs.db_flags))
- prefs.db_flags = 0 //This PROBABLY won't happen, but better safe than sorry.
+ prefs.db_flags = NONE //This PROBABLY won't happen, but better safe than sorry.
qdel(flags_read)
return TRUE
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index dd0a8dcd47c15..81d42b051dff8 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -243,6 +243,11 @@
if(!SSdbcore.Connect())
return 0
+ // If they have been exempted from date availability checks, we assume they are old enough for all jobs.
+ // This is only added whenever an admin manually ticks the box for this player.
+ if(player.prefs?.db_flags & DB_FLAG_EXEMPT)
+ return 0
+
// As of the time of writing this comment, verifying database connection isn't "solved". Sometimes rust-g will report a
// connection mid-shift despite the database dying.
// If the client age is -1, it means that no code path has overwritten it. Even first time connections get it set to 0,
diff --git a/code/modules/jobs/job_types/antagonists/voidwalker.dm b/code/modules/jobs/job_types/antagonists/voidwalker.dm
new file mode 100644
index 0000000000000..c29d165efb807
--- /dev/null
+++ b/code/modules/jobs/job_types/antagonists/voidwalker.dm
@@ -0,0 +1,2 @@
+/datum/job/voidwalker
+ title = ROLE_VOIDWALKER
diff --git a/code/modules/jobs/job_types/assistant/assistant.dm b/code/modules/jobs/job_types/assistant/assistant.dm
index 2732d5e21ca9c..6a590d3e8f8dc 100644
--- a/code/modules/jobs/job_types/assistant/assistant.dm
+++ b/code/modules/jobs/job_types/assistant/assistant.dm
@@ -39,7 +39,7 @@ Assistant
/datum/job/assistant/get_outfit(consistent)
if(consistent)
- return /datum/outfit/job/assistant/consistent
+ return /datum/outfit/job/assistant/preview
if(!HAS_TRAIT(SSstation, STATION_TRAIT_ASSISTANT_GIMMICKS))
return ..()
@@ -96,3 +96,12 @@ Assistant
if (SSatoms.initialized == INITIALIZATION_INSSATOMS)
H.w_uniform?.update_greyscale()
H.update_worn_undersuit()
+
+/datum/outfit/job/assistant/preview
+ name = "Assistant - Preview"
+
+/datum/outfit/job/assistant/preview/give_jumpsuit(mob/living/carbon/human/target)
+ if (target.jumpsuit_style == PREF_SUIT)
+ uniform = /obj/item/clothing/under/color/grey
+ else
+ uniform = /obj/item/clothing/under/color/jumpskirt/grey
diff --git a/code/modules/jobs/job_types/assistant/gimmick_assistants.dm b/code/modules/jobs/job_types/assistant/gimmick_assistants.dm
index ed2851366153c..23fd9616ee3cb 100644
--- a/code/modules/jobs/job_types/assistant/gimmick_assistants.dm
+++ b/code/modules/jobs/job_types/assistant/gimmick_assistants.dm
@@ -167,6 +167,7 @@
belt = /obj/item/storage/belt/utility/full
head = /obj/item/clothing/head/utility/hardhat
uniform = /obj/item/clothing/under/color/yellow
+ l_pocket = /obj/item/modular_computer/pda/assistant
outfit_weight = 6
diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm
index 13a4162ff3be9..c0f200c82f7b6 100644
--- a/code/modules/jobs/job_types/bartender.dm
+++ b/code/modules/jobs/job_types/bartender.dm
@@ -59,6 +59,8 @@
glasses = /obj/item/clothing/glasses/sunglasses/reagent
shoes = /obj/item/clothing/shoes/laceup
+ skillchips = list(/obj/item/skillchip/drunken_brawler)
+
/datum/outfit/job/bartender/post_equip(mob/living/carbon/human/H, visualsOnly)
. = ..()
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
index df658d71dddc2..dd7208d170618 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
@@ -564,7 +564,7 @@
/obj/item/nullrod/bostaff
name = "monk's staff"
desc = "A long, tall staff made of polished wood. Traditionally used in ancient old-Earth martial arts, it is now used to harass the clown."
- force = 15
+ force = 14
block_chance = 40
block_sound = 'sound/weapons/genhit.ogg'
slot_flags = ITEM_SLOT_BACK
@@ -574,11 +574,24 @@
attack_verb_simple = list("smash", "slam", "whack", "thwack")
icon = 'icons/obj/weapons/staff.dmi'
icon_state = "bostaff0"
+ base_icon_state = "bostaff"
inhand_icon_state = "bostaff0"
worn_icon_state = "bostaff0"
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
- menu_description = "A staff which provides a medium-low chance of blocking incoming melee attacks and deals a little less damage. Can be worn on the back."
+ menu_description = "A staff which provides a medium-low chance of blocking incoming melee attacks and deals less damage, unless dual-wielded. Can be worn on the back."
+
+/obj/item/nullrod/bostaff/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/two_handed, \
+ force_unwielded = 14, \
+ force_wielded = 18, \
+ )
+
+/obj/item/nullrod/bostaff/update_icon_state()
+ icon_state = inhand_icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]"
+ return ..()
+
// Arrhythmic Knife - Lets your walk without rhythm by varying your walk speed. Can't be put away.
@@ -692,6 +705,28 @@
hitsound = 'sound/weapons/bladeslice.ogg'
menu_description = "A pointy spear which penetrates armor a little. Can be worn only on the belt."
+// Unholy version of above, since the gamemode is dead in the water
+
+/obj/item/brass_spear
+ name = "dull brass spear"
+ desc = "An ancient spear made of brass. The point seems sharp, but it feels so dull.. you get a feeling brass isn't good nonmagical material for a weapon."
+ icon = 'icons/obj/weapons/spear.dmi'
+ icon_state = "ratvarian_spear"
+ inhand_icon_state = "ratvarian_spear"
+ lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi'
+ slot_flags = ITEM_SLOT_BELT
+ force = 15
+ throw_speed = 3
+ throw_range = 7
+ throwforce = 15
+ armour_penetration = 10
+ sharpness = SHARP_POINTY
+ w_class = WEIGHT_CLASS_HUGE
+ attack_verb_continuous = list("stabs", "pokes", "slashes", "clocks")
+ attack_verb_simple = list("stab", "poke", "slash", "clock")
+ hitsound = 'sound/weapons/bladeslice.ogg'
+
// Nullblade - For when you really want to feel like rolling dice during combat
/obj/item/nullrod/nullblade
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
index ec484ebe6ebc5..4b07baaa05890 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
@@ -10,7 +10,7 @@ If the scythe isn't empowered when you sheath it, you take a heap of damage and
desc = "This shard seems to be directly linked to some sinister entity. It might be your god! It also gives you a really horrible rash when you hold onto it for too long."
items_to_create = list(/obj/item/vorpalscythe)
-/obj/item/organ/internal/cyberimp/arm/shard/scythe/Insert(mob/living/carbon/receiver, special, movement_flags)
+/obj/item/organ/internal/cyberimp/arm/shard/scythe/mob_insert(mob/living/carbon/receiver, special, movement_flags)
. = ..()
if(receiver.mind)
ADD_TRAIT(receiver.mind, TRAIT_MORBID, ORGAN_TRAIT)
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index 999117dc1be15..f3627c3c2a257 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -59,7 +59,7 @@
belt = /obj/item/modular_computer/pda/heads/hos
ears = /obj/item/radio/headset/heads/hos/alt
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
- gloves = /obj/item/clothing/gloves/color/black
+ gloves = /obj/item/clothing/gloves/color/black/security
head = /obj/item/clothing/head/hats/hos/beret
shoes = /obj/item/clothing/shoes/jackboots/sec
l_pocket = /obj/item/restraints/handcuffs
diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm
index 5d3c620322759..8bfbe362327cb 100644
--- a/code/modules/jobs/job_types/research_director.dm
+++ b/code/modules/jobs/job_types/research_director.dm
@@ -76,7 +76,10 @@
messenger = /obj/item/storage/backpack/messenger/science
chameleon_extras = /obj/item/stamp/head/rd
- skillchips = list(/obj/item/skillchip/research_director, /obj/item/skillchip/job/roboticist)
+ skillchips = list(
+ /obj/item/skillchip/research_director,
+ /obj/item/skillchip/job/roboticist,
+ )
/datum/outfit/job/rd/mod
name = "Research Director (MODsuit)"
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index 4fb52ec77a026..9b2dc91137cd5 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -218,7 +218,7 @@ GLOBAL_LIST_EMPTY(security_officer_distribution)
)
belt = /obj/item/modular_computer/pda/security
ears = /obj/item/radio/headset/headset_sec/alt
- gloves = /obj/item/clothing/gloves/color/black
+ gloves = /obj/item/clothing/gloves/color/black/security
head = /obj/item/clothing/head/helmet/sec
shoes = /obj/item/clothing/shoes/jackboots/sec
l_pocket = /obj/item/restraints/handcuffs
diff --git a/code/modules/jobs/job_types/station_trait/human_ai.dm b/code/modules/jobs/job_types/station_trait/human_ai.dm
index 0768505b66553..032ad08af5a60 100644
--- a/code/modules/jobs/job_types/station_trait/human_ai.dm
+++ b/code/modules/jobs/job_types/station_trait/human_ai.dm
@@ -98,9 +98,7 @@
/obj/item/door_remote/omni = 1,
/obj/item/machine_remote = 1,
/obj/item/secure_camera_console_pod = 1,
- )
- implants = list(
- /obj/item/implant/teleport_blocker,
+ /obj/item/sensor_device = 1,
)
uniform = /obj/item/clothing/under/rank/station_trait/human_ai
@@ -108,9 +106,6 @@
ears = /obj/item/radio/headset/silicon/human_ai
glasses = /obj/item/clothing/glasses/hud/diagnostic
- suit = /obj/item/clothing/suit/costume/cardborg
- head = /obj/item/clothing/head/costume/cardborg
-
l_pocket = /obj/item/laser_pointer/infinite_range //to punish borgs, this works through the camera console.
r_pocket = /obj/item/assembly/flash/handheld
diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm
index 643611201c8c1..c1d2c032901f7 100644
--- a/code/modules/jobs/job_types/warden.dm
+++ b/code/modules/jobs/job_types/warden.dm
@@ -56,7 +56,7 @@
belt = /obj/item/modular_computer/pda/warden
ears = /obj/item/radio/headset/headset_sec/alt
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
- gloves = /obj/item/clothing/gloves/color/black
+ gloves = /obj/item/clothing/gloves/color/black/security
head = /obj/item/clothing/head/hats/warden/red
shoes = /obj/item/clothing/shoes/jackboots/sec
l_pocket = /obj/item/restraints/handcuffs
diff --git a/code/modules/language/_language_manuals.dm b/code/modules/language/_language_manuals.dm
index eb4ca456440d8..7a4298a06b150 100644
--- a/code/modules/language/_language_manuals.dm
+++ b/code/modules/language/_language_manuals.dm
@@ -85,6 +85,14 @@
. = ..()
name = "extended [initial(language.name)] manual"
+/obj/item/language_manual/piratespeak
+ name = "\improper Captain Pete's Guide to Pirate Lingo"
+ icon_state = "book_pirate"
+ desc = "A book containing all the knowledge, jargon and buzzwords to speak like a true old salt."
+ language = /datum/language/piratespeak
+ flavour_text = "Blimey! I feel less of a landlubber now."
+ charges = 5
+
// So drones can teach borgs and AI dronespeak. For best effect, combine with mother drone lawset.
/obj/item/language_manual/dronespeak_manual
name = "dronespeak manual"
diff --git a/code/modules/language/_language_menu.dm b/code/modules/language/_language_menu.dm
index 0bfb7a79977af..905be8169e26a 100644
--- a/code/modules/language/_language_menu.dm
+++ b/code/modules/language/_language_menu.dm
@@ -56,7 +56,7 @@
return data
-/datum/language_menu/ui_act(action, params)
+/datum/language_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm
index 39abe7baa509b..5221dc8047422 100644
--- a/code/modules/library/bibles.dm
+++ b/code/modules/library/bibles.dm
@@ -316,7 +316,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
playsound(src,'sound/hallucinations/veryfar_noise.ogg',40,TRUE)
if(do_after(user, 4 SECONDS, target = sword))
playsound(src,'sound/effects/pray_chaplain.ogg',60,TRUE)
- new /obj/item/nullrod/claymore(get_turf(sword))
+ new /obj/item/nullrod/nullblade(get_turf(sword))
user.visible_message(span_notice("[user] exorcises [sword]!"))
qdel(sword)
return ITEM_INTERACT_SUCCESS
diff --git a/code/modules/library/bookcase.dm b/code/modules/library/bookcase.dm
index 16925dd5138e7..dc1f6e7b595b6 100644
--- a/code/modules/library/bookcase.dm
+++ b/code/modules/library/bookcase.dm
@@ -3,6 +3,7 @@
#define BOOKCASE_FINISHED 2
/obj/structure/bookcase
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "bookcase"
icon = 'icons/obj/service/library.dmi'
icon_state = "bookempty"
@@ -94,63 +95,81 @@
I.forceMove(Tsec)
update_appearance()
-/obj/structure/bookcase/attackby(obj/item/I, mob/user, params)
- switch(state)
- if(BOOKCASE_UNANCHORED)
- if(I.tool_behaviour == TOOL_WRENCH)
- if(I.use_tool(src, user, 20, volume=50))
- to_chat(user, span_notice("You wrench the frame into place."))
- set_anchored(TRUE)
- else if(I.tool_behaviour == TOOL_CROWBAR)
- if(I.use_tool(src, user, 20, volume=50))
- to_chat(user, span_notice("You pry the frame apart."))
- deconstruct(TRUE)
+/obj/structure/bookcase/attackby(obj/item/attacking_item, mob/user, params)
+ if(state == BOOKCASE_UNANCHORED)
+ if(attacking_item.tool_behaviour == TOOL_WRENCH)
+ if(attacking_item.use_tool(src, user, 20, volume=50))
+ balloon_alert(user, "wrenched in place")
+ set_anchored(TRUE)
+ return
+
+ if(attacking_item.tool_behaviour == TOOL_CROWBAR)
+ if(attacking_item.use_tool(src, user, 20, volume=50))
+ balloon_alert(user, "pried apart")
+ deconstruct(TRUE)
+ return
+ return ..()
- if(BOOKCASE_ANCHORED)
- if(istype(I, /obj/item/stack/sheet/mineral/wood))
- var/obj/item/stack/sheet/mineral/wood/W = I
- if(W.get_amount() >= 2)
- W.use(2)
- to_chat(user, span_notice("You add a shelf."))
- state = BOOKCASE_FINISHED
- update_appearance()
- else if(I.tool_behaviour == TOOL_WRENCH)
- I.play_tool_sound(src, 100)
- to_chat(user, span_notice("You unwrench the frame."))
- set_anchored(FALSE)
+ if(state == BOOKCASE_ANCHORED)
+ if(istype(attacking_item, /obj/item/stack/sheet/mineral/wood))
+ var/obj/item/stack/sheet/mineral/wood/W = attacking_item
+ if(W.get_amount() < 2)
+ balloon_alert(user, "not enough wood")
+ return
+ W.use(2)
+ balloon_alert(user, "shelf added")
+ state = BOOKCASE_FINISHED
+ update_appearance()
+ return
+
+ if(attacking_item.tool_behaviour == TOOL_WRENCH)
+ attacking_item.play_tool_sound(src, 100)
+ balloon_alert(user, "unwrenched the frame")
+ set_anchored(FALSE)
+ return
+ return ..()
- if(BOOKCASE_FINISHED)
- if(isbook(I))
- if(!user.transferItemToLoc(I, src))
- return
- update_appearance()
- else if(atom_storage)
- for(var/obj/item/T in I.contents)
- if(istype(T, /obj/item/book) || istype(T, /obj/item/spellbook))
- atom_storage.attempt_remove(T, src)
- to_chat(user, span_notice("You empty \the [I] into \the [src]."))
- update_appearance()
- else if(IS_WRITING_UTENSIL(I))
- if(!user.can_perform_action(src) || !user.can_write(I))
- return
- var/newname = tgui_input_text(user, "What would you like to title this bookshelf?", "Bookshelf Renaming", max_length = MAX_NAME_LEN)
- if(!user.can_perform_action(src) || !user.can_write(I))
- return
- if(!newname)
- return
- else
- name = "bookcase ([sanitize(newname)])"
- else if(I.tool_behaviour == TOOL_CROWBAR)
- if(length(contents))
- to_chat(user, span_warning("You need to remove the books first!"))
- else
- I.play_tool_sound(src, 100)
- to_chat(user, span_notice("You pry the shelf out."))
- new /obj/item/stack/sheet/mineral/wood(drop_location(), 2)
- state = BOOKCASE_ANCHORED
- update_appearance()
- else
- return ..()
+ if(isbook(attacking_item))
+ if(!user.transferItemToLoc(attacking_item, src))
+ return ..()
+ update_appearance()
+ return
+
+ if(atom_storage)
+ var/found_anything = FALSE
+ for(var/obj/item/T in attacking_item.contents)
+ if(istype(T, /obj/item/book) || istype(T, /obj/item/spellbook))
+ atom_storage.attempt_remove(T, src)
+ found_anything = TRUE
+
+ if (found_anything)
+ balloon_alert(user, "emptied into [src]")
+ update_appearance()
+ return
+
+ if(IS_WRITING_UTENSIL(attacking_item))
+ if(!user.can_perform_action(src) || !user.can_write(attacking_item))
+ return ..()
+ var/newname = tgui_input_text(user, "What would you like to title this bookshelf?", "Bookshelf Renaming", max_length = MAX_NAME_LEN)
+ if(!user.can_perform_action(src) || !user.can_write(attacking_item))
+ return ..()
+ if(!newname)
+ return
+ name = "bookcase ([sanitize(newname)])"
+ return
+
+ if(attacking_item.tool_behaviour == TOOL_CROWBAR)
+ if(length(contents))
+ balloon_alert(user, "remove the books first")
+ return
+ attacking_item.play_tool_sound(src, 100)
+ balloon_alert(user, "pried the shelf out")
+ new /obj/item/stack/sheet/mineral/wood(drop_location(), 2)
+ state = BOOKCASE_ANCHORED
+ update_appearance()
+ return
+
+ return ..()
/obj/structure/bookcase/attack_hand(mob/living/user, list/modifiers)
. = ..()
diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm
index a65159a3f8225..06f2cf5e8c800 100644
--- a/code/modules/library/lib_machines.dm
+++ b/code/modules/library/lib_machines.dm
@@ -80,7 +80,7 @@ GLOBAL_VAR_INIT(library_table_modified, 0)
data["params_changed"] = params_changed
return data
-/obj/machinery/computer/libraryconsole/ui_act(action, params)
+/obj/machinery/computer/libraryconsole/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -424,7 +424,7 @@ GLOBAL_VAR_INIT(library_table_modified, 0)
scanner = WEAKREF(foundya)
return foundya
-/obj/machinery/computer/libraryconsole/bookmanagement/ui_act(action, params)
+/obj/machinery/computer/libraryconsole/bookmanagement/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
//The parent call takes care of stuff like searching, don't forget about that yeah?
. = ..()
if(.)
@@ -767,6 +767,7 @@ GLOBAL_VAR_INIT(library_table_modified, 0)
* Book binder
*/
/obj/machinery/bookbinder
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "book binder"
icon = 'icons/obj/service/library.dmi'
icon_state = "binder"
diff --git a/code/modules/library/skill_learning/generic_skillchips/matrix_flip.dm b/code/modules/library/skill_learning/generic_skillchips/matrix_flip.dm
deleted file mode 100644
index a836442eca052..0000000000000
--- a/code/modules/library/skill_learning/generic_skillchips/matrix_flip.dm
+++ /dev/null
@@ -1,40 +0,0 @@
-#define FLIP_STAMINA_COST 19
-
-/obj/item/skillchip/matrix_flip
- name = "BULLET_DODGER skillchip"
- skill_name = "Flip 2 Dodge"
- skill_description = "At the cost of stamina, your flips can also be used to dodge incoming projectiles."
- skill_icon = FA_ICON_SPINNER
- activate_message = span_notice("You feel the urge to flip scenically as if you are the 'Chosen One'.")
- deactivate_message = span_notice("The urge to flip goes away.")
-
-/obj/item/skillchip/matrix_flip/on_activate(mob/living/carbon/user, silent = FALSE)
- . = ..()
- ADD_TRAIT(user, TRAIT_SLOW_FLIP, SKILLCHIP_TRAIT)
- RegisterSignal(user, COMSIG_MOB_EMOTED("flip"), PROC_REF(on_flip))
- RegisterSignal(user, COMSIG_MOB_PRE_EMOTED, PROC_REF(check_if_we_can_flip))
-
-/obj/item/skillchip/matrix_flip/on_deactivate(mob/living/carbon/user, silent=FALSE)
- REMOVE_TRAIT(user, TRAIT_SLOW_FLIP, SKILLCHIP_TRAIT)
- UnregisterSignal(user, list(COMSIG_MOB_EMOTED("flip"), COMSIG_MOB_PRE_EMOTED))
- return ..()
-
-///Prevent players from stamcritting from INTENTIONAL flips. 1.4s of bullet immunity isn't worth several secs of stun.
-/obj/item/skillchip/matrix_flip/proc/check_if_we_can_flip(mob/living/source, key, params, type_override, intentional, datum/emote/emote)
- SIGNAL_HANDLER
- if(key != "flip" || !intentional)
- return
- if((source.maxHealth - (source.getStaminaLoss() + FLIP_STAMINA_COST)) <= source.crit_threshold)
- source.balloon_alert(source, "too tired!")
- return COMPONENT_CANT_EMOTE
-
-/obj/item/skillchip/matrix_flip/proc/on_flip(mob/living/source)
- SIGNAL_HANDLER
- if(HAS_TRAIT_FROM(source, TRAIT_UNHITTABLE_BY_PROJECTILES, SKILLCHIP_TRAIT))
- return
- playsound(source, 'sound/weapons/fwoosh.ogg', 90, FALSE, frequency = 0.7)
- ADD_TRAIT(source, TRAIT_UNHITTABLE_BY_PROJECTILES, SKILLCHIP_TRAIT)
- source.adjustStaminaLoss(FLIP_STAMINA_COST)
- addtimer(TRAIT_CALLBACK_REMOVE(source, TRAIT_UNHITTABLE_BY_PROJECTILES, SKILLCHIP_TRAIT), FLIP_EMOTE_DURATION * 2)
-
-#undef FLIP_STAMINA_COST
diff --git a/code/modules/library/skill_learning/generic_skillchips/matrix_taunt.dm b/code/modules/library/skill_learning/generic_skillchips/matrix_taunt.dm
new file mode 100644
index 0000000000000..cfe61b08e0c3c
--- /dev/null
+++ b/code/modules/library/skill_learning/generic_skillchips/matrix_taunt.dm
@@ -0,0 +1,37 @@
+#define TAUNT_STAMINA_COST 19
+
+/obj/item/skillchip/matrix_taunt
+ name = "BULLET_DODGER skillchip"
+ skill_name = "Taunt 2 Dodge"
+ skill_description = "At the cost of stamina, your taunts can also be used to dodge incoming projectiles."
+ skill_icon = FA_ICON_SPINNER
+ activate_message = span_notice("You feel the urge to taunt scenically as if you are the 'Chosen One'.")
+ deactivate_message = span_notice("The urge to taunt goes away.")
+
+/obj/item/skillchip/matrix_taunt/on_activate(mob/living/carbon/user, silent = FALSE)
+ . = ..()
+ RegisterSignal(user, COMSIG_MOB_EMOTED("taunt"), PROC_REF(on_taunt))
+ RegisterSignal(user, COMSIG_MOB_PRE_EMOTED, PROC_REF(check_if_we_can_taunt))
+
+/obj/item/skillchip/matrix_taunt/on_deactivate(mob/living/carbon/user, silent=FALSE)
+ UnregisterSignal(user, list(COMSIG_MOB_EMOTED("taunt"), COMSIG_MOB_PRE_EMOTED))
+ return ..()
+
+///Prevent players from stamcritting from INTENTIONAL flips. 1.4s of bullet immunity isn't worth several secs of stun.
+/obj/item/skillchip/matrix_taunt/proc/check_if_we_can_taunt(mob/living/source, key, params, type_override, intentional, datum/emote/emote)
+ SIGNAL_HANDLER
+ if(key != "taunt" || !intentional)
+ return
+ if((source.maxHealth - (source.getStaminaLoss() + TAUNT_STAMINA_COST)) <= source.crit_threshold)
+ source.balloon_alert(source, "too tired!")
+ return COMPONENT_CANT_EMOTE
+
+/obj/item/skillchip/matrix_taunt/proc/on_taunt(mob/living/source)
+ SIGNAL_HANDLER
+ if(HAS_TRAIT_FROM(source, TRAIT_UNHITTABLE_BY_PROJECTILES, SKILLCHIP_TRAIT))
+ return
+ ADD_TRAIT(source, TRAIT_UNHITTABLE_BY_PROJECTILES, SKILLCHIP_TRAIT)
+ source.adjustStaminaLoss(TAUNT_STAMINA_COST)
+ addtimer(TRAIT_CALLBACK_REMOVE(source, TRAIT_UNHITTABLE_BY_PROJECTILES, SKILLCHIP_TRAIT), TAUNT_EMOTE_DURATION * 1.5)
+
+#undef TAUNT_STAMINA_COST
diff --git a/code/modules/library/skill_learning/generic_skillchips/point.dm b/code/modules/library/skill_learning/generic_skillchips/point.dm
new file mode 100644
index 0000000000000..761a482268952
--- /dev/null
+++ b/code/modules/library/skill_learning/generic_skillchips/point.dm
@@ -0,0 +1,99 @@
+/**
+ * A skillchip that gives the user bigger arrows when pointing at things (like some id trims do).
+ * As a bonus, they can costumize the color of the arrow/pointer too.
+ */
+/obj/item/skillchip/big_pointer
+ name = "Kommand skillchip"
+ desc = "A biochip detailing various techniques employed by historical leaders to points at things like a true boss."
+ skill_name = "Enhanced pointing"
+ skill_description = "Learn to point at things in a more noticeable way."
+ skill_icon = FA_ICON_ARROW_DOWN
+ activate_message = span_notice("From \"The Definitive Compendium of Body Language for the Aspiring Leader\", page 164, paragraph 3...")
+ deactivate_message = span_notice("So, uh, yeah, how do I point at things again?")
+
+ ///The action for changing the pointer color
+ var/datum/action/change_pointer_color/action
+
+/obj/item/skillchip/big_pointer/Destroy()
+ action = null
+ return ..()
+
+/obj/item/skillchip/big_pointer/on_activate(mob/living/carbon/user, silent=FALSE)
+ . = ..()
+ RegisterSignal(user, COMSIG_MOVABLE_POINTED, PROC_REF(fancier_pointer))
+ if(!action)
+ action = new(src)
+ action.Grant(user)
+
+/obj/item/skillchip/big_pointer/on_deactivate(mob/living/carbon/user, silent=FALSE)
+ UnregisterSignal(user, COMSIG_MOVABLE_POINTED)
+ action?.arrow_color = null
+ action?.arrow_overlay = null
+ action?.Remove(user)
+ return ..()
+
+/obj/item/skillchip/big_pointer/proc/fancier_pointer(mob/living/user, atom/pointed, obj/effect/temp_visual/point/point)
+ SIGNAL_HANDLER
+ if(HAS_TRAIT(user, TRAIT_UNKNOWN))
+ return
+ point.cut_overlays()
+ if(!action.arrow_color)
+ point.icon_state = "arrow_large"
+ return
+ point.icon_state = "arrow_large_white"
+ point.color = action.arrow_color
+ var/mutable_appearance/highlight = mutable_appearance(point.icon, "arrow_large_white_highlights", appearance_flags = RESET_COLOR)
+ point.add_overlay(highlight)
+
+/datum/action/change_pointer_color
+ name = "Change Pointer Color"
+ desc = "Set your custom pointer color, or reset it to the default."
+ button_icon = /obj/effect/temp_visual/point::icon
+ button_icon_state = "arrow_large_still"
+ check_flags = AB_CHECK_CONSCIOUS
+ ///the color of our arrow
+ var/arrow_color
+ ///the arrow overlay shown on the button
+ var/mutable_appearance/arrow_overlay
+
+/datum/action/change_pointer_color/Destroy()
+ . = ..()
+ arrow_overlay = null
+
+/datum/action/change_pointer_color/Trigger(trigger_flags)
+ . = ..()
+ if(!.)
+ return
+ var/mob/user = owner
+ if(!arrow_color)
+ pick_color(user)
+ return
+ var/choice = tgui_alert(owner, "Reset or update pointer color?","Pointer Color", list("Reset","Update"))
+ if(user != owner || !choice || !IsAvailable(feedback = TRUE))
+ return
+ if(choice == "Update")
+ pick_color(user)
+ else
+ arrow_color = null
+ owner.balloon_alert(owner, "pointer reset")
+ build_all_button_icons(update_flags = UPDATE_BUTTON_ICON, force = TRUE)
+
+/datum/action/change_pointer_color/proc/pick_color(mob/user)
+ var/ncolor = input(owner, "Pick new color", "Pointer Color", arrow_color) as color|null
+ if(user != owner || !IsAvailable(feedback = TRUE))
+ return
+ arrow_color = ncolor
+ owner.balloon_alert(owner, "pointer updated")
+ build_all_button_icons(update_flags = UPDATE_BUTTON_ICON, force = TRUE)
+
+/datum/action/change_pointer_color/apply_button_icon(atom/movable/screen/movable/action_button/current_button, force = FALSE)
+ if(!arrow_color)
+ return ..()
+
+ current_button.icon = current_button.icon_state = null
+ current_button.cut_overlay(arrow_overlay)
+
+ arrow_overlay = mutable_appearance(icon = /obj/effect/temp_visual/point::icon, icon_state = "arrow_large_white_still")
+ arrow_overlay.color = arrow_color
+ arrow_overlay.overlays += mutable_appearance(icon = /obj/effect/temp_visual/point::icon, icon_state = "arrow_large_white_still_highlights", appearance_flags = RESET_COLOR)
+ current_button.add_overlay(arrow_overlay)
diff --git a/code/modules/library/skill_learning/skill_station.dm b/code/modules/library/skill_learning/skill_station.dm
index b376501f758fd..eaeb1fa3e0813 100644
--- a/code/modules/library/skill_learning/skill_station.dm
+++ b/code/modules/library/skill_learning/skill_station.dm
@@ -2,6 +2,7 @@
#define SKILLCHIP_REMOVAL_TIME (15 SECONDS)
/obj/machinery/skill_station
+ SET_BASE_VISUAL_PIXEL(0, DEPTH_OFFSET)
name = "\improper Skillsoft station"
desc = "Learn skills with only minimal chance for brain damage."
@@ -252,7 +253,7 @@
current_skills += list(skill_chip.get_chip_data())
.["current"] = current_skills
-/obj/machinery/skill_station/ui_act(action, list/params)
+/obj/machinery/skill_station/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/library/skill_learning/skillchip.dm b/code/modules/library/skill_learning/skillchip.dm
index cc284b91454aa..b8903e5bde09a 100644
--- a/code/modules/library/skill_learning/skillchip.dm
+++ b/code/modules/library/skill_learning/skillchip.dm
@@ -141,6 +141,7 @@
* * silent - Boolean. Whether or not an activation message should be shown to the user.
*/
/obj/item/skillchip/proc/on_activate(mob/living/carbon/user, silent=FALSE)
+ SHOULD_CALL_PARENT(TRUE)
if(!silent && activate_message)
to_chat(user, activate_message)
@@ -175,6 +176,7 @@
* * silent - Boolean. Whether or not a deactivation message should be shown to the user.
*/
/obj/item/skillchip/proc/on_deactivate(mob/living/carbon/user, silent=FALSE)
+ SHOULD_CALL_PARENT(TRUE)
if(!silent && deactivate_message)
to_chat(user, deactivate_message)
@@ -484,9 +486,9 @@
/obj/item/skillchip/master_angler
name = "Mast-Angl-Er skillchip"
- auto_traits = list(TRAIT_REVEAL_FISH, TRAIT_EXAMINE_FISHING_SPOT)
+ auto_traits = list(TRAIT_REVEAL_FISH, TRAIT_EXAMINE_FISHING_SPOT, TRAIT_EXAMINE_FISH, TRAIT_EXAMINE_DEEPER_FISH)
skill_name = "Fisherman's Discernment"
- skill_description = "Lists fishes when examining a fishing spot, and gives a hint of whatever thing's biting the hook."
+ skill_description = "Lists fishes when examining a fishing spot, gives a hint of whatever thing's biting the hook and more."
skill_icon = "fish"
activate_message = span_notice("You feel the knowledge and passion of several sunbaked, seasoned fishermen burn within you.")
deactivate_message = span_notice("You no longer feel like casting a fishing rod by the sunny riverside.")
@@ -499,3 +501,104 @@
skill_icon = FA_ICON_DRUMSTICK_BITE
activate_message = span_notice("You think of your favourite food and realise that you can rotate its flavour in your mind.")
deactivate_message = span_notice("You feel your food-based mind palace crumbling...")
+
+/obj/item/skillchip/drunken_brawler
+ name = "F0RC3 4DD1CT10N skillchip"
+ auto_traits = list(TRAIT_DRUNKEN_BRAWLER)
+ skill_name = "Drunken Unarmed Proficiency"
+ skill_description = "When intoxicated, you gain increased unarmed effectiveness."
+ skill_icon = "wine-bottle"
+ activate_message = span_notice("You honestly could do with a drink. Never know when someone might try and jump you around here.")
+ deactivate_message = span_notice("You suddenly feel a lot safer going around the station sober... ")
+
+/obj/item/skillchip/musical
+ name = "\improper Old Copy of \"Space Station 13: The Musical\""
+ desc = "An old copy of \"Space Station 13: The Musical\", \
+ ran on the station's 100th anniversary...Or maybe it was the 200th?"
+ skill_name = "Memory of a Musical"
+ skill_description = "Allows you to hit that high note, like those that came a century before us."
+ skill_icon = FA_ICON_MUSIC
+ activate_message = span_notice("You feel like you could \u2669 sing a soooong! \u266B")
+ deactivate_message = span_notice("The musical fades from your mind, leaving you with a sense of nostalgia.")
+ custom_premium_price = PAYCHECK_CREW * 4
+
+/obj/item/skillchip/musical/Initialize(mapload, is_removable)
+ . = ..()
+ name = replacetext(name, "Old", round(CURRENT_STATION_YEAR - pick(50, 100, 150, 200, 250), 5))
+
+/obj/item/skillchip/musical/on_activate(mob/living/carbon/user, silent = FALSE)
+ . = ..()
+ RegisterSignal(user, COMSIG_MOB_SAY, PROC_REF(make_music))
+
+/obj/item/skillchip/musical/on_deactivate(mob/living/carbon/user, silent)
+ . = ..()
+ UnregisterSignal(user, COMSIG_MOB_SAY)
+
+/obj/item/skillchip/musical/proc/make_music(mob/living/carbon/source, list/say_args)
+ SIGNAL_HANDLER
+
+ var/raw_message = say_args[SPEECH_MESSAGE]
+ var/list/words = splittext(raw_message, " ")
+ if(length(words) <= 1)
+ say_args[SPEECH_MODS][MODE_SING] = TRUE
+ return
+ var/last_word = words[length(words)]
+ var/num_chars = length_char(last_word)
+ var/last_vowel = ""
+ // find the last vowel present in the word
+ for(var/i in 1 to num_chars)
+ var/char = copytext_char(last_word, i, i + 1)
+ if(char in VOWELS)
+ last_vowel = char
+
+ // now we'll reshape the final word to make it sound like they're singing it
+ var/final_word = ""
+ var/has_ellipsis = copytext(last_word, -3) == "..."
+ for(var/i in 1 to num_chars)
+ var/char = copytext_char(last_word, i, i + 1)
+ // replacing any final periods with exclamation marks (so long as it's not an ellipsis)
+ if(char == "." && i == num_chars && !has_ellipsis)
+ final_word += "!"
+ // or if it's the vowel we found, we're gonna repeat it a few times (holding the note)
+ else if(char == last_vowel)
+ for(var/j in 1 to 4)
+ final_word += char
+ // if we dragged out the last character of the word, just period it
+ if(i == num_chars)
+ final_word += "."
+ // no special handing otherwise
+ else
+ final_word += char
+
+ if(!has_ellipsis)
+ // adding an extra exclamation mark at the end if there's no period
+ var/last_char = copytext_char(final_word, -1)
+ if(last_char != ".")
+ final_word += "!"
+
+ words[length(words)] = final_word
+ // now we siiiiiiing
+ say_args[SPEECH_MESSAGE] = jointext(words, " ")
+ say_args[SPEECH_MODS][MODE_SING] = TRUE
+
+/obj/item/skillchip/musical/examine(mob/user)
+ . = ..()
+ . += span_tinynoticeital("Huh, looks like it'd fit in a skillchip adapter.")
+
+/obj/item/skillchip/musical/examine_more(mob/user)
+ . = ..()
+ var/list/songs = list()
+ songs += "• \"The Ballad of Space Station 13\""
+ songs += "• \"The Captain's Call\""
+ songs += "• \"A Mime's Lament\""
+ songs += "• \"Banned from Cargo\""
+ songs += "• \"Botany Blues\""
+ songs += "• \"Clown Song\""
+ songs += "• \"Elegy to an Engineer\""
+ songs += "• \"Medical Malpractitioner\""
+ songs += "• \"Security Strike\""
+ songs += "• \"Send for the Shuttle\""
+ songs += "• And one song scratched out..."
+
+ . += span_notice("On the back of the chip, you see a list of songs:")
+ . += span_smallnotice("[jointext(songs, " ")]")
diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm
index e3f72da5bbffd..5a68932a62cbd 100644
--- a/code/modules/lighting/lighting_atom.dm
+++ b/code/modules/lighting/lighting_atom.dm
@@ -90,6 +90,8 @@
if(isnull(.))
return
recalculate_directional_opacity()
+ // Need this for split vis stuff
+ QUEUE_SMOOTH_NEIGHBORS(src)
/atom/proc/flash_lighting_fx(range = FLASH_LIGHT_RANGE, power = FLASH_LIGHT_POWER, color = COLOR_WHITE, duration = FLASH_LIGHT_DURATION, light_type = /obj/effect/dummy/lighting_obj)
if(!duration)
diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm
index c5ce42e45edd8..aa878d13f2137 100644
--- a/code/modules/lighting/lighting_object.dm
+++ b/code/modules/lighting/lighting_object.dm
@@ -90,13 +90,13 @@ GLOBAL_LIST_EMPTY(default_lighting_underlays_by_z)
(red_corner.cache_g + green_corner.cache_g + blue_corner.cache_g + alpha_corner.cache_g + \
red_corner.cache_b + green_corner.cache_b + blue_corner.cache_b + alpha_corner.cache_b == 8))
//anything that passes the first case is very likely to pass the second, and addition is a little faster in this case
- current_underlay.icon_state = "lighting_transparent"
+ current_underlay.icon_state = "transparent-[affected_turf.lighting_state]"
current_underlay.color = null
else if(!set_luminosity)
- current_underlay.icon_state = "lighting_dark"
+ current_underlay.icon_state = "dark-[affected_turf.lighting_state]"
current_underlay.color = null
else
- current_underlay.icon_state = null
+ current_underlay.icon_state = affected_turf.lighting_state
current_underlay.color = list(
red_corner.cache_r, red_corner.cache_g, red_corner.cache_b, 00,
green_corner.cache_r, green_corner.cache_g, green_corner.cache_b, 00,
diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm
deleted file mode 100644
index c148530d1cd86..0000000000000
--- a/code/modules/lighting/lighting_setup.dm
+++ /dev/null
@@ -1,12 +0,0 @@
-
-/proc/create_all_lighting_objects()
- for(var/area/area as anything in GLOB.areas)
- if(!area.static_lighting)
- continue
- for (var/list/zlevel_turfs as anything in area.get_zlevel_turf_lists())
- for(var/turf/area_turf as anything in zlevel_turfs)
- if(area_turf.space_lit)
- continue
- new /datum/lighting_object(area_turf)
- CHECK_TICK
- CHECK_TICK
diff --git a/code/modules/loadout/categories/pocket.dm b/code/modules/loadout/categories/pocket.dm
index e1cddde76e5b9..fd3f11e112b27 100644
--- a/code/modules/loadout/categories/pocket.dm
+++ b/code/modules/loadout/categories/pocket.dm
@@ -106,10 +106,6 @@
name = "Plush (Moth)"
item_path = /obj/item/toy/plush/moth
-/datum/loadout_item/pocket_items/plush/narsie
- name = "Plush (Nar'sie)"
- item_path = /obj/item/toy/plush/narplush
-
/datum/loadout_item/pocket_items/plush/nukie
name = "Plush (Nukie)"
item_path = /obj/item/toy/plush/nukeplushie
@@ -122,10 +118,6 @@
name = "Plush (Plasmaman)"
item_path = /obj/item/toy/plush/plasmamanplushie
-/datum/loadout_item/pocket_items/plush/ratvar
- name = "Plush (Ratvar)"
- item_path = /obj/item/toy/plush/ratplush
-
/datum/loadout_item/pocket_items/plush/rouny
name = "Plush (Rouny)"
item_path = /obj/item/toy/plush/rouny
diff --git a/code/modules/loadout/loadout_items.dm b/code/modules/loadout/loadout_items.dm
index 53d0a7cc6cd5b..37a457f06c5ef 100644
--- a/code/modules/loadout/loadout_items.dm
+++ b/code/modules/loadout/loadout_items.dm
@@ -66,7 +66,7 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
if(can_be_greyscale == DONT_GREYSCALE)
can_be_greyscale = FALSE
- else if(item_path::flags_1 & IS_PLAYER_COLORABLE_1)
+ else if((item_path::flags_1 & IS_PLAYER_COLORABLE_1) && item_path::greyscale_config && item_path::greyscale_colors)
can_be_greyscale = TRUE
if(isnull(name))
diff --git a/code/modules/lootpanel/_lootpanel.dm b/code/modules/lootpanel/_lootpanel.dm
index 45862ebf45542..d74023b57efe6 100644
--- a/code/modules/lootpanel/_lootpanel.dm
+++ b/code/modules/lootpanel/_lootpanel.dm
@@ -62,7 +62,7 @@
return UI_INTERACTIVE
-/datum/lootpanel/ui_act(action, list/params)
+/datum/lootpanel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
diff --git a/code/modules/lootpanel/contents.dm b/code/modules/lootpanel/contents.dm
index 44f4acd47f24c..2fcd4201d8a7c 100644
--- a/code/modules/lootpanel/contents.dm
+++ b/code/modules/lootpanel/contents.dm
@@ -21,6 +21,8 @@
if(!istype(thing))
stack_trace("Non-atom in the contents of [source_turf]!")
continue
+ if(QDELETED(thing))
+ continue
if(thing.mouse_opacity == MOUSE_OPACITY_TRANSPARENT)
continue
if(thing.IsObscured())
diff --git a/code/modules/lootpanel/search_object.dm b/code/modules/lootpanel/search_object.dm
index 149be76e71064..2cb0bdf85dba9 100644
--- a/code/modules/lootpanel/search_object.dm
+++ b/code/modules/lootpanel/search_object.dm
@@ -62,6 +62,7 @@
/datum/search_object/Destroy(force)
item = null
+ icon = null
return ..()
@@ -75,6 +76,9 @@
/datum/search_object/proc/on_item_moved(atom/source)
SIGNAL_HANDLER
+ if(QDELETED(src))
+ return
+
qdel(src)
@@ -82,4 +86,4 @@
/datum/search_object/proc/on_turf_change(turf/source, path, list/new_baseturfs, flags, list/post_change_callbacks)
SIGNAL_HANDLER
- post_change_callbacks += CALLBACK(src, GLOBAL_PROC_REF(qdel), src)
+ post_change_callbacks += CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), src)
diff --git a/code/modules/mafia/outfits.dm b/code/modules/mafia/outfits.dm
index 3b805bd92f9c0..0fdc90c2f3213 100644
--- a/code/modules/mafia/outfits.dm
+++ b/code/modules/mafia/outfits.dm
@@ -89,7 +89,7 @@
name = "Mafia Security Officer"
uniform = /obj/item/clothing/under/rank/security/officer
- gloves = /obj/item/clothing/gloves/color/black
+ gloves = /obj/item/clothing/gloves/color/black/security
head = /obj/item/clothing/head/helmet/sec
suit = /obj/item/clothing/suit/armor/vest/alt
shoes = /obj/item/clothing/shoes/jackboots
@@ -116,7 +116,7 @@
uniform = /obj/item/clothing/under/rank/security/head_of_security
shoes = /obj/item/clothing/shoes/jackboots
suit = /obj/item/clothing/suit/armor/hos/trenchcoat
- gloves = /obj/item/clothing/gloves/color/black
+ gloves = /obj/item/clothing/gloves/color/black/security
head = /obj/item/clothing/head/hats/hos/beret
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
@@ -126,7 +126,7 @@
uniform = /obj/item/clothing/under/rank/security/warden
shoes = /obj/item/clothing/shoes/jackboots
suit = /obj/item/clothing/suit/armor/vest/warden/alt
- gloves = /obj/item/clothing/gloves/color/black
+ gloves = /obj/item/clothing/gloves/color/black/security
head = /obj/item/clothing/head/hats/warden/red
glasses = /obj/item/clothing/glasses/hud/security/sunglasses
diff --git a/code/modules/mafia/roles/roles.dm b/code/modules/mafia/roles/roles.dm
index ab1a1cc0e454b..4cfd7662d843e 100644
--- a/code/modules/mafia/roles/roles.dm
+++ b/code/modules/mafia/roles/roles.dm
@@ -189,6 +189,6 @@
team_span = "comradio"
the = FALSE
result += span_notice("The [span_bold("[name]")] is aligned with [the ? "the " : ""][team_desc]")
- result += "\"[desc]\""
+ result += "\"[initial(desc)]\""
result += span_notice("[name] wins when they [win_condition]")
to_chat(clueless, result.Join(""))
diff --git a/code/modules/mapfluff/ruins/icemoonruin_code/hotsprings.dm b/code/modules/mapfluff/ruins/icemoonruin_code/hotsprings.dm
index 9e188d4585638..7c674e98e24a1 100644
--- a/code/modules/mapfluff/ruins/icemoonruin_code/hotsprings.dm
+++ b/code/modules/mapfluff/ruins/icemoonruin_code/hotsprings.dm
@@ -14,6 +14,7 @@
baseturfs = /turf/open/water/cursed_spring
planetary_atmos = TRUE
initial_gas_mix = ICEMOON_DEFAULT_ATMOS
+ fishing_datum = /datum/fish_source/cursed_spring
/turf/open/water/cursed_spring/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
diff --git a/code/modules/mapfluff/ruins/icemoonruin_code/library.dm b/code/modules/mapfluff/ruins/icemoonruin_code/library.dm
index 2f3b6381f0484..266b04a20eaee 100644
--- a/code/modules/mapfluff/ruins/icemoonruin_code/library.dm
+++ b/code/modules/mapfluff/ruins/icemoonruin_code/library.dm
@@ -8,14 +8,14 @@
/obj/machinery/door/puzzle/keycard/library/animation_length(animation)
switch(animation)
if(DOOR_OPENING_ANIMATION)
- return 1.2 SECONDS
+ return 0.5 SECONDS
/obj/machinery/door/puzzle/keycard/library/animation_segment_delay(animation)
switch(animation)
if(DOOR_OPENING_PASSABLE)
- return 1.0 SECONDS
+ return 0.5 SECONDS
if(DOOR_OPENING_FINISHED)
- return 1.2 SECONDS
+ return 0.5 SECONDS
/obj/item/keycard/library
name = "golden key"
diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm b/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm
index 4aecd64aa1699..33c23feba9670 100644
--- a/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm
+++ b/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm
@@ -5,7 +5,6 @@
max_integrity = 120
impressiveness = 18 // Carved from the bones of a massive creature, it's going to be a specticle to say the least
layer = ABOVE_ALL_MOB_LAYER
- plane = ABOVE_GAME_PLANE
custom_materials = list(/datum/material/bone=SHEET_MATERIAL_AMOUNT*5)
abstract_type = /obj/structure/statue/bone
@@ -72,8 +71,7 @@
turf_type = /turf/open/misc/asteroid/basalt/wasteland
baseturfs = /turf/open/misc/asteroid/basalt/wasteland
icon = 'icons/turf/walls/rock_wall.dmi'
- base_icon_state = "rock_wall"
- smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER
+ smoothing_flags = SMOOTH_BITMASK
/turf/closed/mineral/strong/wasteland/drop_ores()
if(prob(10))
@@ -87,13 +85,16 @@
/obj/structure/sink/oil_well //You're not going to enjoy bathing in this...
name = "oil well"
desc = "A bubbling pool of oil. This would probably be valuable, had bluespace technology not destroyed the need for fossil fuels 200 years ago."
- icon = 'icons/obj/watercloset.dmi'
+ icon = 'icons/obj/mining_zones/terrain.dmi'
icon_state = "puddle-oil"
dispensedreagent = /datum/reagent/fuel/oil
- pixel_shift = 0
+
+// This is a hole
+/obj/structure/sink/oil_well/find_and_hang_on_wall(directional, custom_drop_callback, wall_layer)
+ return
/obj/structure/sink/oil_well/Initialize(mapload)
- .=..()
+ . = ..()
create_reagents(20)
reagents.add_reagent(dispensedreagent, 20)
//I'm pretty much aware that, because how oil wells and sinks work, attackby() won't work unless in combat mode.
diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/surface.dm b/code/modules/mapfluff/ruins/lavalandruin_code/surface.dm
index 13db0e2d73077..d420526a2572f 100644
--- a/code/modules/mapfluff/ruins/lavalandruin_code/surface.dm
+++ b/code/modules/mapfluff/ruins/lavalandruin_code/surface.dm
@@ -14,7 +14,6 @@
flags_1 = ON_BORDER_1
appearance_flags = LONG_GLIDE
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
anchored = TRUE
density = TRUE
bound_width = 416
diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/museum.dm b/code/modules/mapfluff/ruins/objects_and_mobs/museum.dm
index 57f20abb1aa19..fb9d78a009b8c 100644
--- a/code/modules/mapfluff/ruins/objects_and_mobs/museum.dm
+++ b/code/modules/mapfluff/ruins/objects_and_mobs/museum.dm
@@ -51,7 +51,7 @@
/obj/structure/fluff/preserved_borer
name = "preserved borer exhibit"
desc = "A preserved cortical borer. Probably been there long enough to not last long outside the exhibit."
- icon = 'icons/obj/structures.dmi'
+ icon = 'icons/obj/fluff/general.dmi'
icon_state = "preservedborer"
density = TRUE
@@ -122,7 +122,7 @@
anchored = TRUE
density = TRUE
smoothing_flags = SMOOTH_BITMASK
- smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
+ smoothing_groups = SMOOTH_GROUP_WALLS + SMOOTH_GROUP_TALL_WALLS + SMOOTH_GROUP_CLOSED_TURFS
/obj/item/paper/fluff/museum/noend
name = "scrambled note"
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 8dc9cec326d95..6910dc3b3604b 100644
--- a/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm
+++ b/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm
@@ -7,7 +7,6 @@
flags_1 = ON_BORDER_1
appearance_flags = 0
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
anchored = TRUE
density = TRUE
pixel_x = -32
@@ -15,6 +14,7 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
light_range = 8
light_color = LIGHT_COLOR_LAVA
+ can_atmos_pass = ATMOS_PASS_DENSITY
var/open = FALSE
var/changing_openness = FALSE
var/locked = FALSE
@@ -224,7 +224,6 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate)
icon_state = "arch_full"
appearance_flags = 0
layer = FLY_LAYER
- plane = ABOVE_GAME_PLANE
anchored = TRUE
pixel_x = -64
pixel_y = -40
diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm b/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm
index 15566603a9322..0327c77874b9e 100644
--- a/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm
+++ b/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm
@@ -6,7 +6,7 @@
anchored = TRUE
density = TRUE
icon_state = "blob"
- icon = 'icons/mob/nonhuman-player/blob.dmi'
+ icon = 'icons/mob/nonhuman-player/blob_tall.dmi'
color = rgb(145, 150, 0)
/obj/effect/gluttony/CanAllowThrough(atom/movable/mover, border_dir)//So bullets will fly over and stuff.
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/TheDerelict.dm b/code/modules/mapfluff/ruins/spaceruin_code/TheDerelict.dm
index 3507cb7d94746..8be91920719c1 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/TheDerelict.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/TheDerelict.dm
@@ -140,7 +140,7 @@
ui = new(user, src, "VaultController", name)
ui.open()
-/obj/machinery/computer/vaultcontroller/ui_act(action, params)
+/obj/machinery/computer/vaultcontroller/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -213,7 +213,7 @@
"\[15:29\] USELESS clown",
"\[15:35\] And BATSHIT fucking janitors",
"\[15:46\] That includes the crotchety fucking IBM piece of shit we're supposed to call an AI",
- "\[15:52\] And it's legion of cyborg assholes",
+ "\[15:52\] And its legion of cyborg assholes",
"\[15:58\] If this wasn't bad enough there is the wizards federation to worry about",
"\[16:06\] Crazy bastards",
"\[16:10\] What can be worse than a bunch of plasma-humping-space-freaks?",
@@ -244,7 +244,7 @@
"\[47:32\] Once they called me captain, but when it's all said and done",
"\[47:41\] I'll be a hero",
"\[47:45\] If you run across this transmission by chance",
- "\[47:52\] Get you pudgy little nerd ass over to Space Station 13 and start busting heads.",
+ "\[47:52\] Get your pudgy little nerd ass over to Space Station 13 and start busting heads.",
"\[48:00\] (farting noises)",
)
timestamp = list(
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm b/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm
new file mode 100644
index 0000000000000..ebcd04c626b07
--- /dev/null
+++ b/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm
@@ -0,0 +1,381 @@
+//code & items for the hauntedtradingpost.dmm ruin
+//CONTAINS: [Lore Papers],[Outpost ID Cards],[Gimmick Treasure],[Hazards & Traps],[Custom Turrets]
+
+// [Lore Papers]
+// clues to traps that exist in the ruin or just insights into the backstory of the place
+/obj/item/paper/fluff/ruins/hauntedtradingpost/warning
+ name = "Last Warning"
+ default_raw_text = "Next person who breaks a vending machine fucking around with those fucking toy guns gets fired on the spot. Try me. I am SICK of this shit.
Signed, Your Fucking Boss (Who Can Fucking Fire Your Ass)"
+
+/obj/item/paper/fluff/ruins/hauntedtradingpost/warning/turrets
+ name = "Warning! Important! Read this!"
+ default_raw_text = "Foam darts do not go in the defence turrets! Live ammo only!"
+
+/obj/item/paper/fluff/ruins/hauntedtradingpost/brainstorming
+ name = "Notes"
+ default_raw_text = "Branding: Pizza In Your Pocket (check focus groups)
Tomato Mozzerella Basil etc
Spider 17-02667 Store 31-00314
18,000 approx BSD
common allergens - ?
6127"
+
+/obj/item/paper/fluff/ruins/hauntedtradingpost/brainstorming/eureka
+ default_raw_text = "Got some ingredients from the moth trading fleet and used some of our discretionary budget to hire some factory space. Prototypes are going down well with both public and employees. If we can get central to fund mass production we'll be seeing a 18% permanant increase in regional profit according to AI. This fits the local brunch market *perfectly*."
+
+/obj/item/paper/fluff/ruins/hauntedtradingpost/brainstorming/eureka2
+ default_raw_text = "Early experiments with a fully carb-free recipe going well. Taste tests are all positive, just need a way to reduce costs."
+
+/obj/item/paper/fluff/ruins/hauntedtradingpost/brainstorming/eureka3
+ default_raw_text = "PROJECT BIG DONK RnD has a few prototypes prepared. Testing will be complete by the end of the week."
+
+/obj/item/paper/fluff/ruins/hauntedtradingpost/rpgclub
+ name = "RPG Club"
+ default_raw_text = "RPG Club is every Thursday from 20:00 to 01:00 AM. Entry to the break room is strictly by invitation only during that period of time.
We apologise for any inconvenience."
+
+/obj/item/paper/fluff/ruins/hauntedtradingpost/rpgrules
+ name = "GM Notes"
+ default_raw_text = "Session 4 NPCS Shadow Warriors S A T C H 40 65 40 15 10
Shadow Clan Underlord S A T C H 40 65 40 15 10 Note: Gets shadow magic.
Dire Corgi S A T C H 60 25 65 25 12
If they beat this let them roll on loot table 4 twice but if it's 65-70 or 15-30 make it magic boots instead."
+
+/obj/item/paper/fluff/ruins/hauntedtradingpost/curatorsnote
+ name = "For Adventurers"
+ default_raw_text = "The food court and the stalls are safe, everywhere else isn't. There's safes in the stalls and I didn't have a way to open them so if you can get whatever's inside, good for you. The employees area can be entered by tailing the bots, but security systems are active back there. I got shot by a turret taking a look, and when I stitched myself up and tried the other door I walked into a booby trap and nearly lost an arm.
If you're investigating this signal - BEWARE. For the record, I decided nothing in there's worth the risk. If you're braver than me, good luck. Signed, Curator P."
+
+/obj/item/paper/fluff/ruins/hauntedtradingpost/officememo
+ name = "Memo"
+ default_raw_text = "The AI-Guided Defense System Will Stay Active Indefinitely To Protect Company Property. Please Ensure All Personal Items Are Removed From The Premises, As They Will Be Impossible To Recover If Forgotten.
Donk Co. Takes No Responsibility For Lost Personal Property Or Affects."
+
+/obj/item/paper/fluff/ruins/hauntedtradingpost/receipt
+ name = "Old Receipt"
+ desc = "A ratty old sales receipt printed on cheap thermal paper."
+ default_raw_text = "DONK CO OUTLET 6013 YOUR SERVER TODAY WAS: COLM