"
break_counter = 0
for(var/job in long_job_lists[department])
if(break_counter > 0 && (break_counter % 10 == 0))
diff --git a/code/modules/antagonists/abductor/abductor_structures.dm b/code/modules/antagonists/abductor/abductor_structures.dm
index 4668a67b65b..92b73488767 100644
--- a/code/modules/antagonists/abductor/abductor_structures.dm
+++ b/code/modules/antagonists/abductor/abductor_structures.dm
@@ -7,6 +7,7 @@
icon = 'icons/obj/antags/abductor.dmi'
build_stack_type = /obj/item/stack/sheet/mineral/abductor
icon_state = "bed"
+ custom_materials = list(/datum/material/alloy/alien = SHEET_MATERIAL_AMOUNT * 2)
/obj/structure/table_frame/abductor
name = "alien table frame"
@@ -14,6 +15,7 @@
icon_state = "alien_frame"
framestack = /obj/item/stack/sheet/mineral/abductor
framestackamount = 1
+ custom_materials = list(/datum/material/alloy/alien = SHEET_MATERIAL_AMOUNT)
/obj/structure/table_frame/abductor/get_table_type(obj/item/stack/our_stack)
if(istype(our_stack, /obj/item/stack/sheet/mineral/abductor))
@@ -36,6 +38,7 @@
frame = /obj/structure/table_frame/abductor
custom_materials = list(/datum/material/silver =SHEET_MATERIAL_AMOUNT)
can_flip = FALSE
+ custom_materials = list(/datum/material/alloy/alien = SHEET_MATERIAL_AMOUNT * 2)
/obj/structure/table/optable/abductor
name = "alien operating table"
@@ -49,6 +52,7 @@
icon_state = "bed"
can_buckle = TRUE
buckle_lying = 90
+ custom_materials = list(/datum/material/alloy/alien = SHEET_MATERIAL_AMOUNT, /datum/material/silver = SHEET_MATERIAL_AMOUNT)
/// Amount to inject per second
var/inject_amount = 0.5
@@ -88,6 +92,7 @@
can_weld_shut = FALSE
door_anim_time = 0
material_drop = /obj/item/stack/sheet/mineral/abductor
+ custom_materials = list(/datum/material/alloy/alien = SHEET_MATERIAL_AMOUNT * 2)
/obj/structure/door_assembly/door_assembly_abductor
name = "alien airlock assembly"
@@ -97,3 +102,4 @@
airlock_type = /obj/machinery/door/airlock/abductor
material_type = /obj/item/stack/sheet/mineral/abductor
noglass = TRUE
+ custom_materials = list(/datum/material/alloy/alien = SHEET_MATERIAL_AMOUNT * 4)
diff --git a/code/modules/antagonists/changeling/powers/_biodegrade_bioacid.dm b/code/modules/antagonists/changeling/powers/_biodegrade_bioacid.dm
new file mode 100644
index 00000000000..7278a16c89a
--- /dev/null
+++ b/code/modules/antagonists/changeling/powers/_biodegrade_bioacid.dm
@@ -0,0 +1,25 @@
+/datum/reagent/toxin/acid/bio_acid
+ name = "adaptive bio-acid"
+ description = "An immensely strong, acidic substance of seemingly biological origin. It is teeming with microscopic\
+ organisms that seem to alter its composition to most adaptively dissolve whatever it comes into contact with."
+ color = "#9455ff"
+ creation_purity = 100
+ toxpwr = 0
+ acidpwr = 0
+ ph = 0.0
+ penetrates_skin = TOUCH
+
+/datum/reagent/toxin/acid/bio_acid/expose_mob(mob/living/exposed_mob, methods = TOUCH, reac_volume, show_message = TRUE, touch_protection)
+ if(IS_CHANGELING(exposed_mob))
+ to_chat(exposed_mob, span_changeling("We excrete a bio-agent to neutralize the bio-acid. It is routine and reflexive to do so."))
+ volume = min(0.1, volume)
+ holder.update_total()
+ return
+ . = ..()
+ exposed_mob.adjust_fire_loss(round(reac_volume * min(1 - touch_protection), 0.1) * 3, required_bodytype = BODYTYPE_ORGANIC) // full bio protection = 100% damage reduction
+ exposed_mob.acid_act(10, 50)
+
+/datum/reagent/toxin/acid/bio_acid/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
+ . = ..()
+ if(SPT_PROB(50, seconds_per_tick))
+ affected_mob.emote(pick("screech", "cry"))
diff --git a/code/modules/antagonists/changeling/powers/biodegrade.dm b/code/modules/antagonists/changeling/powers/biodegrade.dm
index 8a5fae3bd8a..101bd935844 100644
--- a/code/modules/antagonists/changeling/powers/biodegrade.dm
+++ b/code/modules/antagonists/changeling/powers/biodegrade.dm
@@ -1,116 +1,109 @@
/datum/action/changeling/biodegrade
name = "Biodegrade"
desc = "Dissolves restraints or other objects preventing free movement. Costs 30 chemicals."
- helptext = "This is obvious to nearby people, and can destroy standard restraints and closets."
+ helptext = "This is obvious to nearby people, and can destroy standard restraints and closets. Works against grabs."
button_icon_state = "biodegrade"
- chemical_cost = 30 //High cost to prevent spam
+ chemical_cost = 30
dna_cost = 2
req_human = TRUE
disabled_by_fire = FALSE
+ var/static/bio_acid_path = /datum/reagent/toxin/acid/bio_acid
+ var/static/bio_acid_amount_per_spray = 6
+ var/static/bio_acid_color = "#9455ff"
/datum/action/changeling/biodegrade/sting_action(mob/living/carbon/human/user)
- if(user.handcuffed)
- var/obj/O = user.get_item_by_slot(ITEM_SLOT_HANDCUFFED)
- if(!istype(O))
- return FALSE
- user.visible_message(span_warning("[user] vomits a glob of acid on [user.p_their()] [O]!"), \
- span_warning("We vomit acidic ooze onto our restraints!"))
+ . = FALSE
+ var/list/obj/restraints = list()
+ var/obj/handcuffs = user.get_item_by_slot(ITEM_SLOT_HANDCUFFED)
+ var/obj/legcuffs = user.get_item_by_slot(ITEM_SLOT_LEGCUFFED)
+ var/obj/item/clothing/suit/straitjacket = user.get_item_by_slot(ITEM_SLOT_OCLOTHING)
+ var/obj/item/clothing/shoes/sneakers/orange/prisoner_shoes = user.get_item_by_slot(ITEM_SLOT_FEET)
+ var/obj/item/clothing/shoes/knotted_shoes = user.get_item_by_slot(ITEM_SLOT_FEET)
+ var/obj/some_manner_of_cage = astype(user.loc, /obj)
+ var/mob/living/space_invader = user.pulledby || user.buckled
- addtimer(CALLBACK(src, PROC_REF(dissolve_handcuffs), user, O), 3 SECONDS)
- log_combat(user, user.handcuffed, "melted handcuffs", addition = "(biodegrade)")
- ..()
- return TRUE
+ if(!istype(prisoner_shoes) || !prisoner_shoes.attached_cuffs)
+ prisoner_shoes = null
+ if(!istype(knotted_shoes) || knotted_shoes.tied != SHOES_KNOTTED)
+ knotted_shoes = null
+ if(!straitjacket?.breakouttime)
+ straitjacket = null
- if(user.legcuffed)
- var/obj/O = user.get_item_by_slot(ITEM_SLOT_LEGCUFFED)
- if(!istype(O))
- return FALSE
- user.visible_message(span_warning("[user] vomits a glob of acid on [user.p_their()] [O]!"), \
- span_warning("We vomit acidic ooze onto our restraints!"))
+ if(!handcuffs && !legcuffs && !straitjacket && !prisoner_shoes && !knotted_shoes && !some_manner_of_cage && !space_invader)
+ user.balloon_alert(user, "already free!")
+ return .
+ ..()
- addtimer(CALLBACK(src, PROC_REF(dissolve_legcuffs), user, O), 3 SECONDS)
- log_combat(user, user.legcuffed, "melted legcuffs", addition = "(biodegrade)")
- ..()
- return TRUE
+ if(handcuffs)
+ restraints.Add(handcuffs)
+ if(legcuffs)
+ restraints.Add(legcuffs)
+ if(straitjacket)
+ restraints.Add(straitjacket)
+ if(prisoner_shoes)
+ restraints.Add(prisoner_shoes)
+ if(knotted_shoes)
+ restraints.Add(knotted_shoes)
+ if(some_manner_of_cage)
+ restraints.Add(some_manner_of_cage)
- if(user.wear_suit?.breakouttime)
- var/obj/item/clothing/suit/S = user.get_item_by_slot(ITEM_SLOT_OCLOTHING)
- if(!istype(S))
- return FALSE
- user.visible_message(span_warning("[user] vomits a glob of acid across the front of [user.p_their()] [S]!"), \
- span_warning("We vomit acidic ooze onto our [user.wear_suit.name]!"))
- addtimer(CALLBACK(src, PROC_REF(dissolve_straightjacket), user, S), 3 SECONDS)
- log_combat(user, user.wear_suit, "melted [user.wear_suit]", addition = "(biodegrade)")
- ..()
- return TRUE
+ for(var/obj/restraint as anything in restraints)
+ if(restraint.obj_flags & (INDESTRUCTIBLE | ACID_PROOF | UNACIDABLE))
+ to_chat(user, span_changeling("We cannot use bio-acid to destroy [restraint]!"))
+ continue
- if(istype(user.loc, /obj/structure/closet))
- var/obj/structure/closet/C = user.loc
- if(!istype(C))
- return FALSE
- C.visible_message(span_warning("[C]'s hinges suddenly begin to melt and run!"))
- to_chat(user, span_warning("We vomit acidic goop onto the interior of [C]!"))
- addtimer(CALLBACK(src, PROC_REF(open_closet), user, C), 7 SECONDS)
- log_combat(user, user.loc, "melted locker", addition = "(biodegrade)")
- ..()
- return TRUE
-
- if(istype(user.loc, /obj/structure/spider/cocoon))
- var/obj/structure/spider/cocoon/C = user.loc
- if(!istype(C))
- return FALSE
- C.visible_message(span_warning("[src] shifts and starts to fall apart!"))
- to_chat(user, span_warning("We secrete acidic enzymes from our skin and begin melting our cocoon..."))
- addtimer(CALLBACK(src, PROC_REF(dissolve_cocoon), user, C), 25) //Very short because it's just webs
- log_combat(user, user.loc, "melted cocoon", addition = "(biodegrade)")
- ..()
- return TRUE
-
- var/obj/item/clothing/shoes/shoes = user.shoes
- if(istype(shoes) && shoes.tied == SHOES_KNOTTED && !(shoes.resistance_flags & (INDESTRUCTIBLE|UNACIDABLE|ACID_PROOF)))
- new /obj/effect/decal/cleanable/greenglow(shoes.drop_location())
+ if(restraint == user.loc)
+ restraint.visible_message(span_warning("Bubbling acid start spewing out of [restraint]..."))
+ addtimer(CALLBACK(restraint, TYPE_PROC_REF(/atom, atom_destruction), ACID), 4 SECONDS)
+ for(var/beat in 1 to 3)
+ addtimer(CALLBACK(src, PROC_REF(make_puddle), restraint), beat SECONDS)
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), restraint, 'sound/items/tools/welder.ogg', 50, TRUE), beat SECONDS)
+ log_combat(user = user, target = restraint, what_done = "melted restraining container", addition = "(biodegrade)")
+ return
+ //otherwise it's some kind of worn restraint
+ addtimer(CALLBACK(restraint, TYPE_PROC_REF(/atom, atom_destruction), ACID), 1.5 SECONDS)
+ log_combat(user = user, target = restraint, what_done = "melted restraining item", addition = "(biodegrade)")
user.visible_message(
- span_warning("[user] vomits a glob of acid on [user.p_their()] tied up [shoes.name], melting [shoes.p_them()] into a pool of goo!"),
- span_warning("We vomit acidic ooze onto our tied up [shoes.name], melting [shoes.p_them()] into a pool of goo!"),
- )
- log_combat(user, shoes, "melted own shoes", addition = "(biodegrade)")
- qdel(shoes)
- ..()
- return TRUE
+ span_warning("[user] spews torrents of acid onto [restraint], melting them with horrifying ease."),
+ user.balloon_alert(user, "melting restraints..."),
+ span_danger("You hear retching, then the sizzling of powerful acid, closer to the sound of hissing steam."))
+ playsound(user, 'sound/items/tools/welder.ogg', 50, TRUE)
+ . = TRUE
- user.balloon_alert(user, "already free!")
- return FALSE
+ if(space_invader)
+ punish_with_acid(user, space_invader)
+ . = TRUE
+ return .
-/datum/action/changeling/biodegrade/proc/dissolve_handcuffs(mob/living/carbon/human/user, obj/O)
- if(O && user.handcuffed == O)
- user.visible_message(span_warning("[O] dissolve[O.gender == PLURAL?"":"s"] into a puddle of sizzling goop."))
- new /obj/effect/decal/cleanable/greenglow(O.drop_location())
- qdel(O)
+/// Spawn green acid puddle underneath obj, used for callback
+/datum/action/changeling/biodegrade/proc/make_puddle(obj/melted_restraint)
+ if (melted_restraint) // incase obj gets qdel'd
+ return new /obj/effect/decal/cleanable/greenglow(get_turf(melted_restraint))
-/datum/action/changeling/biodegrade/proc/dissolve_legcuffs(mob/living/carbon/human/user, obj/O)
- if(O && user.legcuffed == O)
- user.visible_message(span_warning("[O] dissolve[O.gender == PLURAL?"":"s"] into a puddle of sizzling goop."))
- new /obj/effect/decal/cleanable/greenglow(O.drop_location())
- qdel(O)
+/datum/action/changeling/biodegrade/proc/acid_blast(atom/movable/user, atom/movable/target)
+ var/datum/reagents/ephemeral_acid = new
+ ephemeral_acid.add_reagent(bio_acid_path, bio_acid_amount_per_spray)
+ var/mutable_appearance/splash_animation = mutable_appearance('icons/effects/effects.dmi', "splash")
+ splash_animation.color = bio_acid_color
+ target.flick_overlay_view(splash_animation, 3 SECONDS)
+ ephemeral_acid.expose(target, TOUCH)
-/datum/action/changeling/biodegrade/proc/dissolve_straightjacket(mob/living/carbon/human/user, obj/S)
- if(S && user.wear_suit == S)
- user.visible_message(span_warning("[S] dissolves into a puddle of sizzling goop."))
- new /obj/effect/decal/cleanable/greenglow(S.drop_location())
- qdel(S)
-
-/datum/action/changeling/biodegrade/proc/open_closet(mob/living/carbon/human/user, obj/structure/closet/C)
- if(C && user.loc == C)
- C.visible_message(span_warning("[C]'s door breaks and opens!"))
- new /obj/effect/decal/cleanable/greenglow(C.drop_location())
- C.welded = FALSE
- C.locked = FALSE
- C.broken = TRUE
- C.open()
- to_chat(user, span_warning("We open the container restraining us!"))
-
-/datum/action/changeling/biodegrade/proc/dissolve_cocoon(mob/living/carbon/human/user, obj/structure/spider/cocoon/C)
- if(C && user.loc == C)
- new /obj/effect/decal/cleanable/greenglow(C.drop_location())
- qdel(C) //The cocoon's destroy will move the changeling outside of it without interference
- to_chat(user, span_warning("We dissolve the cocoon!"))
+/datum/action/changeling/biodegrade/proc/punish_with_acid(mob/living/carbon/human/user, mob/living/hapless_manhandler)
+ acid_blast(user, hapless_manhandler)
+ playsound(user, 'sound/mobs/non-humanoids/bileworm/bileworm_spit.ogg', 50, TRUE)
+ if(IS_CHANGELING(hapless_manhandler))
+ user.visible_message(
+ span_danger("[user] spews a mist of sizzling acid onto [hapless_manhandler]... but nothing happens!"),
+ span_changeling("We prepare our escape, spraying bio-acid on our captor... [span_danger("But nothing happened?!")]"),
+ span_danger("You hear retching, then a sizzling that terminates quite abruptly.")
+ )
+ to_chat(hapless_manhandler, span_changeling("Our prey attempts to dissuade us with one of our biology's simplest adaptions. Quaint."))
+ return
+ user.visible_message(
+ span_danger("[user] spews a mist of sizzling acid onto [hapless_manhandler], using the opportunity to wrestle away."),
+ user.balloon_alert(user, "dissuading captor..."),
+ span_danger("You hear retching, then sizzling, quickly muffled by a loud keening of pain."))
+ hapless_manhandler.Stun(2 SECONDS)
+ hapless_manhandler.emote("scream")
+ hapless_manhandler.stop_pulling()
+ log_combat(user = user, target = hapless_manhandler, what_done = "acid-spewed to escape a grab", addition = "(biodegrade)")
diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm
index cc2350e685c..762b6b4c90d 100644
--- a/code/modules/antagonists/changeling/powers/mutations.dm
+++ b/code/modules/antagonists/changeling/powers/mutations.dm
@@ -227,6 +227,8 @@
)
/obj/item/melee/arm_blade/afterattack(atom/target, mob/user, list/modifiers, list/attack_modifiers)
+ if(QDELETED(target))
+ return
if(istype(target, /obj/structure/table))
var/obj/smash = target
smash.deconstruct(FALSE)
@@ -523,6 +525,9 @@
loc.visible_message(span_warning("The end of [loc.name]\'s hand inflates rapidly, forming a huge shield-like mass!"), span_warning("We inflate our hand into a strong shield."), span_hear("You hear organic matter ripping and tearing!"))
/obj/item/shield/changeling/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 == OVERWHELMING_ATTACK)
+ return FALSE
+
if(remaining_uses < 1)
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
diff --git a/code/modules/antagonists/cult/cult_armor.dm b/code/modules/antagonists/cult/cult_armor.dm
index 13fe799a43b..e76d2313cdd 100644
--- a/code/modules/antagonists/cult/cult_armor.dm
+++ b/code/modules/antagonists/cult/cult_armor.dm
@@ -246,6 +246,7 @@
actions_types = null
color_cutoffs = list(40, 0, 0) //red
glass_colour_type = null
+ custom_materials = null
/obj/item/clothing/glasses/hud/health/night/cultblind/equipped(mob/living/user, slot)
. = ..()
diff --git a/code/modules/antagonists/cult/cult_items.dm b/code/modules/antagonists/cult/cult_items.dm
index ed423464aa3..2a150443e77 100644
--- a/code/modules/antagonists/cult/cult_items.dm
+++ b/code/modules/antagonists/cult/cult_items.dm
@@ -55,7 +55,7 @@ Striking a noncultist, however, will tear their flesh."}
if(owner.get_active_held_item() != src)
block_message = "[owner] parries [attack_text] with [src] in their offhand"
- if(IS_CULTIST(owner) && prob(final_block_chance) && attack_type != (PROJECTILE_ATTACK || OVERWHELMING_ATTACK))
+ if(IS_CULTIST(owner) && prob(final_block_chance) && (attack_type != PROJECTILE_ATTACK || attack_type != OVERWHELMING_ATTACK))
new /obj/effect/temp_visual/cult/sparks(get_turf(owner))
owner.visible_message(span_danger("[block_message]"))
return TRUE
diff --git a/code/modules/antagonists/cult/cult_structure_altar.dm b/code/modules/antagonists/cult/cult_structure_altar.dm
index 237d695996f..a6b646587c7 100644
--- a/code/modules/antagonists/cult/cult_structure_altar.dm
+++ b/code/modules/antagonists/cult/cult_structure_altar.dm
@@ -12,6 +12,7 @@
icon_state = "talismanaltar"
break_message = span_warning("The altar shatters, leaving only the wailing of the damned!")
mansus_conversion_path = /obj/effect/heretic_rune
+ custom_materials = list(/datum/material/runedmetal = SHEET_MATERIAL_AMOUNT * 3)
/obj/structure/destructible/cult/item_dispenser/altar/setup_options()
var/static/list/altar_items = list(
diff --git a/code/modules/antagonists/cult/cult_structure_archives.dm b/code/modules/antagonists/cult/cult_structure_archives.dm
index 29c00cf0168..9445d200219 100644
--- a/code/modules/antagonists/cult/cult_structure_archives.dm
+++ b/code/modules/antagonists/cult/cult_structure_archives.dm
@@ -14,6 +14,7 @@
light_color = LIGHT_COLOR_FIRE
break_message = span_warning("The books and tomes of the archives burn into ash as the desk shatters!")
mansus_conversion_path = /obj/item/codex_cicatrix
+ custom_materials = list(/datum/material/runedmetal = SHEET_MATERIAL_AMOUNT * 3)
/obj/structure/destructible/cult/item_dispenser/archives/setup_options()
var/static/list/archive_items = list(
diff --git a/code/modules/antagonists/cult/cult_structure_forge.dm b/code/modules/antagonists/cult/cult_structure_forge.dm
index 689cdd2edcb..3fe9a884369 100644
--- a/code/modules/antagonists/cult/cult_structure_forge.dm
+++ b/code/modules/antagonists/cult/cult_structure_forge.dm
@@ -14,6 +14,7 @@
light_color = LIGHT_COLOR_LAVA
break_message = span_warning("The forge breaks apart into shards with a howling scream!")
mansus_conversion_path = /obj/structure/destructible/eldritch_crucible
+ custom_materials = list(/datum/material/runedmetal = SHEET_MATERIAL_AMOUNT * 3)
/obj/structure/destructible/cult/item_dispenser/forge/setup_options()
var/static/list/forge_items = list(
diff --git a/code/modules/antagonists/cult/cult_structure_pylon.dm b/code/modules/antagonists/cult/cult_structure_pylon.dm
index 54151f1171e..506e8dd0ef9 100644
--- a/code/modules/antagonists/cult/cult_structure_pylon.dm
+++ b/code/modules/antagonists/cult/cult_structure_pylon.dm
@@ -8,6 +8,7 @@
break_sound = 'sound/effects/glass/glassbr2.ogg'
break_message = span_warning("The blood-red crystal falls to the floor and shatters!")
/// Length of the cooldown in between tile corruptions. Doubled if no turfs are found.
+ custom_materials = list(/datum/material/runedmetal = SHEET_MATERIAL_AMOUNT * 4)
var/corruption_cooldown_duration = 5 SECONDS
/// The cooldown for corruptions.
COOLDOWN_DECLARE(corruption_cooldown)
diff --git a/code/modules/antagonists/heretic/heretic_antag.dm b/code/modules/antagonists/heretic/heretic_antag.dm
index ea0708f2796..e292b4dc5dc 100644
--- a/code/modules/antagonists/heretic/heretic_antag.dm
+++ b/code/modules/antagonists/heretic/heretic_antag.dm
@@ -1179,6 +1179,6 @@
/datum/outfit/heretic
name = "Heretic (Preview only)"
- suit = /obj/item/clothing/suit/hooded/cultrobes/eldritch
- head = /obj/item/clothing/head/hooded/cult_hoodie/eldritch
+ suit = /obj/item/clothing/suit/hooded/cultrobes/eldritch/rust
+ head = /obj/item/clothing/head/hooded/cult_hoodie/eldritch/rust
r_hand = /obj/item/melee/touch_attack/mansus_fist
diff --git a/code/modules/antagonists/heretic/items/heretic_armor.dm b/code/modules/antagonists/heretic/items/heretic_armor.dm
index cc2bf66e4fa..da3aaec7453 100644
--- a/code/modules/antagonists/heretic/items/heretic_armor.dm
+++ b/code/modules/antagonists/heretic/items/heretic_armor.dm
@@ -6,7 +6,7 @@
/obj/item/clothing/suit/hooded/cultrobes/eldritch
name = "ominous armor"
desc = "A ragged, dusty set of robes. Strange eyes line the inside."
- icon_state = "eldritch_armor"
+ icon_state = "armor"
inhand_icon_state = null
flags_inv = HIDESHOES | HIDEJUMPSUIT | HIDEBELT
body_parts_covered = CHEST | GROIN | LEGS | FEET | ARMS
@@ -71,7 +71,7 @@
name = "ominous hood"
icon = 'icons/obj/clothing/head/helmet.dmi'
worn_icon = 'icons/mob/clothing/head/helmet.dmi'
- icon_state = "eldritch"
+ icon_state = "helmet"
desc = "A torn, dust-caked hood. Strange eyes line the inside."
flags_inv = HIDEMASK | HIDEEARS | HIDEEYES | HIDEFACE | HIDEHAIR | HIDEFACIALHAIR | HIDESNOUT
flags_cover = HEADCOVERSEYES | PEPPERPROOF
@@ -551,7 +551,7 @@
//"You are needed in [area name]. You need to be there. %ATTACKER might want you to stay, but you are needed in [area name].",
//"You see %ATTACKER strike a [name of animal]. The face of the beast is a mirror of your own. How strange.",
"%ATTACKER bumps you and you spill your tea. It's fine. You've plenty of cups.",
- "You hear a roaring crash. The waves hit the boat. The is sea vast and dark. You see %ATTACKER striking the water, cursing its master.",
+ "You hear a roaring crash. The waves hit the boat. The sea is vast and dark. You see %ATTACKER striking the water, cursing its master.",
"Sequins scatter into the air around %ATTACKER. The sequins...",
"You notice that a button has popped off your collar. How did that happen? Maybe %ATTACKER is to blame.",
"%ATTACKER isn't very funny, and you're struggling to see the punchline.",
diff --git a/code/modules/antagonists/heretic/items/heretic_blades.dm b/code/modules/antagonists/heretic/items/heretic_blades.dm
index 3d4924d2211..30433933ec8 100644
--- a/code/modules/antagonists/heretic/items/heretic_blades.dm
+++ b/code/modules/antagonists/heretic/items/heretic_blades.dm
@@ -166,7 +166,7 @@
/obj/item/melee/sickly_blade/dark/afterattack(atom/target, mob/user, list/modifiers, list/attack_modifiers)
. = ..()
- if(!infused || target == user || !isliving(target))
+ if(!infused || target == user || !isliving(target) || QDELETED(target))
return
var/datum/antagonist/heretic/heretic_datum = GET_HERETIC(user)
var/mob/living/living_target = target
@@ -304,6 +304,11 @@
return ITEM_INTERACT_BLOCKING
return NONE
+/obj/item/melee/sickly_blade/cursed/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 == OVERWHELMING_ATTACK)
+ return FALSE
+ return ..()
+
// Weaker blade variant given to people so they can participate in the heretic arena spell
/obj/item/melee/sickly_blade/training
name = "\improper imperfect blade"
diff --git a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm
index 041e3223998..2a0cb869a98 100644
--- a/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm
+++ b/code/modules/antagonists/heretic/knowledge/sacrifice_knowledge/sacrifice_buff.dm
@@ -94,21 +94,8 @@
* Slow and stop any blood loss the owner's experiencing.
*/
/datum/status_effect/unholy_determination/proc/adjust_bleed_wounds(seconds_between_ticks)
- if(!iscarbon(owner) || !CAN_HAVE_BLOOD(owner))
- return
-
owner.adjust_blood_volume(2 * seconds_between_ticks, maximum = BLOOD_VOLUME_NORMAL)
-
- var/mob/living/carbon/carbon_owner = owner
- var/datum/wound/bloodiest_wound
- for(var/datum/wound/iter_wound as anything in carbon_owner.all_wounds)
- if(iter_wound.blood_flow && (iter_wound.blood_flow > bloodiest_wound?.blood_flow))
- bloodiest_wound = iter_wound
-
- if(!bloodiest_wound)
- return
-
- bloodiest_wound.adjust_blood_flow(-0.5 * seconds_between_ticks)
+ owner.coagulant_effect(0.5 * seconds_between_ticks)
/// Torment the target with a frightening hand
/proc/fire_curse_hand(mob/living/carbon/victim, turf/forced_turf, range = 8, projectile_type = /obj/projectile/curse_hand/hel)
diff --git a/code/modules/antagonists/malf_ai/malf_ai.dm b/code/modules/antagonists/malf_ai/malf_ai.dm
index 71fd784490e..765816668dc 100644
--- a/code/modules/antagonists/malf_ai/malf_ai.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai.dm
@@ -46,7 +46,6 @@
malfunction_flavor = strings(MALFUNCTION_FLAVOR_FILE, employer)
add_law_zero()
- RegisterSignal(owner.current, COMSIG_SILICON_AI_CORE_STATUS, PROC_REF(core_status))
if(malf_sound)
owner.current.playsound_local(get_turf(owner.current), malf_sound, 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE)
owner.current.grant_language(/datum/language/codespeak, source = LANGUAGE_MALF)
@@ -69,7 +68,6 @@
// SKYRAT EDIT END
QDEL_NULL(malf_ai.malf_picker)
- UnregisterSignal(owner, COMSIG_SILICON_AI_CORE_STATUS)
return ..()
/// Generates a complete set of malf AI objectives up to the traitor objective limit.
@@ -283,14 +281,6 @@
return malf_ai_icon
-/datum/antagonist/malf_ai/proc/core_status(datum/source)
- SIGNAL_HANDLER
-
- var/mob/living/silicon/ai/malf_owner = owner.current
- if(malf_owner?.linked_core)
- return COMPONENT_CORE_ALL_GOOD
- return COMPONENT_CORE_DISCONNECTED
-
//Subtype of Malf AI datum, used for one of the traitor final objectives
/datum/antagonist/malf_ai/infected
name = "Infected AI"
diff --git a/code/modules/antagonists/malf_ai/malf_ai_modules.dm b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
index 0035a44167b..430c8b10662 100644
--- a/code/modules/antagonists/malf_ai/malf_ai_modules.dm
+++ b/code/modules/antagonists/malf_ai/malf_ai_modules.dm
@@ -505,15 +505,16 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/ai_module/malf))
cooldown_period = 10 SECONDS
/datum/action/innate/ai/destroy_rcds/Activate()
- for(var/I in GLOB.rcd_list)
+ for(var/potential_rcd in GLOB.rcd_list)
// BUBBER EDIT BEGIN - Ghost role RCDs are spared
- var/turf/rcd_loc = get_turf(I)
+ var/turf/rcd_loc = get_turf(potential_rcd)
if(!is_station_level(rcd_loc.z))
continue
// BUBBER EDIT END
- if(!istype(I, /obj/item/construction/rcd/borg)) //Ensures that cyborg RCDs are spared.
- var/obj/item/construction/rcd/RCD = I
- RCD.detonate_pulse()
+ if(istype(potential_rcd, /obj/item/construction/rcd/borg)) //Ensures that cyborg RCDs are spared.
+ continue
+ var/obj/item/construction/rcd/definite_rcd = potential_rcd
+ definite_rcd.detonate_pulse()
to_chat(owner, span_danger("RCD detonation pulse emitted."))
owner.playsound_local(owner, 'sound/machines/beep/twobeep.ogg', 50, 0)
diff --git a/code/modules/antagonists/ninja/energy_net_nets.dm b/code/modules/antagonists/ninja/energy_net_nets.dm
index 5f08762b341..8d4b37cc0ad 100644
--- a/code/modules/antagonists/ninja/energy_net_nets.dm
+++ b/code/modules/antagonists/ninja/energy_net_nets.dm
@@ -27,6 +27,7 @@
underlay.layer = BELOW_MOB_LAYER
SET_PLANE_EXPLICIT(underlay, GAME_PLANE, src)
add_overlay(underlay)
+ ADD_TRAIT(src, TRAIT_DANGEROUS_BUCKLE, INNATE_TRAIT)
/obj/structure/energy_net/play_attack_sound(damage, damage_type = BRUTE, damage_flag = 0)
if(damage_type == BRUTE || damage_type == BURN)
diff --git a/code/modules/antagonists/pirate/pirate.dm b/code/modules/antagonists/pirate/pirate.dm
index e4899a8eca3..7b09a2540ec 100644
--- a/code/modules/antagonists/pirate/pirate.dm
+++ b/code/modules/antagonists/pirate/pirate.dm
@@ -12,7 +12,7 @@
/datum/antagonist/pirate/greet()
. = ..()
- to_chat(owner, "
The station refused to pay for your protection. Protect the ship, siphon the credits from the station, and raid it for even more loot.")
+ to_chat(owner, "
The station refused to pay for your protection. Protect the ship, siphon the [MONEY_NAME] from the station, and raid it for even more loot.")
owner.announce_objectives()
/datum/antagonist/pirate/get_team()
@@ -81,7 +81,7 @@
/datum/objective/loot/update_explanation_text()
if(cargo_hold)
var/area/storage_area = get_area(cargo_hold)
- explanation_text = "Acquire loot and store [target_value] of credits worth in [storage_area.name] cargo hold."
+ explanation_text = "Acquire loot and store [target_value] of [MONEY_NAME] worth in [storage_area.name] cargo hold."
/datum/objective/loot/proc/loot_listing()
//Lists notable loot.
@@ -117,7 +117,7 @@
parts += "Loot stolen: "
var/datum/objective/loot/L = locate() in objectives
parts += L.loot_listing()
- parts += "Total loot value : [L.get_loot_value()]/[L.target_value] credits"
+ parts += "Total loot value : [L.get_loot_value()]/[L.target_value] [MONEY_NAME]"
if(L.check_completion() && !all_dead)
parts += "
The pirate crew was successful!"
diff --git a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
index 6d23171401e..f2750e4a5ba 100644
--- a/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
+++ b/code/modules/antagonists/pirate/pirate_shuttle_equipment.dm
@@ -65,7 +65,7 @@
/obj/machinery/shuttle_scrambler/proc/dump_loot(mob/user)
if(credits_stored) // Prevents spamming empty holochips
new /obj/item/holochip(drop_location(), credits_stored)
- to_chat(user,span_notice("You retrieve the siphoned credits!"))
+ to_chat(user,span_notice("You retrieve the siphoned [MONEY_NAME]!"))
credits_stored = 0
else
to_chat(user,span_notice("There's nothing to withdraw."))
diff --git a/code/modules/antagonists/spy/spy_bounty.dm b/code/modules/antagonists/spy/spy_bounty.dm
index c285362bcd2..2adbe7f05c7 100644
--- a/code/modules/antagonists/spy/spy_bounty.dm
+++ b/code/modules/antagonists/spy/spy_bounty.dm
@@ -31,6 +31,8 @@
VAR_FINAL/initalized = FALSE
/// Whether the bounty has been completed.
VAR_FINAL/claimed = FALSE
+ /// What pool we picked the loot from.
+ VAR_FINAL/loot_difficulty
/// What uplink item the bounty will reward on completion.
VAR_FINAL/datum/uplink_item/reward_item
@@ -70,16 +72,47 @@
/// Selects what uplink item the bounty will reward on completion.
/datum/spy_bounty/proc/select_reward(datum/spy_bounty_handler/handler)
- var/list/loot_pool = handler.possible_uplink_items[difficulty]
+ loot_difficulty = difficulty
+
+ var/list/loot_pool
+ // work backwards from the highest difficulty loot pool to find one that has items
+ for(var/i in length(handler.possible_uplink_items) to 1 step -1)
+ var/pool_tier = handler.possible_uplink_items[i]
+ // we're not looking for this difficult, skip
+ if(pool_tier != loot_difficulty)
+ continue
+ // we found our difficulty's loot pool, if it has items we're done
+ loot_pool = handler.possible_uplink_items[pool_tier]
+ if(length(loot_pool) || i == 1)
+ break
+ // if our difficult does not have items, and we're not at the lowest difficulty, step down and try again
+ loot_difficulty = handler.possible_uplink_items[i - 1]
if(!length(loot_pool))
- reward_item = /datum/uplink_item/bundles_tc/telecrystal
+ reward_item = SStraitor.uplink_items_by_type[/datum/uplink_item/bundles_tc/telecrystal]
return // future todo : add some junk items for when we run out of items
reward_item = pick(loot_pool)
+ // we remove here, rather than on claim, to reduce the chance of duplicate rewards in a single batch
+ // otherwise it would be not only possible, but *likely* to get the same reward simultaneously across bounties
+ // (though the reason this is a probability is so there is a rare chance this can happen anyways, for the fun of it)
if(prob(80))
loot_pool -= reward_item
+/**
+ * Called when the bounty gets cleared after the end of a bounty period
+ *
+ * * handler - The bounty handler that is handling this bounty.
+ */
+/datum/spy_bounty/proc/clear_bounty(datum/spy_bounty_handler/handler)
+ ASSERT(initalized, "Trying to clear an uninitialized bounty!")
+
+ // another chance to return unclaimed reward items to the bounty pool
+ if(!claimed && reward_item && prob(40))
+ handler.possible_uplink_items[loot_difficulty || difficulty] |= reward_item
+
+ qdel(src)
+
/**
* Checks if the passed movable is a valid target for this bounty.
*
@@ -702,10 +735,10 @@
bot_type = /mob/living/simple_animal/bot/secbot/pingsky
help = "Abduct Officer Pingsky - commonly found protecting the station's AI."
-/datum/spy_bounty/some_bot/scrubbs
+/datum/spy_bounty/some_bot/scrubs
difficulty = SPY_DIFFICULTY_EASY
bot_type = /mob/living/basic/bot/cleanbot/medbay
- help = "Abduct Scrubbs, MD - commonly found mopping up blood in Medbay."
+ help = "Abduct Scrubs, MD - commonly found mopping up blood in Medbay."
-/datum/spy_bounty/some_bot/scrubbs/can_claim(mob/user)
+/datum/spy_bounty/some_bot/scrubs/can_claim(mob/user)
return !(user.mind?.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_MEDICAL)
diff --git a/code/modules/antagonists/spy/spy_bounty_handler.dm b/code/modules/antagonists/spy/spy_bounty_handler.dm
index 002e7d2fb4f..93900caacb4 100644
--- a/code/modules/antagonists/spy/spy_bounty_handler.dm
+++ b/code/modules/antagonists/spy/spy_bounty_handler.dm
@@ -113,22 +113,43 @@
continue
bounty_types[difficulty][bounty] = weight
+ var/list/limited_items = list()
+ var/list/show_anyways = list()
+ show_anyways += typesof(/datum/uplink_item/spy_unique/shotgun_ammo) // acquiring a shotgun is not difficult
+
for(var/datum/uplink_item/item as anything in SStraitor.uplink_items)
- if(isnull(item.item) || item.item == ABSTRACT_UPLINK_ITEM)
+ // limited items is populated as we go
+ if(item in limited_items)
continue
- if(!(item.purchasable_from & UPLINK_SPY))
+ // proc handles checking if the item is valid
+ if(!try_add_to_loot_pool(item))
continue
- // This will have some overlap, and that's intentional -
- // Adds some variety, rare moments where you can get a hard reward for an easier bounty (or visa versa)
- if(item.cost <= CONFIG_GET(number/spy_easy_reward_tc_threshold))
- possible_uplink_items[SPY_DIFFICULTY_EASY] += item
- if(item.cost >= CONFIG_GET(number/spy_easy_reward_tc_threshold) && item.cost <= CONFIG_GET(number/spy_hard_reward_tc_threshold))
- possible_uplink_items[SPY_DIFFICULTY_MEDIUM] += item
- if(item.cost >= CONFIG_GET(number/spy_hard_reward_tc_threshold))
- possible_uplink_items[SPY_DIFFICULTY_HARD] += item
+ // any child items, such as ammo, are removed from the pool until the parent item is rewarded
+ for(var/child_item_type in (item.relevant_child_items || list()) - show_anyways)
+ if(prob(10)) // 10% chance to have it anyways though. teehee
+ continue
+ var/child_item = SStraitor.uplink_items_by_type[child_item_type]
+ for(var/difficulty in possible_uplink_items)
+ possible_uplink_items[difficulty] -= child_item
+ limited_items |= child_item
refresh_bounty_list()
+/// Helper to attempt to add the passed uplink item datum to the possible bounty pool(s).
+/datum/spy_bounty_handler/proc/try_add_to_loot_pool(datum/uplink_item/item)
+ if(isnull(item.item) || item.item == ABSTRACT_UPLINK_ITEM || !(item.purchasable_from & UPLINK_SPY))
+ return FALSE
+
+ // This will have some overlap, and that's intentional -
+ // Adds some variety, rare moments where you can get a hard reward for an easier bounty (or visa versa)
+ if(item.cost <= CONFIG_GET(number/spy_easy_reward_tc_threshold))
+ possible_uplink_items[SPY_DIFFICULTY_EASY] |= item
+ if(item.cost >= CONFIG_GET(number/spy_easy_reward_tc_threshold) && item.cost <= CONFIG_GET(number/spy_hard_reward_tc_threshold))
+ possible_uplink_items[SPY_DIFFICULTY_MEDIUM] |= item
+ if(item.cost >= CONFIG_GET(number/spy_hard_reward_tc_threshold))
+ possible_uplink_items[SPY_DIFFICULTY_HARD] |= item
+ return TRUE
+
/// Helper that returns a list of all active bounties in a single list, regardless of difficulty.
/datum/spy_bounty_handler/proc/get_all_bounties() as /list
var/list/all_bounties = list()
@@ -152,7 +173,9 @@
bounties_to_give[SPY_DIFFICULTY_MEDIUM] += converted_medium_bounties
for(var/difficulty in bounties)
- QDEL_LIST(bounties[difficulty])
+ for(var/datum/spy_bounty/bounty as anything in bounties[difficulty])
+ bounty.clear_bounty(src)
+ bounties[difficulty].Cut()
var/list/pool = bounty_types[difficulty]
var/amount_to_give = bounties_to_give[difficulty]
diff --git a/code/modules/antagonists/spy/spy_uplink.dm b/code/modules/antagonists/spy/spy_uplink.dm
index 7131a0028ca..879061ed25a 100644
--- a/code/modules/antagonists/spy/spy_uplink.dm
+++ b/code/modules/antagonists/spy/spy_uplink.dm
@@ -148,12 +148,16 @@
return FALSE
var/bounty_key = bounty.get_dupe_protection_key(stealing)
+ // record that we've claimed this type of bounty
handler.all_claimed_bounty_types[bounty_key] += 1
handler.claimed_bounties_from_last_pool[bounty_key] = TRUE
-
+ // clear up the bounty itself
bounty.clean_up_stolen_item(stealing, spy, handler)
bounty.claimed = TRUE
-
+ // adds child items to the bounty pool, ie ammo for a newly acquired gun
+ for(var/child_item_type in bounty.reward_item.relevant_child_items)
+ handler.try_add_to_loot_pool(SStraitor.uplink_items_by_type[child_item_type])
+ // and finally, spawn the reward
var/atom/movable/reward = bounty.reward_item.spawn_item_for_generic_use(spy)
if(isitem(reward))
spy.put_in_hands(reward)
diff --git a/code/modules/antagonists/traitor/contractor/syndicate_contract.dm b/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
index 71ae454df72..fb1212fb931 100644
--- a/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
+++ b/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
@@ -141,7 +141,7 @@
return
contractor_id.registered_account.adjust_money(ransom * 0.35)
contractor_id.registered_account.bank_card_talk("We've processed the ransom, agent. \
- Here's your cut - your balance is now [contractor_id.registered_account.account_balance] cr.", TRUE)
+ Here's your cut - your balance is now [contractor_id.registered_account.account_balance] [MONEY_SYMBOL].", TRUE)
#define VICTIM_EXPERIENCE_START 0
#define VICTIM_EXPERIENCE_FIRST_HIT 1
diff --git a/code/modules/art/paintings.dm b/code/modules/art/paintings.dm
index 8515b179cb4..fd6639023fc 100644
--- a/code/modules/art/paintings.dm
+++ b/code/modules/art/paintings.dm
@@ -12,6 +12,7 @@
density = TRUE
resistance_flags = FLAMMABLE
max_integrity = 60
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 5)
var/obj/item/canvas/painting = null
//Adding canvases
@@ -258,7 +259,10 @@
LAZYREMOVE(zoom_by_observer, user.key)
/obj/item/canvas/proc/finalize(mob/user)
- if(painting_metadata.loaded_from_json || finalized)
+ if(finalized || painting_metadata.loaded_from_json)
+ return
+ if(!in_range(src, user))
+ user.balloon_alert(user, "too far away!")
return
if(!try_rename(user))
return
@@ -314,7 +318,7 @@
if(curator_cut)
for(var/datum/bank_account/curator as anything in curator_accounts)
curator.adjust_money(curator_cut, "Painting: Patronage cut")
- curator.bank_card_talk("Cut on patronage received, account now holds [curator.account_balance] cr.")
+ curator.bank_card_talk("Cut on patronage received, account now holds [curator.account_balance] [MONEY_SYMBOL].")
if(istype(loc, /obj/structure/sign/painting))
var/obj/structure/sign/painting/frame = loc
diff --git a/code/modules/art/statues.dm b/code/modules/art/statues.dm
index d4e5915d54a..7cfb01d1da2 100644
--- a/code/modules/art/statues.dm
+++ b/code/modules/art/statues.dm
@@ -279,7 +279,7 @@
name = "Elder Atmosian"
desc = "A statue of an Elder Atmosian, capable of bending the laws of thermodynamics to their will."
icon_state = "eng"
- custom_materials = list(/datum/material/metalhydrogen = SHEET_MATERIAL_AMOUNT*10)
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 30, /datum/material/metalhydrogen = SHEET_MATERIAL_AMOUNT * 20, /datum/material/zaukerite = SHEET_MATERIAL_AMOUNT * 15)
max_integrity = 1000
impressiveness = 100
uncarveable = TRUE
diff --git a/code/modules/assembly/flash.dm b/code/modules/assembly/flash.dm
index 82522207e51..c1df928d8b6 100644
--- a/code/modules/assembly/flash.dm
+++ b/code/modules/assembly/flash.dm
@@ -10,6 +10,7 @@
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
throwforce = 0
w_class = WEIGHT_CLASS_TINY
+ assembly_flags = ASSEMBLY_NO_DUPLICATES
custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT*3, /datum/material/glass = SMALL_MATERIAL_AMOUNT*3)
light_system = OVERLAY_LIGHT //Used as a flash here.
light_range = FLASH_LIGHT_RANGE
diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm
index 2b2065a67a0..fb61b7c74ab 100644
--- a/code/modules/assembly/mousetrap.dm
+++ b/code/modules/assembly/mousetrap.dm
@@ -3,7 +3,7 @@
desc = "A handy little spring-loaded trap for catching pesty rodents."
icon_state = "mousetrap"
inhand_icon_state = "mousetrap"
- custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT)
+ custom_materials = list(/datum/material/cardboard = SHEET_MATERIAL_AMOUNT, /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT)
assembly_behavior = ASSEMBLY_TOGGLEABLE_INPUT
var/armed = FALSE
drop_sound = 'sound/items/handling/component_drop.ogg'
diff --git a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
index f1ce4a22c56..08e84b8810c 100644
--- a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
+++ b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
@@ -125,12 +125,14 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
select_mode(src, /datum/air_alarm_mode/filtering, should_apply = FALSE)
AddElement(/datum/element/connect_loc, atmos_connections)
- AddComponent(/datum/component/usb_port, list(
- /obj/item/circuit_component/air_alarm_general,
- /obj/item/circuit_component/air_alarm,
- /obj/item/circuit_component/air_alarm_scrubbers,
- /obj/item/circuit_component/air_alarm_vents
- ))
+ AddComponent(/datum/component/usb_port, \
+ typecacheof(list(
+ /obj/item/circuit_component/air_alarm_general,
+ /obj/item/circuit_component/air_alarm,
+ /obj/item/circuit_component/air_alarm_scrubbers,
+ /obj/item/circuit_component/air_alarm_vents
+ ), only_root_path = TRUE) \
+ )
GLOB.air_alarms += src
if(mapload)
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 6061fbfc032..943a10766bb 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()
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
index f281fee74f2..cf729cef15f 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
@@ -24,9 +24,7 @@
/obj/machinery/atmospherics/components/binary/pump/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/usb_port, list(
- /obj/item/circuit_component/atmos_pump,
- ))
+ AddComponent(/datum/component/usb_port, typecacheof(list(/obj/item/circuit_component/atmos_pump), only_root_path = TRUE))
register_context()
/obj/machinery/atmospherics/components/binary/pump/add_context(atom/source, list/context, obj/item/held_item, mob/user)
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm
index 812ca4b300c..b728805cec6 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm
@@ -87,7 +87,7 @@ It's like a regular ol' straight pipe, but you can turn it on and off.
/obj/machinery/atmospherics/components/binary/valve/digital/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/usb_port, list(/obj/item/circuit_component/digital_valve))
+ AddComponent(/datum/component/usb_port, typecacheof(list(/obj/item/circuit_component/digital_valve), only_root_path = TRUE))
/obj/item/circuit_component/digital_valve
display_name = "Digital Valve"
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 4072cbd6a51..6ea471f7476 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
@@ -28,9 +28,11 @@
/obj/machinery/atmospherics/components/binary/volume_pump/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/usb_port, list(
- /obj/item/circuit_component/atmos_volume_pump,
- ))
+ AddComponent(/datum/component/usb_port, \
+ typecacheof(list(
+ /obj/item/circuit_component/atmos_volume_pump,
+ ), only_root_path = TRUE) \
+ )
register_context()
/obj/machinery/atmospherics/components/binary/volume_pump/click_ctrl(mob/user)
diff --git a/code/modules/atmospherics/machinery/other/meter.dm b/code/modules/atmospherics/machinery/other/meter.dm
index d74841072e2..0d1b66bdaee 100644
--- a/code/modules/atmospherics/machinery/other/meter.dm
+++ b/code/modules/atmospherics/machinery/other/meter.dm
@@ -36,9 +36,11 @@
if(!target)
reattach_to_layer()
- AddComponent(/datum/component/usb_port, list(
- /obj/item/circuit_component/atmos_meter,
- ))
+ AddComponent(/datum/component/usb_port, \
+ typecacheof(list(
+ /obj/item/circuit_component/atmos_meter,
+ ), only_root_path = TRUE) \
+ )
return ..()
/obj/machinery/meter/proc/reattach_to_layer()
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index b1615a82032..7ed1d9ca32a 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -18,6 +18,7 @@
integrity_failure = 0.4
pressure_resistance = 7 * ONE_ATMOSPHERE
req_access = list()
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 10)
///Is the valve open?
var/valve_open = FALSE
diff --git a/code/modules/awaymissions/mission_code/heretic/cbrn.dm b/code/modules/awaymissions/mission_code/heretic/cbrn.dm
new file mode 100644
index 00000000000..7c73b0edda3
--- /dev/null
+++ b/code/modules/awaymissions/mission_code/heretic/cbrn.dm
@@ -0,0 +1,22 @@
+/obj/item/clothing/head/utility/radiation/cbrnhood
+ name = "\improper CBRN hood"
+ desc = "A hood with radiation protective properties along with acidic and biological protective properties. The label reads, 'Made with thin lead sheets, please do not consume.'"
+ armor_type = /datum/armor/utility_radiation/cbrn
+
+/datum/armor/utility_radiation/cbrn
+ melee = 60
+ bullet = 40
+ laser = 30
+ energy = 80
+ bomb = 20
+ bio = 100
+ fire = 75
+ acid = 100
+ wound = 25
+
+/obj/item/clothing/suit/utility/radiation/cbrnsuit
+ name = "\improper CBRN suit"
+ desc = "A hood with radiation protective properties along with acidic and biological protective properties. The label reads, 'Made with thin lead sheets, please do not consume.'"
+ allowed = null
+ slowdown = 0
+ armor_type = /datum/armor/utility_radiation/cbrn
diff --git a/code/modules/awaymissions/mission_code/heretic/fake_items.dm b/code/modules/awaymissions/mission_code/heretic/fake_items.dm
new file mode 100644
index 00000000000..ab6c72941ce
--- /dev/null
+++ b/code/modules/awaymissions/mission_code/heretic/fake_items.dm
@@ -0,0 +1,57 @@
+/obj/item/fake_items
+ name = "An item, of which is fake"
+ desc = "If you see this you feel like you should contact an 'admin' or make an 'issue report' whatever that means"
+ icon = 'icons/ui/chat/pepe.dmi'
+ icon_state = "rare_pepe"
+
+/obj/item/fake_items/sm_sliver
+ name = "supermatter sliver"
+ desc = "A tiny, highly volatile sliver of a supermatter crystal. Do not handle without protection!"
+ icon = 'icons/obj/antags/syndicate_tools.dmi'
+ icon_state = "supermatter_sliver"
+ anchored = 1
+
+/obj/item/fake_items/feddy_fazbear
+ name = "Springlock Module"
+ desc = "A springlock module disguised as a dna lock module, thankfully there is a note on it telling you this."
+ icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
+ icon_state = /obj/item/mod/module/dna_lock::icon_state
+ anchored = 1
+
+/obj/item/fake_items/time_stopper
+ name = "MOD timestopper module"
+ desc = "A module that can halt time in a small radius around the user... for as long as they \
+ want! Great for monologues or lunch breaks. Keep in mind moving will end the stop, and the \
+ module has a hefty cooldown period to avoid reality errors."
+ icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
+ icon_state = "timestop"
+ anchored = 1
+
+/obj/item/fake_items/wabbajack
+ name = "Wabbajack"
+ desc = "If there is some deity out there, they've definitely skipped their psych appointment before creating this."
+ icon = 'icons/obj/weapons/guns/magic.dmi'
+ icon_state = "the_wabbajack"
+ anchored = 1
+
+/obj/item/fake_items/abductor_win_stick
+ name = "advanced baton"
+ desc = "A quad-mode baton used for incapacitation and restraining of specimens."
+ icon = 'icons/obj/antags/abductor.dmi'
+ icon_state = "wonderprodStun"
+ anchored = 1
+
+/obj/item/fake_items/l6_saw
+ name = "L6 SAW"
+ desc = "A heavily modified 7mm light machine gun, designated 'L6 SAW'. Has 'Aussec Armoury - 2531' engraved on the receiver below the designation."
+ icon = 'icons/obj/weapons/guns/ballistic.dmi'
+ icon_state = "l6"
+ anchored = 1
+
+/obj/item/fake_items/lahti_l39
+ name = "Lahti L-39"
+ desc = "The Lahti L-39, now manufactured in space with better materials making it more portable and reliable- still loaded in the same massive cartridge, \
+ this thing was made to go through a tank and come out the other end- imagine what it could do to an exosuit, there's also a completely useless sight which is totally obstructed by the magazine."
+ icon = 'icons/obj/weapons/guns/lahtil39.dmi'
+ icon_state = "lahtil"
+ anchored = 1
diff --git a/code/modules/awaymissions/mission_code/heretic/heretic.dm b/code/modules/awaymissions/mission_code/heretic/heretic.dm
new file mode 100644
index 00000000000..9613bdbdde5
--- /dev/null
+++ b/code/modules/awaymissions/mission_code/heretic/heretic.dm
@@ -0,0 +1,22 @@
+/area/awaymission/beach/heretic
+ name = "heretic beach"
+ icon_state = "away2"
+ requires_power = FALSE
+ static_lighting = TRUE
+ area_flags = NOTELEPORT
+ default_gravity = 1
+
+/area/awaymission/caves/heretic_laboratory
+ name = "heretic lab"
+ requires_power = FALSE
+ static_lighting = TRUE
+ area_flags = NOTELEPORT
+ default_gravity = 1
+
+/area/awaymission/caves/heretic_laboratory_clean
+ name = "heretic lab clean"
+ icon_state = "away2"
+ requires_power = FALSE
+ static_lighting = TRUE
+ area_flags = NOTELEPORT
+ default_gravity = 1
diff --git a/code/modules/awaymissions/mission_code/heretic/heretic_gateway_guns.dm b/code/modules/awaymissions/mission_code/heretic/heretic_gateway_guns.dm
new file mode 100644
index 00000000000..a8990d14474
--- /dev/null
+++ b/code/modules/awaymissions/mission_code/heretic/heretic_gateway_guns.dm
@@ -0,0 +1,52 @@
+/obj/item/gun/energy/shrink_ray/one_shot
+ name = "shrink ray blaster"
+ desc = "This is a piece of frightening alien tech that enhances the magnetic pull of atoms in a localized space to temporarily make an object shrink. \
+ That or it's just space magic. Either way, it shrinks stuff, This one is jerry-rigged to work with a non alien cell. It still recharges though."
+ ammo_type = list(/obj/item/ammo_casing/energy/shrink/worse)
+
+/obj/item/ammo_casing/energy/shrink/worse
+ projectile_type = /obj/projectile/magic/shrink/alien
+ select_name = "shrink ray"
+ e_cost = LASER_SHOTS(1, STANDARD_CELL_CHARGE)
+
+/obj/item/gun/ballistic/automatic/napad
+ name = "\improper 'Napad' Submachine Gun"
+ desc = "A 9mm submachine gun with a sizeable magazine, there are no other markings on it, why is it so big?"
+ icon = 'icons/obj/weapons/guns/ninemmsmg/napad_item.dmi'
+ icon_state = "napad"
+ worn_icon = 'icons/obj/weapons/guns/ninemmsmg/napad_worn.dmi'
+ worn_icon_state = "napad"
+ lefthand_file = 'icons/obj/weapons/guns/ninemmsmg/napad_lefthand.dmi'
+ righthand_file = 'icons/obj/weapons/guns/ninemmsmg/napad_righthand.dmi'
+ inhand_icon_state = "napad"
+ special_mags = FALSE
+ bolt_type = BOLT_TYPE_LOCKING
+ w_class = WEIGHT_CLASS_BULKY
+ weapon_weight = WEAPON_HEAVY
+ slot_flags = ITEM_SLOT_BACK
+ accepted_magazine_type = /obj/item/ammo_box/magazine/napad
+ fire_sound = 'sound/items/weapons/gun/rifle/smg_heavy.ogg'
+ fire_sound_volume = 80
+ can_suppress = FALSE
+ burst_size = 1
+ fire_delay = 0.55 SECONDS
+ actions_types = list()
+ projectile_wound_bonus = -10
+ projectile_damage_multiplier = 0.65
+
+/obj/item/gun/ballistic/automatic/napad/no_mag
+ spawnwithmagazine = FALSE
+
+/obj/item/ammo_box/magazine/napad
+ name = "\improper Napad submachinegun magazine"
+ desc = "A magazine for a submachine gun. Holds twenty five rounds of 9mm ammunition."
+ icon = 'icons/obj/weapons/guns/ammo.dmi'
+ icon_state = "napad_mag"
+ w_class = WEIGHT_CLASS_NORMAL
+ multiple_sprites = AMMO_BOX_FULL_EMPTY
+ ammo_type = /obj/item/ammo_casing/c9mm
+ caliber = CALIBER_9MM
+ max_ammo = 25
+
+/obj/item/ammo_box/magazine/napad/spawns_empty
+ start_empty = TRUE
diff --git a/code/modules/awaymissions/mission_code/heretic/heretic_gateway_misc.dm b/code/modules/awaymissions/mission_code/heretic/heretic_gateway_misc.dm
new file mode 100644
index 00000000000..afa5be882af
--- /dev/null
+++ b/code/modules/awaymissions/mission_code/heretic/heretic_gateway_misc.dm
@@ -0,0 +1,91 @@
+/obj/item/keycard/heretic_entrance
+ name = "secure storage keycard"
+ desc = "A keycard that simply states, basic access."
+ color = "#000000"
+ puzzle_id = "heretic_gateway0"
+
+/obj/machinery/door/puzzle/keycard/heretic_entrance
+ name = "secure airlock"
+ puzzle_id = "heretic_gateway0"
+
+/obj/item/keycard/highsec_access
+ name = "secure storage keycard"
+ desc = "A keycard that simply states, 'only under exteme circumstances'."
+ color = "#440000"
+ puzzle_id = "heretic_gateway1"
+
+/obj/machinery/door/puzzle/keycard/highsec_access
+ name = "secure airlock"
+ puzzle_id = "heretic_gateway1"
+
+/obj/item/keycard/cbrn_area
+ name = "CBRN storage keycard"
+ desc = "A keycard that has a few weird logos and stickers on it all related to biohazards or radiation."
+ color = "#80e71f"
+ puzzle_id = "heretic_gateway2"
+
+/obj/machinery/door/puzzle/keycard/cbrn_area
+ name = "secure airlock"
+ puzzle_id = "heretic_gateway2"
+
+/obj/item/keycard/biological_anomalies
+ name = "Bio storage keycard"
+ desc = "A keycard that looks like the basic access card however it has a biological hazard warning on it."
+ color = "#357735"
+ puzzle_id = "heretic_gateway3"
+
+/obj/machinery/door/puzzle/keycard/biological_anomalies
+ name = "secure airlock"
+ puzzle_id = "heretic_gateway3"
+
+/obj/item/keycard/weapon_anomalies
+ name = "Weapon storage keycard"
+ desc = "A keycard that looks like the basic access card however it has a simple recognizable handgun on it."
+ color = "#4b4b4b"
+ puzzle_id = "heretic_gateway4"
+
+/obj/machinery/door/puzzle/keycard/weapon_anomalies
+ name = "secure airlock"
+ puzzle_id = "heretic_gateway4"
+
+/obj/item/keycard/misc_anomalies
+ name = "Misc storage keycard"
+ desc = "A keycard that looks like the basic access card however it has a staff on it."
+ color = "#df2190"
+ puzzle_id = "heretic_gateway5"
+
+/obj/machinery/door/puzzle/keycard/misc_anomalies
+ name = "secure airlock"
+ puzzle_id = "heretic_gateway5"
+
+/obj/item/paper/fluff/awaymissions/heretic
+ name = "a hint"
+ desc = "This place was designed with many failsafes to keep whats in it safe"
+
+/obj/item/paper/fluff/awaymissions/heretic/floorsafe
+ default_raw_text = "
X X X X X X X X X X \
+
X X X X X X X X X X \
+
X X X X X X X X X X \
+
X X X X X X X X X X \
+
X X X X X X X X X X \
+
X X X X X X X X X X \
+
X X + X X X X X X X \
+
X X X X X X X X X X \
+
X X X X X X X X X X \
+
X X X X X X X X X X"
+
+/obj/item/paper/fluff/awaymissions/heretic/blackroomhint
+ default_raw_text = "Hey, one of the high sec guards came through and closed things off and told us to evacuate, they hid the keycard to the facility in this room in its proper spot. If you can read this it should be fine and remember to look into the walls to find the keycard."
+
+/obj/item/paper/fluff/awaymissions/heretic/gravehint
+ default_raw_text = "there are rumors around the office that there are a few fake graves in the graveyard down the way that are empty and instead have, insurance- in them whatever that means, Yours truly, Jeramy."
+
+/turf/open/misc/ashplanet/wateryrock/safeair
+ initial_gas_mix = "o2=22;n2=82;TEMP=293.15"
+
+/obj/machinery/mass_driver/feeder
+ name = "mass driver"
+ id = "MASSDRIVER_HERETIC"
+
+/obj/machinery/computer/pod/old/mass_driver_controller/feeder
+ id = "MASSDRIVER_HERETIC"
diff --git a/code/modules/awaymissions/mission_code/heretic/heretic_gateway_spawners.dm b/code/modules/awaymissions/mission_code/heretic/heretic_gateway_spawners.dm
new file mode 100644
index 00000000000..8997813061e
--- /dev/null
+++ b/code/modules/awaymissions/mission_code/heretic/heretic_gateway_spawners.dm
@@ -0,0 +1,21 @@
+/obj/effect/spawner/random/heretic_gateway
+ name = "random heretic keycard spawn"
+ desc = "Spawns a random keycard, but probably trash."
+ loot = list(
+ /obj/item/keycard/cbrn_area = 5,
+ /obj/item/keycard/biological_anomalies = 20,
+ /obj/item/keycard/misc_anomalies = 20,
+ /obj/item/keycard/weapon_anomalies = 20,
+ /obj/effect/spawner/random/trash/deluxe_garbage = 35
+ )
+
+/obj/effect/spawner/random/heretic_gateway_low
+ name = "random heretic keycard spawn"
+ desc = "Spawns a random keycard, but definitely just trash."
+ loot = list(
+ /obj/item/keycard/cbrn_area = 0.5,
+ /obj/item/keycard/biological_anomalies = 5,
+ /obj/item/keycard/misc_anomalies = 5,
+ /obj/item/keycard/weapon_anomalies = 4,
+ /obj/effect/spawner/random/trash/deluxe_garbage = 85.5
+ )
diff --git a/code/modules/awaymissions/mission_code/heretic/heretic_gateway_tram.dm b/code/modules/awaymissions/mission_code/heretic/heretic_gateway_tram.dm
new file mode 100644
index 00000000000..bd636076633
--- /dev/null
+++ b/code/modules/awaymissions/mission_code/heretic/heretic_gateway_tram.dm
@@ -0,0 +1,29 @@
+/obj/effect/landmark/transport/transport_id/heretic
+ specific_transport_id = HERETIC_LINE_1
+
+/obj/effect/landmark/transport/nav_beacon/tram/nav/heretic
+ name = HERETIC_LINE_1
+ specific_transport_id = TRAM_NAV_BEACONS
+
+/obj/effect/landmark/transport/nav_beacon/tram/platform/heretic/left
+ name = "Port"
+ specific_transport_id = HERETIC_LINE_1
+ platform_code = HERETIC_PORT
+
+/obj/effect/landmark/transport/nav_beacon/tram/platform/heretic/middle
+ name = "Central"
+ specific_transport_id = HERETIC_LINE_1
+ platform_code = HERETIC_CENTRAL
+
+/obj/effect/landmark/transport/nav_beacon/tram/platform/heretic/right
+ name = "Starboard"
+ specific_transport_id = HERETIC_LINE_1
+ platform_code = HERETIC_STARBOARD
+
+/obj/machinery/transport/tram_controller/heretic_tram_controller
+ configured_transport_id = HERETIC_LINE_1
+
+/obj/machinery/computer/tram_controls/heretic_tram_computer
+ icon = 'icons/obj/tram/heretic_tram.dmi'
+ icon_screen = HERETIC_LINE_1
+ specific_transport_id = HERETIC_LINE_1
diff --git a/code/modules/awaymissions/mission_code/heretic/zlevel.dm b/code/modules/awaymissions/mission_code/heretic/zlevel.dm
new file mode 100644
index 00000000000..792be643b3a
--- /dev/null
+++ b/code/modules/awaymissions/mission_code/heretic/zlevel.dm
@@ -0,0 +1,3 @@
+/obj/effect/landmark/awaystart/heretic
+ name = "Heretic"
+ id = AWAYSTART_HERETIC
diff --git a/code/modules/basketball/hoop.dm b/code/modules/basketball/hoop.dm
index a00a1f39d99..0017ffdf741 100644
--- a/code/modules/basketball/hoop.dm
+++ b/code/modules/basketball/hoop.dm
@@ -18,6 +18,7 @@
density = TRUE
layer = ABOVE_MOB_LAYER
interaction_flags_click = NEED_DEXTERITY | NEED_HANDS | FORBID_TELEKINESIS_REACH
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 3.5)
/// Keeps track of the total points scored
var/total_score = 0
/// The chance to score a ball into the hoop based on distance
diff --git a/code/modules/bitrunning/job.dm b/code/modules/bitrunning/job.dm
index 519f4974cbd..5baf875ccf7 100644
--- a/code/modules/bitrunning/job.dm
+++ b/code/modules/bitrunning/job.dm
@@ -1,7 +1,6 @@
/datum/job/bitrunner
title = JOB_BITRUNNER
description = "Surf the virtual domain for gear and loot. Decrypt your rewards on station."
- department_head = list(JOB_QUARTERMASTER)
faction = FACTION_STATION
total_positions = 3
spawn_positions = 3
diff --git a/code/modules/bitrunning/objects/vendor.dm b/code/modules/bitrunning/objects/vendor.dm
index c42a954b978..951e8ff4636 100644
--- a/code/modules/bitrunning/objects/vendor.dm
+++ b/code/modules/bitrunning/objects/vendor.dm
@@ -1,5 +1,3 @@
-#define CREDIT_TYPE_BITRUNNING "np"
-
/obj/machinery/computer/order_console/bitrunning
name = "bitrunning supplies order console"
desc = "NexaCache(tm)! Dubiously authentic gear for the digital daredevil."
@@ -15,7 +13,7 @@
and hopefully get delivered by them.
35% cheaper than express delivery."}
express_tooltip = @{"Sends your purchases instantly."}
- credit_type = CREDIT_TYPE_BITRUNNING
+ credit_type = MONEY_BITRUNNING_SYMBOL
order_categories = list(
CATEGORY_BITRUNNING_FLAIR,
@@ -74,7 +72,7 @@
/datum/supply_pack/bitrunning
name = "bitrunning order"
- hidden = TRUE
+ order_flags = ORDER_INVISIBLE
crate_name = "bitrunning delivery crate"
access = list(ACCESS_BIT_DEN)
test_ignored = TRUE
@@ -84,5 +82,3 @@
name = "[purchaser]'s Bitrunning Order"
src.cost = cost
src.contains = contains
-
-#undef CREDIT_TYPE_BITRUNNING
diff --git a/code/modules/bitrunning/util/virtual_megafauna.dm b/code/modules/bitrunning/util/virtual_megafauna.dm
index 2707db9e2e7..4daf0014197 100644
--- a/code/modules/bitrunning/util/virtual_megafauna.dm
+++ b/code/modules/bitrunning/util/virtual_megafauna.dm
@@ -6,8 +6,23 @@
true_spawn = FALSE
+ // rebuild the achievement element's arguments to remove it appropriately
+ if (achievement_type || score_achievement_type)
+ var/list/achievements = list(/datum/award/achievement/boss/boss_killer, /datum/award/score/boss_score)
+ if (achievement_type)
+ achievements += achievement_type
+ if (score_achievement_type)
+ achievements += score_achievement_type
+ RemoveElement(/datum/element/kill_achievement, string_list(achievements), crusher_achievement_type, /datum/memory/megafauna_slayer)
+
+ // remove the crusher loot element's arguments also to remove it appropriately
+ RemoveElement(\
+ /datum/element/crusher_loot,\
+ trophy_type = crusher_loot,\
+ guaranteed_drop = 0.6,\
+ replace_all = replace_crusher_drop,\
+ drop_immediately = del_on_death,\
+ )
+
loot.Cut()
loot += /obj/structure/closet/crate/secure/bitrunning/encrypted
-
- crusher_loot.Cut()
- crusher_loot += /obj/structure/closet/crate/secure/bitrunning/encrypted
diff --git a/code/modules/capture_the_flag/ctf_equipment.dm b/code/modules/capture_the_flag/ctf_equipment.dm
index 92394051ef6..214ca0ec691 100644
--- a/code/modules/capture_the_flag/ctf_equipment.dm
+++ b/code/modules/capture_the_flag/ctf_equipment.dm
@@ -172,6 +172,7 @@
projectile_type = /obj/projectile/beam/instakill
e_cost = 0 // Not possible to use the macro
select_name = "DESTROY"
+ muzzle_flash_color = LIGHT_COLOR_BLUE
/obj/projectile/beam/instakill
name = "instagib laser"
diff --git a/code/modules/cargo/exports.dm b/code/modules/cargo/exports.dm
index 03fcdcb9180..78241aa6d74 100644
--- a/code/modules/cargo/exports.dm
+++ b/code/modules/cargo/exports.dm
@@ -237,7 +237,7 @@ Then the player gets the profit from selling his own wasted time.
var/total_value = ex.total_value[src]
var/total_amount = ex.total_amount[src]
- var/msg = "[total_value] credits: Received [total_amount] "
+ var/msg = "[total_value] [MONEY_NAME]: Received [total_amount] "
if(total_value > 0)
msg = "+" + msg
diff --git a/code/modules/cargo/exports/large_objects.dm b/code/modules/cargo/exports/large_objects.dm
index 240841e7974..9b5cada6739 100644
--- a/code/modules/cargo/exports/large_objects.dm
+++ b/code/modules/cargo/exports/large_objects.dm
@@ -8,6 +8,7 @@
/obj/structure/closet/crate/large,
/obj/structure/closet/crate/mail,
/obj/structure/closet/crate/wooden,
+ /obj/structure/closet/crate/cardboard,
)
/datum/export/crate/total_printout(datum/export_report/ex, notes = TRUE) // That's why a goddamn metal crate costs that much.
@@ -36,6 +37,11 @@
unit_name = "coffin"
export_types = list(/obj/structure/closet/crate/coffin)
+/datum/export/crate/cardboard
+ cost = CARGO_CRATE_VALUE/5
+ unit_name = "cardboard box"
+ export_types = list(/obj/structure/closet/crate/cardboard, /obj/structure/closet/cardboard)
+
/datum/export/reagent_dispenser
abstract_type = /datum/export/reagent_dispenser
cost = CARGO_CRATE_VALUE * 0.5 // +0-400 depending on amount of reagents left
diff --git a/code/modules/cargo/exports/materials.dm b/code/modules/cargo/exports/materials.dm
index d5c48706cdf..5678fb4459f 100644
--- a/code/modules/cargo/exports/materials.dm
+++ b/code/modules/cargo/exports/materials.dm
@@ -57,6 +57,7 @@
/datum/export/material/plasma
cost = CARGO_CRATE_VALUE * 0.4
+ k_elasticity = 0
material_id = /datum/material/plasma
message = "cm3 of plasma"
diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm
index 86a4d4f3a4f..5bc0337fa0d 100644
--- a/code/modules/cargo/expressconsole.dm
+++ b/code/modules/cargo/expressconsole.dm
@@ -119,7 +119,7 @@
data["beaconError"] = using_beacon && !canBeacon ? "(BEACON ERROR)" : ""//changes button text to include an error alert if necessary
data["hasBeacon"] = beacon != null//is there a linked beacon?
data["beaconName"] = beacon ? beacon.name : "No Beacon Found"
- data["printMsg"] = COOLDOWN_FINISHED(src, beacon_print_cooldown) ? "Print Beacon for [BEACON_COST] credits" : "Print Beacon for [BEACON_COST] credits ([COOLDOWN_TIMELEFT(src, beacon_print_cooldown)])" //buttontext for printing beacons
+ data["printMsg"] = COOLDOWN_FINISHED(src, beacon_print_cooldown) ? "Print Beacon for [BEACON_COST] [MONEY_NAME]" : "Print Beacon for [BEACON_COST] [MONEY_NAME] ([COOLDOWN_TIMELEFT(src, beacon_print_cooldown)])" //buttontext for printing beacons
data["supplies"] = list()
message = "Sales are near-instantaneous - please choose carefully."
if(SSshuttle.supply_blocked)
diff --git a/code/modules/cargo/goodies.dm b/code/modules/cargo/goodies.dm
index a98ae4a8e5b..a87d22ae8d8 100644
--- a/code/modules/cargo/goodies.dm
+++ b/code/modules/cargo/goodies.dm
@@ -2,7 +2,7 @@
/datum/supply_pack/goody
access = NONE
group = "Goodies"
- goody = TRUE
+ order_flags = ORDER_GOODY
crate_type = null
discountable = SUPPLY_PACK_STD_DISCOUNTABLE
@@ -106,14 +106,14 @@
contains = list(/obj/item/gun/energy/laser)
/datum/supply_pack/goody/carbine_single
- name = "Type 5R Laser Carbine Single_Pack"
+ name = "Type 5/R Laser Carbine Single-Pack"
desc = "Contains one laser carbine. Fires a rapid burst of slightly weaker laser projectiles."
cost = PAYCHECK_COMMAND * 8
access_view = ACCESS_WEAPONS
contains = list(/obj/item/gun/energy/laser/carbine)
/datum/supply_pack/goody/laser_pistol_single
- name = "Type 5C Laser Pistol Single-Pack"
+ name = "Type 5/C Laser Pistol Single-Pack"
desc = "Contains one Type 5C laser pistol in an energy shoulder holster. Groovy."
cost = PAYCHECK_COMMAND * 2
access_view = ACCESS_WEAPONS
@@ -128,14 +128,16 @@
/datum/supply_pack/goody/smg_single
name = "Disabler SMG Single_Pack"
- desc = "Contains one disabler SMGs, capable of rapidly firing weak disabler beams."
+ desc = "Contains one disabler SMG, capable of rapidly firing weak disabler beams."
cost = PAYCHECK_COMMAND * 6
access_view = ACCESS_WEAPONS
contains = list(/obj/item/gun/energy/disabler/smg)
/datum/supply_pack/goody/hell_single
name = "Hellgun Kit Single-Pack"
- desc = "Contains one hellgun degradation kit, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids."
+ desc = "Contains one hellgun degradation kit, to convert regular laser guns into an older pattern of laser gun, \
+ infamous for its ability to horribly disfigure targets with burns. \
+ Technically violates the Space Geneva Convention when used on humanoids."
cost = PAYCHECK_CREW * 2
access_view = ACCESS_WEAPONS
contains = list(/obj/item/weaponcrafting/gunkit/hellgun)
diff --git a/code/modules/cargo/markets/_market.dm b/code/modules/cargo/markets/_market.dm
index e2a21eb12eb..4212ac63d86 100644
--- a/code/modules/cargo/markets/_market.dm
+++ b/code/modules/cargo/markets/_market.dm
@@ -61,7 +61,7 @@
// I can't get the price of the item and shipping in a clean way to the UI, so I have to do this.
if(balance < price)
- to_chat(user, span_warning("You don't have enough credits in [uplink] for [item] with [method] shipping."))
+ to_chat(user, span_warning("You don't have enough [MONEY_NAME] in [uplink] for [item] with [method] shipping."))
return FALSE
if(item.buy(uplink, user, method, legal_status))
diff --git a/code/modules/cargo/markets/market_telepad.dm b/code/modules/cargo/markets/market_telepad.dm
index ea04c1f5bb9..445cd329603 100644
--- a/code/modules/cargo/markets/market_telepad.dm
+++ b/code/modules/cargo/markets/market_telepad.dm
@@ -93,8 +93,8 @@
. = ..()
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("Current market restock price: [EXAMINE_HINT("[restock_cost] [MONEY_SYMBOL]")].")
+ . += span_tinynoticeital("Market placement fee: [EXAMINE_HINT("[PLACE_ON_MARKET_COST] [MONEY_SYMBOL]")].")
. += span_tinynoticeital("Withholding tax on local items: [EXAMINE_HINT("[MARKET_WITHHOLDING_TAX * 100]%")].")
/obj/machinery/ltsrbt/update_icon_state()
@@ -192,7 +192,7 @@
return
if(creds_value < restock_cost)
- say("Insufficient credits!")
+ say("Insufficient [MONEY_NAME]!")
playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 40, FALSE)
return ITEM_INTERACT_BLOCKING
@@ -289,7 +289,7 @@
playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 40, FALSE)
return
if(!card.registered_account.adjust_money(-PLACE_ON_MARKET_COST, "Market: Placement Fee"))
- say("Insufficient credits!")
+ say("Insufficient [MONEY_NAME]!")
playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 40, FALSE)
return
account = card.registered_account
diff --git a/code/modules/cargo/markets/market_uplink.dm b/code/modules/cargo/markets/market_uplink.dm
index 9a5509b5e8b..5585f434c23 100644
--- a/code/modules/cargo/markets/market_uplink.dm
+++ b/code/modules/cargo/markets/market_uplink.dm
@@ -3,7 +3,7 @@
desc = "A market uplink. Usable with markets. You probably shouldn't have this!"
icon = 'icons/obj/devices/blackmarket.dmi'
icon_state = "uplink"
-
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 0.65, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 3.3)
// UI variables.
/// What category is the current uplink viewing?
var/viewing_category
@@ -56,7 +56,7 @@
current_user = null
data["categories"] = market ? market.categories : null
data["delivery_methods"] = list()
- data["money"] = "N/A cr"
+ data["money"] = "N/A [MONEY_SYMBOL]"
if(current_user)
data["money"] = current_user.account_balance
data["buying"] = buying
diff --git a/code/modules/cargo/materials_market.dm b/code/modules/cargo/materials_market.dm
index 019c8f94503..83b047957c9 100644
--- a/code/modules/cargo/materials_market.dm
+++ b/code/modules/cargo/materials_market.dm
@@ -71,7 +71,7 @@
var/obj/item/stock_block/new_block = new /obj/item/stock_block(drop_location())
new_block.export_value = price
new_block.set_custom_materials(materials)
- to_chat(user, span_notice("You have created a stock block worth [new_block.export_value * exportable.amount] cr! Sell it before it becomes liquid!"))
+ to_chat(user, span_notice("You have created a stock block worth [new_block.export_value * exportable.amount] [MONEY_SYMBOL]! Sell it before it becomes liquid!"))
playsound(src, 'sound/machines/synth/synth_yes.ogg', 50, FALSE)
qdel(exportable)
use_energy(active_power_usage)
@@ -317,7 +317,7 @@
orderer_rank = GALATIC_MATERIAL_ORDER,
orderer_ckey = living_user.ckey,
paying_account = is_ordering_private ? account_payable : null,
- cost_type = "cr",
+ cost_type = MONEY_SYMBOL,
can_be_cancelled = FALSE
)
//first time order compute the correct cost and compare
@@ -363,7 +363,7 @@
var/datum/material/export_mat = custom_materials[1]
var/quantity = custom_materials[export_mat] / SHEET_MATERIAL_AMOUNT
- . += span_notice("\The [src] is worth [quantity * export_value] cr, from selling [quantity] sheets of [export_mat.name].")
+ . += span_notice("\The [src] is worth [quantity * export_value] [MONEY_SYMBOL], from selling [quantity] sheets of [export_mat.name].")
if(fluid)
. += span_warning("\The [src] is currently liquid! Its value is based on the market price.")
diff --git a/code/modules/cargo/order.dm b/code/modules/cargo/order.dm
index 0638efd406d..702913379b8 100644
--- a/code/modules/cargo/order.dm
+++ b/code/modules/cargo/order.dm
@@ -73,7 +73,7 @@
coupon,
charge_on_purchase = TRUE,
manifest_can_fail = TRUE,
- cost_type = "cr",
+ cost_type = MONEY_SYMBOL,
can_be_cancelled = TRUE,
)
id = SSshuttle.order_number++
@@ -99,7 +99,7 @@
var/cost = pack.get_cost()
if(applied_coupon) //apply discount price
cost *= (1 - applied_coupon.discount_pct_off)
- if(paying_account?.add_to_accounts && !pack.goody) //privately purchased and not a goody means 1.1x the cost
+ if(paying_account?.add_to_accounts && !(pack.order_flags & ORDER_GOODY)) //privately purchased and not a goody means 1.1x the cost
cost *= 1.1
return round(cost)
@@ -142,7 +142,7 @@
manifest_text += "Contents:
"
manifest_text += "
"
var/container_contents = list() // Associative list with the format (item_name = nº of occurrences, ...)
- for(var/obj/item/stuff in container.contents - manifest_paper)
+ for(var/atom/movable/stuff as anything in container.contents - manifest_paper)
if(isstack(stuff))
var/obj/item/stack/thing = stuff
container_contents[thing.singular_name] += thing.amount
@@ -190,7 +190,7 @@
else
account_holder = "Cargo"
var/obj/structure/closet/crate/crate = pack.generate(A, paying_account)
- if(pack.contraband)
+ if(pack.order_flags & ORDER_CONTRABAND)
for(var/atom/movable/item_within as anything in crate.get_all_contents())
ADD_TRAIT(item_within, TRAIT_CONTRABAND, INNATE_TRAIT)
if(department_destination)
diff --git a/code/modules/cargo/orderconsole.dm b/code/modules/cargo/orderconsole.dm
index c71cdd58c7e..70adfb00154 100644
--- a/code/modules/cargo/orderconsole.dm
+++ b/code/modules/cargo/orderconsole.dm
@@ -157,6 +157,9 @@
"packs" = get_packs_data(pack.group),
)
+ data["displayed_currency_full_name"] = " [MONEY_NAME]"
+ data["displayed_currency_name"] = " [MONEY_SYMBOL]"
+
return data
/**
@@ -171,14 +174,17 @@
if(pack.group != group)
continue
+ if(pack.order_flags & ORDER_INVISIBLE)
+ continue
+
// Express console packs check
- if(express && (pack.hidden || pack.special))
+ if(express && (pack.order_flags & (ORDER_EMAG_ONLY | ORDER_SPECIAL)))
continue
- if(!express && ((pack.hidden && !(obj_flags & EMAGGED)) || (pack.special && !pack.special_enabled) || pack.drop_pod_only))
+ if(!express && (((pack.order_flags & ORDER_EMAG_ONLY) && !(obj_flags & EMAGGED)) || ((pack.order_flags & ORDER_SPECIAL) && !(pack.order_flags & ORDER_SPECIAL_ENABLED)) || (pack.order_flags & ORDER_POD_ONLY)))
continue
- if(pack.contraband && !contraband)
+ if((pack.order_flags & ORDER_CONTRABAND) && !contraband)
continue
var/obj/item/first_item = length(pack.contains) > 0 ? pack.contains[1] : null
@@ -189,9 +195,9 @@
"desc" = pack.desc || pack.name, // If there is a description, use it. Otherwise use the pack's name.
"first_item_icon" = first_item?.icon,
"first_item_icon_state" = first_item?.icon_state,
- "goody" = pack.goody,
+ "goody" = (pack.order_flags & ORDER_GOODY),
"access" = pack.access,
- "contraband" = pack.contraband,
+ "contraband" = (pack.order_flags & ORDER_CONTRABAND),
"contains" = pack.get_contents_ui_data(),
))
@@ -219,7 +225,8 @@
CRASH("Unknown supply pack id given by order console ui. ID: [id]")
if(amount > CARGO_MAX_ORDER || amount < 1) // Holy shit fuck off
CRASH("Invalid amount passed into add_item")
- if((pack.hidden && !(obj_flags & EMAGGED)) || (pack.contraband && !contraband) || pack.drop_pod_only || (pack.special && !pack.special_enabled))
+
+ if(((pack.order_flags & ORDER_EMAG_ONLY) && !(obj_flags & EMAGGED)) || ((pack.order_flags & ORDER_CONTRABAND) && !contraband) || (pack.order_flags & ORDER_POD_ONLY) || ((pack.order_flags & ORDER_SPECIAL) && !(pack.order_flags & ORDER_SPECIAL_ENABLED)))
return
var/name = "*None Provided*"
@@ -257,7 +264,7 @@
var/list/working_list = SSshuttle.shopping_list
var/reason = ""
var/datum/bank_account/personal_department
- if(requestonly && !self_paid && !pack.goody)
+ if(requestonly && !self_paid && !(pack.order_flags & ORDER_GOODY))
working_list = SSshuttle.request_list
reason = tgui_input_text(user, "Reason", name, max_length = MAX_MESSAGE_LEN)
if(isnull(reason))
@@ -273,7 +280,7 @@
if(dept_choice == "Cargo Budget")
personal_department = null
- if(pack.goody && !self_paid)
+ if((pack.order_flags & ORDER_GOODY) && !self_paid)
playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
say("ERROR: Small crates may only be purchased by private accounts.")
return
diff --git a/code/modules/cargo/packs/_packs.dm b/code/modules/cargo/packs/_packs.dm
index 71f8ed26c7a..e4650000d15 100644
--- a/code/modules/cargo/packs/_packs.dm
+++ b/code/modules/cargo/packs/_packs.dm
@@ -3,10 +3,6 @@
var/name = "Crate"
/// The group that the supply pack is sorted into within the cargo purchasing UI.
var/group = ""
- /// Is this cargo supply pack visible to the cargo purchasing UI.
- var/hidden = FALSE
- /// Is this supply pack purchasable outside of the standard purchasing band? Contraband is available by multitooling the cargo purchasing board.
- var/contraband = FALSE
/// Cost of the crate. DO NOT GO ANY LOWER THAN X1.4 the "CARGO_CRATE_VALUE" value if using regular crates, or infinite profit will be possible!
var/cost = CARGO_CRATE_VALUE * 1.4
/// What access is required to open the crate when spawned?
@@ -25,24 +21,14 @@
var/desc = ""
/// What typepath of crate do you spawn?
var/crate_type = /obj/structure/closet/crate
- /// Should we message admins?
- var/dangerous = FALSE
- /// Event/Station Goals/Admin enabled packs
- var/special = FALSE
- /// When a cargo pack can be unlocked by special events (as seen in special), this toggles if it's been enabled in the round yet (For example, after the station alert, we can now enable buying the station goal pack).
- var/special_enabled = FALSE
- /// Only usable by the Bluespace Drop Pod via the express cargo console
- var/drop_pod_only = FALSE
/// If this pack comes shipped in a specific pod when launched from the express console
var/special_pod
- /// Was this spawned through an admin proc?
- var/admin_spawned = FALSE
- /// Goodies can only be purchased by private accounts and can have coupons apply to them. They also come in a lockbox instead of a full crate, so the crate price min doesn't apply
- var/goody = FALSE
/// Can coupons target this pack? If so, how rarely?
var/discountable = SUPPLY_PACK_NOT_DISCOUNTABLE
/// Is this supply pack considered unpredictable for the purposes of testing unit testing? Examples include the stock market, or miner supply crates. If true, exempts from unit testing
var/test_ignored = FALSE
+ /// Various properties for cargo order mostly used to determine which consoles can see it
+ var/order_flags = NONE
/datum/supply_pack/New()
id = type
@@ -95,7 +81,7 @@
contains[item] = 1
for(var/iteration = 1 to contains[item])
var/atom/A = new item(C)
- if(!admin_spawned)
+ if(!(order_flags & ORDER_ADMIN_SPAWNED))
continue
A.flags_1 |= ADMIN_SPAWNED_1
@@ -126,7 +112,7 @@
*/
/datum/supply_pack/custom
name = "mining order"
- hidden = TRUE
+ order_flags = ORDER_INVISIBLE
crate_name = "shaft mining delivery crate"
access = ACCESS_MINING
test_ignored = TRUE
@@ -186,6 +172,6 @@
while(sheets_to_spawn)
var/spawn_quantity = min(sheets_to_spawn, MAX_STACK_SIZE)
var/obj/item/stack/sheet/ordered_stack = new possible_stack(C, spawn_quantity)
- if(admin_spawned)
+ if(order_flags & ORDER_ADMIN_SPAWNED)
ordered_stack.flags_1 |= ADMIN_SPAWNED_1
sheets_to_spawn -= spawn_quantity
diff --git a/code/modules/cargo/packs/costumes_toys.dm b/code/modules/cargo/packs/costumes_toys.dm
index 3638a539686..753ea79671c 100644
--- a/code/modules/cargo/packs/costumes_toys.dm
+++ b/code/modules/cargo/packs/costumes_toys.dm
@@ -74,7 +74,7 @@
name = "Hilarious Firing Pin Crate"
desc = "I uh... I'm not really sure what this does. Wanna buy it?"
cost = CARGO_CRATE_VALUE * 10
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
contains = list(/obj/item/firing_pin/clown)
crate_name = "toy crate" // It's /technically/ a toy. For the clown, at least.
crate_type = /obj/structure/closet/crate/wooden
@@ -110,7 +110,7 @@
name = "Laser Tag Firing Pins Crate"
desc = "Three laser tag firing pins used in laser-tag units to ensure users are wearing their vests."
cost = CARGO_CRATE_VALUE * 3.5
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
contains = list(/obj/item/storage/box/lasertagpins)
crate_name = "laser tag crate"
@@ -281,3 +281,39 @@
crate_name = "long balloons kit"
crate_type = /obj/structure/closet/crate/wooden
discountable = SUPPLY_PACK_STD_DISCOUNTABLE
+
+/datum/supply_pack/costumes_toys/christmas
+ name = "Surplus Christmas Gifts"
+ desc = "This crate contains pre-wrapped gifts addressed to station employees, sourced at random from Nanotrasen surplus as part of a morale boosting initiative."
+ cost = CARGO_CRATE_VALUE * 30
+ contains = list()
+ order_flags = ORDER_SPECIAL
+ crate_type = /obj/structure/closet/crate/mail
+ discountable = SUPPLY_PACK_NOT_DISCOUNTABLE
+
+/datum/supply_pack/costumes_toys/christmas/fill(obj/structure/closet/crate/crate)
+ var/list/mail_recipients = list()
+ for(var/mob/living/carbon/human/human in GLOB.player_list)
+ if(human.stat == DEAD || !human.mind)
+ continue
+ // Skip wizards, nuke ops, cyborgs; They're not written down as employees
+ if(!(human.mind.assigned_role.job_flags & JOB_CREW_MEMBER))
+ continue
+ mail_recipients += human.mind
+
+ for (var/i in 1 to rand(4, 6))
+ var/obj/item/gift/anything/gift = new(crate)
+ var/datum/mind/recipient = pick_n_take(mail_recipients)
+ if(recipient)
+ gift.assign_recipient(recipient)
+ else
+ qdel(gift)
+ break
+
+ var/obj/item/delivery/big/parcel = new(crate.loc)
+ parcel.base_icon_state = crate.delivery_icon
+ parcel.giftwrapped = TRUE
+ parcel.update_icon()
+ parcel.drag_slowdown = crate.drag_slowdown
+ crate.forceMove(parcel)
+ parcel.name = "christmas gifts"
diff --git a/code/modules/cargo/packs/engineering.dm b/code/modules/cargo/packs/engineering.dm
index 36afce95808..84e0ee501c1 100644
--- a/code/modules/cargo/packs/engineering.dm
+++ b/code/modules/cargo/packs/engineering.dm
@@ -160,7 +160,7 @@
devastating feat of human engineering and testament to wartime determination. \
Highly advanced research is required for proper construction."
cost = CARGO_CRATE_VALUE * 30
- special = TRUE
+ order_flags = ORDER_SPECIAL
access_view = ACCESS_COMMAND
contains = list(/obj/item/paper/guides/jobs/engineering/bsa,
/obj/item/circuitboard/machine/bsa/front,
@@ -176,7 +176,7 @@
library of scientific knowledge, capable of granting superhuman powers and abilities. \
Highly advanced research is required for proper construction. Also contains five DNA probes."
cost = CARGO_CRATE_VALUE * 24
- special = TRUE
+ order_flags = ORDER_SPECIAL
access_view = ACCESS_COMMAND
contains = list(/obj/item/circuitboard/machine/dna_vault,
/obj/item/dna_probe = 5,
@@ -187,7 +187,7 @@
name = "DNA Vault Samplers"
desc = "Contains five DNA probes for use in the DNA vault."
cost = CARGO_CRATE_VALUE * 6
- special = TRUE
+ order_flags = ORDER_SPECIAL
access_view = ACCESS_COMMAND
contains = list(/obj/item/dna_probe = 5)
crate_name= "dna samplers crate"
@@ -236,7 +236,7 @@
contains = list(/obj/machinery/power/emitter = 2)
crate_name = "emitter crate"
crate_type = /obj/structure/closet/crate/secure/engineering
- dangerous = TRUE
+ order_flags = ORDER_DANGEROUS
/datum/supply_pack/engine/field_gen
name = "Field Generator Crate"
@@ -276,7 +276,7 @@
contains = list(/obj/machinery/power/supermatter_crystal/shard)
crate_name = "supermatter shard crate"
crate_type = /obj/structure/closet/crate/secure/radiation
- dangerous = TRUE
+ order_flags = ORDER_DANGEROUS
discountable = SUPPLY_PACK_RARE_DISCOUNTABLE
/datum/supply_pack/engine/tesla_coils
@@ -302,12 +302,12 @@
)
crate_name = "HFR crate"
crate_type = /obj/structure/closet/crate/secure/engineering/atmos
- dangerous = TRUE
+ order_flags = ORDER_DANGEROUS
/datum/supply_pack/engineering/rad_protection_modules
name = "Radiation Protection Modules"
desc = "Contains multiple radiation protections modules for MODsuits."
- hidden = TRUE
+ order_flags = ORDER_INVISIBLE
contains = list(/obj/item/mod/module/rad_protection = 3)
crate_name = "modsuit radiation modules"
crate_type = /obj/structure/closet/crate/engineering
@@ -317,7 +317,7 @@
desc = "Contains circuitboards and radiation modules for constructing radioactive nebula shielding."
cost = CARGO_CRATE_VALUE * 2
- special = TRUE
+ order_flags = ORDER_SPECIAL
contains = list(
/obj/item/mod/module/rad_protection = 5,
/obj/item/circuitboard/machine/radioactive_nebula_shielding = 5,
diff --git a/code/modules/cargo/packs/exploration.dm b/code/modules/cargo/packs/exploration.dm
index 17f1ce1cb2c..12b56130c2c 100644
--- a/code/modules/cargo/packs/exploration.dm
+++ b/code/modules/cargo/packs/exploration.dm
@@ -1,7 +1,7 @@
/// Exploration drone unlockables ///
/datum/supply_pack/exploration
- special = TRUE
+ order_flags = ORDER_SPECIAL
group = "Outsourced"
/datum/supply_pack/exploration/scrapyard
diff --git a/code/modules/cargo/packs/general.dm b/code/modules/cargo/packs/general.dm
index ca70a167428..ab0a1ed5cfb 100644
--- a/code/modules/cargo/packs/general.dm
+++ b/code/modules/cargo/packs/general.dm
@@ -113,7 +113,7 @@
/obj/item/folder/red,
/obj/item/folder/yellow,
/obj/item/clipboard = 2,
- /obj/item/stamp,
+ /obj/item/stamp/granted,
/obj/item/stamp/denied,
/obj/item/laser_pointer/purple,
)
@@ -156,7 +156,7 @@
Call today and we'll shoot over a demo unit for just 300 credits!"
cost = CARGO_CRATE_VALUE * 0.6 //Empty pod, so no crate refund
contains = list()
- drop_pod_only = TRUE
+ order_flags = ORDER_POD_ONLY
crate_type = null
special_pod = /obj/structure/closet/supplypod/bluespacepod
@@ -211,7 +211,7 @@
/datum/supply_pack/misc/syndicate
name = "Assorted Syndicate Gear"
desc = "Contains a random assortment of syndicate gear."
- special = TRUE //Cannot be ordered via cargo
+ order_flags = ORDER_SPECIAL //Cannot be ordered via cargo
contains = list()
crate_name = "syndicate gear crate"
crate_type = /obj/structure/closet/crate
@@ -224,8 +224,7 @@
///Generate assorted uplink items, taking into account the same surplus modifiers used for surplus crates
/datum/supply_pack/misc/syndicate/fill(obj/structure/closet/crate/C)
var/list/uplink_items = list()
- for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type)
- var/datum/uplink_item/item = SStraitor.uplink_items_by_type[item_path]
+ for(var/datum/uplink_item/item as anything in SStraitor.uplink_items)
if(item.purchasable_from & contents_uplink_type && item.item)
uplink_items += item
diff --git a/code/modules/cargo/packs/imports.dm b/code/modules/cargo/packs/imports.dm
index f20348d745b..70dc45dc543 100644
--- a/code/modules/cargo/packs/imports.dm
+++ b/code/modules/cargo/packs/imports.dm
@@ -24,7 +24,7 @@
name = "Foam Force Pistols Crate"
desc = "Psst.. hey bud... remember those old foam force pistols that got discontinued for being too cool? \
Well I got two of those right here with your name on em. I'll even throw in a spare mag for each, waddya say?"
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
cost = CARGO_CRATE_VALUE * 3
contains = list(
/obj/item/gun/ballistic/automatic/pistol/toy = 2,
@@ -56,7 +56,7 @@
desc = "Wait, is this the right crate? It has a frowny face, what does that mean?"
cost = CARGO_CRATE_VALUE * 4
contains = list(/mob/living/basic/spider/giant/hunter)
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
/datum/supply_pack/imports/bamboo50
name = "50 Bamboo Cuttings"
@@ -125,7 +125,7 @@
name = "NULL_ENTRY"
desc = "(*!&@#OKAY, OPERATIVE, WE SEE HOW MUCH MONEY YOU'RE FLAUNTING. FINE. HAVE THIS, AND GOOD LUCK PUTTING IT TOGETHER!#@*$"
cost = CARGO_CRATE_VALUE * 100
- hidden = TRUE
+ order_flags = ORDER_EMAG_ONLY
contains = list(/obj/item/book/granter/crafting_recipe/regal_condor)
/datum/supply_pack/imports/mafia
@@ -133,7 +133,7 @@
desc = "This crate contains everything you need to set up your own ethnicity-based racketeering operation."
cost = CARGO_CRATE_VALUE * 4
contains = list()
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
/datum/supply_pack/imports/mafia/fill(obj/structure/closet/crate/our_crate)
for(var/items in 1 to 4)
@@ -148,7 +148,7 @@
name = "'Contraband' Crate"
desc = "Psst.. bud... want some contraband? I can get you a poster, some nice cigs, dank, even some \
sponsored items...you know, the good stuff. Just keep it away from the cops, kay?"
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
cost = CARGO_CRATE_VALUE * 20
contains = list(
/obj/effect/spawner/random/contraband = 5,
@@ -162,7 +162,7 @@
SMUGGLING THIS CRATE THROUGH A FEW OUTDATED CUSTOMS CHECKPOINTS, WE'VE THE NEXT BEST THING! \
SERVICE AUTORIFLES. DON'T WORRY, THE RUMORS ABOUT THE GUN MELTING YOU ARE JUST THAT! RUMORS! \
THESE THINGS WORK FINE! MIGHT BE SLIGHTLY DIRTY.!#@*$"
- hidden = TRUE
+ order_flags = ORDER_EMAG_ONLY
cost = CARGO_CRATE_VALUE * 7
contains = list(
/obj/item/gun/ballistic/automatic/wt550 = 2,
@@ -173,7 +173,7 @@
/datum/supply_pack/imports/wt550ammo
name = "Smuggled WT-550 Ammo Crate"
desc = "(*!&@#OPERATIVE, YOU LIKE THAT WT-550? THEN WHY NOT EQUIP YOURSELF WITH SOME MORE AMMO!!#@*$"
- hidden = TRUE
+ order_flags = ORDER_EMAG_ONLY
cost = CARGO_CRATE_VALUE * 4
contains = list(
/obj/item/ammo_box/magazine/wt550m9 = 2,
@@ -187,7 +187,7 @@
name = "Shocktrooper Crate"
desc = "(*!&@#WANT TO PUT THE FEAR OF DEATH INTO YOUR ENEMIES? THIS CRATE OF GOODIES CAN HELP MAKE THAT A REALITY. \
CONTAINS AN ARMOR VEST AND HELMET, A BOX OF FIVE EMP GRENADES, THREE SMOKEBOMBS, TWO GLUON GRENADES AND TWO FRAG GRENADES!#@*$"
- hidden = TRUE
+ order_flags = ORDER_EMAG_ONLY
cost = CARGO_CRATE_VALUE * 10
contains = list(
/obj/item/storage/box/emps,
@@ -203,7 +203,7 @@
name = "Special Ops Crate"
desc = "(*!&@#THE PIGS ON YOUR TAIL? MAYBE YOU CAN BUY SOME TIME WITH THIS CRATE! \
CONTAINS A CHAMELEON MASK, BELT AND JUMPSUIT, MIRAGE GRENADES AND AN AGENT CARD! AND A KNIFE!!#@*$"
- hidden = TRUE
+ order_flags = ORDER_EMAG_ONLY
cost = CARGO_CRATE_VALUE * 10
contains = list(
/obj/item/clothing/mask/chameleon,
@@ -220,7 +220,7 @@
desc = "Hello <;~insert appropriate greeting here: 'Comrade'|'Imperalist Scum'|'Quartermaster of Reputable Station'~;>, \
we have the most modern russian military equipment the black market can offer, for the right price of course. \
No lock, best price."
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
cost = CARGO_CRATE_VALUE * 12
contains = list(
/obj/item/food/rationpack,
@@ -252,7 +252,7 @@
Then we have the perfect weapon for you! Special price for good friends! \
We don't have enough spare ammo, so you'll have to pick up the weapon of \
dead comrade when you run out."
- hidden = TRUE
+ order_flags = ORDER_EMAG_ONLY
cost = CARGO_CRATE_VALUE * 6
contains = list(/obj/item/gun/ballistic/rifle/boltaction = 6)
@@ -261,7 +261,7 @@
desc = "TUNNEL SNAKES OWN THIS TOWN. Contains an unbranded All Terrain Vehicle, and a \
complete gang outfit -- consists of black gloves, a menacing skull bandanna, and a SWEET leather overcoat!"
cost = CARGO_CRATE_VALUE * 4
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
contains = list(
/obj/vehicle/ridden/atv,
/obj/item/key/atv,
@@ -280,8 +280,7 @@
contains = list()
crate_type = /obj/structure/closet/crate/secure/loot
crate_name = "abandoned crate"
- contraband = TRUE
- dangerous = TRUE //these are literally bombs so....
+ order_flags = ORDER_CONTRABAND | ORDER_DANGEROUS //these are literally bombs so....
discountable = SUPPLY_PACK_RARE_DISCOUNTABLE
/datum/supply_pack/imports/shambler_evil
@@ -290,7 +289,7 @@
cost = CARGO_CRATE_VALUE * 50
contains = list(/obj/item/reagent_containers/cup/soda_cans/shamblers/eldritch = 1)
crate_name = "illegal shambler's juice crate"
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
/datum/supply_pack/imports/hide
name = "Animal Hide Crate"
@@ -313,7 +312,7 @@
name = "Big Slappy parts"
desc = "Illegal Big Slappy parts. The fastest and statistically most dangerous wrench."
cost = CARGO_CRATE_VALUE * 22
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
contains = list(/obj/item/weaponcrafting/giant_wrench)
crate_name = "unknown parts crate"
@@ -339,7 +338,7 @@ Removal End */
espionage uniform used by the very best. Providing the best \
flexibility, with our latest Camo-tech threads. Perfect for \
risky espionage hallway operations. Enjoy our product!"
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
cost = CARGO_CRATE_VALUE * 6
contains = list(
/obj/item/clothing/under/syndicate/floortilecamo = 3,
@@ -361,7 +360,7 @@ Removal End */
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
+ order_flags = ORDER_CONTRABAND
contains = list(
/obj/item/circuitboard/machine/ltsrbt,
/obj/item/stack/ore/bluespace_crystal/artificial = 2,
diff --git a/code/modules/cargo/packs/livestock.dm b/code/modules/cargo/packs/livestock.dm
index 9adbd31c84d..51dd406c39b 100644
--- a/code/modules/cargo/packs/livestock.dm
+++ b/code/modules/cargo/packs/livestock.dm
@@ -17,7 +17,7 @@
/datum/supply_pack/critter/butterfly
name = "Butterflies Crate"
desc = "Not a very dangerous insect, but they do give off a better image than, say, flies or cockroaches."//is that a motherfucking worm reference
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
cost = CARGO_CRATE_VALUE * 5
contains = list(/mob/living/basic/butterfly)
crate_name = "entomology samples crate"
@@ -113,7 +113,7 @@
cost = CARGO_CRATE_VALUE * 8
contains = list(/mob/living/basic/crab)
crate_name = "look sir free crabs"
- drop_pod_only = TRUE
+ order_flags = ORDER_POD_ONLY
/datum/supply_pack/critter/crab/generate()
. = ..()
@@ -218,7 +218,7 @@
/datum/supply_pack/critter/garden_gnome
name = "Garden Gnome Crate"
desc = "Collect them all for your garden. Comes with three!"
- hidden = TRUE
+ order_flags = ORDER_EMAG_ONLY
cost = CARGO_CRATE_VALUE * 15
contains = list(/mob/living/basic/garden_gnome)
crate_name = "garden gnome crate"
diff --git a/code/modules/cargo/packs/medical.dm b/code/modules/cargo/packs/medical.dm
index 13a08798af9..ddd21237451 100644
--- a/code/modules/cargo/packs/medical.dm
+++ b/code/modules/cargo/packs/medical.dm
@@ -176,7 +176,7 @@
)
crate_name = "virus crate"
crate_type = /obj/structure/closet/crate/secure/plasma
- dangerous = TRUE
+ order_flags = ORDER_DANGEROUS
/datum/supply_pack/medical/cmoturtlenecks
name = "Chief Medical Officer Turtlenecks"
diff --git a/code/modules/cargo/packs/organic.dm b/code/modules/cargo/packs/organic.dm
index 71563699a40..d6f6468899a 100644
--- a/code/modules/cargo/packs/organic.dm
+++ b/code/modules/cargo/packs/organic.dm
@@ -139,7 +139,7 @@
cost = CARGO_CRATE_VALUE * 12
contains = list(/obj/item/storage/backpack/duffelbag/clown/cream_pie)
crate_name = "party equipment crate"
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
access = ACCESS_THEATRE
access_view = ACCESS_THEATRE
crate_type = /obj/structure/closet/crate/secure
@@ -360,7 +360,7 @@
crate_type = /obj/structure/closet/crate/cardboard
/datum/supply_pack/organic/syrup_contraband
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
name = "Contraband Syrups Box"
desc = "A packaged box containing illegal coffee syrups. Possession of these carries a penalty established in the galactic penal code."
cost = CARGO_CRATE_VALUE * 6
diff --git a/code/modules/cargo/packs/science.dm b/code/modules/cargo/packs/science.dm
index 51a01c4b8a1..177ba2689b5 100644
--- a/code/modules/cargo/packs/science.dm
+++ b/code/modules/cargo/packs/science.dm
@@ -155,7 +155,7 @@
contains = list(/obj/item/transfer_valve = 2)
crate_name = "tank transfer valves crate"
crate_type = /obj/structure/closet/crate/secure/science
- dangerous = TRUE
+ order_flags = ORDER_DANGEROUS
/datum/supply_pack/science/monkey_helmets
name = "Monkey Mind Magnification Helmet crate"
diff --git a/code/modules/cargo/packs/security.dm b/code/modules/cargo/packs/security.dm
index 48ffd674de8..9754885e423 100644
--- a/code/modules/cargo/packs/security.dm
+++ b/code/modules/cargo/packs/security.dm
@@ -144,7 +144,7 @@
The Alpha and the Omega of security headwear. Guaranteed to strike fear into the hearts \
of each and every criminal aboard the station. Also comes with a security gasmask."
cost = CARGO_CRATE_VALUE * 6 //justice comes at a price. An expensive, noisy price.
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
contains = list(/obj/item/clothing/head/helmet/toggleable/justice,
/obj/item/clothing/mask/gas/sechailer,
)
@@ -171,7 +171,7 @@
desc = "Spare equipment found in a warehouse. Contains a constable's outfit, \
whistle, and conversion kit."
cost = CARGO_CRATE_VALUE * 2.2
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
contains = list(/obj/item/clothing/under/rank/security/constable,
/obj/item/clothing/head/costume/constable,
/obj/item/clothing/gloves/color/white,
@@ -250,7 +250,7 @@
crate_type = /obj/structure/closet/crate/secure/plasma
/datum/supply_pack/security/armory/laser_carbine
- name = "Type 5R Laser Carbine Crate"
+ name = "Type 5/R Laser Carbine Crate"
desc = "Contains three Type 5R laser carbines, developed by Nanotrasen. Fires a rapid burst of slightly weaker laser projectiles."
cost = CARGO_CRATE_VALUE * 9
contains = list(/obj/item/gun/energy/laser/carbine = 3)
@@ -308,7 +308,7 @@
)
crate_name = "incendiary weapons crate"
crate_type = /obj/structure/closet/crate/secure/plasma
- dangerous = TRUE
+ order_flags = ORDER_DANGEROUS
/datum/supply_pack/security/armory/mindshield
name = "Mindshield Implants Crate"
diff --git a/code/modules/cargo/packs/service.dm b/code/modules/cargo/packs/service.dm
index 858040bb544..919d4ca6153 100644
--- a/code/modules/cargo/packs/service.dm
+++ b/code/modules/cargo/packs/service.dm
@@ -7,7 +7,7 @@
back to work with this crate containing stamps, an export scanner, \
destination tagger, hand labeler and some package wrapping."
cost = CARGO_CRATE_VALUE * 1.75
- contains = list(/obj/item/stamp,
+ contains = list(/obj/item/stamp/granted,
/obj/item/stamp/denied,
/obj/item/universal_scanner,
/obj/item/dest_tagger,
diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm
index a7c61deed5a..fdb7a04d317 100644
--- a/code/modules/cargo/supplypod.dm
+++ b/code/modules/cargo/supplypod.dm
@@ -71,6 +71,19 @@
bluespace = TRUE
explosionSize = list(0,0,0,0)
+/// Quick setup for if you want a pod that transports a specific object somewhere and makes it look like it is flying away
+/obj/structure/closet/supplypod/transport
+ style = /datum/pod_style/seethrough
+ specialised = TRUE
+ explosionSize = list(0,0,0,0)
+ reversing = TRUE
+ delays = list(POD_TRANSIT = 0, POD_FALLING = 0, POD_OPENING = 0, POD_LEAVING = 0)
+ reverse_delays = list(POD_TRANSIT = 15, POD_FALLING = 10, POD_OPENING = 0, POD_LEAVING = 0)
+ custom_rev_delay = TRUE
+ effectQuiet = TRUE
+ close_sound = null
+ pod_flags = FIRST_SOUNDS
+
/obj/structure/closet/supplypod/podspawn/deathmatch
desc = "A blood-red styled drop pod."
specialised = TRUE
@@ -300,6 +313,7 @@
if (custom_rev_delay)
delays = reverse_delays
backToNonReverseIcon()
+ SEND_SIGNAL(src, COMSIG_SUPPLYPOD_RETURNING)
var/turf/return_turf = locate(reverse_dropoff_coords[1], reverse_dropoff_coords[2], reverse_dropoff_coords[3])
new /obj/effect/pod_landingzone(return_turf, src)
@@ -308,9 +322,9 @@
var/list/boom = explosionSize
resistance_flags = initial(resistance_flags)
set_density(TRUE) //Density is originally false so the pod doesn't block anything while it's still falling through the air
- AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_magnitude)
if(effectShrapnel)
- SEND_SIGNAL(src, COMSIG_SUPPLYPOD_LANDED)
+ AddComponent(/datum/component/pellet_cloud, projectile_type=shrapnel_type, magnitude=shrapnel_magnitude)
+ SEND_SIGNAL(src, COMSIG_SUPPLYPOD_LANDED)
for (var/mob/living/target_living in turf_underneath)
if (iscarbon(target_living)) //If effectLimb is true (which means we pop limbs off when we hit people):
if (effectLimb)
@@ -318,8 +332,7 @@
for (var/bp in carbon_target_mob.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands
var/obj/item/bodypart/bodypart = bp
if(bodypart.body_part != HEAD && bodypart.body_part != CHEST)//we dont want to kill him, just teach em a lesson!
- if (!(bodypart.bodypart_flags & BODYPART_UNREMOVABLE))
- bodypart.dismember() //Using the power of flextape i've sawed this man's limb in half!
+ if(bodypart.dismember()) //Using the power of flextape i've sawed this man's limb in half!
break
if (effectOrgans) //effectOrgans means remove every organ in our mob
var/mob/living/carbon/carbon_target_mob = target_living
@@ -337,8 +350,7 @@
for (var/bp in carbon_target_mob.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands
var/obj/item/bodypart/bodypart = bp
var/destination = get_edge_target_turf(turf_underneath, pick(GLOB.alldirs))
- if (!(bodypart.bodypart_flags & BODYPART_UNREMOVABLE))
- bodypart.dismember() //Using the power of flextape i've sawed this man's bodypart in half!
+ if (bodypart.dismember()) //Using the power of flextape i've sawed this man's bodypart in half!
bodypart.throw_at(destination, 2, 3)
sleep(0.1 SECONDS)
@@ -369,6 +381,7 @@
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 (ispath(style, /datum/pod_style/seethrough))
+ transform = matrix()
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
@@ -420,7 +433,8 @@
if (!holder)
return
take_contents(holder)
- playsound(holder, close_sound, soundVolume*0.75, TRUE, -3)
+ if (close_sound)
+ playsound(holder, close_sound, soundVolume*0.75, TRUE, -3)
holder.setClosed()
addtimer(CALLBACK(src, PROC_REF(preReturn), holder), delays[POD_LEAVING] * 0.2) //Start to leave a bit after closing for cinematic effect
@@ -663,7 +677,11 @@
stack_trace("Pod landingzone effect created with no pod")
return INITIALIZE_HINT_QDEL
transform = matrix() * 1.5
- animate(src, transform = matrix()*0.01, time = pod.delays[POD_TRANSIT]+pod.delays[POD_FALLING])
+ var/arrival_time = pod.delays[POD_TRANSIT] + pod.delays[POD_FALLING]
+ if (arrival_time > 0)
+ animate(src, transform = matrix()*0.01, time = arrival_time)
+ else
+ alpha = 0
/obj/effect/pod_landingzone //This is the object that forceMoves the supplypod to its location
name = "Landing Zone Indicator"
@@ -690,13 +708,17 @@
if (!pod.effectStealth)
helper = new (drop_location(), pod)
alpha = 255
- animate(src, transform = matrix().Turn(90), time = pod.delays[POD_TRANSIT]+pod.delays[POD_FALLING])
+ var/arrival_time = pod.delays[POD_TRANSIT] + pod.delays[POD_FALLING]
+ if (arrival_time > 0)
+ animate(src, transform = matrix().Turn(90), time = arrival_time)
+ else
+ alpha = 0
if (single_order)
if (istype(single_order, /datum/supply_order))
var/datum/supply_order/SO = single_order
if (SO.pack.crate_type)
SO.generate(pod)
- else if (SO.pack.goody) //Goody orders lack a crate_type and need special handling
+ else if (SO.pack.order_flags & ORDER_GOODY) //Goody orders lack a crate_type and need special handling
SO.generateCombo(pod, SO.orderer, SO.pack.contains, SO.pack.cost)
else if (istype(single_order, /atom/movable))
var/atom/movable/O = single_order
@@ -706,7 +728,7 @@
if(pod.effectStun) //If effectStun is true, stun any mobs caught on this pod_landingzone until the pod gets a chance to hit them
for (var/mob/living/target_living in get_turf(src))
target_living.Stun(pod.delays[POD_TRANSIT]+10, ignore_canstun = TRUE)//you ain't goin nowhere, kid.
- if (pod.delays[POD_TRANSIT] + pod.delays[POD_FALLING] < pod.fallingSoundLength)
+ if (arrival_time < pod.fallingSoundLength)
pod.fallingSoundLength = 3 //The default falling sound is a little long, so if the landing time is shorter than the default falling sound, use a special, shorter default falling sound
pod.fallingSound = 'sound/items/weapons/mortar_whistle.ogg'
var/soundStartTime = pod.delays[POD_TRANSIT] - pod.fallingSoundLength + pod.delays[POD_FALLING]
diff --git a/code/modules/cargo/universal_scanner.dm b/code/modules/cargo/universal_scanner.dm
index 01d9f4e7f55..9668c4c0bea 100644
--- a/code/modules/cargo/universal_scanner.dm
+++ b/code/modules/cargo/universal_scanner.dm
@@ -120,7 +120,7 @@
if(!chosen_price || QDELETED(user) || QDELETED(src) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH) || loc != user)
return
new_custom_price = chosen_price
- to_chat(user, span_notice("[src] will now give things a [new_custom_price] cr tag."))
+ to_chat(user, span_notice("[src] will now give things a [new_custom_price] [MONEY_SYMBOL] tag."))
/obj/item/universal_scanner/item_ctrl_click(mob/user)
. = CLICK_ACTION_BLOCKING
@@ -149,7 +149,7 @@
. += span_notice("Ctrl-click to clear the registered account.")
if(scanning_mode == SCAN_PRICE_TAG)
- . += span_notice("The current custom price is set to [new_custom_price] cr. Right-click to change.")
+ . += span_notice("The current custom price is set to [new_custom_price] [MONEY_SYMBOL]. Right-click to change.")
/obj/item/universal_scanner/add_context(atom/source, list/context, obj/item/held_item, mob/user)
switch(scanning_mode)
@@ -178,7 +178,7 @@
if(length(target.contents))
message = "Scanned [target] and its contents"
if(price)
- message += ", total value: [price] credits"
+ message += ", total value: [price] [MONEY_NAME]"
else
message += ", no export values"
warning = TRUE
@@ -190,7 +190,7 @@
message += ", unable to determine value."
warning = TRUE
else if(price)
- message += ", value: [price] credits."
+ message += ", value: [price] [MONEY_NAME]."
else
message += ", no export value."
warning = TRUE
@@ -231,7 +231,7 @@
cube.AddComponent(/datum/component/pricetag, scanner_account, cube.handler_tip, FALSE)
cube.bounty_handler_account = scanner_account
- cube.bounty_handler_account.bank_card_talk("Bank account for [price ? "[price * cube.handler_tip] credit " : ""]handling tip successfully registered.")
+ cube.bounty_handler_account.bank_card_talk("Bank account for [price ? "[price * cube.handler_tip] [MONEY_NAME_SINGULAR] " : ""]handling tip successfully registered.")
if(cube.bounty_holder_account != cube.bounty_handler_account) //No need to send a tracking update to the person scanning it
cube.bounty_holder_account.bank_card_talk("[cube] was scanned in \the [get_area(cube)] by [scan_human] ([scan_human.job]).")
@@ -246,7 +246,7 @@
if(isitem(target))
var/obj/item/selected_target = target
selected_target.custom_price = new_custom_price
- to_chat(user, span_notice("You set the price of [selected_target] to [new_custom_price] cr."))
+ to_chat(user, span_notice("You set the price of [selected_target] to [new_custom_price] [MONEY_SYMBOL]."))
/**
* check_menu: Checks if we are allowed to interact with a radial menu
diff --git a/code/modules/client/client_colour.dm b/code/modules/client/client_colour.dm
index 524f24eb149..bb4ad106f73 100644
--- a/code/modules/client/client_colour.dm
+++ b/code/modules/client/client_colour.dm
@@ -170,6 +170,14 @@
// Color types
+///we want it to be less harsh for players to take blindness quirk, this adds enough color to not cause too much eye strain
+/datum/client_colour/blindness
+ priority = CLIENT_COLOR_HELMET_PRIORITY
+ split_filters = TRUE
+ color = list(/*R*/ 0.51,0.3,0.3,0, /*G*/ 0.29,0.51,0.29,0, /*B*/ 0.3,0.3,0.61,0, /*A*/ 0,0,0,1, /*C*/ 0,0,0,0) // dim and less saturated
+ fade_in = 2 SECONDS
+ fade_out = 2 SECONDS
+
///A client color that makes the screen look a bit more grungy, halloweenesque even.
/datum/client_colour/halloween_helmet
priority = CLIENT_COLOR_HELMET_PRIORITY
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 77946f4faea..a50fe5132cc 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -331,30 +331,47 @@ GLOBAL_LIST_INIT(unrecommended_builds, list(
if(!joined_player_preferences)
continue //this shouldn't happen.
- var/client/C = GLOB.directory[joined_player_ckey]
- var/in_round = ""
- if (joined_players[joined_player_ckey])
- in_round = " who has played in the current round"
- var/message_type = "Notice"
+ var/client/potential_match = GLOB.directory[joined_player_ckey]
+
+ var/matched_ip = null
+ var/matched_cid = null
+ var/same_round = FALSE
- var/matches
if(joined_player_preferences.last_ip == address)
- matches += "IP ([address])"
+ matched_ip = "IP [address]"
+
if(joined_player_preferences.last_id == computer_id)
- if(matches)
- matches = "BOTH [matches] and "
- alert_admin_multikey = TRUE
- message_type = "MULTIKEY"
- matches += "Computer ID ([computer_id])"
+ matched_cid = "Computer ID [computer_id]"
alert_mob_dupe_login = TRUE
- if(matches)
- if(C)
- message_admins(span_danger("[message_type]: Connecting player [key_name_admin(src)] has the same [matches] as [key_name_admin(C)][in_round]."))
- log_admin_private("[message_type]: Connecting player [key_name(src)] has the same [matches] as [key_name(C)][in_round].")
- else
- message_admins(span_danger("[message_type]: Connecting player [key_name_admin(src)] has the same [matches] as [joined_player_ckey](no longer logged in)[in_round]. "))
- log_admin_private("[message_type]: Connecting player [key_name(src)] has the same [matches] as [joined_player_ckey](no longer logged in)[in_round].")
+ if(isnull(matched_ip) && isnull(matched_cid))
+ continue
+
+ if (joined_players[joined_player_ckey])
+ same_round = TRUE
+
+ var/double_match = !isnull(matched_ip) && !isnull(matched_cid)
+
+ if(double_match && same_round)
+ alert_admin_multikey = TRUE
+
+ var/list/concatables = list()
+ concatables += span_danger(span_bold("[double_match ? "MULTIKEY" : "Notice"]:"))
+ concatables += "Connecting player [key_name_admin(src)] has the same"
+ if(double_match)
+ concatables += "!BOTH! [matched_ip] and [matched_cid]"
+ else
+ concatables += (!isnull(matched_ip) ? matched_ip : matched_cid)
+ concatables += "as [isnull(potential_match) ? "[joined_player_ckey] (no longer logged in)" : "[key_name_admin(potential_match)]"]"
+ if(same_round)
+ concatables += span_bold("in the current round")
+
+ concatables += ""
+
+ var/sendable_string = jointext(concatables, " ")
+
+ message_admins(sendable_string)
+ log_admin_private(strip_html_full(sendable_string, MAX_MESSAGE_LEN))
. = ..() //calls mob.Login()
diff --git a/code/modules/client/preferences/accessibility.dm b/code/modules/client/preferences/accessibility.dm
index e7486719978..6af31083f24 100644
--- a/code/modules/client/preferences/accessibility.dm
+++ b/code/modules/client/preferences/accessibility.dm
@@ -18,3 +18,28 @@
default_value = FALSE
savefile_key = "remove_double_click"
savefile_identifier = PREFERENCE_PLAYER
+
+/datum/preference/numeric/min_recoil_multiplier
+ category = PREFERENCE_CATEGORY_GAME_PREFERENCES
+ maximum = 200
+ minimum = 0
+ savefile_key = "min_recoil_multiplier"
+ savefile_identifier = PREFERENCE_PLAYER
+
+/datum/preference/numeric/min_recoil_multiplier/create_default_value()
+ return 100
+
+/// When toggled, enables staircase indicators
+/datum/preference/toggle/stair_indicator
+ category = PREFERENCE_CATEGORY_GAME_PREFERENCES
+ default_value = TRUE
+ savefile_key = "stair_indicator"
+ savefile_identifier = PREFERENCE_PLAYER
+
+/datum/preference/toggle/stair_indicator/apply_to_client_updated(client/client, value)
+ if(value || !isliving(client.mob)) // only hide, showing is more trouble than it's worth
+ return
+
+ var/datum/weakref/climber_ref = WEAKREF(client.mob)
+ for(var/obj/structure/stairs/stair as anything in GLOB.stairs)
+ stair.clear_climber_image(climber_ref)
diff --git a/code/modules/client/preferences/species_features/ethereal.dm b/code/modules/client/preferences/species_features/ethereal.dm
index fc93cf4008e..03fcbd4287b 100644
--- a/code/modules/client/preferences/species_features/ethereal.dm
+++ b/code/modules/client/preferences/species_features/ethereal.dm
@@ -20,8 +20,8 @@
ethereal_base.blend_icon(uni_icon('icons/mob/human/species/ethereal/bodyparts.dmi', "ethereal_l_arm"), ICON_OVERLAY)
ethereal_base.blend_icon(uni_icon('icons/mob/human/species/ethereal/bodyparts.dmi', "ethereal_r_arm"), ICON_OVERLAY)
- var/datum/universal_icon/eyes = uni_icon('icons/mob/human/human_face.dmi', "eyes_l")
- eyes.blend_icon(uni_icon('icons/mob/human/human_face.dmi', "eyes_r"), ICON_OVERLAY)
+ var/datum/universal_icon/eyes = uni_icon('icons/mob/human/human_eyes.dmi', "eyes_l")
+ eyes.blend_icon(uni_icon('icons/mob/human/human_eyes.dmi', "eyes_r"), ICON_OVERLAY)
eyes.blend_color(COLOR_BLACK, ICON_MULTIPLY)
ethereal_base.blend_icon(eyes, ICON_OVERLAY)
diff --git a/code/modules/client/preferences/species_features/lizard.dm b/code/modules/client/preferences/species_features/lizard.dm
index 2da264229d1..fde6210c7e6 100644
--- a/code/modules/client/preferences/species_features/lizard.dm
+++ b/code/modules/client/preferences/species_features/lizard.dm
@@ -5,7 +5,7 @@
if (isnull(lizard))
lizard = uni_icon('icons/mob/human/species/lizard/bodyparts.dmi', "lizard_head", EAST)
- var/datum/universal_icon/eyes = uni_icon('icons/mob/human/human_face.dmi', "eyes_l", EAST)
+ var/datum/universal_icon/eyes = uni_icon(/obj/item/organ/eyes/lizard::eye_icon, "[/obj/item/organ/eyes/lizard::eye_icon_state]_l", EAST)
eyes.blend_color(COLOR_GRAY, ICON_MULTIPLY)
lizard.blend_icon(eyes, ICON_OVERLAY)
diff --git a/code/modules/client/preferences/species_features/moth.dm b/code/modules/client/preferences/species_features/moth.dm
index 5ebd4475dfa..ecb9c32a1c6 100644
--- a/code/modules/client/preferences/species_features/moth.dm
+++ b/code/modules/client/preferences/species_features/moth.dm
@@ -12,8 +12,8 @@
if (isnull(moth_head))
moth_head = uni_icon('icons/mob/human/species/moth/bodyparts.dmi', "moth_head")
- moth_head.blend_icon(uni_icon('icons/mob/human/human_face.dmi', "motheyes_l"), ICON_OVERLAY)
- moth_head.blend_icon(uni_icon('icons/mob/human/human_face.dmi', "motheyes_r"), ICON_OVERLAY)
+ moth_head.blend_icon(uni_icon(/obj/item/organ/eyes/moth::eye_icon, "[/obj/item/organ/eyes/moth::eye_icon_state]_l"), ICON_OVERLAY)
+ moth_head.blend_icon(uni_icon(/obj/item/organ/eyes/moth::eye_icon, "[/obj/item/organ/eyes/moth::eye_icon_state]_r"), ICON_OVERLAY)
var/datum/sprite_accessory/antennae = get_accessory_for_value(value)
@@ -47,8 +47,8 @@
for (var/obj/item/bodypart/body_part as anything in body_parts)
moth_body.blend_icon(uni_icon('icons/mob/human/species/moth/bodyparts.dmi', initial(body_part.icon_state)), ICON_OVERLAY)
- moth_body.blend_icon(uni_icon('icons/mob/human/human_face.dmi', "motheyes_l"), ICON_OVERLAY)
- moth_body.blend_icon(uni_icon('icons/mob/human/human_face.dmi', "motheyes_r"), ICON_OVERLAY)
+ moth_body.blend_icon(uni_icon(/obj/item/organ/eyes/moth::eye_icon, "[/obj/item/organ/eyes/moth::eye_icon_state]_l"), ICON_OVERLAY)
+ moth_body.blend_icon(uni_icon(/obj/item/organ/eyes/moth::eye_icon, "[/obj/item/organ/eyes/moth::eye_icon_state]_r"), ICON_OVERLAY)
var/datum/sprite_accessory/markings = get_accessory_for_value(value)
var/datum/universal_icon/icon_with_markings = moth_body.copy()
diff --git a/code/modules/client/preferences/tgui.dm b/code/modules/client/preferences/tgui.dm
index 4cf57be94da..a42e047be49 100644
--- a/code/modules/client/preferences/tgui.dm
+++ b/code/modules/client/preferences/tgui.dm
@@ -1,13 +1,3 @@
-/datum/preference/toggle/tgui_fancy
- category = PREFERENCE_CATEGORY_GAME_PREFERENCES
- savefile_key = "tgui_fancy"
- savefile_identifier = PREFERENCE_PLAYER
-
-/datum/preference/toggle/tgui_fancy/apply_to_client(client/client, value)
- for (var/datum/tgui/tgui as anything in client.mob?.tgui_open_uis)
- // Force it to reload either way
- tgui.update_static_data(client.mob)
-
// Determines if input boxes are in tgui or old fashioned
/datum/preference/toggle/tgui_input
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
diff --git a/code/modules/clothing/chameleon/_chameleon_action.dm b/code/modules/clothing/chameleon/_chameleon_action.dm
index 4befe80fbe8..88ca8e51fe8 100644
--- a/code/modules/clothing/chameleon/_chameleon_action.dm
+++ b/code/modules/clothing/chameleon/_chameleon_action.dm
@@ -93,7 +93,7 @@
chameleon_typecache |= typecacheof(type_to_add)
for(var/obj/item/item_type as anything in chameleon_typecache)
- if(chameleon_blacklist[item_type] || (item_type::item_flags & ABSTRACT) || !item_type::icon_state)
+ if(chameleon_blacklist[item_type] || (item_type::item_flags & ABSTRACT) || item_type == item_type::abstract_type || !item_type::icon_state)
continue
var/chameleon_item_name = "[item_type::name] ([item_type::post_init_icon_state || item_type::icon_state])"
chameleon_list[chameleon_item_name] = item_type
diff --git a/code/modules/clothing/chameleon/generic_chameleon_clothing.dm b/code/modules/clothing/chameleon/generic_chameleon_clothing.dm
index 052b1ef2c57..fcfc3b7e927 100644
--- a/code/modules/clothing/chameleon/generic_chameleon_clothing.dm
+++ b/code/modules/clothing/chameleon/generic_chameleon_clothing.dm
@@ -325,6 +325,7 @@ do { \
// Cham Stamp
/obj/item/stamp/chameleon
+ icon_state = "stamp-syndicate"
actions_types = list(/datum/action/item_action/chameleon/change/stamp)
action_slots = ALL
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index b08118221d9..1f2177a72e6 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -315,9 +315,6 @@
. += span_warning("[p_Theyre()] completely shredded and require[p_s()] mending before [p_they()] can be worn again!")
return
- if(TRAIT_FAST_CUFFING in clothing_traits)
- . += "[src] increase the speed that you handcuff others."
-
for(var/zone in damage_by_parts)
var/pct_damage_part = damage_by_parts[zone] / limb_integrity * 100
var/zone_name = parse_zone(zone)
@@ -359,7 +356,7 @@
.["pressure-proof"] = "Protects the wearer from extremely low or high pressure, such as vacuum of space."
if(flags_cover & PEPPERPROOF)
.["pepper-proof"] = "Protects the wearer from the effects of pepperspray."
- if (heat_protection || cold_protection)
+ if(heat_protection || cold_protection)
var/heat_desc
var/cold_desc
switch (max_heat_protection_temperature)
@@ -377,6 +374,12 @@
if (0 to 71)
cold_desc = "extremely low"
.["thermally insulated"] = "Protects the wearer from [jointext(list(heat_desc, cold_desc) - null, " and ")] temperatures."
+ if((TRAIT_QUICK_CARRY in clothing_traits) || (TRAIT_QUICKER_CARRY in clothing_traits))
+ .["tactile"] = "Decreases the time it takes to pick up creatures by [(TRAIT_QUICKER_CARRY in clothing_traits) ? "2 seconds" : "1 second"]."
+ if(TRAIT_FASTMED in clothing_traits)
+ .["sterile"] = "Increases the speed at which reagents are administered to others by [round((1/NITRILE_GLOVES_MULTIPLIER-1)*100, 1)]%."
+ if(TRAIT_FAST_CUFFING in clothing_traits)
+ .["secure"] = "Increases the speed at which you apply restraints."
/obj/item/clothing/examine_descriptor(mob/user)
return "clothing"
@@ -405,7 +408,7 @@
continue
if(!added_durability_header)
readout += "DURABILITY (I-X)"
- added_damage_header = TRUE
+ added_durability_header = TRUE
readout += "[armor_to_protection_name(durability_key)] [armor_to_protection_class(rating)]"
if((flags_cover & HEADCOVERSMOUTH) || (flags_cover & PEPPERPROOF))
diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm
index a879aceaa6b..a8e54da65b3 100644
--- a/code/modules/clothing/glasses/_glasses.dm
+++ b/code/modules/clothing/glasses/_glasses.dm
@@ -189,6 +189,7 @@
pickup_sound = null
drop_sound = null
equip_sound = null
+ custom_materials = null
/obj/item/clothing/glasses/eyepatch/click_alt(mob/user)
. = ..()
@@ -409,6 +410,7 @@
icon_state = "sunhudsci"
desc = "A pair of tacky purple sunglasses that allow the wearer to recognize various chemical compounds with only a glance."
clothing_traits = list(TRAIT_REAGENT_SCANNER, TRAIT_RESEARCH_SCANNER)
+ custom_materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT * 0.55, /datum/material/iron = SMALL_MATERIAL_AMOUNT / 2)
/obj/item/clothing/glasses/sunglasses/chemical/add_glasses_slapcraft_component()
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/scienceglassesremoval)
@@ -531,6 +533,7 @@
flags_cover = GLASSESCOVERSEYES
tint = INFINITY // You WILL Be blind, no matter what
dog_fashion = /datum/dog_fashion/head
+ custom_materials = null
/obj/item/clothing/glasses/blindfold/Initialize(mapload)
. = ..()
@@ -542,6 +545,7 @@
icon_state = "trickblindfold"
inhand_icon_state = "blindfold"
flags_cover = GLASSESCOVERSEYES
+ custom_materials = null
/obj/item/clothing/glasses/blindfold/white
name = "blind personnel blindfold"
@@ -822,3 +826,4 @@
icon_state = "phantom_glasses"
inhand_icon_state = null
flags_cover = GLASSESCOVERSEYES
+ custom_materials = null
diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm
index 170d624a32f..3887807503c 100644
--- a/code/modules/clothing/glasses/hud.dm
+++ b/code/modules/clothing/glasses/hud.dm
@@ -100,6 +100,7 @@
flags_cover = GLASSESCOVERSEYES
tint = 1
glass_colour_type = /datum/client_colour/glass_colour/blue
+ custom_materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT * 0.55, /datum/material/iron = SMALL_MATERIAL_AMOUNT / 2)
/obj/item/clothing/glasses/hud/health/sunglasses/Initialize(mapload)
. = ..()
@@ -141,6 +142,7 @@
flash_protect = FLASH_PROTECTION_FLASH
flags_cover = GLASSESCOVERSEYES
tint = 1
+ custom_materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT * 0.55, /datum/material/iron = SMALL_MATERIAL_AMOUNT / 2)
/obj/item/clothing/glasses/hud/diagnostic/sunglasses/Initialize(mapload)
. = ..()
@@ -184,6 +186,7 @@
flags_cover = GLASSESCOVERSEYES
tint = 1
glass_colour_type = /datum/client_colour/glass_colour/darkred
+ custom_materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT * 0.55, /datum/material/iron = SMALL_MATERIAL_AMOUNT / 2)
/obj/item/clothing/glasses/hud/security/sunglasses/Initialize(mapload)
. = ..()
diff --git a/code/modules/clothing/gloves/bone.dm b/code/modules/clothing/gloves/bone.dm
index a29d4cfd223..f429d938241 100644
--- a/code/modules/clothing/gloves/bone.dm
+++ b/code/modules/clothing/gloves/bone.dm
@@ -11,6 +11,7 @@
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF | LAVA_PROOF
armor_type = /datum/armor/gloves_bracer
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 2)
/obj/item/clothing/gloves/bracer/Initialize(mapload)
. = ..()
diff --git a/code/modules/clothing/gloves/punch_mitts.dm b/code/modules/clothing/gloves/punch_mitts.dm
index 28ffb67d59d..2d1a345dd70 100644
--- a/code/modules/clothing/gloves/punch_mitts.dm
+++ b/code/modules/clothing/gloves/punch_mitts.dm
@@ -1,7 +1,7 @@
/obj/item/clothing/gloves/fingerless/punch_mitts
name = "punching mitts"
desc = "Fingerless gloves with nasty spikes attached. Allows the wearer to utilize the ill-reputed fighting technique known as Hunter Boxing. The style \
- allows the user to rapidly punch wildlife and rock into smithereens. Great workout. Extremely ill-advised for ensuring your own personal survival."
+ allows the user to rapidly punch wildlife into smithereens. Great workout. Extremely ill-advised for ensuring your own personal survival."
icon_state = "punch_mitts"
body_parts_covered = HANDS|ARMS
resistance_flags = LAVA_PROOF | FIRE_PROOF
diff --git a/code/modules/clothing/gloves/special.dm b/code/modules/clothing/gloves/special.dm
index 4207b9eafda..875fdcfda52 100644
--- a/code/modules/clothing/gloves/special.dm
+++ b/code/modules/clothing/gloves/special.dm
@@ -68,6 +68,7 @@
icon_state = "radio_g"
inhand_icon_state = null
clothing_traits = list(TRAIT_CAN_SIGN_ON_COMMS)
+ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.9, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 0.4)
/obj/item/clothing/gloves/race
name = "race gloves"
diff --git a/code/modules/clothing/head/cone.dm b/code/modules/clothing/head/cone.dm
index a77d40b541f..844558b5515 100644
--- a/code/modules/clothing/head/cone.dm
+++ b/code/modules/clothing/head/cone.dm
@@ -16,6 +16,7 @@
pickup_sound = 'sound/items/handling/materials/plastic_pick_up.ogg'
drop_sound = 'sound/items/handling/materials/plastic_drop.ogg'
resistance_flags = NONE
+ custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 2)
/obj/item/clothing/head/cone/Initialize(mapload)
. = ..()
diff --git a/code/modules/clothing/head/costume.dm b/code/modules/clothing/head/costume.dm
index ff386aba546..dca79cd254b 100644
--- a/code/modules/clothing/head/costume.dm
+++ b/code/modules/clothing/head/costume.dm
@@ -124,6 +124,7 @@
clothing_flags = SNUG_FIT
flags_cover = HEADCOVERSEYES
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
+ custom_materials = list(/datum/material/cardboard = SHEET_MATERIAL_AMOUNT)
dog_fashion = /datum/dog_fashion/head/cardborg
@@ -142,6 +143,7 @@
clothing_flags = SNUG_FIT
flags_inv = HIDEEARS|HIDEHAIR
armor_type = /datum/armor/costume_bronze
+ custom_materials = list(/datum/material/bronze = SHEET_MATERIAL_AMOUNT)
/obj/item/clothing/head/costume/fancy
name = "fancy hat"
@@ -173,6 +175,7 @@
clothing_flags = SNUG_FIT
flags_cover = HEADCOVERSEYES|HEADCOVERSMOUTH
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 7)
/datum/armor/costume_bronze
melee = 5
diff --git a/code/modules/clothing/head/crown.dm b/code/modules/clothing/head/crown.dm
index b441453a1c3..5cb914875f1 100644
--- a/code/modules/clothing/head/crown.dm
+++ b/code/modules/clothing/head/crown.dm
@@ -4,6 +4,7 @@
icon_state = "crown"
armor_type = /datum/armor/costume_crown
resistance_flags = FIRE_PROOF
+ custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT * 5)
/datum/armor/costume_crown
melee = 15
diff --git a/code/modules/clothing/head/garlands.dm b/code/modules/clothing/head/garlands.dm
index 374bd2fdafd..3bf2506c6e8 100644
--- a/code/modules/clothing/head/garlands.dm
+++ b/code/modules/clothing/head/garlands.dm
@@ -18,6 +18,7 @@
desc = "A flower crown made out of the flowers of the rainbow bunch plant."
icon_state = "rainbow_bunch_crown_1"
base_icon_state = "rainbow_bunch_crown"
+ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.3, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 0.3) //tied together with cable coil
/obj/item/clothing/head/costume/garland/rainbowbunch/Initialize(mapload)
. = ..()
@@ -38,16 +39,19 @@
desc = "A bright flower crown made out sunflowers that is sure to brighten up anyone's day!"
icon_state = "sunflower_crown"
worn_icon_state = "sunflower_crown"
+ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.3, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 0.3) //tied together with cable coil
/obj/item/clothing/head/costume/garland/poppy
name = "poppy crown"
desc = "A flower crown made out of a string of bright red poppies."
icon_state = "poppy_crown"
worn_icon_state = "poppy_crown"
+ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.3, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 0.3) //tied together with cable coil
/obj/item/clothing/head/costume/garland/lily
name = "lily crown"
desc = "A leafy flower crown with a cluster of large white lilies at the front."
icon_state = "lily_crown"
worn_icon_state = "lily_crown"
+ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.3, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 0.3) //tied together with cable coil
diff --git a/code/modules/clothing/head/hat.dm b/code/modules/clothing/head/hat.dm
index 2a2caf88cde..7a6b8fb9549 100644
--- a/code/modules/clothing/head/hat.dm
+++ b/code/modules/clothing/head/hat.dm
@@ -392,3 +392,4 @@
icon_state = "paper"
worn_icon_state = "paper"
dog_fashion = /datum/dog_fashion/head
+ custom_materials = list(/datum/material/paper = HALF_SHEET_MATERIAL_AMOUNT / 2)
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index a9954c1e818..d879b144c16 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -61,10 +61,11 @@
to_chat(user, span_notice("You add [attached_signaler] to [src]."))
qdel(attached_signaler)
- var/obj/item/bot_assembly/secbot/secbot_frame = new(loc)
- user.put_in_hands(secbot_frame)
-
+ var/obj/item/bot_assembly/secbot/secbot_frame = new(drop_location())
+ var/held_index = user.is_holding(src)
qdel(src)
+ if (held_index)
+ user.put_in_hand(secbot_frame, held_index)
return TRUE
return ..()
diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm
index 1a313c5c6c0..ff8c725218f 100644
--- a/code/modules/clothing/head/jobs.dm
+++ b/code/modules/clothing/head/jobs.dm
@@ -54,7 +54,7 @@
return locate(/mob/living/basic) in mousey_holder.contents
/// Relays emotes emoted by your boss to the hat wearer for full immersion
-/obj/item/clothing/head/utility/chefhat/proc/on_mouse_emote(mob/living/source, key, emote_message, type_override)
+/obj/item/clothing/head/utility/chefhat/proc/on_mouse_emote(mob/living/source, key, emote_message, type_override, intentional, datum/emote/emote)
SIGNAL_HANDLER
var/mob/living/carbon/wearer = loc
if(!wearer || INCAPACITATED_IGNORING(wearer, INCAPABLE_RESTRAINTS))
@@ -444,6 +444,7 @@
worn_icon = 'icons/mob/large-worn-icons/64x64/head.dmi'
worn_x_dimension = 64
worn_y_dimension = 64
+ custom_materials = list(/datum/material/alloy/plasteel = SHEET_MATERIAL_AMOUNT * 2, /datum/material/gold = SHEET_MATERIAL_AMOUNT * 2)
/obj/item/clothing/head/hats/hos/beret
name = "head of security's beret"
diff --git a/code/modules/clothing/head/papersack.dm b/code/modules/clothing/head/papersack.dm
index 984b73d6bcc..1c1aa342792 100644
--- a/code/modules/clothing/head/papersack.dm
+++ b/code/modules/clothing/head/papersack.dm
@@ -3,6 +3,7 @@
desc = "A paper sack with crude holes cut out for eyes. Useful for hiding one's identity or ugliness."
icon_state = "papersack"
flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS|HIDESNOUT
+ custom_materials = list(/datum/material/paper = SHEET_MATERIAL_AMOUNT * 1.25)
/obj/item/clothing/head/costume/papersack/smiley
name = "paper sack hat"
diff --git a/code/modules/clothing/masks/_masks.dm b/code/modules/clothing/masks/_masks.dm
index 8682dc4a927..672b2eeef1c 100644
--- a/code/modules/clothing/masks/_masks.dm
+++ b/code/modules/clothing/masks/_masks.dm
@@ -9,7 +9,6 @@
strip_delay = 4 SECONDS
equip_delay_other = 4 SECONDS
visor_vars_to_toggle = NONE
- unique_reskin_changes_base_icon_state = TRUE
var/adjusted_flags = null
///Did we install a filtering cloth?
diff --git a/code/modules/clothing/masks/costume.dm b/code/modules/clothing/masks/costume.dm
index 8ddfcf5520f..27d17af36e0 100644
--- a/code/modules/clothing/masks/costume.dm
+++ b/code/modules/clothing/masks/costume.dm
@@ -1,3 +1,28 @@
+// Mask skins
+/datum/atom_skin/joy_mask
+ abstract_type = /datum/atom_skin/joy_mask
+ change_base_icon_state = TRUE
+
+/datum/atom_skin/joy_mask/joy
+ preview_name = "Joy"
+ new_icon_state = "joy"
+
+/datum/atom_skin/joy_mask/flushed
+ preview_name = "Flushed"
+ new_icon_state = "flushed"
+
+/datum/atom_skin/joy_mask/pensive
+ preview_name = "Pensive"
+ new_icon_state = "pensive"
+
+/datum/atom_skin/joy_mask/angry
+ preview_name = "Angry"
+ new_icon_state = "angry"
+
+/datum/atom_skin/joy_mask/pleading
+ preview_name = "Pleading"
+ new_icon_state = "pleading"
+
/obj/item/clothing/mask/joy
name = "emotion mask"
desc = "Express your happiness or hide your sorrows with this cultured cutout."
@@ -5,19 +30,10 @@
base_icon_state = "joy"
clothing_flags = MASKINTERNALS
flags_inv = HIDESNOUT
- obj_flags = parent_type::obj_flags | INFINITE_RESKIN
- unique_reskin = list(
- "Joy" = "joy",
- "Flushed" = "flushed",
- "Pensive" = "pensive",
- "Angry" = "angry",
- "Pleading" = "pleading"
- )
-
-/obj/item/clothing/mask/joy/reskin_obj(mob/user)
+/obj/item/clothing/mask/joy/Initialize(mapload)
. = ..()
- user.update_worn_mask()
+ AddComponent(/datum/component/reskinable_item, /datum/atom_skin/joy_mask, infinite = TRUE)
/obj/item/clothing/mask/mummy
name = "mummy mask"
@@ -77,3 +93,4 @@
greyscale_config = /datum/greyscale_config/rebellion_mask
greyscale_config_worn = /datum/greyscale_config/rebellion_mask/worn
flags_1 = IS_PLAYER_COLORABLE_1
+ custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT)
diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm
index b164cf81345..560873b50d1 100644
--- a/code/modules/clothing/masks/gasmask.dm
+++ b/code/modules/clothing/masks/gasmask.dm
@@ -467,7 +467,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
desc = "A creepy wooden mask. Surprisingly expressive for a poorly carved bit of wood."
icon_state = "tiki_eyebrow"
inhand_icon_state = null
- custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 1.25)
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 2)
resistance_flags = FLAMMABLE
flags_cover = MASKCOVERSEYES
max_integrity = 100
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index 83d7e0d2258..89f1454795f 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -496,6 +496,7 @@
desc = "It's for pets."
icon_state = "petcollar"
var/tagname = null
+ var/human_wearable = FALSE
/datum/armor/large_scarf_syndie
fire = 50
@@ -503,11 +504,14 @@
/*
/obj/item/clothing/neck/petcollar/mob_can_equip(mob/M, slot, disable_warning = FALSE, bypass_equip_delay_self = FALSE, ignore_equipped = FALSE, indirect_action = FALSE)
- if(!ismonkey(M))
+ if(!ismonkey(M) && !human_wearable)
return FALSE
return ..()
*/ //BUBBER EDIT END: LET ME WEAR MY PET COLLARS
+/obj/item/clothing/neck/petcollar/wearable
+ human_wearable = TRUE
+
/obj/item/clothing/neck/petcollar/attack_self(mob/user)
tagname = sanitize_name(tgui_input_text(user, "Would you like to change the name on the tag?", "Pet Naming", "Spot", MAX_NAME_LEN))
if (!tagname || !length(tagname))
@@ -546,7 +550,8 @@
if(price)
var/true_price = round(price*profit_scaling)
- to_chat(user, span_notice("[selling ? "Sold" : "Getting the price of"] [interacting_with], value: [true_price] credits[interacting_with.contents.len ? " (exportable contents included)" : ""].[profit_scaling < 1 && selling ? "[round(price-true_price)] credit\s taken as processing fee\s." : ""]"))
+ var/fee_display = round(price-true_price)
+ to_chat(user, span_notice("[selling ? "Sold" : "Getting the price of"] [interacting_with], value: [true_price] [MONEY_NAME][interacting_with.contents.len ? " (exportable contents included)" : ""].[profit_scaling < 1 && selling ? "[fee_display] [MONEY_NAME_AUTOPURAL(fee_display)] taken as processing fee\s." : ""]"))
if(selling)
new /obj/item/holochip(get_turf(user), true_price)
else
@@ -575,6 +580,7 @@
worn_y_offset = 10
alternate_worn_layer = ABOVE_BODY_FRONT_HEAD_LAYER
resistance_flags = FIRE_PROOF | LAVA_PROOF
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 2, /datum/material/diamond = SHEET_MATERIAL_AMOUNT * 2)
/obj/item/clothing/neck/wreath/worn_overlays(mutable_appearance/standing, isinhands, icon_file)
. = ..()
@@ -586,3 +592,4 @@
desc = "An elaborate crown made from the twisted flesh and sinew of an icewing watcher. \
Wearing it sends shivers down your spine just from being near it."
icon_state = "icewing_wreath"
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT, /datum/material/diamond = SHEET_MATERIAL_AMOUNT * 2)
diff --git a/code/modules/clothing/outfits/ert.dm b/code/modules/clothing/outfits/ert.dm
index 14020b5c34b..69dcb4ef968 100644
--- a/code/modules/clothing/outfits/ert.dm
+++ b/code/modules/clothing/outfits/ert.dm
@@ -188,7 +188,7 @@
name = "Inquisition Commander"
back = /obj/item/mod/control/pre_equipped/responsory/inquisitory/commander
- r_hand = /obj/item/nullrod/vibro/talking/chainsword
+ r_hand = /obj/item/nullrod/claymore/talking/chainsword
backpack_contents = null
/datum/outfit/centcom/ert/security/inquisitor
diff --git a/code/modules/clothing/outfits/standard.dm b/code/modules/clothing/outfits/standard.dm
index c5497f783a5..9dcb90b0e3d 100644
--- a/code/modules/clothing/outfits/standard.dm
+++ b/code/modules/clothing/outfits/standard.dm
@@ -117,10 +117,10 @@
name = "Heretic Hallucination"
uniform = /obj/item/clothing/under/color/grey
- suit = /obj/item/clothing/suit/hooded/cultrobes/eldritch
+ suit = /obj/item/clothing/suit/hooded/cultrobes/eldritch/rust
neck = /obj/item/clothing/neck/heretic_focus
r_hand = /obj/item/melee/touch_attack/mansus_fist
- head = /obj/item/clothing/head/hooded/cult_hoodie/eldritch
+ head = /obj/item/clothing/head/hooded/cult_hoodie/eldritch/rust
shoes = /obj/item/clothing/shoes/sneakers/black
/datum/outfit/rev_cutout
diff --git a/code/modules/clothing/shoes/costume.dm b/code/modules/clothing/shoes/costume.dm
index 6b44d607077..bc361e3685b 100644
--- a/code/modules/clothing/shoes/costume.dm
+++ b/code/modules/clothing/shoes/costume.dm
@@ -7,6 +7,7 @@
equip_delay_other = 10 SECONDS
armor_type = /datum/armor/shoes_roman
fastening_type = SHOES_STRAPS
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT)
/obj/item/clothing/shoes/griffin
name = "griffon boots"
@@ -41,6 +42,7 @@
icon = 'icons/obj/clothing/shoes.dmi'
icon_state = "clockwork_treads"
fastening_type = SHOES_SLIPON
+ custom_materials = list(/datum/material/bronze = SHEET_MATERIAL_AMOUNT)
/obj/item/clothing/shoes/bronze/Initialize(mapload)
. = ..()
diff --git a/code/modules/clothing/shoes/sandals.dm b/code/modules/clothing/shoes/sandals.dm
index 097bf62ecbf..e1f8b445a10 100644
--- a/code/modules/clothing/shoes/sandals.dm
+++ b/code/modules/clothing/shoes/sandals.dm
@@ -3,7 +3,7 @@
name = "sandals"
icon_state = "wizard"
inhand_icon_state = "wizshoe"
- custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 0.5)
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT)
resistance_flags = FLAMMABLE
strip_delay = 0.5 SECONDS
equip_delay_other = 5 SECONDS
diff --git a/code/modules/clothing/suits/costume.dm b/code/modules/clothing/suits/costume.dm
index de859e50044..ac3dc596405 100644
--- a/code/modules/clothing/suits/costume.dm
+++ b/code/modules/clothing/suits/costume.dm
@@ -47,6 +47,10 @@
equip_delay_other = 2 SECONDS
species_exception = null
+/obj/item/clothing/suit/costume/pirate/armored/Initialize(mapload)
+ . = ..()
+ allowed += GLOB.security_vest_allowed
+
/obj/item/clothing/suit/costume/pirate/captain
name = "pirate captain coat"
desc = "Yarr."
@@ -176,6 +180,7 @@
body_parts_covered = CHEST|GROIN|LEGS
flags_inv = HIDEJUMPSUIT
dog_fashion = /datum/dog_fashion/back
+ custom_materials = list(/datum/material/cardboard = SHEET_MATERIAL_AMOUNT * 3)
var/in_use = FALSE
/obj/item/clothing/suit/costume/cardborg/equipped(mob/living/user, slot)
@@ -568,6 +573,7 @@
/obj/item/toy/clockwork_watch,
)
armor_type = /datum/armor/costume_bronze
+ custom_materials = list(/datum/material/bronze = SHEET_MATERIAL_AMOUNT)
/obj/item/clothing/suit/hooded/mysticrobe
name = "mystic's robe"
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index 1dedb57bd0f..2a1b30d0017 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -345,19 +345,20 @@
allowed = list(
/obj/item/bonesetter,
/obj/item/cautery,
+ /obj/item/defibrillator/compact,
/obj/item/flashlight/pen,
/obj/item/healthanalyzer,
/obj/item/hemostat,
- /obj/item/reagent_containers/dropper,
/obj/item/reagent_containers/cup/beaker,
/obj/item/reagent_containers/cup/bottle,
/obj/item/reagent_containers/cup/tube,
+ /obj/item/reagent_containers/dropper,
/obj/item/reagent_containers/hypospray,
/obj/item/reagent_containers/syringe,
/obj/item/retractor,
/obj/item/scalpel,
- /obj/item/surgical_drapes,
/obj/item/storage/pill_bottle,
+ /obj/item/surgical_drapes,
/obj/item/tank/internals/emergency_oxygen,
)
diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm
index 297aedc2ed8..4996abb69a1 100644
--- a/code/modules/clothing/suits/labcoat.dm
+++ b/code/modules/clothing/suits/labcoat.dm
@@ -10,25 +10,26 @@
allowed = list(
/obj/item/analyzer,
/obj/item/biopsy_tool,
+ /obj/item/defibrillator/compact,
/obj/item/dnainjector,
/obj/item/flashlight/pen,
+ /obj/item/gun/syringe,
/obj/item/healthanalyzer,
/obj/item/paper,
- /obj/item/reagent_containers/dropper,
+ /obj/item/reagent_containers/applicator,
/obj/item/reagent_containers/cup/beaker,
/obj/item/reagent_containers/cup/bottle,
/obj/item/reagent_containers/cup/tube,
+ /obj/item/reagent_containers/dropper,
/obj/item/reagent_containers/hypospray,
- /obj/item/reagent_containers/applicator,
/obj/item/reagent_containers/syringe,
- /obj/item/gun/syringe,
/obj/item/sensor_device,
/obj/item/soap,
/obj/item/stack/medical,
/obj/item/storage/pill_bottle,
/obj/item/tank/internals/emergency_oxygen,
/obj/item/tank/internals/plasmaman,
- )
+ )
armor_type = /datum/armor/toggle_labcoat
species_exception = list(/datum/species/golem)
diff --git a/code/modules/clothing/suits/wetfloor.dm b/code/modules/clothing/suits/wetfloor.dm
index 09368cbed41..9a623d88f8b 100644
--- a/code/modules/clothing/suits/wetfloor.dm
+++ b/code/modules/clothing/suits/wetfloor.dm
@@ -23,6 +23,7 @@
/obj/item/tank/internals/plasmaman,
/obj/item/gun/ballistic/rifle/boltaction/pipegun,
)
+ custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 2)
/datum/armor/suit_caution
melee = 5
diff --git a/code/modules/clothing/suits/wintercoats.dm b/code/modules/clothing/suits/wintercoats.dm
index dba73bcb12f..f0e9ded6b6a 100644
--- a/code/modules/clothing/suits/wintercoats.dm
+++ b/code/modules/clothing/suits/wintercoats.dm
@@ -298,15 +298,16 @@
icon_state = "coatmedical"
inhand_icon_state = "coatmedical"
allowed = list(
+ /obj/item/defibrillator/compact,
/obj/item/flashlight/pen,
/obj/item/gun/syringe,
/obj/item/healthanalyzer,
- /obj/item/reagent_containers/dropper,
+ /obj/item/reagent_containers/applicator,
/obj/item/reagent_containers/cup/beaker,
/obj/item/reagent_containers/cup/bottle,
/obj/item/reagent_containers/cup/tube,
+ /obj/item/reagent_containers/dropper,
/obj/item/reagent_containers/hypospray,
- /obj/item/reagent_containers/applicator,
/obj/item/reagent_containers/syringe,
/obj/item/sensor_device,
/obj/item/storage/pill_bottle,
diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm
index 81f6ae57109..5839c2a43e5 100644
--- a/code/modules/clothing/under/_under.dm
+++ b/code/modules/clothing/under/_under.dm
@@ -73,13 +73,6 @@
. = ..()
dump_attachments()
-/obj/item/clothing/under/setup_reskinning()
- if(!check_setup_reskinning())
- return
-
- // We already register context in Initialize.
- RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin))
-
/obj/item/clothing/under/add_context(atom/source, list/context, obj/item/held_item, mob/living/user)
. = ..()
diff --git a/code/modules/clothing/under/accessories/_accessories.dm b/code/modules/clothing/under/accessories/_accessories.dm
index 0e4e2f98190..993771e4888 100644
--- a/code/modules/clothing/under/accessories/_accessories.dm
+++ b/code/modules/clothing/under/accessories/_accessories.dm
@@ -31,13 +31,6 @@
. = ..()
register_context()
-/obj/item/clothing/accessory/setup_reskinning()
- if(!check_setup_reskinning())
- return
-
- // We already register context regardless in Initialize.
- RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin))
-
/**
* Can we be attached to the passed clothing article?
*/
diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm
index 9d7d87a0846..9f871c71015 100644
--- a/code/modules/clothing/under/accessories/badges.dm
+++ b/code/modules/clothing/under/accessories/badges.dm
@@ -177,28 +177,51 @@
name = "Pre-Approved Cyborg Candidate dogtag"
display = "This employee has been screened for negative mental traits to an acceptable level of accuracy, and is approved for the NT Cyborg program as an alternative to medical resuscitation."
+// Pride pin skins
+/datum/atom_skin/pride_pin
+ abstract_type = /datum/atom_skin/pride_pin
+
+/datum/atom_skin/pride_pin/gay
+ preview_name = "Rainbow Pride"
+ new_icon_state = "pride"
+
+/datum/atom_skin/pride_pin/bi
+ preview_name = "Bisexual Pride"
+ new_icon_state = "pride_bi"
+
+/datum/atom_skin/pride_pin/pan
+ preview_name = "Pansexual Pride"
+ new_icon_state = "pride_pan"
+
+/datum/atom_skin/pride_pin/ace
+ preview_name = "Asexual Pride"
+ new_icon_state = "pride_ace"
+
+/datum/atom_skin/pride_pin/enby
+ preview_name = "Non-binary Pride"
+ new_icon_state = "pride_enby"
+
+/datum/atom_skin/pride_pin/trans
+ preview_name = "Transgender Pride"
+ new_icon_state = "pride_trans"
+
+/datum/atom_skin/pride_pin/intersex
+ preview_name = "Intersex Pride"
+ new_icon_state = "pride_intersex"
+
+/datum/atom_skin/pride_pin/lesbian
+ preview_name = "Lesbian Pride"
+ new_icon_state = "pride_lesbian"
+
/obj/item/clothing/accessory/pride
name = "pride pin"
desc = "A Nanotrasen Diversity & Inclusion Center-sponsored holographic pin to show off your pride, reminding the crew of their unwavering commitment to equity, diversity, and inclusion!"
icon_state = "pride"
- obj_flags = UNIQUE_RENAME | INFINITE_RESKIN
- unique_reskin = list(
- "Rainbow Pride" = "pride",
- "Bisexual Pride" = "pride_bi",
- "Pansexual Pride" = "pride_pan",
- "Asexual Pride" = "pride_ace",
- "Non-binary Pride" = "pride_enby",
- "Transgender Pride" = "pride_trans",
- "Intersex Pride" = "pride_intersex",
- "Lesbian Pride" = "pride_lesbian",
- )
+ obj_flags = UNIQUE_RENAME
-/obj/item/clothing/accessory/pride/setup_reskinning()
- if(!check_setup_reskinning())
- return
-
- // We already register context regardless in Initialize.
- RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin))
+/obj/item/clothing/accessory/pride/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/reskinable_item, /datum/atom_skin/pride_pin, infinite = TRUE)
/obj/item/clothing/accessory/deaf_pin
name = "deaf personnel pin"
diff --git a/code/modules/clothing/under/accessories/tribal.dm b/code/modules/clothing/under/accessories/tribal.dm
index 552751bd8ea..c75b8940f8a 100644
--- a/code/modules/clothing/under/accessories/tribal.dm
+++ b/code/modules/clothing/under/accessories/tribal.dm
@@ -5,6 +5,7 @@
icon_state = "talisman"
attachment_slot = NONE
resistance_flags = FIRE_PROOF | LAVA_PROOF
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 2)
/obj/item/clothing/accessory/skullcodpiece
name = "skull codpiece"
@@ -12,6 +13,7 @@
icon_state = "skull"
attachment_slot = GROIN
resistance_flags = FIRE_PROOF | LAVA_PROOF
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 2)
/obj/item/clothing/accessory/skilt
name = "sinew skirt"
@@ -20,3 +22,4 @@
minimize_when_attached = FALSE
attachment_slot = GROIN
resistance_flags = FIRE_PROOF | LAVA_PROOF
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT)
diff --git a/code/modules/clothing/under/costume.dm b/code/modules/clothing/under/costume.dm
index c63d046f8f0..30dbc08ff10 100644
--- a/code/modules/clothing/under/costume.dm
+++ b/code/modules/clothing/under/costume.dm
@@ -275,6 +275,26 @@
can_adjust = FALSE
resistance_flags = NONE
+// Mech suit skins
+/datum/atom_skin/mech_suit
+ abstract_type = /datum/atom_skin/mech_suit
+
+/datum/atom_skin/mech_suit/red
+ preview_name = "Red"
+ new_icon_state = "red_mech_suit"
+
+/datum/atom_skin/mech_suit/white
+ preview_name = "White"
+ new_icon_state = "white_mech_suit"
+
+/datum/atom_skin/mech_suit/blue
+ preview_name = "Blue"
+ new_icon_state = "blue_mech_suit"
+
+/datum/atom_skin/mech_suit/black
+ preview_name = "Black"
+ new_icon_state = "black_mech_suit"
+
/obj/item/clothing/under/costume/mech_suit
name = "mech pilot's suit"
desc = "A mech pilot's suit. Might make your butt look big."
@@ -286,13 +306,9 @@
alternate_worn_layer = GLOVES_LAYER //covers hands but gloves can go over it. This is how these things work in my head.
can_adjust = FALSE
- unique_reskin = list(
- "Red" = "red_mech_suit",
- "White" = "white_mech_suit",
- "Blue" = "blue_mech_suit",
- "Black" = "black_mech_suit",
- )
-
+/obj/item/clothing/under/costume/mech_suit/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/reskinable_item, /datum/atom_skin/mech_suit)
/obj/item/clothing/under/costume/russian_officer
name = "\improper Russian officer's uniform"
diff --git a/code/modules/deathmatch/deathmatch_loadouts.dm b/code/modules/deathmatch/deathmatch_loadouts.dm
index a8b363b938b..60ea2877dee 100644
--- a/code/modules/deathmatch/deathmatch_loadouts.dm
+++ b/code/modules/deathmatch/deathmatch_loadouts.dm
@@ -351,10 +351,11 @@
l_hand = /obj/item/gun/ballistic/shotgun/hook
r_hand = /obj/item/gun/ballistic/shotgun/hook
uniform = /obj/item/clothing/under/costume/skeleton
- suit = /obj/item/clothing/suit/hooded/cultrobes/eldritch
+ suit = /obj/item/clothing/suit/chaplainsuit/armor/heretic
mask = /obj/item/clothing/mask/gas/cyborg
shoes = /obj/item/clothing/shoes/sandal
belt = /obj/item/melee/cleric_mace
+ head = /obj/item/clothing/head/helmet/chaplain/heretic
/datum/outfit/deathmatch_loadout/battler/cowboy
name = "Deathmatch: Cowboy"
@@ -873,7 +874,7 @@
head = /obj/item/clothing/head/soft/rainbow
glasses = null
ears = /obj/item/radio/headset
- neck = /obj/item/clothing/neck/petcollar
+ neck = /obj/item/clothing/neck/petcollar/wearable
//suit
uniform = /obj/item/clothing/under/color/rainbow
l_pocket = /obj/item/toy/cattoy
@@ -999,9 +1000,9 @@
display_name = "Heretic Warrior"
desc = "Prove the furious strength of the Mansus!"
- head = /obj/item/clothing/head/hooded/cult_hoodie/eldritch
+ head = /obj/item/clothing/head/hooded/cult_hoodie/eldritch/blade
neck = /obj/item/clothing/neck/heretic_focus
- suit = /obj/item/clothing/suit/hooded/cultrobes/eldritch
+ suit = /obj/item/clothing/suit/hooded/cultrobes/eldritch/blade
suit_store = /obj/item/melee/sickly_blade/dark
uniform = /obj/item/clothing/under/color/darkgreen
id_trim = null
diff --git a/code/modules/economy/account.dm b/code/modules/economy/account.dm
index 986def70031..a7bdc7bf93c 100644
--- a/code/modules/economy/account.dm
+++ b/code/modules/economy/account.dm
@@ -152,7 +152,7 @@
return 0
else
add_log_to_history(-amount, "Other: Debt Collection")
- log_econ("[amount_to_pay] credits were removed from [account_holder]'s bank account to pay a debt of [account_debt]")
+ log_econ("[amount_to_pay] [MONEY_NAME] were removed from [account_holder]'s bank account to pay a debt of [account_debt]")
account_debt -= amount_to_pay
SEND_SIGNAL(src, COMSIG_BANK_ACCOUNT_DEBT_PAID)
return amount_to_pay
@@ -180,7 +180,7 @@
adjust_money(amount, reason_to)
from.adjust_money(-amount, reason_from)
SSblackbox.record_feedback("amount", "credits_transferred", amount)
- log_econ("[amount] credits were transferred from [from.account_holder]'s account to [src.account_holder]")
+ log_econ("[amount] [MONEY_NAME] were transferred from [from.account_holder]'s account to [src.account_holder]")
return TRUE
return FALSE
@@ -212,7 +212,7 @@
adjust_money(money_to_transfer, "Nanotrasen: Shift Payment")
SSblackbox.record_feedback("amount", "free_income", money_to_transfer)
SSeconomy.station_target += money_to_transfer
- log_econ("[money_to_transfer] credits were given to [src.account_holder]'s account from income.")
+ log_econ("[money_to_transfer] [MONEY_NAME] were given to [src.account_holder]'s account from income.")
return TRUE
var/datum/bank_account/department_account = SSeconomy.get_dep_account(account_job.paycheck_department)
if(isnull(department_account))
@@ -221,7 +221,7 @@
if(!transfer_money(department_account, money_to_transfer))
bank_card_talk("ERROR: [event] aborted, departmental funds insufficient.")
return FALSE
- bank_card_talk("[event] processed, account now holds [account_balance] cr.")
+ bank_card_talk("[event] processed, account now holds [account_balance] [MONEY_SYMBOL].")
return TRUE
/**
@@ -318,8 +318,11 @@
/datum/bank_account/department/adjust_money(amount, reason)
. = ..()
+
+ SSblackbox.record_feedback("amount", "[department_id]_balance", account_balance, world.time) //Provides the cargo balance alongside a timestamp for comparison afterwards.
if(department_id != ACCOUNT_CAR)
return
+
// If we're under (or equal) 3 crates woth of money (600?) in the cargo department, we unlock the scrapheap, which gives us a buncha money. Useful in an emergency?
if(account_balance >= CARGO_CRATE_VALUE * 3)
return
diff --git a/code/modules/economy/holopay.dm b/code/modules/economy/holopay.dm
index 45eb59c8135..3043296b72a 100644
--- a/code/modules/economy/holopay.dm
+++ b/code/modules/economy/holopay.dm
@@ -27,7 +27,7 @@
/obj/structure/holopay/examine(mob/user)
. = ..()
if(force_fee)
- . += span_boldnotice("This holopay forces a payment of [force_fee] credit\s per swipe instead of a variable amount.")
+ . += span_boldnotice("This holopay forces a payment of [force_fee] [MONEY_NAME_AUTOPURAL(force_fee)] per swipe instead of a variable amount.")
/obj/structure/holopay/Initialize(mapload)
. = ..()
@@ -80,7 +80,7 @@
return ..()
/// Users can pay with an ID to skip the UI
if(isidcard(held_item))
- if(force_fee && tgui_alert(item_holder, "This holopay has a [force_fee] cr fee. Confirm?", "Holopay Fee", list("Pay", "Cancel")) != "Pay")
+ if(force_fee && tgui_alert(item_holder, "This holopay has a [force_fee] [MONEY_SYMBOL] fee. Confirm?", "Holopay Fee", list("Pay", "Cancel")) != "Pay")
return TRUE
process_payment(user)
return TRUE
@@ -90,7 +90,7 @@
var/obj/item/holochip/chip = held_item
if(!chip.credits)
balloon_alert(user, "holochip is empty")
- to_chat(user, span_warning("There doesn't seem to be any credits here."))
+ to_chat(user, span_warning("There doesn't seem to be any [MONEY_NAME] here."))
return FALSE
/// Charges force fee or uses pay what you want
var/cash_deposit = force_fee || tgui_input_number(user, "How much? (Max: [chip.credits])", "Patronage", max_value = chip.credits)
@@ -100,8 +100,8 @@
if(QDELETED(held_item) || QDELETED(user) || QDELETED(src) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH))
return FALSE
if(!chip.spend(cash_deposit, FALSE))
- balloon_alert(user, "insufficient credits")
- to_chat(user, span_warning("You don't have enough credits to pay with this chip."))
+ balloon_alert(user, "insufficient [MONEY_NAME]")
+ to_chat(user, span_warning("You don't have enough [MONEY_NAME] to pay with this chip."))
return FALSE
/// Success: Alert buyer
alert_buyer(user, cash_deposit)
@@ -270,7 +270,7 @@
if(!amount || QDELETED(user) || QDELETED(src) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH))
return FALSE
if(!payee.adjust_money(-amount, "Holopay: [capitalize(name)]"))
- balloon_alert(user, "insufficient credits")
+ balloon_alert(user, "insufficient [MONEY_NAME]")
to_chat(user, span_warning("You don't have the money to pay for this."))
return FALSE
/// Success: Alert the buyer
@@ -290,10 +290,10 @@
/// Pay the owner
linked_card.registered_account.adjust_money(amount, "Holopay: [name]")
/// Make alerts
- linked_card.registered_account.bank_card_talk("[payee] has deposited [amount] cr at your holographic pay stand.")
+ linked_card.registered_account.bank_card_talk("[payee] has deposited [amount] [MONEY_SYMBOL] at your holographic pay stand.")
say("Thank you for your patronage, [payee]!")
playsound(src, 'sound/effects/cashregister.ogg', 20, TRUE)
/// Log the event
- log_econ("[amount] credits were transferred from [payee]'s transaction to [linked_card.registered_account.account_holder]")
+ log_econ("[amount] [MONEY_NAME] were transferred from [payee]'s transaction to [linked_card.registered_account.account_holder]")
SSblackbox.record_feedback("amount", "credits_transferred", amount)
return TRUE
diff --git a/code/modules/escape_menu/home_page.dm b/code/modules/escape_menu/home_page.dm
index 0ab16e9ab37..02a050535b0 100644
--- a/code/modules/escape_menu/home_page.dm
+++ b/code/modules/escape_menu/home_page.dm
@@ -157,6 +157,20 @@
))
offset_order -= offset_order[1]
+ var/configurl = CONFIG_GET(string/configurl)
+ if(configurl)
+ resource_panels += page_holder.give_screen_object(new /atom/movable/screen/escape_menu/lobby_button/small/collapsible(
+ null,
+ /* hud_owner = */ null,
+ "Config",
+ "View the server configuration files",
+ /* button_screen_loc */ "BOTTOM:30,RIGHT:-20",
+ CALLBACK(client, TYPE_VERB_REF(/client, config)),
+ /* button_overlay = */ "config",
+ /* end_point */ offset_order[1],
+ ))
+ offset_order -= offset_order[1]
+
resource_panels += page_holder.give_screen_object(new /atom/movable/screen/escape_menu/lobby_button/small/collapsible(
null,
/* hud_owner = */ null,
diff --git a/code/modules/events/stray_cargo.dm b/code/modules/events/stray_cargo.dm
index 287f7547e17..8d589ea1c72 100644
--- a/code/modules/events/stray_cargo.dm
+++ b/code/modules/events/stray_cargo.dm
@@ -64,7 +64,7 @@
stray_spawnable_supply_packs = SSshuttle.supply_packs.Copy()
for(var/pack in stray_spawnable_supply_packs)
var/datum/supply_pack/pack_type = pack
- if(initial(pack_type.special))
+ if(initial(pack_type.order_flags) & ORDER_SPECIAL)
stray_spawnable_supply_packs -= pack
///Spawns a random supply pack, puts it in a pod, and spawns it on a random tile of the selected area
diff --git a/code/modules/experisci/handheld_scanner.dm b/code/modules/experisci/handheld_scanner.dm
index a89fcfd16d7..a91db73a017 100644
--- a/code/modules/experisci/handheld_scanner.dm
+++ b/code/modules/experisci/handheld_scanner.dm
@@ -49,19 +49,8 @@
return MANUAL_SUICIDE
/obj/item/experi_scanner/proc/make_meat_toilet(mob/living/carbon/user)
- ///The suicide victim's brain that will be placed inside the toilet's cistern
- var/obj/item/organ/brain/toilet_brain = user.get_organ_slot(ORGAN_SLOT_BRAIN)
///The toilet we're about to unleash unto this cursed plane of existence
- var/obj/structure/toilet/greyscale/result_toilet = new (drop_location())
-
- result_toilet.set_custom_materials(list(GET_MATERIAL_REF(/datum/material/meat/mob_meat, user) = SHEET_MATERIAL_AMOUNT))
- result_toilet.desc = "A horrendous mass of fused flesh resembling a standard-issue HT-451 model toilet. How it manages to function as one is beyond you. \
- This one seems to be made out of the flesh of a devoted employee of the RnD department."
- result_toilet.buildstacktype = /obj/effect/decal/remains/human //this also prevents the toilet from dropping meat sheets. if you want to cheese the meat exepriments, sacrifice more people
+ new /obj/structure/toilet/greyscale/flesh (drop_location(), user) //the toilet's Initialize proc will handle the rest from here.
icon_state = "experiscanner"
remove_atom_colour(ADMIN_COLOUR_PRIORITY, COLOR_RED)
-
- user.gib(DROP_BRAIN) //we delete everything but the brain, as it's going to be moved to the cistern
- toilet_brain.forceMove(result_toilet)
- result_toilet.w_items += toilet_brain.w_class
diff --git a/code/modules/explorer_drone/loot.dm b/code/modules/explorer_drone/loot.dm
index 7f21b788f3d..6c7bf6ea308 100644
--- a/code/modules/explorer_drone/loot.dm
+++ b/code/modules/explorer_drone/loot.dm
@@ -45,7 +45,7 @@ GLOBAL_LIST_INIT(adventure_loot_generator_index,generate_generator_index())
var/list/still_locked_packs = list()
for(var/pack_type in unlockable_packs)
var/datum/supply_pack/pack_singleton = SSshuttle.supply_packs[pack_type]
- if(!pack_singleton.special_enabled)
+ if(!(pack_singleton.order_flags & ORDER_SPECIAL_ENABLED))
still_locked_packs += pack_type
if(!length(still_locked_packs)) // Just give out some cash instead.
var/datum/adventure_loot_generator/simple/cash/replacement = new
@@ -133,10 +133,10 @@ GLOBAL_LIST_INIT(adventure_loot_generator_index,generate_generator_index())
/obj/item/trade_chip/proc/try_to_unlock_contract(mob/user)
var/datum/supply_pack/pack_singleton = SSshuttle.supply_packs[unlocked_pack_type]
- if(!unlocked_pack_type || !pack_singleton || !pack_singleton.special)
+ if(!unlocked_pack_type || !pack_singleton || !(pack_singleton.order_flags & ORDER_SPECIAL))
to_chat(user,span_danger("This chip is invalid!"))
return
- pack_singleton.special_enabled = TRUE
+ pack_singleton.order_flags |= ORDER_SPECIAL_ENABLED
to_chat(user,span_notice("Contract accepted into nanotrasen supply database."))
qdel(src)
diff --git a/code/modules/fishing/aquarium/aquarium.dm b/code/modules/fishing/aquarium/aquarium.dm
index efe4e2a648c..db01cfb9cb5 100644
--- a/code/modules/fishing/aquarium/aquarium.dm
+++ b/code/modules/fishing/aquarium/aquarium.dm
@@ -9,6 +9,7 @@
base_icon_state = "aquarium"
integrity_failure = 0.3
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 10, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 10)
//This is the area where fish can swim
var/aquarium_zone_min_pw = 2
@@ -129,6 +130,7 @@
throw_range = 3
w_class = WEIGHT_CLASS_BULKY
item_flags = SLOWS_WHILE_IN_HAND
+ custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 5)
custom_price = PAYCHECK_CREW * 9
diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm
index aaec020028d..cfbaed0c63b 100644
--- a/code/modules/fishing/fish/_fish.dm
+++ b/code/modules/fishing/fish/_fish.dm
@@ -259,6 +259,11 @@ GLOBAL_LIST_INIT(fish_compatible_fluid_types, list(
slapperoni(user, iteration = 1)
return MANUAL_SUICIDE
+/obj/item/fish/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 == OVERWHELMING_ATTACK)
+ return FALSE
+ return ..()
+
/obj/item/fish/proc/slapperoni(mob/living/user, iteration)
stoplag(0.1 SECONDS)
user.visible_message(span_bolddanger(suicide_slap_text))
diff --git a/code/modules/fishing/fish_mount.dm b/code/modules/fishing/fish_mount.dm
index 2363a206548..0aa95678051 100644
--- a/code/modules/fishing/fish_mount.dm
+++ b/code/modules/fishing/fish_mount.dm
@@ -4,7 +4,7 @@
icon = 'icons/obj/wallmounts.dmi'
icon_state = "fish_mount_item"
result_path = /obj/structure/fish_mount
- custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT)
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 2)
pixel_shift = 31
///Reference to the persistent_id of the mount this was spawned from.
var/persistence_id
diff --git a/code/modules/fishing/fishing_equipment.dm b/code/modules/fishing/fishing_equipment.dm
index 66bd2c8a1d2..941639871c0 100644
--- a/code/modules/fishing/fishing_equipment.dm
+++ b/code/modules/fishing/fishing_equipment.dm
@@ -291,6 +291,7 @@
desc = "A simple hook carved from sharpened bone"
icon_state = "hook_bone"
wiki_desc = "A generic fishing hook carved out of sharpened bone. Bone fishing rods come pre-equipped with it."
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT)
/obj/item/fishing_hook/stabilized
name = "gyro-stabilized hook"
diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm
index 69387aa293e..542295d92fe 100644
--- a/code/modules/fishing/fishing_rod.dm
+++ b/code/modules/fishing/fishing_rod.dm
@@ -695,6 +695,7 @@
frame_state = "frame_bone"
line = null //sinew line (usable to fish in lava) not included
hook = /obj/item/fishing_hook/bone
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 2)
/obj/item/fishing_rod/telescopic
name = "telescopic fishing rod"
diff --git a/code/modules/food_and_drinks/machinery/grill.dm b/code/modules/food_and_drinks/machinery/grill.dm
index 8d0aa921a7b..bfad1a4dc95 100644
--- a/code/modules/food_and_drinks/machinery/grill.dm
+++ b/code/modules/food_and_drinks/machinery/grill.dm
@@ -12,6 +12,7 @@
pass_flags_self = PASSMACHINE | LETPASSTHROW
processing_flags = START_PROCESSING_MANUALLY
use_power = NO_POWER_USE
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 8, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 0.5)
///The amount of fuel gained from stacks or reagents
var/grill_fuel = 0
diff --git a/code/modules/food_and_drinks/machinery/processor.dm b/code/modules/food_and_drinks/machinery/processor.dm
index eda53ab1777..f0940348bee 100644
--- a/code/modules/food_and_drinks/machinery/processor.dm
+++ b/code/modules/food_and_drinks/machinery/processor.dm
@@ -207,9 +207,7 @@
/obj/machinery/processor/slime/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/usb_port, list(
- /obj/item/circuit_component/slime_processor,
- ))
+ AddComponent(/datum/component/usb_port, typecacheof(list(/obj/item/circuit_component/slime_processor), only_root_path = TRUE))
/obj/machinery/processor/slime/adjust_item_drop_location(atom/movable/atom_to_drop)
var/static/list/slimecores = subtypesof(/obj/item/slime_extract)
diff --git a/code/modules/food_and_drinks/machinery/smartfridge.dm b/code/modules/food_and_drinks/machinery/smartfridge.dm
index 394c84fb4c2..243197066f0 100644
--- a/code/modules/food_and_drinks/machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/machinery/smartfridge.dm
@@ -550,6 +550,7 @@
base_build_path = /obj/machinery/smartfridge/drying/rack
use_power = NO_POWER_USE
idle_power_usage = 0
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 10)
/obj/machinery/smartfridge/drying/rack/Initialize(mapload)
. = ..()
diff --git a/code/modules/food_and_drinks/recipes/food_mixtures.dm b/code/modules/food_and_drinks/recipes/food_mixtures.dm
index ec5b91f41b7..ffa4b2c5c4f 100644
--- a/code/modules/food_and_drinks/recipes/food_mixtures.dm
+++ b/code/modules/food_and_drinks/recipes/food_mixtures.dm
@@ -4,8 +4,9 @@
/obj/item/reagent_containers/cup/bowl,
/obj/item/popsicle_stick,
/obj/item/stack/rods,
+ /obj/item/reagent_containers/cup/glass/sillycup,
)
- crafting_flags = parent_type::crafting_flags | CRAFT_TRANSFERS_REAGENTS | CRAFT_CLEARS_REAGENTS | CRAFT_ENFORCE_MATERIALS_PARITY
+ crafting_flags = parent_type::crafting_flags | CRAFT_TRANSFERS_REAGENTS | CRAFT_CLEARS_REAGENTS
///The food types that are added to the result when the recipe is completed
var/added_foodtypes = NONE
///The food types that are removed to the result when the recipe is completed
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_drink.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_drink.dm
index 82016dab5c3..ada80461970 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_drink.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_drink.dm
@@ -32,6 +32,7 @@
/datum/reagent/consumable/ethanol/hooch = 100
)
result = /obj/item/reagent_containers/cup/glass/bottle/hooch
+ requirements_mats_blacklist = list(/obj/item/storage/box/papersack)
category = CAT_DRINK
/datum/crafting_recipe/blazaambottle
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm
index 8ac20e53398..37213a2a684 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_frozen.dm
@@ -73,7 +73,7 @@
//////////////////////////SNOW CONES///////////////////////
-/datum/crafting_recipe/food/flavorless_sc
+/datum/crafting_recipe/food/snowcone
name = "Flavorless snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -82,7 +82,7 @@
result = /obj/item/food/snowcones
category = CAT_ICE
-/datum/crafting_recipe/food/pineapple_sc
+/datum/crafting_recipe/food/snowcone/pineapple
name = "Pineapple snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -90,9 +90,8 @@
/datum/reagent/consumable/pineapplejuice = 5
)
result = /obj/item/food/snowcones/pineapple
- category = CAT_ICE
-/datum/crafting_recipe/food/lime_sc
+/datum/crafting_recipe/food/snowcone/lime
name = "Lime snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -100,9 +99,8 @@
/datum/reagent/consumable/limejuice = 5
)
result = /obj/item/food/snowcones/lime
- category = CAT_ICE
-/datum/crafting_recipe/food/lemon_sc
+/datum/crafting_recipe/food/snowcone/lemon
name = "Lemon snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -110,9 +108,8 @@
/datum/reagent/consumable/lemonjuice = 5
)
result = /obj/item/food/snowcones/lemon
- category = CAT_ICE
-/datum/crafting_recipe/food/apple_sc
+/datum/crafting_recipe/food/snowcone/apple
name = "Apple snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -120,9 +117,8 @@
/datum/reagent/consumable/applejuice = 5
)
result = /obj/item/food/snowcones/apple
- category = CAT_ICE
-/datum/crafting_recipe/food/grape_sc
+/datum/crafting_recipe/food/snowcone/grape
name = "Grape snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -130,9 +126,8 @@
/datum/reagent/consumable/grapejuice = 5
)
result = /obj/item/food/snowcones/grape
- category = CAT_ICE
-/datum/crafting_recipe/food/orange_sc
+/datum/crafting_recipe/food/snowcone/orange
name = "Orange snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -140,9 +135,8 @@
/datum/reagent/consumable/orangejuice = 5
)
result = /obj/item/food/snowcones/orange
- category = CAT_ICE
-/datum/crafting_recipe/food/blue_sc
+/datum/crafting_recipe/food/snowcone/blue
name = "Bluecherry snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -150,9 +144,8 @@
/datum/reagent/consumable/bluecherryjelly= 5
)
result = /obj/item/food/snowcones/blue
- category = CAT_ICE
-/datum/crafting_recipe/food/red_sc
+/datum/crafting_recipe/food/snowcone/red
name = "Cherry snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -160,9 +153,8 @@
/datum/reagent/consumable/cherryjelly= 5
)
result = /obj/item/food/snowcones/red
- category = CAT_ICE
-/datum/crafting_recipe/food/berry_sc
+/datum/crafting_recipe/food/snowcone/berry
name = "Berry snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -170,9 +162,8 @@
/datum/reagent/consumable/berryjuice = 5
)
result = /obj/item/food/snowcones/berry
- category = CAT_ICE
-/datum/crafting_recipe/food/fruitsalad_sc
+/datum/crafting_recipe/food/snowcone/fruitsalad
name = "Fruit Salad snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -183,9 +174,8 @@
/datum/reagent/consumable/lemonjuice = 5
)
result = /obj/item/food/snowcones/fruitsalad
- category = CAT_ICE
-/datum/crafting_recipe/food/mime_sc
+/datum/crafting_recipe/food/snowcone/mime
name = "Mime snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -193,9 +183,8 @@
/datum/reagent/consumable/nothing = 5
)
result = /obj/item/food/snowcones/mime
- category = CAT_ICE
-/datum/crafting_recipe/food/clown_sc
+/datum/crafting_recipe/food/snowcone/clown
name = "Clown snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -203,9 +192,8 @@
/datum/reagent/consumable/laughter = 5
)
result = /obj/item/food/snowcones/clown
- category = CAT_ICE
-/datum/crafting_recipe/food/soda_sc
+/datum/crafting_recipe/food/snowcone/soda
name = "Space Cola snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -213,9 +201,8 @@
/datum/reagent/consumable/space_cola = 5
)
result = /obj/item/food/snowcones/soda
- category = CAT_ICE
-/datum/crafting_recipe/food/spacemountainwind_sc
+/datum/crafting_recipe/food/snowcone/spacemountainwind
name = "Space Mountain Wind snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -223,9 +210,8 @@
/datum/reagent/consumable/spacemountainwind = 5
)
result = /obj/item/food/snowcones/spacemountainwind
- category = CAT_ICE
-/datum/crafting_recipe/food/pwrgame_sc
+/datum/crafting_recipe/food/snowcone/pwrgame
name = "Pwrgame snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -233,9 +219,8 @@
/datum/reagent/consumable/pwr_game = 15
)
result = /obj/item/food/snowcones/pwrgame
- category = CAT_ICE
-/datum/crafting_recipe/food/honey_sc
+/datum/crafting_recipe/food/snowcone/honey
name = "Honey snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -243,9 +228,8 @@
/datum/reagent/consumable/honey = 5
)
result = /obj/item/food/snowcones/honey
- category = CAT_ICE
-/datum/crafting_recipe/food/rainbow_sc
+/datum/crafting_recipe/food/snowcone/rainbow
name = "Rainbow snowcone"
reqs = list(
/obj/item/reagent_containers/cup/glass/sillycup = 1,
@@ -253,7 +237,6 @@
/datum/reagent/colorful_reagent = 1 //Harder to make
)
result = /obj/item/food/snowcones/rainbow
- category = CAT_ICE
//////////////////////////POPSICLES///////////////////////
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 acf3109431c..0a7bc4c97d5 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
@@ -555,6 +555,7 @@
result = /obj/item/food/fortunecookie
added_foodtypes = SUGAR
category = CAT_PASTRY
+ requirements_mats_blacklist = list(/obj/item/paper)
/datum/crafting_recipe/food/poppypretzel
time = 1.5 SECONDS
diff --git a/code/modules/hallucination/eyes_in_dark.dm b/code/modules/hallucination/eyes_in_dark.dm
index ec18da445ec..181c8e17a4b 100644
--- a/code/modules/hallucination/eyes_in_dark.dm
+++ b/code/modules/hallucination/eyes_in_dark.dm
@@ -69,15 +69,15 @@
/obj/effect/abstract/floating_eyes/update_overlays()
. = ..()
- var/mutable_appearance/r_eye = mutable_appearance(icon = 'icons/mob/human/human_face.dmi', icon_state = "eyes_glow_r")
+ var/mutable_appearance/r_eye = mutable_appearance(icon = 'icons/mob/human/human_eyes.dmi', icon_state = "eyes_glow_r")
r_eye.color = COLOR_DARK_RED
. += r_eye
- var/mutable_appearance/l_eye = mutable_appearance(icon = 'icons/mob/human/human_face.dmi', icon_state = "eyes_glow_l")
+ var/mutable_appearance/l_eye = mutable_appearance(icon = 'icons/mob/human/human_eyes.dmi', icon_state = "eyes_glow_l")
l_eye.color = COLOR_DARK_RED
. += l_eye
- . += emissive_appearance('icons/mob/human/human_face.dmi', "eyes_glow_l", src)
- . += emissive_appearance('icons/mob/human/human_face.dmi', "eyes_glow_r", src)
+ . += emissive_appearance('icons/mob/human/human_eyes.dmi', "eyes_glow_l", src)
+ . += emissive_appearance('icons/mob/human/human_eyes.dmi', "eyes_glow_r", src)
/obj/effect/abstract/floating_eyes/process(seconds_per_tick)
var/turf/below_us = get_turf(src)
diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm
index 679b1709bcc..556c099ecc7 100644
--- a/code/modules/holiday/holidays.dm
+++ b/code/modules/holiday/holidays.dm
@@ -876,6 +876,27 @@ GLOBAL_LIST_INIT(holiday_mail, list())
/datum/holiday/xmas/greet()
return "Have a merry Christmas!"
+/datum/holiday/xmas/celebrate()
+ . = ..()
+ SSticker.OnRoundstart(CALLBACK(src, PROC_REF(roundstart_celebrate)))
+ GLOB.maintenance_loot += list(
+ list(
+ /obj/item/clothing/head/costume/santa = 1,
+ /obj/item/gift/anything = 1,
+ /obj/item/toy/xmas_cracker = 3,
+ ) = maint_holiday_weight,
+ )
+
+/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))
+
+ var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/costumes_toys/christmas]
+ pack.order_flags |= ORDER_SPECIAL_ENABLED
+
/datum/holiday/boxing
name = "Boxing Day"
begin_day = 26
@@ -984,27 +1005,6 @@ GLOBAL_LIST_INIT(holiday_mail, list())
/datum/holiday/hebrew/passover/getStationPrefix()
return pick("Matzah", "Moses", "Red Sea")
-// HOLIDAY ADDONS
-
-/datum/holiday/xmas/celebrate()
- . = ..()
- SSticker.OnRoundstart(CALLBACK(src, PROC_REF(roundstart_celebrate)))
- GLOB.maintenance_loot += list(
- list(
- /obj/item/clothing/head/costume/santa = 1,
- /obj/item/gift/anything = 1,
- /obj/item/toy/xmas_cracker = 3,
- ) = maint_holiday_weight,
- )
-
-/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 its own spot should be understandable)
/datum/holiday/easter
diff --git a/code/modules/hydroponics/beekeeping/beebox.dm b/code/modules/hydroponics/beekeeping/beebox.dm
index cb073c99b03..ff25eac7976 100644
--- a/code/modules/hydroponics/beekeeping/beebox.dm
+++ b/code/modules/hydroponics/beekeeping/beebox.dm
@@ -30,6 +30,7 @@
icon_state = "beebox"
anchored = TRUE
density = TRUE
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 40)
var/mob/living/basic/bee/queen/queen_bee = null
var/list/bees = list() //bees owned by the box, not those inside it
var/list/honeycombs = list()
diff --git a/code/modules/hydroponics/beekeeping/honey_frame.dm b/code/modules/hydroponics/beekeeping/honey_frame.dm
index 5c4c1f3f8d1..bb92091b6d1 100644
--- a/code/modules/hydroponics/beekeeping/honey_frame.dm
+++ b/code/modules/hydroponics/beekeeping/honey_frame.dm
@@ -4,6 +4,7 @@
desc = "A scaffold for bees to build honeycomb on."
icon = 'icons/obj/service/hydroponics/equipment.dmi'
icon_state = "honey_frame"
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 5)
var/honeycomb_capacity = 10 //10 Honeycomb per frame by default, researchable frames perhaps?
diff --git a/code/modules/hydroponics/fermenting_barrel.dm b/code/modules/hydroponics/fermenting_barrel.dm
index ab52f9e6c56..b4c15f221a2 100644
--- a/code/modules/hydroponics/fermenting_barrel.dm
+++ b/code/modules/hydroponics/fermenting_barrel.dm
@@ -10,6 +10,7 @@
anchored = FALSE
pressure_resistance = 2 * ONE_ATMOSPHERE
max_integrity = 300
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 8)
/// Is the barrel currently opened?
var/open = FALSE
/// Can the barrel be opened?
diff --git a/code/modules/hydroponics/grown/towercap.dm b/code/modules/hydroponics/grown/towercap.dm
index f2d611b1aef..cf7b7411c48 100644
--- a/code/modules/hydroponics/grown/towercap.dm
+++ b/code/modules/hydroponics/grown/towercap.dm
@@ -137,6 +137,7 @@
density = FALSE
anchored = TRUE
buckle_lying = 90
+ custom_materials = list(/datum/material/bamboo = SHEET_MATERIAL_AMOUNT * 5)
/// Overlay we apply when impaling a mob.
var/mutable_appearance/stab_overlay
diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm
index 49d2400c820..158a9f1708c 100644
--- a/code/modules/hydroponics/hydroitemdefines.dm
+++ b/code/modules/hydroponics/hydroitemdefines.dm
@@ -64,7 +64,7 @@
attack_verb_continuous = list("slashes", "slices", "bashes", "claws")
attack_verb_simple = list("slash", "slice", "bash", "claw")
hitsound = null
- custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 1.5)
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 5)
resistance_flags = FLAMMABLE
flags_1 = NONE
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index 0f325a5d740..5ff742ba4a4 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -176,7 +176,7 @@
. = ..()
AddComponent(/datum/component/simple_rotation)
AddComponent(/datum/component/plumbing/hydroponics)
- AddComponent(/datum/component/usb_port, list(/obj/item/circuit_component/hydroponics))
+ AddComponent(/datum/component/usb_port, typecacheof(list(/obj/item/circuit_component/hydroponics), only_root_path = TRUE))
AddComponent(/datum/component/fishing_spot, /datum/fish_source/hydro_tray)
/obj/machinery/hydroponics/constructable/RefreshParts()
diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm
index d572dc01b02..b8810f70f7f 100644
--- a/code/modules/hydroponics/seeds.dm
+++ b/code/modules/hydroponics/seeds.dm
@@ -582,7 +582,7 @@
if(new_trait?.can_add(src))
genes += new_trait
else
- return
+ new_trait = null
// Adjust stats based on graft stats
set_lifespan(round(max(lifespan, (lifespan + (2/3)*(snip.plant_dna.lifespan - lifespan)))))
diff --git a/code/modules/hydroponics/soil.dm b/code/modules/hydroponics/soil.dm
index 4b6389923a2..afa100cf4a3 100644
--- a/code/modules/hydroponics/soil.dm
+++ b/code/modules/hydroponics/soil.dm
@@ -13,6 +13,7 @@
maxnutri = 15
tray_flags = SOIL
armor_type = /datum/armor/obj_soil
+ custom_materials = list(/datum/material/sand = SHEET_MATERIAL_AMOUNT * 3)
//which type of sack to create when shovled.
var/sack_type = /obj/item/soil_sack
@@ -175,6 +176,11 @@
qdel(src)
return ITEM_INTERACT_SUCCESS
+/obj/item/soil_sack/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 == OVERWHELMING_ATTACK)
+ return FALSE
+ return ..()
+
///Remove slowdown and add block chance when wielded.
/obj/item/soil_sack/proc/on_wield()
slowdown = 0
diff --git a/code/modules/instruments/items.dm b/code/modules/instruments/items.dm
index cde0adbdc94..af22d2a8df8 100644
--- a/code/modules/instruments/items.dm
+++ b/code/modules/instruments/items.dm
@@ -48,6 +48,7 @@
inhand_icon_state = "violin"
hitsound = SFX_SWING_HIT
allowed_instrument_ids = "violin"
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 4, /datum/material/iron = SHEET_MATERIAL_AMOUNT)
/obj/item/instrument/violin/golden
name = "golden violin"
@@ -55,6 +56,7 @@
icon_state = "golden_violin"
inhand_icon_state = "golden_violin"
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT * 4, /datum/material/iron = SHEET_MATERIAL_AMOUNT)
/obj/item/instrument/banjo
name = "banjo"
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index 25cf131e388..7b3d4e01842 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -11,11 +11,8 @@
/// Innate skill levels unlocked at roundstart. Based on config.jobs_have_minimal_access config setting, for example with a full crew. Format is list(/datum/skill/foo = SKILL_EXP_NOVICE) with exp as an integer or as per code/_DEFINES/skills.dm
var/list/minimal_skills
- /// Determines who can demote this position
- var/department_head = list()
-
/// Tells the given channels that the given mob is the new department head. See communications.dm for valid channels.
- var/list/head_announce = null
+ var/head_announce
/// Bitflags for the job
var/auto_deadmin_role_flags = NONE
@@ -204,7 +201,7 @@
/// Note the joining mob has no client at this point.
/datum/job/proc/announce_job(mob/living/joining_mob, job_title) // SKYRAT EDIT CHANGE - ALTERNATIVE_JOB_TITLES - Original: /datum/job/proc/announce_job(mob/living/joining_mob)
if(head_announce)
- announce_head(joining_mob, head_announce, job_title) // SKYRAT EDIT CHANGE - ALTERNATIVE_JOB_TITLES - Original: announce_head(joining_mob, head_announce)
+ announce_head(joining_mob, list(head_announce))
//Used for a special check of whether to allow a client to latejoin as this job.
diff --git a/code/modules/jobs/job_types/atmospheric_technician.dm b/code/modules/jobs/job_types/atmospheric_technician.dm
index d016688042a..0a2472d7b06 100644
--- a/code/modules/jobs/job_types/atmospheric_technician.dm
+++ b/code/modules/jobs/job_types/atmospheric_technician.dm
@@ -1,7 +1,6 @@
/datum/job/atmospheric_technician
title = JOB_ATMOSPHERIC_TECHNICIAN
description = "Ensure the air is breathable on the station, fill oxygen tanks, fight fires, purify the air."
- department_head = list(JOB_CHIEF_ENGINEER)
faction = FACTION_STATION
total_positions = 3
spawn_positions = 2
diff --git a/code/modules/jobs/job_types/bartender.dm b/code/modules/jobs/job_types/bartender.dm
index 8084fc6cf62..8d39b5efcb3 100644
--- a/code/modules/jobs/job_types/bartender.dm
+++ b/code/modules/jobs/job_types/bartender.dm
@@ -1,7 +1,6 @@
/datum/job/bartender
title = JOB_BARTENDER
description = "Serve booze, mix drinks, keep the crew drunk."
- department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/botanist.dm b/code/modules/jobs/job_types/botanist.dm
index 7ccc4b31ceb..cff826ec505 100644
--- a/code/modules/jobs/job_types/botanist.dm
+++ b/code/modules/jobs/job_types/botanist.dm
@@ -1,7 +1,6 @@
/datum/job/botanist
title = JOB_BOTANIST
description = "Grow plants for the cook, for medicine, and for recreation."
- department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 3
spawn_positions = 2
diff --git a/code/modules/jobs/job_types/bridge_assistant.dm b/code/modules/jobs/job_types/bridge_assistant.dm
index 94e4af8ab42..01ef0e78ef9 100644
--- a/code/modules/jobs/job_types/bridge_assistant.dm
+++ b/code/modules/jobs/job_types/bridge_assistant.dm
@@ -2,7 +2,6 @@
title = JOB_BRIDGE_ASSISTANT
description = "Watch over the Bridge, command its consoles, and spend your days brewing coffee for higher-ups."
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD //not really a head but close enough
- department_head = list(JOB_CAPTAIN)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/captain.dm b/code/modules/jobs/job_types/captain.dm
index 9d58d8ca2cc..d52c523b4e3 100644
--- a/code/modules/jobs/job_types/captain.dm
+++ b/code/modules/jobs/job_types/captain.dm
@@ -4,7 +4,6 @@
keep the crew alive, be prepared to do anything and everything or die \
horribly trying."
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD|DEADMIN_POSITION_SECURITY
- department_head = list("CentCom")
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/cargo_technician.dm b/code/modules/jobs/job_types/cargo_technician.dm
index 008ddd6df0d..2cef58a09e8 100644
--- a/code/modules/jobs/job_types/cargo_technician.dm
+++ b/code/modules/jobs/job_types/cargo_technician.dm
@@ -3,7 +3,6 @@
description = "Distribute supplies to the departments that ordered them, \
collect empty crates, load and unload the supply shuttle, \
ship bounty cubes."
- department_head = list(JOB_QUARTERMASTER)
faction = FACTION_STATION
total_positions = 5
spawn_positions = 3
diff --git a/code/modules/jobs/job_types/chaplain/chaplain.dm b/code/modules/jobs/job_types/chaplain/chaplain.dm
index bb99965d368..75fd0a7c00a 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain.dm
@@ -2,7 +2,6 @@
title = JOB_CHAPLAIN
description = "Hold services and funerals, cremate people, preach your \
religion, protect the crew against cults."
- department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_costumes.dm b/code/modules/jobs/job_types/chaplain/chaplain_costumes.dm
index fcc754f229b..1b46ade2437 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_costumes.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_costumes.dm
@@ -247,6 +247,21 @@
/obj/item/clothing/suit/chaplainsuit/armor/crusader/blue
icon_state = "crusader-blue"
+/obj/item/clothing/head/helmet/chaplain/heretic
+ name = "occultist's hood"
+ desc = "Conceals your features from the things that look back at you."
+ icon_state = "heretichood"
+ inhand_icon_state = null
+ flags_cover = HEADCOVERSEYES
+ flags_inv = HIDEHAIR|HIDEFACE|HIDEEARS
+
+/obj/item/clothing/suit/chaplainsuit/armor/heretic
+ name = "occultist's robes"
+ desc = "Shields your body from the things others fail to notice."
+ icon_state = "hereticrobe"
+ inhand_icon_state = null
+ body_parts_covered = CHEST|GROIN|LEGS
+
/obj/item/clothing/suit/hooded/chaplain_hoodie
name = "follower hoodie"
desc = "Hoodie made for acolytes of the chaplain."
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
index 51f9e6566a0..1627c99fb5f 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
@@ -131,7 +131,7 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants())
AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -3)
/obj/item/nullrod/claymore/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 || LEAP_ATTACK || OVERWHELMING_ATTACK))
+ if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK || attack_type == OVERWHELMING_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. Or a road roller, if one happened to hit you.
return ..()
@@ -194,62 +194,26 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants())
SET_ATTACK_FORCE(attack_modifiers, rand(max(force - 15, 1), force + 15))
return ..()
-/// Vibro Variant
-/// This subtype possesses armor penetration and is sharp.
-
-/obj/item/nullrod/vibro
- name = "high frequency blade"
- desc = "Bad references are the DNA of the soul."
- icon = 'icons/obj/weapons/sword.dmi'
- icon_state = "hfrequency0"
- inhand_icon_state = "hfrequency1"
- worn_icon_state = "hfrequency0"
- icon_angle = -45
- lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- w_class = WEIGHT_CLASS_BULKY
- armour_penetration = 35
- slot_flags = ITEM_SLOT_BACK
- sharpness = SHARP_EDGED
- attack_verb_continuous = list("chops", "slices", "cuts", "zandatsu's")
- attack_verb_simple = list("chop", "slice", "cut", "zandatsu")
- hitsound = 'sound/items/weapons/rapierhit.ogg'
- menu_description = "A sharp blade which partially penetrates armor. Very effective at butchering bodies. Can be worn on the back."
- var/list/alt_continuous = list("stabs", "pierces", "impales")
- var/list/alt_simple = list("stab", "pierce", "impale")
-
-/obj/item/nullrod/vibro/Initialize(mapload)
- . = ..()
- alt_continuous = string_list(alt_continuous)
- alt_simple = string_list(alt_simple)
- AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -3)
-
-/obj/item/nullrod/vibro/Initialize(mapload)
- . = ..()
- AddComponent(
- /datum/component/butchering, \
- speed = 7 SECONDS, \
- effectiveness = 110, \
- )
-
-/obj/item/nullrod/vibro/spellblade
+/obj/item/nullrod/claymore/spellblade
name = "dormant spellblade"
desc = "The blade grants the wielder nearly limitless power...if they can figure out how to turn it on, that is."
icon = 'icons/obj/weapons/guns/magic.dmi'
icon_state = "spellblade"
inhand_icon_state = "spellblade"
+ slot_flags = ITEM_SLOT_BACK
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
worn_icon_state = "spellblade"
hitsound = 'sound/items/weapons/rapierhit.ogg'
- menu_description = "A sharp blade which partially penetrates armor. Very effective at butchering bodies. Can be worn on the back."
+ menu_description = "A sharp blade which provides a low chance of blocking incoming melee attacks. Can be worn on the back."
-/obj/item/nullrod/vibro/talking
+/obj/item/nullrod/claymore/talking
name = "possessed blade"
desc = "When the station falls into chaos, it's nice to have a friend by your side."
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "talking_sword"
inhand_icon_state = "talking_sword"
+ slot_flags = ITEM_SLOT_BACK
icon_angle = 45
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
@@ -257,13 +221,13 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants())
attack_verb_continuous = list("chops", "slices", "cuts")
attack_verb_simple= list("chop", "slice", "cut")
hitsound = 'sound/items/weapons/rapierhit.ogg'
- menu_description = "A sharp blade which partially penetrates armor. Able to awaken a friendly spirit to provide guidance. Very effective at butchering bodies. Can be worn on the back."
+ menu_description = "A sharp blade which provides a low chance of blocking incoming melee attacks. Able to awaken a friendly spirit to provide guidance. Can be worn on the back."
-/obj/item/nullrod/vibro/talking/Initialize(mapload)
+/obj/item/nullrod/spellblade/talking/Initialize(mapload)
. = ..()
AddComponent(/datum/component/spirit_holding)
-/obj/item/nullrod/vibro/talking/chainsword
+/obj/item/nullrod/claymore/talking/chainsword
name = "possessed chainsaw sword"
desc = "Suffer not a heretic to live."
icon_state = "chainswordon"
@@ -278,13 +242,83 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants())
toolspeed = 0.5 //same speed as an active chainsaw
chaplain_spawnable = FALSE //prevents being pickable as a chaplain weapon (it has 30 force)
-/obj/item/nullrod/vibro/talking/chainsword/Initialize(mapload)
+/obj/item/nullrod/spellblade/talking/chainsword/Initialize(mapload)
. = ..()
AddElement(/datum/element/cuffable_item) //Thanks goodness it cannot be selected by chappies
+ AddComponent(
+ /datum/component/butchering, \
+ speed = 7 SECONDS, \
+ effectiveness = 110, \
+ )
+
+/obj/item/nullrod/claymore/heretic
+ name = "occultist's khopesh"
+ desc = "Steels your hand to slay foes beyond comprehension."
+ icon = 'icons/obj/weapons/khopesh.dmi'
+ icon_state = "eldritch_blade"
+ lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
+ inhand_x_dimension = 64
+ inhand_y_dimension = 64
+ inhand_icon_state = "eldritch_blade"
+ worn_icon_state = "eldritch_blade"
+ menu_description = "A sharp curved blade which provides a low chance of blocking incoming melee attacks. Can be worn on the back or belt."
/// Other Variants
/// Not a special category on their own, but usually possess more unique mechanics
+// High Frequency Blade - Two-handed, has armor penetration, and can block exosuit attacks relatively easily. Can't block anything else.
+
+/obj/item/nullrod/vibro
+ name = "high frequency blade"
+ desc = "Bad references are the DNA of the soul."
+ icon = 'icons/obj/weapons/sword.dmi'
+ icon_state = "hfrequency0"
+ inhand_icon_state = "hfrequency0"
+ base_icon_state = "hfrequency"
+ worn_icon_state = "hfrequency0"
+ icon_angle = -45
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
+ w_class = WEIGHT_CLASS_BULKY
+ force = 10
+ armour_penetration = 35
+ block_chance = 40
+ slot_flags = ITEM_SLOT_BACK
+ sharpness = SHARP_EDGED
+ attack_verb_continuous = list("chops", "slices", "cuts", "zandatsu's")
+ attack_verb_simple = list("chop", "slice", "cut", "zandatsu")
+ hitsound = 'sound/items/weapons/rapierhit.ogg'
+ block_sound = 'sound/items/weapons/parry.ogg'
+ menu_description = "A sharp blade which partially penetrates armor. Unusualy adept at blocking melee attacks from exosuits. Very effective at butchering bodies. Can be worn on the back."
+ var/list/alt_continuous = list("stabs", "pierces", "impales")
+ var/list/alt_simple = list("stab", "pierce", "impale")
+
+/obj/item/nullrod/vibro/Initialize(mapload)
+ . = ..()
+ alt_continuous = string_list(alt_continuous)
+ alt_simple = string_list(alt_simple)
+ AddComponent(/datum/component/alternative_sharpness, SHARP_POINTY, alt_continuous, alt_simple, -3)
+ AddComponent(/datum/component/two_handed, \
+ force_unwielded = 10, \
+ force_wielded = 18, \
+ )
+ AddComponent(
+ /datum/component/butchering, \
+ speed = 7 SECONDS, \
+ effectiveness = 110, \
+ )
+
+/obj/item/nullrod/vibro/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(prob(final_block_chance * (HAS_TRAIT(src, TRAIT_WIELDED) ? 2 : 1)) && attack_type == OVERWHELMING_ATTACK)
+ owner.visible_message(span_danger("[owner] parries [attack_text] with [src]!"))
+ return TRUE
+ return FALSE
+
+/obj/item/nullrod/vibro/update_icon_state()
+ icon_state = inhand_icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]"
+ return ..()
+
// God Hand - Cannot be dropped. Does burn damage.
/obj/item/nullrod/godhand
@@ -588,7 +622,7 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants())
AddElement(/datum/element/nullrod_core)
AddComponent(/datum/component/faction_granter, FACTION_CARP, holy_role_required = HOLY_ROLE_PRIEST, grant_message = span_boldnotice("You are blessed by Carp-Sie. Wild space carp will no longer attack you."))
-// Monk's Staff - Higher block, lower damage.
+// Monk's Staff - Good block, two-handed. Great for showing off.
/obj/item/nullrod/bostaff
name = "monk's staff"
@@ -623,7 +657,7 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants())
return ..()
/obj/item/nullrod/bostaff/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 || LEAP_ATTACK || OVERWHELMING_ATTACK))
+ if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK || attack_type == OVERWHELMING_ATTACK)
final_block_chance = 0 //Don't bring a stick to a gunfight, and also you aren't going to really block someone full body tackling you with a stick. Or a road roller, if one happened to hit you.
return ..()
@@ -828,7 +862,7 @@ GLOBAL_LIST_INIT(nullrod_variants, init_nullrod_variants())
if(user == living_target)
return
- if(living_target.stat == DEAD)
+ if(living_target.stat == DEAD || QDELETED(living_target))
return
sneak_attack(living_target, user)
diff --git a/code/modules/jobs/job_types/chemist.dm b/code/modules/jobs/job_types/chemist.dm
index bdb48c84f35..44346103466 100644
--- a/code/modules/jobs/job_types/chemist.dm
+++ b/code/modules/jobs/job_types/chemist.dm
@@ -2,7 +2,6 @@
title = JOB_CHEMIST
description = "Supply the doctors with chemicals, make medicine, as well as \
less likable substances in the comfort of a fully reinforced room."
- department_head = list(JOB_CHIEF_MEDICAL_OFFICER)
faction = FACTION_STATION
total_positions = 2
spawn_positions = 2
diff --git a/code/modules/jobs/job_types/chief_engineer.dm b/code/modules/jobs/job_types/chief_engineer.dm
index 4cff930e11d..a35186f0cbf 100644
--- a/code/modules/jobs/job_types/chief_engineer.dm
+++ b/code/modules/jobs/job_types/chief_engineer.dm
@@ -3,8 +3,7 @@
description = "Coordinate engineering, ensure equipment doesn't get stolen, \
make sure the Supermatter doesn't blow up, maintain telecommunications."
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
- department_head = list(JOB_CAPTAIN)
- head_announce = list("Engineering")
+ head_announce = RADIO_CHANNEL_ENGINEERING
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/chief_medical_officer.dm b/code/modules/jobs/job_types/chief_medical_officer.dm
index eda435e2aec..4cab7531da4 100644
--- a/code/modules/jobs/job_types/chief_medical_officer.dm
+++ b/code/modules/jobs/job_types/chief_medical_officer.dm
@@ -2,9 +2,8 @@
title = JOB_CHIEF_MEDICAL_OFFICER
description = "Coordinate doctors and other medbay employees, ensure they \
know how to save lives, check for injuries on the crew monitor."
- department_head = list(JOB_CAPTAIN)
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
- head_announce = list(RADIO_CHANNEL_MEDICAL)
+ head_announce = RADIO_CHANNEL_MEDICAL
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/clown.dm b/code/modules/jobs/job_types/clown.dm
index a7a7fc77896..a28bfea710b 100644
--- a/code/modules/jobs/job_types/clown.dm
+++ b/code/modules/jobs/job_types/clown.dm
@@ -1,7 +1,6 @@
/datum/job/clown
title = JOB_CLOWN
description = "Entertain the crew, make bad jokes, go on a holy quest to find bananium, HONK!"
- department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/cook.dm b/code/modules/jobs/job_types/cook.dm
index 15fa431ef0f..672d2f616e5 100644
--- a/code/modules/jobs/job_types/cook.dm
+++ b/code/modules/jobs/job_types/cook.dm
@@ -1,7 +1,6 @@
/datum/job/cook
title = JOB_COOK
description = "Serve food, cook meat, keep the crew fed."
- department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 2
spawn_positions = 2
diff --git a/code/modules/jobs/job_types/coroner.dm b/code/modules/jobs/job_types/coroner.dm
index dd0b8e7a0bf..4fe6ad3cdb2 100644
--- a/code/modules/jobs/job_types/coroner.dm
+++ b/code/modules/jobs/job_types/coroner.dm
@@ -2,7 +2,6 @@
title = JOB_CORONER
description = "Perform Autopsies whenever needed, \
Update medical records accordingly, apply formaldehyde."
- department_head = list(JOB_CHIEF_MEDICAL_OFFICER)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/curator.dm b/code/modules/jobs/job_types/curator.dm
index f6d12eaf5e1..9c0580fea6b 100644
--- a/code/modules/jobs/job_types/curator.dm
+++ b/code/modules/jobs/job_types/curator.dm
@@ -2,7 +2,6 @@
title = JOB_CURATOR
description = "Read and write books and hand them to people, stock \
bookshelves, report on station news."
- department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm
index 65c4e423deb..64245d312ce 100644
--- a/code/modules/jobs/job_types/detective.dm
+++ b/code/modules/jobs/job_types/detective.dm
@@ -3,7 +3,6 @@
description = "Investigate crimes, gather evidence, perform interrogations, \
look badass, smoke cigarettes."
auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY
- department_head = list(JOB_HEAD_OF_SECURITY)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/geneticist.dm b/code/modules/jobs/job_types/geneticist.dm
index 2498eb7c2a3..68a178bbf55 100644
--- a/code/modules/jobs/job_types/geneticist.dm
+++ b/code/modules/jobs/job_types/geneticist.dm
@@ -1,7 +1,6 @@
/datum/job/geneticist
title = JOB_GENETICIST
description = "Alter genomes, turn monkeys into humans (and vice-versa), and make DNA backups."
- department_head = list(JOB_RESEARCH_DIRECTOR)
faction = FACTION_STATION
total_positions = 2
spawn_positions = 2
diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm
index 4cd19feec4f..1136063a73c 100644
--- a/code/modules/jobs/job_types/head_of_personnel.dm
+++ b/code/modules/jobs/job_types/head_of_personnel.dm
@@ -3,8 +3,7 @@
description = "Alter access on ID cards, manage the service department, \
protect Ian, run the station when the captain dies."
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
- department_head = list(JOB_CAPTAIN)
- head_announce = list(RADIO_CHANNEL_SERVICE)
+ head_announce = RADIO_CHANNEL_SERVICE
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index 9a20bb8ff23..5e737ab1b18 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -3,8 +3,7 @@
description = "Coordinate security personnel, ensure they are not corrupt, \
make sure every department is protected."
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD|DEADMIN_POSITION_SECURITY
- department_head = list(JOB_CAPTAIN)
- head_announce = list(RADIO_CHANNEL_SECURITY)
+ head_announce = RADIO_CHANNEL_SECURITY
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/janitor.dm b/code/modules/jobs/job_types/janitor.dm
index 2cf5997d32b..7c7656d8b46 100644
--- a/code/modules/jobs/job_types/janitor.dm
+++ b/code/modules/jobs/job_types/janitor.dm
@@ -1,7 +1,6 @@
/datum/job/janitor
title = JOB_JANITOR
description = "Clean up trash and blood. Replace broken lights. Slip people over."
- department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 2
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/lawyer.dm b/code/modules/jobs/job_types/lawyer.dm
index 449d004e76b..7a1c8d58bc7 100644
--- a/code/modules/jobs/job_types/lawyer.dm
+++ b/code/modules/jobs/job_types/lawyer.dm
@@ -2,7 +2,6 @@
title = JOB_LAWYER
description = "Advocate for prisoners, create law-binding contracts, \
ensure Security is following protocol and Space Law."
- department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 2
spawn_positions = 2
diff --git a/code/modules/jobs/job_types/medical_doctor.dm b/code/modules/jobs/job_types/medical_doctor.dm
index ec9a10525bc..7bc267b124b 100644
--- a/code/modules/jobs/job_types/medical_doctor.dm
+++ b/code/modules/jobs/job_types/medical_doctor.dm
@@ -2,7 +2,6 @@
title = JOB_MEDICAL_DOCTOR
description = "Save lives, run around the station looking for victims, \
scan everyone in sight"
- department_head = list(JOB_CHIEF_MEDICAL_OFFICER)
faction = FACTION_STATION
total_positions = 6
spawn_positions = 4
diff --git a/code/modules/jobs/job_types/mime.dm b/code/modules/jobs/job_types/mime.dm
index f4bc4955208..7e10eac2204 100644
--- a/code/modules/jobs/job_types/mime.dm
+++ b/code/modules/jobs/job_types/mime.dm
@@ -1,7 +1,6 @@
/datum/job/mime
title = JOB_MIME
description = "..."
- department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/paramedic.dm b/code/modules/jobs/job_types/paramedic.dm
index 9122c17064b..85aae2f1f94 100644
--- a/code/modules/jobs/job_types/paramedic.dm
+++ b/code/modules/jobs/job_types/paramedic.dm
@@ -2,7 +2,6 @@
title = JOB_PARAMEDIC
description = "Run around the station looking for patients, respond to \
emergencies, give patients a roller bed ride to medbay."
- department_head = list(JOB_CHIEF_MEDICAL_OFFICER)
faction = FACTION_STATION
total_positions = 2
spawn_positions = 2
diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm
index 3cc485b34f0..ae0c7b9ae92 100644
--- a/code/modules/jobs/job_types/prisoner.dm
+++ b/code/modules/jobs/job_types/prisoner.dm
@@ -1,7 +1,6 @@
/datum/job/prisoner
title = JOB_PRISONER
description = "Keep yourself occupied in permabrig."
- department_head = list("The Security Team")
faction = FACTION_STATION
total_positions = 12 // SKYRAT EDIT: Original value (0)
spawn_positions = 2
diff --git a/code/modules/jobs/job_types/psychologist.dm b/code/modules/jobs/job_types/psychologist.dm
index 4a1d291c867..e4a8bfd4418 100644
--- a/code/modules/jobs/job_types/psychologist.dm
+++ b/code/modules/jobs/job_types/psychologist.dm
@@ -2,7 +2,6 @@
title = JOB_PSYCHOLOGIST
description = "Advocate sanity, self-esteem, and teamwork in a station \
staffed with headcases."
- department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/quartermaster.dm b/code/modules/jobs/job_types/quartermaster.dm
index 251032662bf..962f558c602 100644
--- a/code/modules/jobs/job_types/quartermaster.dm
+++ b/code/modules/jobs/job_types/quartermaster.dm
@@ -3,8 +3,7 @@
description = "Coordinate cargo technicians and shaft miners, assist with \
economical purchasing."
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
- department_head = list(JOB_CAPTAIN)
- head_announce = list(RADIO_CHANNEL_SUPPLY)
+ head_announce = RADIO_CHANNEL_SUPPLY
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
@@ -29,7 +28,7 @@
/datum/job_department/cargo,
/datum/job_department/command,
)
- family_heirlooms = list(/obj/item/stamp, /obj/item/stamp/denied)
+ family_heirlooms = list(/obj/item/stamp/granted, /obj/item/stamp/denied)
mail_goodies = list(
/obj/item/circuitboard/machine/emitter = 3
)
diff --git a/code/modules/jobs/job_types/research_director.dm b/code/modules/jobs/job_types/research_director.dm
index cfd5044c267..095dd9f158b 100644
--- a/code/modules/jobs/job_types/research_director.dm
+++ b/code/modules/jobs/job_types/research_director.dm
@@ -4,8 +4,7 @@
order, make sure the AI and its Cyborgs aren't rogue, replacing them if \
they are."
auto_deadmin_role_flags = DEADMIN_POSITION_HEAD
- department_head = list(JOB_CAPTAIN)
- head_announce = list("Science")
+ head_announce = RADIO_CHANNEL_SCIENCE
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/job_types/roboticist.dm b/code/modules/jobs/job_types/roboticist.dm
index 7370af5d9ec..9f91c6f22e0 100644
--- a/code/modules/jobs/job_types/roboticist.dm
+++ b/code/modules/jobs/job_types/roboticist.dm
@@ -1,7 +1,6 @@
/datum/job/roboticist
title = JOB_ROBOTICIST
description = "Build and repair the AI and cyborgs, create mechs."
- department_head = list(JOB_RESEARCH_DIRECTOR)
faction = FACTION_STATION
total_positions = 2
spawn_positions = 2
diff --git a/code/modules/jobs/job_types/scientist.dm b/code/modules/jobs/job_types/scientist.dm
index 878167f2548..f76aa7f417a 100644
--- a/code/modules/jobs/job_types/scientist.dm
+++ b/code/modules/jobs/job_types/scientist.dm
@@ -1,7 +1,6 @@
/datum/job/scientist
title = JOB_SCIENTIST
description = "Do experiments, perform research, feed the slimes, make bombs."
- department_head = list(JOB_RESEARCH_DIRECTOR)
faction = FACTION_STATION
total_positions = 5
spawn_positions = 3
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index 5255db1b7b8..4d02b89ab1b 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -3,7 +3,6 @@
description = "Protect company assets, follow the Standard Operating \
Procedure, eat donuts."
auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY
- department_head = list(JOB_HEAD_OF_SECURITY)
faction = FACTION_STATION
total_positions = 8 //Handled in /datum/controller/occupations/proc/setup_officer_positions() //SKYRAT EDIT: SET TO 8, WAS 5
spawn_positions = 8 //Handled in /datum/controller/occupations/proc/setup_officer_positions() //SKYRAT EDIT: SEE ABOVE
diff --git a/code/modules/jobs/job_types/shaft_miner.dm b/code/modules/jobs/job_types/shaft_miner.dm
index f1e2bb01de9..e1ed464c85e 100644
--- a/code/modules/jobs/job_types/shaft_miner.dm
+++ b/code/modules/jobs/job_types/shaft_miner.dm
@@ -2,7 +2,6 @@
title = JOB_SHAFT_MINER
description = "Travel to strange lands. Mine ores. \
Meet strange creatures. Kill them for their gold."
- department_head = list(JOB_QUARTERMASTER)
faction = FACTION_STATION
total_positions = 3
spawn_positions = 3
diff --git a/code/modules/jobs/job_types/station_engineer.dm b/code/modules/jobs/job_types/station_engineer.dm
index 015f2a443e4..dbac0debcd4 100644
--- a/code/modules/jobs/job_types/station_engineer.dm
+++ b/code/modules/jobs/job_types/station_engineer.dm
@@ -2,7 +2,6 @@
title = JOB_STATION_ENGINEER
description = "Start the Supermatter, wire the solars, repair station hull \
and wiring damage."
- department_head = list(JOB_CHIEF_ENGINEER)
faction = FACTION_STATION
total_positions = 5
spawn_positions = 5
diff --git a/code/modules/jobs/job_types/station_trait/cargo_gorilla.dm b/code/modules/jobs/job_types/station_trait/cargo_gorilla.dm
index c11c931a3ef..9fe2cccc934 100644
--- a/code/modules/jobs/job_types/station_trait/cargo_gorilla.dm
+++ b/code/modules/jobs/job_types/station_trait/cargo_gorilla.dm
@@ -1,7 +1,6 @@
/datum/job/cargo_gorilla
title = JOB_CARGO_GORILLA
description = "Assist the supply department by moving freight and disposing of unwanted fruits."
- department_head = list(JOB_QUARTERMASTER)
faction = FACTION_STATION
total_positions = 0
spawn_positions = 0
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 4a4eb04bce9..8a59d55f49d 100644
--- a/code/modules/jobs/job_types/station_trait/human_ai.dm
+++ b/code/modules/jobs/job_types/station_trait/human_ai.dm
@@ -2,7 +2,6 @@
title = JOB_HUMAN_AI
description = "Assist the crew, open airlocks, follow your lawset, and coordinate your cyborgs."
auto_deadmin_role_flags = DEADMIN_POSITION_SILICON
- department_head = list(JOB_RESEARCH_DIRECTOR)
faction = FACTION_STATION
total_positions = 0
spawn_positions = 0
diff --git a/code/modules/jobs/job_types/station_trait/pun_pun.dm b/code/modules/jobs/job_types/station_trait/pun_pun.dm
index b6ac7b813bf..7b1f8d7906c 100644
--- a/code/modules/jobs/job_types/station_trait/pun_pun.dm
+++ b/code/modules/jobs/job_types/station_trait/pun_pun.dm
@@ -2,7 +2,6 @@
/datum/job/pun_pun
title = JOB_PUN_PUN
description = "Assist the service department by serving drinks and food and entertaining the crew."
- department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 0
spawn_positions = 0
diff --git a/code/modules/jobs/job_types/station_trait/veteran_advisor.dm b/code/modules/jobs/job_types/station_trait/veteran_advisor.dm
index 6bd84ce0c0f..ed4c63e25bd 100644
--- a/code/modules/jobs/job_types/station_trait/veteran_advisor.dm
+++ b/code/modules/jobs/job_types/station_trait/veteran_advisor.dm
@@ -3,7 +3,6 @@
description = "Advise HoS, and Captain on matters of Security. Train green Officers. \
Lay back in your wheelchair and say \"I told you\" to the HoS when all of the station collapses."
auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY
- department_head = list(JOB_HEAD_OF_SECURITY)
faction = FACTION_STATION
total_positions = 0
spawn_positions = 0
diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm
index 4a5166d1fe8..528063e1001 100644
--- a/code/modules/jobs/job_types/warden.dm
+++ b/code/modules/jobs/job_types/warden.dm
@@ -4,7 +4,6 @@
their time is up, issue equipment to security, be a security officer when \
they all eventually die."
auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY
- department_head = list(JOB_HEAD_OF_SECURITY)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/code/modules/jobs/jobs.dm b/code/modules/jobs/jobs.dm
index 994d0d34c4c..823e353136c 100644
--- a/code/modules/jobs/jobs.dm
+++ b/code/modules/jobs/jobs.dm
@@ -39,16 +39,6 @@ GLOBAL_LIST_INIT(exp_specialmap, list(
))
GLOBAL_PROTECT(exp_specialmap)
-//this is necessary because antags happen before job datums are handed out, but NOT before they come into existence
-//so I can't simply use job datum.department_head straight from the mind datum, laaaaame.
-/proc/get_department_heads(job_title)
- if(!job_title)
- return list()
-
- for(var/datum/job/job as anything in SSjob.joinable_occupations)
- if(job.title == job_title)
- return job.department_head //this is a list
-
/proc/get_full_job_name(job)
var/static/regex/cap_expand = new("cap(?!tain)")
var/static/regex/cmo_expand = new("cmo")
diff --git a/code/modules/library/bookcase.dm b/code/modules/library/bookcase.dm
index a591b15d15c..f0440ba70e4 100644
--- a/code/modules/library/bookcase.dm
+++ b/code/modules/library/bookcase.dm
@@ -13,6 +13,7 @@
resistance_flags = FLAMMABLE
max_integrity = 200
armor_type = /datum/armor/structure_bookcase
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 4)
var/state = BOOKCASE_UNANCHORED
/// When enabled, books_to_load number of random books will be generated for this bookcase
var/load_random_books = FALSE
diff --git a/code/modules/loadout/categories/accessories.dm b/code/modules/loadout/categories/accessories.dm
index f6899b9ff12..53a61c9bc0e 100644
--- a/code/modules/loadout/categories/accessories.dm
+++ b/code/modules/loadout/categories/accessories.dm
@@ -84,4 +84,4 @@
/datum/loadout_item/accessory/pride
name = "Pride Pin"
item_path = /obj/item/clothing/accessory/pride
- loadout_flags = parent_type::loadout_flags | LOADOUT_FLAG_ALLOW_RESKIN // BUBBER EDIT CHANGE - Original: loadout_flags = LOADOUT_FLAG_ALLOW_RESKIN
+ reskin_datum = /datum/atom_skin/pride_pin
diff --git a/code/modules/loadout/categories/heads.dm b/code/modules/loadout/categories/heads.dm
index 6a9ada07bfc..a76860d4f86 100644
--- a/code/modules/loadout/categories/heads.dm
+++ b/code/modules/loadout/categories/heads.dm
@@ -159,3 +159,8 @@
/datum/loadout_item/head/wig
name = "Natural Wig"
item_path = /obj/item/clothing/head/wig/natural
+
+/datum/loadout_item/head/santa
+ name = "Santa Hat"
+ item_path = /obj/item/clothing/head/costume/santa/gags
+ required_holiday = FESTIVE_SEASON
diff --git a/code/modules/loadout/loadout_categories.dm b/code/modules/loadout/loadout_categories.dm
index c64a564b2ec..863b792eb56 100644
--- a/code/modules/loadout/loadout_categories.dm
+++ b/code/modules/loadout/loadout_categories.dm
@@ -60,6 +60,9 @@
var/list/formatted_list = list()
for(var/datum/loadout_item/item as anything in associated_items)
+ if(item.is_disabled())
+ continue
+
var/list/item_data = item.to_ui_data()
UNTYPED_LIST_ADD(formatted_list, item_data)
diff --git a/code/modules/loadout/loadout_helpers.dm b/code/modules/loadout/loadout_helpers.dm
index 02e85518a41..5a437abab27 100644
--- a/code/modules/loadout/loadout_helpers.dm
+++ b/code/modules/loadout/loadout_helpers.dm
@@ -28,9 +28,16 @@
else
CRASH("Invalid outfit passed to equip_outfit_and_loadout ([outfit])")
- var/list/preference_list = preference_source.read_preference(/datum/preference/loadout)
+ var/list/item_details = preference_source.read_preference(/datum/preference/loadout)
+ var/list/loadout_datums = loadout_list_to_datums(item_details)
+ // Slap our things into the outfit given
+ for(var/datum/loadout_item/item as anything in loadout_datums)
+ if(!item.is_equippable(src, item_details?[item.item_path] || list()))
+ loadout_datums -= item
+ continue
+
+ item.insert_path_into_outfit(equipped_outfit, src, visuals_only)
preference_list = preference_list[preference_source.read_preference(/datum/preference/loadout_index)] // BUBBER EDIT ADDITION: Multiple loadout presets
- var/list/loadout_datums = loadout_list_to_datums(preference_list)
// SKYRAT EDIT ADDITION BEGIN
var/obj/item/storage/briefcase/empty/travel_suitcase
var/loadout_placement_preference = preference_source.read_preference(/datum/preference/choiced/loadout_override_preference)
@@ -82,7 +89,7 @@
for(var/datum/loadout_item/item as anything in loadout_datums)
update |= item.on_equip_item(
equipped_item = (loadout_placement_preference == LOADOUT_OVERRIDE_CASE && !visuals_only) ? locate(item.item_path) in travel_suitcase : locate(item.item_path) in new_contents, // BUBBER EDIT CHANGE - ORIGINAL: equipped_item = locate(item.item_path) in new_contents,
- item_details = preference_list?[item.item_path] || list(),
+ item_details = item_details?[item.item_path] || list(),
equipper = src,
outfit = equipped_outfit,
visuals_only = visuals_only,
diff --git a/code/modules/loadout/loadout_items.dm b/code/modules/loadout/loadout_items.dm
index 22c1e7a930e..bff857cbfac 100644
--- a/code/modules/loadout/loadout_items.dm
+++ b/code/modules/loadout/loadout_items.dm
@@ -42,6 +42,8 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
var/group = null
/// Loadout flags, see LOADOUT_FLAG_* defines
var/loadout_flags = LOADOUT_FLAG_ALLOW_NAMING // BUBBER EDIT CHANGE - Original: var/loadout_flags = NONE
+ /// If set, this item can only be selected during the holiday specified.
+ var/required_holiday
/// The actual item path of the loadout item.
var/obj/item/item_path
/// Icon file (DMI) for the UI to use for preview icons.
@@ -50,8 +52,9 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
/// Icon state for the UI to use for preview icons.
/// Set automatically if null
var/ui_icon_state
- /// Reskin options of this item if it can be reskinned.
- VAR_FINAL/list/cached_reskin_options
+ /// Base typepath to what reskin datum this item can use to reskin into
+ /// Doesn't verify that the item_path actually has these reskins
+ var/reskin_datum
/// A list of greyscale colors that are used for items that have greyscale support, but don't allow full customization.
/// This is an assoc list of /datum/job_department -> colors, or /datum/job -> colors, allowing for preset colors based on player chosen job.
/// Jobs are prioritized over departments.
@@ -156,7 +159,7 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
// BUBBER EDIT ADDITION END
if("set_skin")
- if(loadout_flags & LOADOUT_FLAG_ALLOW_RESKIN)
+ if(reskin_datum)
return set_skin(manager, user, params)
return TRUE
@@ -239,7 +242,8 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
/// Used for reskinning an item to an alt skin.
/datum/loadout_item/proc/set_skin(datum/preference_middleware/loadout/manager, mob/user, params)
- var/reskin_to = params["skin"]
+ var/reskin_to = params["skin"] // sanity checking isn't necessary because it's all checked when equipped anyways
+ var/list/loadout = manager.preferences.read_preference(/datum/preference/loadout)
if(!cached_reskin_options[reskin_to])
return FALSE
@@ -328,11 +332,13 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
SEND_SIGNAL(equipped_item, COMSIG_NAME_CHANGED)
// BUBBER EDIT CHANGE END - Descriptions
- if((loadout_flags & LOADOUT_FLAG_ALLOW_RESKIN) && item_details?[INFO_RESKIN])
+ if(reskin_datum && item_details?[INFO_RESKIN])
var/skin_chosen = item_details[INFO_RESKIN]
- if(skin_chosen in equipped_item.unique_reskin)
- equipped_item.current_skin = skin_chosen
- equipped_item.icon_state = equipped_item.unique_reskin[skin_chosen]
+ for(var/datum/atom_skin/skin_path as anything in valid_subtypesof(reskin_datum))
+ if(skin_path::preview_name != skin_chosen)
+ continue
+ var/datum/atom_skin/skin_instance = GLOB.atom_skins[skin_path]
+ skin_instance.apply(equipped_item)
if(istype(equipped_item, /obj/item/clothing/accessory))
// Snowflake handing for accessories, because we need to update the thing it's attached to instead
if(isclothing(equipped_item.loc))
@@ -341,6 +347,7 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
update_flag |= (ITEM_SLOT_OCLOTHING|ITEM_SLOT_ICLOTHING)
else
update_flag |= equipped_item.slot_flags
+ break
return update_flag
@@ -371,6 +378,18 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
return formatted_item
+/**
+ * Checks if this item is disabled and cannot be selected or granted
+ */
+/datum/loadout_item/proc/is_disabled()
+ return required_holiday && !check_holidays(required_holiday)
+
+/**
+ * Checks if this item is disabled or unequippable for the given item details.
+ */
+/datum/loadout_item/proc/is_equippable(mob/living/carbon/human/equipper, list/item_details)
+ return !is_disabled()
+
/**
* Returns a list of information to display about this item in the loadout UI.
* Icon -> tooltip displayed when its hovered over
@@ -383,9 +402,12 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
if((loadout_flags & LOADOUT_FLAG_GREYSCALING_ALLOWED) && !(loadout_flags & LOADOUT_FLAG_JOB_GREYSCALING))
displayed_text[FA_ICON_PALETTE] = "Recolorable"
- if(loadout_flags & LOADOUT_FLAG_ALLOW_RESKIN)
+ if(reskin_datum)
displayed_text[FA_ICON_SWATCHBOOK] = "Reskinnable"
+ if(required_holiday)
+ displayed_text[FA_ICON_CALENDAR_CHECK] = "Only available: [required_holiday]"
+
// SKYRAT EDIT ADDITION
if(donator_only)
displayed_text[FA_ICON_MONEY_BILL] = "Donator only"
@@ -453,16 +475,17 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
* Returns a list of options this item can be reskinned into.
*/
/datum/loadout_item/proc/get_reskin_options() as /list
- if(!(loadout_flags & LOADOUT_FLAG_ALLOW_RESKIN))
+ if(!reskin_datum)
return null
var/list/reskins = list()
- for(var/skin in cached_reskin_options)
+ for(var/datum/atom_skin/skin as anything in valid_subtypesof(reskin_datum))
UNTYPED_LIST_ADD(reskins, list(
- "name" = skin,
- "tooltip" = skin,
- "skin_icon_state" = cached_reskin_options[skin],
+ "name" = skin::new_name || skin::preview_name,
+ "tooltip" = skin::preview_name,
+ "skin_icon" = skin::new_icon,
+ "skin_icon_state" = skin::new_icon_state,
))
return reskins
diff --git a/code/modules/loadout/loadout_menu.dm b/code/modules/loadout/loadout_menu.dm
index 728898e7132..13d237793b8 100644
--- a/code/modules/loadout/loadout_menu.dm
+++ b/code/modules/loadout/loadout_menu.dm
@@ -27,7 +27,7 @@
if(params["deselect"])
deselect_item(interacted_item)
- else
+ else if(!interacted_item.is_disabled())
select_item(interacted_item)
return TRUE
diff --git a/code/modules/loadout/loadout_preference.dm b/code/modules/loadout/loadout_preference.dm
index 06e71ea96b5..2b3374ae314 100644
--- a/code/modules/loadout/loadout_preference.dm
+++ b/code/modules/loadout/loadout_preference.dm
@@ -53,9 +53,11 @@
You may want to check your loadout settings."))
continue
+ var/datum/loadout_item/loadout_item = GLOB.all_loadout_datums[real_path]
+ if(loadout_item.is_disabled())
+ continue // this just falls off silently
// SKYRAT EDIT ADDITION
else if(owner_client)
- var/datum/loadout_item/loadout_item = GLOB.all_loadout_datums[real_path]
if(loadout_item?.ckeywhitelist && !(owner_client?.ckey in loadout_item.ckeywhitelist))
continue
// SKYRAT EDIT END
diff --git a/code/modules/lost_crew/character/job_datums.dm b/code/modules/lost_crew/character/job_datums.dm
index 5b9ad43311f..a1d1b47959e 100644
--- a/code/modules/lost_crew/character/job_datums.dm
+++ b/code/modules/lost_crew/character/job_datums.dm
@@ -4,30 +4,24 @@
/datum/job/recovered_crew/doctor
title = JOB_LOSTCREW_MEDICAL
- department_head = list(JOB_CHIEF_MEDICAL_OFFICER)
supervisors = SUPERVISOR_CMO
/datum/job/recovered_crew/engineer
title = JOB_LOSTCREW_ENGINEER
- department_head = list(JOB_CHIEF_ENGINEER)
supervisors = SUPERVISOR_CE
/datum/job/recovered_crew/security
title = JOB_LOSTCREW_SECURITY
- department_head = list(JOB_HEAD_OF_SECURITY)
supervisors = SUPERVISOR_HOS
/datum/job/recovered_crew/cargo
title = JOB_LOSTCREW_CARGO
- department_head = list(JOB_QUARTERMASTER)
supervisors = SUPERVISOR_QM
/datum/job/recovered_crew/scientist
title = JOB_LOSTCREW_SCIENCE
- department_head = list(JOB_RESEARCH_DIRECTOR)
supervisors = SUPERVISOR_RD
/datum/job/recovered_crew/civillian
title = JOB_LOSTCREW_CIVILLIAN
- department_head = list(JOB_HEAD_OF_PERSONNEL)
supervisors = SUPERVISOR_HOP
diff --git a/code/modules/lost_crew/lost_crew_manager.dm b/code/modules/lost_crew/lost_crew_manager.dm
index 22dc887393a..1494204ad71 100644
--- a/code/modules/lost_crew/lost_crew_manager.dm
+++ b/code/modules/lost_crew/lost_crew_manager.dm
@@ -82,7 +82,7 @@ GLOBAL_DATUM_INIT(lost_crew_manager, /datum/lost_crew_manager, new)
owner.mind.add_antag_datum(/datum/antagonist/recovered_crew) //for tracking mostly
var/datum/bank_account/bank_account = new(owner.real_name, owner.mind.assigned_role, owner.dna.species.payday_modifier)
- bank_account.adjust_money(starting_funds, "[starting_funds]cr given to [owner.name] as starting fund.")
+ bank_account.adjust_money(starting_funds, "[starting_funds][MONEY_SYMBOL] given to [owner.name] as starting fund.")
owner.account_id = bank_account.account_id
bank_account.replaceable = FALSE
diff --git a/code/modules/manufactorio/machines/storagebox.dm b/code/modules/manufactorio/machines/storagebox.dm
index 408493d4a89..bd528d349b4 100644
--- a/code/modules/manufactorio/machines/storagebox.dm
+++ b/code/modules/manufactorio/machines/storagebox.dm
@@ -2,6 +2,7 @@
name = "manufacturing storage unit"
desc = "Its basically a box. Receives resources (if anchored). Needs a machine to take stuff out of without dumping everything out."
icon_state = "box"
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 10)
/// how much can we hold
var/max_stuff = 16
diff --git a/code/modules/mapfluff/ruins/lavaland_ruin_code.dm b/code/modules/mapfluff/ruins/lavaland_ruin_code.dm
index 18db493cf84..1e5d10beb0f 100644
--- a/code/modules/mapfluff/ruins/lavaland_ruin_code.dm
+++ b/code/modules/mapfluff/ruins/lavaland_ruin_code.dm
@@ -35,6 +35,7 @@
icon_state = "shell_unfinished"
desc = "The incomplete body of a golem. Add ten sheets of certain minerals to finish."
w_class = WEIGHT_CLASS_BULKY
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 20)
/// Amount of minerals you need to feed the shell to wake it up
var/required_stacks = 10
/// Type of shell to create
@@ -65,3 +66,4 @@
/obj/item/golem_shell/servant
name = "incomplete servant golem shell"
shell_type = /obj/effect/mob_spawn/ghost_role/human/golem/servant
+ custom_materials = list(/datum/material/adamantine = SHEET_MATERIAL_AMOUNT * 3)
diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm b/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm
index 2fe5f1a9b45..9e2c52e453f 100644
--- a/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm
+++ b/code/modules/mapfluff/ruins/lavalandruin_code/elephantgraveyard.dm
@@ -6,7 +6,7 @@
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)
+ custom_materials = list(/datum/material/bone= SHEET_MATERIAL_AMOUNT * 5)
abstract_type = /obj/structure/statue/bone
/obj/structure/statue/bone/Initialize(mapload)
@@ -17,7 +17,7 @@
/obj/structure/statue/bone/rib
name = "colossal rib"
desc = "It's staggering to think that something this big could have lived, let alone died."
- custom_materials = list(/datum/material/bone=SHEET_MATERIAL_AMOUNT*4)
+ custom_materials = list(/datum/material/bone=SHEET_MATERIAL_AMOUNT * 10)
icon = 'icons/obj/art/statuelarge.dmi'
icon_state = "rib"
icon_preview = 'icons/obj/fluff/previews.dmi'
@@ -26,7 +26,7 @@
/obj/structure/statue/bone/skull
name = "colossal skull"
desc = "The gaping maw of a dead, titanic monster."
- custom_materials = list(/datum/material/bone=SHEET_MATERIAL_AMOUNT*12)
+ custom_materials = list(/datum/material/bone=SHEET_MATERIAL_AMOUNT * 6)
icon = 'icons/obj/art/statuelarge.dmi'
icon_state = "skull"
icon_preview = 'icons/obj/fluff/previews.dmi'
@@ -34,7 +34,7 @@
/obj/structure/statue/bone/skull/half
desc = "The gaping maw of a dead, titanic monster. This one is cracked in half."
- custom_materials = list(/datum/material/bone=SHEET_MATERIAL_AMOUNT*6)
+ custom_materials = list(/datum/material/bone=SHEET_MATERIAL_AMOUNT * 3)
icon = 'icons/obj/art/statuelarge.dmi'
icon_state = "skull-half"
icon_preview = 'icons/obj/fluff/previews.dmi'
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 706235be089..e4af185ebb5 100644
--- a/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm
+++ b/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm
@@ -38,7 +38,7 @@
hitsound = 'sound/items/weapons/bladeslice.ogg'
/obj/item/knife/envy/afterattack(atom/target, mob/living/carbon/human/user, list/modifiers, list/attack_modifiers)
- if(!istype(user) || !ishuman(target))
+ if(!istype(user) || !ishuman(target) || QDELETED(target))
return
var/mob/living/carbon/human/H = target
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/caravanambush.dm b/code/modules/mapfluff/ruins/spaceruin_code/caravanambush.dm
index b765a7b4878..22029f60a01 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/caravanambush.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/caravanambush.dm
@@ -26,6 +26,8 @@
worn_icon_state = "cutters"
toolspeed = 0.3
random_color = FALSE
+ greyscale_config = null
+ greyscale_colors = null
/obj/item/crowbar/red/caravan
icon_state = "crowbar_caravan"
diff --git a/code/modules/mapping/access_helpers.dm b/code/modules/mapping/access_helpers.dm
index fafed05d1ce..16132ef31af 100644
--- a/code/modules/mapping/access_helpers.dm
+++ b/code/modules/mapping/access_helpers.dm
@@ -862,6 +862,11 @@
access_list += ACCESS_VAULT
return access_list
+/obj/effect/mapping_helpers/airlock/access/all/supply/bit_den/get_access()
+ var/list/access_list = ..()
+ access_list += ACCESS_BIT_DEN
+ return access_list
+
// -------------------- Syndicate access helpers
/obj/effect/mapping_helpers/airlock/access/all/syndicate
icon_state = "access_helper_syn"
diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm
index e1f84e9ba24..66d68a7e60d 100644
--- a/code/modules/mapping/mapping_helpers.dm
+++ b/code/modules/mapping/mapping_helpers.dm
@@ -238,7 +238,15 @@
/obj/effect/mapping_helpers/airlock/unres/payload(obj/machinery/door/airlock/airlock)
airlock.unres_sides ^= dir
- airlock.unres_sensor = TRUE
+ airlock.unres_latch = TRUE
+
+/obj/effect/mapping_helpers/airlock/unres/delayed
+ name = "airlock unrestricted side delayed helper"
+ icon_state = "airlock_unres_delayed_helper"
+
+/obj/effect/mapping_helpers/airlock/unres/delayed/payload(obj/machinery/door/airlock/airlock)
+ . = ..()
+ airlock.delayed_unres_open = TRUE
/obj/effect/mapping_helpers/airlock/abandoned
name = "airlock abandoned helper"
diff --git a/code/modules/mapping/reader.dm b/code/modules/mapping/reader.dm
index e5a323d0725..fcb5b445b41 100644
--- a/code/modules/mapping/reader.dm
+++ b/code/modules/mapping/reader.dm
@@ -102,7 +102,7 @@
/// Matches key formats in TMG (IE: newline after the \()
var/static/regex/matches_tgm = new(@'^"[A-z]*"[\s]*=[\s]*\([\s]*\n', "m")
/// Pulls out key value pairs for TGM
- var/static/regex/var_edits_tgm = new(@'^\t([A-z]*) = (.*?);?$')
+ var/static/regex/var_edits_tgm = new(@'^\t([A-z0-9]*) = (.*?);?$')
/// Pulls out model paths for DMM
var/static/regex/model_path = new(@'(\/[^\{]*?(?:\{.*?\})?)(?:,|$)', "g")
diff --git a/code/modules/mapping/ruins.dm b/code/modules/mapping/ruins.dm
index 2e81e198737..94d5dd6195c 100644
--- a/code/modules/mapping/ruins.dm
+++ b/code/modules/mapping/ruins.dm
@@ -206,7 +206,11 @@
forced_ruins[linked] = SSmapping.get_isolated_ruin_z()
- log_mapping("Successfully placed [current_pick.name] ruin.")
+ var/bottom_left_x = placed_turf.x - round(current_pick.width/2)
+ var/bottom_left_y = placed_turf.y - round(current_pick.height/2)
+ var/top_right_x = bottom_left_x + current_pick.width - 1
+ var/top_right_y = bottom_left_y + current_pick.height - 1
+ log_mapping("Successfully placed [current_pick.name] ruin ([bottom_left_x],[bottom_left_y],[placed_turf.z] to [top_right_x],[top_right_y],[placed_turf.z]).")
//Update the available list
for(var/datum/map_template/ruin/R in ruins_available)
diff --git a/code/modules/meteors/meteor_types.dm b/code/modules/meteors/meteor_types.dm
index bdbdaf883d5..ea0adbb4b01 100644
--- a/code/modules/meteors/meteor_types.dm
+++ b/code/modules/meteors/meteor_types.dm
@@ -84,7 +84,8 @@
/obj/effect/meteor/proc/on_loop_stopped(datum/source)
SIGNAL_HANDLER
- qdel(src)
+ if(!move_packet || !length(move_packet.existing_loops))
+ qdel(src)
///Deals with what happens when we stop moving, IE we die
/obj/effect/meteor/proc/moved_off_z()
@@ -116,14 +117,53 @@
/obj/effect/meteor/examine(mob/user)
. = ..()
+ if((user.mind?.get_skill_level(/datum/skill/athletics) >= SKILL_LEVEL_LEGENDARY))
+ . += span_notice("On second thought, it doesn't look too tough.")
check_examine_award(user)
-/obj/effect/meteor/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
- if(I.tool_behaviour == TOOL_MINING)
+/obj/effect/meteor/attack_hand(mob/user, list/modifiers)
+ if(!isliving(user))
+ return ..()
+ var/mob/living/livinguser = user
+
+ if(livinguser.combat_mode && livinguser.mind?.get_skill_level(/datum/skill/athletics) >= SKILL_LEVEL_LEGENDARY)
+ check_punch_award(livinguser)
+ playsound(loc, SFX_PUNCH, 50, TRUE)
+ redirect(livinguser)
+ return TRUE
+
+ return ..()
+
+/obj/effect/meteor/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
+ if(attacking_item.tool_behaviour == TOOL_MINING)
make_debris()
qdel(src)
- else
- . = ..()
+
+ else if (istype(attacking_item, /obj/item/melee/baseball_bat))
+ if(user.mind?.get_skill_level(/datum/skill/athletics) >= SKILL_LEVEL_EXPERT)
+ playsound(src, 'sound/items/baseballhit.ogg', 100, TRUE)
+ redirect(user)
+ return TRUE
+ to_chat(user, span_warning("\The [src] is too heavy for you!"))
+
+ else if (istype(attacking_item, /obj/item/melee/powerfist))
+ var/obj/item/melee/powerfist/fist = attacking_item
+ if(!fist.tank)
+ to_chat(user, span_warning("\The [fist] has no gas tank!"))
+ return ..()
+ var/datum/gas_mixture/gas_used = fist.tank.remove_air(fist.gas_per_fist * 3) // 3 is HIGH_PRESSURE setting on powerfist.
+ if(!gas_used || !molar_cmp_equals(gas_used.total_moles(), fist.gas_per_fist * 3))
+ to_chat(user, span_warning("\The [fist] didn't have enough gas to budge \the [src]!"))
+ return ..()
+ playsound(src, 'sound/items/weapons/resonator_blast.ogg', 50, TRUE)
+ redirect(user)
+ return TRUE
+
+ return ..()
+
+/obj/effect/meteor/proc/redirect(mob/athlete)
+ dest = spaceDebrisStartLoc(get_cardinal_dir(athlete, src), z)
+ chase_target(dest)
/obj/effect/meteor/proc/make_debris()
for(var/throws = dropamt, throws > 0, throws--)
@@ -159,6 +199,11 @@
if(!(flags_1 & ADMIN_SPAWNED_1) && isliving(user))
user.client.give_award(/datum/award/achievement/misc/meteor_examine, user)
+
+/obj/effect/meteor/proc/check_punch_award(mob/user)
+ if(!(flags_1 & ADMIN_SPAWNED_1) && isliving(user))
+ user.client.give_award(/datum/award/achievement/misc/meteor_punch, user)
+
/**
* Handles the meteor's interaction with meteor shields.
*
@@ -203,6 +248,9 @@
/obj/effect/meteor/sand/check_examine_award(mob/user) //Too insignificant and predictable to warrant an award.
return
+/obj/effect/meteor/sand/check_punch_award(mob/user)
+ return
+
//Dust
/obj/effect/meteor/dust
name = "space dust"
diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm
index 4c3a9cb529b..e3b92dda982 100644
--- a/code/modules/mining/equipment/explorer_gear.dm
+++ b/code/modules/mining/equipment/explorer_gear.dm
@@ -194,6 +194,7 @@
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS
max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 6)
/obj/item/clothing/suit/armor/bone/Initialize(mapload)
. = ..()
@@ -214,6 +215,7 @@
max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT
armor_type = /datum/armor/hooded_explorer
resistance_flags = FIRE_PROOF
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 4)
/obj/item/clothing/head/helmet/skull/Initialize(mapload)
. = ..()
diff --git a/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher.dm
index baf71f5cbe3..55533341d89 100644
--- a/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher.dm
+++ b/code/modules/mining/equipment/kinetic_crusher/kinetic_crusher.dm
@@ -12,6 +12,7 @@
suicidal miners against local fauna."
icon = 'icons/obj/mining.dmi'
icon_state = "crusher"
+ base_icon_state = "crusher"
inhand_icon_state = "crusher0"
icon_angle = -45
lefthand_file = 'icons/mob/inhands/weapons/hammers_lefthand.dmi'
@@ -56,25 +57,43 @@
var/detonation_damage = 50
/// Damage that the mark additionally does when hit by the crusher via backstab
var/backstab_bonus = 30
- /// Used by retool kits when changing the crusher's appearance
- var/current_inhand_icon_state = "crusher"
/// The file in which our projectile icon resides
var/projectile_icon = 'icons/obj/weapons/guns/projectiles.dmi'
/// Used by retool kits when changing the crusher's projectile sprite
var/projectile_icon_state = "pulse1"
/// Wielded damage we deal, aka our "real" damage
var/force_wielded = 20
+ /// Set to TRUE if the last projectile fired was point-blank at a living target
+ var/last_projectile_pb = FALSE
/obj/item/kinetic_crusher/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/butchering, \
+ AddComponent( \
+ /datum/component/butchering, \
speed = 6 SECONDS, \
effectiveness = 110, \
)
+ update_reskin(null)
//technically it's huge and bulky, but this provides an incentive to use it
update_wielding()
register_context()
+/**
+ * Adds or updates the reskinning component on the crusher.
+ *
+ * * default_skin_typepath: The typepath of skin to apply by default.
+ * Passing null will either not apply a skin or will reset it to default if one is already applied.
+ * If a supplied skin is blacklisted, it will be un-blacklisted.
+ */
+/obj/item/kinetic_crusher/proc/update_reskin(datum/atom_skin/crusher_skin/default_skin_typepath)
+ AddComponent( \
+ /datum/component/reskinable_item, \
+ /datum/atom_skin/crusher_skin, \
+ infinite = TRUE, \
+ initial_skin = default_skin_typepath ? default_skin_typepath::preview_name : null, \
+ blacklisted_subtypes = subtypesof(/datum/atom_skin/crusher_skin/locked) - default_skin_typepath, \
+ )
+
/obj/item/kinetic_crusher/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
if(!held_item)
@@ -236,6 +255,7 @@
var/turf/proj_turf = user.loc
if(!isturf(proj_turf))
return
+ last_projectile_pb = get_dist(target, user) <= 1 && isliving(target)
var/obj/projectile/destabilizer/destabilizer = new(proj_turf)
SEND_SIGNAL(src, COMSIG_CRUSHER_FIRED_BLAST, target, user, destabilizer)
destabilizer.icon = projectile_icon
@@ -281,7 +301,7 @@
return TRUE
/obj/item/kinetic_crusher/update_icon_state()
- inhand_icon_state = "[current_inhand_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]" // this is not icon_state and not supported by 2hcomponent
+ inhand_icon_state = "[base_icon_state][HAS_TRAIT(src, TRAIT_WIELDED)]" // this is not icon_state and not supported by 2hcomponent
return ..()
/obj/item/kinetic_crusher/update_overlays()
diff --git a/code/modules/mining/equipment/kinetic_crusher/trophies_misc.dm b/code/modules/mining/equipment/kinetic_crusher/trophies_misc.dm
index c4ff738b9cb..dca037e37cd 100644
--- a/code/modules/mining/equipment/kinetic_crusher/trophies_misc.dm
+++ b/code/modules/mining/equipment/kinetic_crusher/trophies_misc.dm
@@ -1,164 +1,95 @@
-/*!
- * Contains crusher trophies that are not obtained from fauna
- */
-/// Cosmetic items for changing the crusher's look
-/obj/item/crusher_trophy/retool_kit
- name = "crusher retool kit"
- desc = "A toolkit for changing the crusher's appearance without affecting the device's function."
- icon = 'icons/obj/mining.dmi'
- icon_state = "retool_kit"
- denied_type = /obj/item/crusher_trophy/retool_kit
-
- /// Currently picked crusher reskin
- var/datum/crusher_skin/active_skin = /datum/crusher_skin/sword
- /// If this kit forces some specific skin, or can pick between subtypes
- var/forced_skin
-
-/obj/item/crusher_trophy/retool_kit/Destroy(force)
- if (istype(active_skin))
- QDEL_NULL(active_skin)
- return ..()
-
-/obj/item/crusher_trophy/retool_kit/effect_desc()
- return "the crusher to have the appearance of \a [active_skin::name]"
-
-/obj/item/crusher_trophy/retool_kit/add_to(obj/item/kinetic_crusher/pkc, mob/user)
- if (!forced_skin)
- var/list/choices = list()
- for (var/datum/crusher_skin/skin as anything in subtypesof(/datum/crusher_skin))
- if (skin::normal_skin)
- choices[skin] = icon(skin::retool_icon || 'icons/obj/mining.dmi', skin::retool_icon_state)
- var/datum/crusher_skin/chosen_skin = show_radial_menu(user, src, choices, tooltips = TRUE, require_near = TRUE)
- if (!chosen_skin)
- return
- active_skin = chosen_skin
- else
- active_skin = forced_skin
- . = ..()
- if(!.)
- return
- active_skin = new active_skin(pkc)
- if (active_skin.retool_icon)
- pkc.icon = active_skin.retool_icon
- pkc.icon_state = active_skin.retool_icon_state
- pkc.current_inhand_icon_state = active_skin.retool_inhand_icon
- if (active_skin.retool_projectile_icon)
- pkc.projectile_icon = active_skin.retool_projectile_icon
- if (active_skin.retool_projectile_icon_state)
- pkc.projectile_icon_state = active_skin.retool_projectile_icon_state
- // Should either have both, or neither
- if (active_skin.retool_lefthand_file)
- pkc.lefthand_file = active_skin.retool_lefthand_file
- pkc.righthand_file = active_skin.retool_righthand_file
- if(active_skin.retool_worn_file)
- pkc.worn_icon = active_skin.retool_worn_file
- pkc.worn_icon_state = active_skin::retool_icon_state
- if (active_skin.retool_inhand_x)
- pkc.inhand_x_dimension = active_skin.retool_inhand_x
- if (active_skin.retool_inhand_y)
- pkc.inhand_y_dimension = active_skin.retool_inhand_y
- pkc.update_appearance()
- pkc.update_slot_icon()
-
-/obj/item/crusher_trophy/retool_kit/remove_from(obj/item/kinetic_crusher/pkc)
- var/skin_type = active_skin.type
- qdel(active_skin)
- active_skin = skin_type
- pkc.icon = initial(pkc.icon)
- pkc.icon_state = initial(pkc.icon_state)
- pkc.current_inhand_icon_state = initial(pkc.current_inhand_icon_state)
- pkc.projectile_icon = initial(pkc.projectile_icon)
- pkc.projectile_icon_state = initial(pkc.projectile_icon_state)
- pkc.lefthand_file = initial(pkc.lefthand_file)
- pkc.righthand_file = initial(pkc.righthand_file)
- pkc.worn_icon = initial(pkc.worn_icon)
- pkc.worn_icon_state = initial(pkc.worn_icon_state)
- pkc.inhand_x_dimension = initial(pkc.inhand_x_dimension)
- pkc.inhand_y_dimension = initial(pkc.inhand_y_dimension)
- pkc.update_appearance()
- pkc.update_slot_icon()
- return ..()
-
-/// Alternate PKC skins
-/datum/crusher_skin
- /// Name of the modification
- var/name = "error that should be reported to coders"
- /// Specifies the icon file in which the crusher's new state is stored.
- var/retool_icon = 'icons/obj/mining.dmi'
- ///Specifies the sprite/icon state which the crusher is changed to as an item. Should appear in the icons/obj/mining.dmi file with accompanying "lit" and "recharging" sprites
- var/retool_icon_state = "ipickaxe"
- ///Specifies the icon state for the crusher's appearance in hand. Should appear in both retool_lefthand_file and retool_righthand_file.
- var/retool_inhand_icon = "ipickaxe"
+// Alternate PKC skins
+/datum/atom_skin/crusher_skin
+ abstract_type = /datum/atom_skin/crusher_skin
+ change_base_icon_state = TRUE
+ new_icon = 'icons/obj/mining.dmi'
+ new_icon_state = "ipickaxe"
+ /// Specifies the icon state for the crusher's appearance in hand. Should appear in both new_lefthand_file and new_righthand_file.
+ var/new_inhand_icon = "ipickaxe"
/// Specifies the icon file in which the crusher's projectile sprite is located.
- var/retool_projectile_icon = 'icons/obj/weapons/guns/projectiles.dmi'
- ///For if the retool kit changes the projectile's appearance.
- var/retool_projectile_icon_state = null
+ var/new_projectile_icon = 'icons/obj/weapons/guns/projectiles.dmi'
+ /// For if the retool kit changes the projectile's appearance.
+ var/new_projectile_icon_state
/// Specifies the left hand inhand icon file. Don't forget to set the right hand file as well.
- var/retool_lefthand_file = null
+ var/new_lefthand_file
/// Specifies the right hand inhand icon file. Don't forget to set the left hand file as well.
- var/retool_righthand_file = null
+ var/new_righthand_file
/// Specifies the worn icon file.
- var/retool_worn_file = null
+ var/new_worn_file
/// Specifies the X dimensions of the new inhand, only relevant with different inhand files.
- var/retool_inhand_x = null
+ var/new_inhandx
/// Specifies the Y dimensions of the new inhand, only relevant with different inhand files.
- var/retool_inhand_y = null
- /// Can this skin be normally selected by a generic retool kit?
- var/normal_skin = TRUE
- /// Crusher this skin is attached to
- var/obj/item/kinetic_crusher/crusher
+ var/new_inhandy
-/datum/crusher_skin/New(obj/item/kinetic_crusher/new_crusher)
+/datum/atom_skin/crusher_skin/apply(obj/item/kinetic_crusher/apply_to)
. = ..()
- crusher = new_crusher
+ APPLY_VAR_OR_RESET_INITIAL(apply_to, inhand_icon_state, new_inhand_icon, reset_missing)
+ APPLY_VAR_OR_RESET_INITIAL(apply_to, projectile_icon, new_projectile_icon, reset_missing)
+ APPLY_VAR_OR_RESET_INITIAL(apply_to, projectile_icon_state, new_projectile_icon_state, reset_missing)
+ APPLY_VAR_OR_RESET_INITIAL(apply_to, lefthand_file, new_lefthand_file, reset_missing)
+ APPLY_VAR_OR_RESET_INITIAL(apply_to, righthand_file, new_righthand_file, reset_missing)
+ APPLY_VAR_OR_RESET_INITIAL(apply_to, worn_icon, new_worn_file, reset_missing)
+ APPLY_VAR_OR_RESET_INITIAL(apply_to, worn_icon_state, new_icon_state, reset_missing)
+ APPLY_VAR_OR_RESET_INITIAL(apply_to, inhand_x_dimension, new_inhandx, reset_missing)
+ APPLY_VAR_OR_RESET_INITIAL(apply_to, inhand_y_dimension, new_inhandy, reset_missing)
-/datum/crusher_skin/Destroy(force)
- crusher = null
- return ..()
-
-/datum/crusher_skin/sword
- name = "sword"
- retool_icon_state = "crusher_sword"
- retool_inhand_icon = "crusher_sword"
-
-/datum/crusher_skin/harpoon
- name = "harpoon"
- retool_icon_state = "crusher_harpoon"
- retool_inhand_icon = "crusher_harpoon"
- retool_projectile_icon_state = "pulse_harpoon"
-
-/datum/crusher_skin/harpoon/New(obj/item/kinetic_crusher/new_crusher)
+/datum/atom_skin/crusher_skin/clear_skin(obj/item/kinetic_crusher/clear_from)
. = ..()
- RegisterSignal(crusher, COMSIG_ITEM_ATTACK_ANIMATION, PROC_REF(on_attack_animation))
+ RESET_INITIAL_IF_SET(clear_from, inhand_icon_state, new_inhand_icon)
+ RESET_INITIAL_IF_SET(clear_from, projectile_icon, new_projectile_icon)
+ RESET_INITIAL_IF_SET(clear_from, projectile_icon_state, new_projectile_icon_state)
+ RESET_INITIAL_IF_SET(clear_from, lefthand_file, new_lefthand_file)
+ RESET_INITIAL_IF_SET(clear_from, righthand_file, new_righthand_file)
+ RESET_INITIAL_IF_SET(clear_from, worn_icon, new_worn_file)
+ RESET_INITIAL_IF_SET(clear_from, worn_icon_state, new_icon_state)
+ RESET_INITIAL_IF_SET(clear_from, inhand_x_dimension, new_inhandx)
+ RESET_INITIAL_IF_SET(clear_from, inhand_y_dimension, new_inhandy)
-/datum/crusher_skin/harpoon/Destroy(force)
- UnregisterSignal(crusher, COMSIG_ITEM_ATTACK_ANIMATION)
- return ..()
+/datum/atom_skin/crusher_skin/sword
+ new_name = "proto-kinetic sword"
+ preview_name = "Sword"
+ new_icon_state = "crusher_sword"
+ new_inhand_icon = "crusher_sword"
-/datum/crusher_skin/harpoon/proc/on_attack_animation(obj/item/source, atom/movable/attacker, atom/attacked_atom, animation_type, list/image_override, list/animation_override)
+/datum/atom_skin/crusher_skin/harpoon
+ new_name = "proto-kinetic harpoon"
+ preview_name = "Harpoon"
+ new_icon_state = "crusher_harpoon"
+ new_inhand_icon = "crusher_harpoon"
+ new_projectile_icon_state = "pulse_harpoon"
+
+/datum/atom_skin/crusher_skin/harpoon/apply(atom/apply_to)
+ . = ..()
+ RegisterSignal(apply_to, COMSIG_ITEM_ATTACK_ANIMATION, PROC_REF(on_attack_animation))
+
+/datum/atom_skin/crusher_skin/harpoon/clear_skin(atom/clear_from)
+ . = ..()
+ UnregisterSignal(clear_from, COMSIG_ITEM_ATTACK_ANIMATION)
+
+/datum/atom_skin/crusher_skin/harpoon/proc/on_attack_animation(obj/item/source, atom/movable/attacker, atom/attacked_atom, animation_type, list/image_override, list/animation_override)
SIGNAL_HANDLER
// If nothing is forcing an animation type, attack with a piercing animation because we're a harpoon
if (!animation_type)
animation_override += ATTACK_ANIMATION_PIERCE
-/datum/crusher_skin/dagger
- name = "dual dagger and blaster"
- retool_icon_state = "crusher_dagger"
- retool_inhand_icon = "crusher_dagger"
- /// Are we doing a blaster animation right now?
- var/blaster_strike = FALSE
+/datum/atom_skin/crusher_skin/dagger
+ new_name = "proto-kinetic dual dagger and blaster"
+ preview_name = "Dagger and Blaster"
+ new_icon_state = "crusher_dagger"
+ new_inhand_icon = "crusher_dagger"
-/datum/crusher_skin/dagger/New(obj/item/kinetic_crusher/new_crusher)
+/datum/atom_skin/crusher_skin/dagger/apply(atom/apply_to)
. = ..()
- RegisterSignal(crusher, COMSIG_ITEM_ATTACK_ANIMATION, PROC_REF(on_attack_animation))
- RegisterSignal(crusher, COMSIG_CRUSHER_FIRED_BLAST, PROC_REF(on_fired_blast))
+ RegisterSignal(apply_to, COMSIG_ITEM_ATTACK_ANIMATION, PROC_REF(on_attack_animation))
+ RegisterSignal(apply_to, COMSIG_CRUSHER_FIRED_BLAST, PROC_REF(on_fired_blast))
-/datum/crusher_skin/dagger/Destroy(force)
- UnregisterSignal(crusher, list(COMSIG_ITEM_ATTACK_ANIMATION, COMSIG_CRUSHER_FIRED_BLAST))
- return ..()
+/datum/atom_skin/crusher_skin/dagger/clear_skin(atom/clear_from)
+ . = ..()
+ UnregisterSignal(clear_from, COMSIG_ITEM_ATTACK_ANIMATION)
+ UnregisterSignal(clear_from, COMSIG_CRUSHER_FIRED_BLAST)
-/datum/crusher_skin/dagger/proc/on_attack_animation(obj/item/kinetic_crusher/source, atom/movable/attacker, atom/attacked_atom, animation_type, list/image_override, list/animation_override, list/angle_override)
+/datum/atom_skin/crusher_skin/dagger/proc/on_attack_animation(obj/item/kinetic_crusher/source, atom/movable/attacker, atom/attacked_atom, animation_type, list/image_override, list/animation_override, list/angle_override)
SIGNAL_HANDLER
// If we've been forcefully assigned an animation type already, we shouldn't do the custom attack animation logic
@@ -167,11 +98,11 @@
if (isliving(attacked_atom))
var/mob/living/target = attacked_atom
- if (blaster_strike)
+ if (source.last_projectile_pb)
image_override += image(icon = 'icons/obj/mining.dmi', icon_state = "crusher_dagger_blaster")
angle_override += 0
animation_override += ATTACK_ANIMATION_PIERCE
- blaster_strike = FALSE
+ source.last_projectile_pb = FALSE
return
if (target.has_status_effect(/datum/status_effect/crusher_mark))
@@ -179,33 +110,62 @@
image_override += image(icon = 'icons/obj/mining.dmi', icon_state = "crusher_dagger_melee")
-/datum/crusher_skin/dagger/proc/on_fired_blast(obj/item/kinetic_crusher/source, atom/target, mob/living/user, obj/projectile/destabilizer/destabilizer)
+/datum/atom_skin/crusher_skin/dagger/proc/on_fired_blast(obj/item/kinetic_crusher/source, atom/target, mob/living/user, obj/projectile/destabilizer/destabilizer)
SIGNAL_HANDLER
if (isliving(target) && get_dist(target, user) <= 1)
- blaster_strike = TRUE
user.do_item_attack_animation(target, used_item = source)
-/datum/crusher_skin/glaive
- name = "glaive"
- retool_icon_state = "crusher_glaive"
- retool_inhand_icon = "crusher_glaive"
- retool_lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
- retool_righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
- retool_inhand_x = 64
- retool_inhand_y = 64
+/datum/atom_skin/crusher_skin/glaive
+ new_name = "proto-kinetic glaive"
+ preview_name = "Glaive"
+ new_icon_state = "crusher_glaive"
+ new_inhand_icon = "crusher_glaive"
+ new_lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi'
+ new_righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
+ new_inhandx = 64
+ new_inhandy = 64
+
+// Locked skins that cannot be selected normally
+/datum/atom_skin/crusher_skin/locked
+ abstract_type = /datum/atom_skin/crusher_skin/locked
+
+/datum/atom_skin/crusher_skin/locked/ashen_skull
+ preview_name = "Skull"
+ new_icon_state = "crusher_skull"
+ new_inhand_icon = "crusher_skull"
+ new_projectile_icon_state = "pulse_skull"
+
+/// Unlockable (or forced) skins
+/obj/item/crusher_trophy/retool_kit
+ name = "crusher retool kit"
+ desc = "A toolkit for changing the crusher's appearance without affecting the device's function."
+ icon = 'icons/obj/mining.dmi'
+ icon_state = "retool_kit"
+ denied_type = /obj/item/crusher_trophy/retool_kit
+
+ /// What skin do we apply when attached
+ var/datum/atom_skin/crusher_skin/forced_skin
+
+/obj/item/crusher_trophy/retool_kit/effect_desc()
+ return "the crusher to have the appearance of \a [forced_skin::preview_name]"
+
+/obj/item/crusher_trophy/retool_kit/add_to(obj/item/kinetic_crusher/pkc, mob/user)
+ . = ..()
+ if(!.)
+ return
+
+ pkc.update_reskin(forced_skin)
+
+/obj/item/crusher_trophy/retool_kit/remove_from(obj/item/kinetic_crusher/pkc)
+ pkc.update_reskin(null) // resets reskin component
+ return ..()
/obj/item/crusher_trophy/retool_kit/ashenskull
name = "ashen skull"
desc = "It burns with the flame of the necropolis, whispering in your ear. It demands to be bound to a suitable weapon."
icon_state = "retool_kit_skull"
- forced_skin = /datum/crusher_skin/ashen_skull
+ forced_skin = /datum/atom_skin/crusher_skin/locked/ashen_skull
/obj/item/crusher_trophy/retool_kit/ashenskull/effect_desc()
return "the crusher to appear corrupted by infernal powers"
-
-/datum/crusher_skin/ashen_skull
- retool_icon_state = "crusher_skull"
- retool_inhand_icon = "crusher_skull"
- retool_projectile_icon_state = "pulse_skull"
- normal_skin = FALSE
diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm
index 3c24a608262..73ebdf10a71 100644
--- a/code/modules/mining/equipment/mining_tools.dm
+++ b/code/modules/mining/equipment/mining_tools.dm
@@ -181,6 +181,7 @@
attack_verb_simple = list("slash", "impale", "stab", "slice")
sharpness = SHARP_EDGED
item_flags = CRUEL_IMPLEMENT
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 4, /datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.5)
/obj/item/shovel/serrated/Initialize(mapload)
. = ..()
@@ -322,6 +323,7 @@
item_flags = SLOWS_WHILE_IN_HAND | IMMUTABLE_SLOW
slowdown = 3
attack_speed = 1.2 SECONDS
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 6.6, /datum/material/alloy/plasteel = SHEET_MATERIAL_AMOUNT * 5)
/// The factor at which the recoil becomes less.
var/recoil_factor = 3
/// Wether we knock down and launch away out enemies when we attack.
diff --git a/code/modules/mining/equipment/monster_organs/brimdust_sac.dm b/code/modules/mining/equipment/monster_organs/brimdust_sac.dm
index 14006b0f726..bcdd0b640a2 100644
--- a/code/modules/mining/equipment/monster_organs/brimdust_sac.dm
+++ b/code/modules/mining/equipment/monster_organs/brimdust_sac.dm
@@ -143,7 +143,7 @@
/datum/status_effect/stacking/brimdust_coating/on_creation(mob/living/new_owner, stacks_to_apply)
. = ..()
- linked_alert?.icon_state = "brimdemon_[stacks]"
+ linked_alert?.overlay_state = "brimdemon_[stacks]"
/datum/status_effect/stacking/brimdust_coating/on_apply()
. = ..()
diff --git a/code/modules/mining/lavaland/ash_flora.dm b/code/modules/mining/lavaland/ash_flora.dm
index 8bb031a88ef..38d6fd87aa4 100644
--- a/code/modules/mining/lavaland/ash_flora.dm
+++ b/code/modules/mining/lavaland/ash_flora.dm
@@ -10,7 +10,6 @@
resistance_flags = LAVA_PROOF
gender = PLURAL
layer = PROJECTILE_HIT_THRESHHOLD_LAYER //sporangiums up don't shoot
- product_types = list(/obj/item/food/grown/ash_flora/shavings = 1)
harvest_with_hands = TRUE
harvested_name = "shortened mushrooms"
harvested_desc = "Some quickly regrowing mushrooms, formerly known to be quite large."
@@ -27,6 +26,9 @@
base_icon_state = "[base_icon_state][rand(1, number_of_variants)]"
icon_state = base_icon_state
+/obj/structure/flora/ash/get_potential_products()
+ return list(/obj/item/food/grown/ash_flora/shavings = 1)
+
/obj/structure/flora/ash/harvest(user, product_amount_multiplier)
if(!..())
return FALSE
@@ -45,7 +47,6 @@
desc = "A number of mushrooms, each of which surrounds a greenish sporangium with a number of leaf-like structures."
icon_state = "s_mushroom1"
base_icon_state = "s_mushroom"
- product_types = list(/obj/item/food/grown/ash_flora/mushroom_leaf = 1)
harvested_name = "leafless mushrooms"
harvested_desc = "A bunch of formerly-leafed mushrooms, with their sporangiums exposed. Scandalous?"
harvest_amount_high = 4
@@ -56,12 +57,14 @@
regrowth_time_low = 2400
regrowth_time_high = 6000
+/obj/structure/flora/ash/leaf_shroom/get_potential_products()
+ return list(/obj/item/food/grown/ash_flora/mushroom_leaf = 1)
+
/obj/structure/flora/ash/cap_shroom
name = "tall mushrooms"
desc = "Several mushrooms, the larger of which have a ring of conks at the midpoint of their stems."
icon_state = "r_mushroom1"
base_icon_state = "r_mushroom"
- product_types = list(/obj/item/food/grown/ash_flora/mushroom_cap = 1)
harvested_name = "small mushrooms"
harvested_desc = "Several small mushrooms near the stumps of what likely were larger mushrooms."
harvest_amount_high = 4
@@ -72,6 +75,9 @@
regrowth_time_low = 3000
regrowth_time_high = 5400
+/obj/structure/flora/ash/cap_shroom/get_potential_products()
+ return list(/obj/item/food/grown/ash_flora/mushroom_cap = 1)
+
/obj/structure/flora/ash/stem_shroom
name = "numerous mushrooms"
desc = "A large number of mushrooms, some of which have long, fleshy stems. They're radiating light!"
@@ -79,7 +85,6 @@
base_icon_state = "t_mushroom"
light_range = 1.5
light_power = 2.1
- product_types = list(/obj/item/food/grown/ash_flora/mushroom_stem = 1)
harvested_name = "tiny mushrooms"
harvested_desc = "A few tiny mushrooms around larger stumps. You can already see them growing back."
harvest_amount_high = 4
@@ -90,12 +95,14 @@
regrowth_time_low = 3000
regrowth_time_high = 6000
+/obj/structure/flora/ash/stem_shroom/get_potential_products()
+ return list(/obj/item/food/grown/ash_flora/mushroom_stem = 1)
+
/obj/structure/flora/ash/cacti
name = "fruiting cacti"
desc = "Several prickly cacti, brimming with ripe fruit and covered in a thin layer of ash."
icon_state = "cactus1"
base_icon_state = "cactus"
- product_types = list(/obj/item/food/grown/ash_flora/cactus_fruit = 20, /obj/item/seeds/lavaland/cactus = 1)
harvested_name = "cacti"
harvested_desc = "A bunch of prickly cacti. You can see fruits slowly growing beneath the covering of ash."
harvest_amount_high = 2
@@ -111,12 +118,14 @@
. = ..()
AddComponent(/datum/component/caltrop, min_damage = 3, max_damage = 6, probability = 70)
+/obj/structure/flora/ash/cacti/get_potential_products()
+ return list(/obj/item/food/grown/ash_flora/cactus_fruit = 20, /obj/item/seeds/lavaland/cactus = 1)
+
/obj/structure/flora/ash/seraka
name = "seraka mushrooms"
desc = "A small cluster of seraka mushrooms. These must have come with the ashlizards."
icon_state = "seraka_mushroom1"
base_icon_state = "seraka_mushroom"
- product_types = list(/obj/item/food/grown/ash_flora/seraka = 1)
harvested_name = "harvested seraka mushrooms"
harvested_desc = "A couple of small seraka mushrooms, with the larger ones clearly having been recently removed. They'll grow back... eventually."
harvest_amount_high = 6
@@ -129,6 +138,9 @@
number_of_variants = 2
harvest_message_true_thresholds = FALSE
+/obj/structure/flora/ash/seraka/get_potential_products()
+ return list(/obj/item/food/grown/ash_flora/seraka = 1)
+
/obj/structure/flora/ash/fireblossom
name = "fire blossom"
desc = "An odd flower that grows commonly near bodies of lava."
@@ -137,7 +149,6 @@
light_range = LIGHT_FIRE_BLOSSOM
light_power = LIGHT_FIRE_BLOSSOM
light_color = COLOR_BIOLUMINESCENCE_YELLOW
- product_types = list(/obj/item/food/grown/ash_flora/fireblossom = 1)
harvested_name = "fire blossom stems"
harvested_desc = "A few fire blossom stems, missing their flowers."
harvest_amount_high = 3
@@ -148,6 +159,9 @@
regrowth_time_high = 4000
number_of_variants = 2
+/obj/structure/flora/ash/fireblossom/get_potential_products()
+ return list(/obj/item/food/grown/ash_flora/fireblossom = 1)
+
/obj/structure/flora/ash/fireblossom/after_harvest()
set_light_power(LIGHT_RANGE_FIRE_BLOSSOM_HARVESTED)
set_light_range(LIGHT_POWER_FIRE_BLOSSOM_HARVESTED)
@@ -166,7 +180,6 @@
desc = "A number of bright, springy blue fruiting plants. They seem to be unconcerned with the hardy, cold environment."
icon_state = "chilly_pepper1"
base_icon_state = "chilly_pepper"
- product_types = list(/obj/item/food/grown/icepepper = 1)
harvested_name = "springy grass"
harvested_desc = "A bunch of springy, bouncy fruiting grass, all picked. Or maybe they were never fruiting at all?"
harvest_amount_high = 3
@@ -178,6 +191,9 @@
regrowth_time_high = 5500
number_of_variants = 2
+/obj/structure/flora/ash/chilly/get_potential_products()
+ return list(/obj/item/food/grown/icepepper = 1)
+
//SNACKS
/obj/item/food/grown/ash_flora
@@ -413,6 +429,7 @@
icon_state = "mushroom_bowl"
fill_icon_state = "fullbowl"
fill_icon = 'icons/obj/mining_zones/ash_flora.dmi'
+ custom_materials = null
/obj/item/reagent_containers/cup/bowl/mushroom_bowl/update_icon_state()
if(!reagents.total_volume)
diff --git a/code/modules/mining/lavaland/mining_loot/cursed_katana.dm b/code/modules/mining/lavaland/mining_loot/cursed_katana.dm
index 8de94828932..4b0019166f7 100644
--- a/code/modules/mining/lavaland/mining_loot/cursed_katana.dm
+++ b/code/modules/mining/lavaland/mining_loot/cursed_katana.dm
@@ -107,7 +107,7 @@
return ..()
/obj/item/cursed_katana/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 || LEAP_ATTACK || OVERWHELMING_ATTACK))
+ if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK || attack_type == OVERWHELMING_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. Or a road roller, if one happened to hit you.
return ..()
diff --git a/code/modules/mining/lavaland/mining_loot/megafauna/ash_drake.dm b/code/modules/mining/lavaland/mining_loot/megafauna/ash_drake.dm
index cb095c47b27..76ab71678e3 100644
--- a/code/modules/mining/lavaland/mining_loot/megafauna/ash_drake.dm
+++ b/code/modules/mining/lavaland/mining_loot/megafauna/ash_drake.dm
@@ -5,6 +5,7 @@
desc = "The gathered remains of a drake. It still crackles with heat, and smells distinctly of brimstone."
icon = 'icons/obj/clothing/head/helmet.dmi'
icon_state = "dragon"
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 10)
/obj/item/drake_remains/Initialize(mapload)
. = ..()
@@ -27,6 +28,7 @@
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF | ACID_PROOF
transparent_protection = HIDEGLOVES|HIDESUITSTORAGE|HIDEJUMPSUIT|HIDESHOES
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 10)
/datum/armor/cloak_drake
melee = 65
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index fb2ae8dd46a..542e5b8598d 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -136,3 +136,4 @@
icon = 'icons/obj/mining_zones/artefacts.dmi'
icon_state = "skeleton_key"
w_class = WEIGHT_CLASS_SMALL
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 5)
diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm
index 5616080fa92..10b01bf5a8b 100644
--- a/code/modules/mining/ores_coins.dm
+++ b/code/modules/mining/ores_coins.dm
@@ -116,7 +116,7 @@
GLOBAL_LIST_INIT(sand_recipes, list(\
new /datum/stack_recipe("pile of dirt", /obj/machinery/hydroponics/soil, 3, time = 1 SECONDS, crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_ONE_PER_TURF | CRAFT_ON_SOLID_GROUND, category = CAT_TOOLS), \
- new /datum/stack_recipe("sandstone", /obj/item/stack/sheet/mineral/sandstone, 1, 1, 50, crafting_flags = NONE, category = CAT_MISC),\
+ new /datum/stack_recipe("sandstone", /obj/item/stack/sheet/mineral/sandstone, 1, 1, 50, crafting_flags = CRAFT_NO_MATERIALS, category = CAT_MISC),\
new /datum/stack_recipe("aesthetic volcanic floor tile", /obj/item/stack/tile/basalt, 2, 1, 50, crafting_flags = NONE, category = CAT_TILES)\
))
@@ -492,7 +492,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
/obj/item/coin/examine(mob/user)
. = ..()
- . += span_info("It's worth [value] credit\s.")
+ . += span_info("It's worth [value] [MONEY_NAME_AUTOPURAL(value)].")
/obj/item/coin/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers)
if(istype(W, /obj/item/stack/cable_coil))
diff --git a/code/modules/mining/satchel_ore_box.dm b/code/modules/mining/satchel_ore_box.dm
index 79bc8618a04..0c2011e2c54 100644
--- a/code/modules/mining/satchel_ore_box.dm
+++ b/code/modules/mining/satchel_ore_box.dm
@@ -8,6 +8,7 @@
desc = "A heavy wooden box, which can be filled with a lot of ores or boulders"
density = TRUE
pressure_resistance = 5 * ONE_ATMOSPHERE
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 4)
/obj/structure/ore_box/Initialize(mapload)
. = ..()
diff --git a/code/modules/mob/dead/new_player/logout.dm b/code/modules/mob/dead/new_player/logout.dm
index 45412c994bf..36dfe6c4aed 100644
--- a/code/modules/mob/dead/new_player/logout.dm
+++ b/code/modules/mob/dead/new_player/logout.dm
@@ -1,5 +1,5 @@
/mob/dead/new_player/Logout()
- ready = 0
+ ready = PLAYER_NOT_READY
..()
if(!spawning)//Here so that if they are spawning and log out, the other procs can play out and they will have a mob to come back to.
key = null//We null their key before deleting the mob, so they are properly kicked out.
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 366e43d32a3..361f111539d 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -8,7 +8,9 @@
//hud_type = /datum/hud/new_player SKYRAT EDIT REMOVAL
hud_possible = list()
- var/ready = FALSE
+ /// String Values tied to Defines that state whether the new_player is ready to play or not.
+ /// Do try your best to compare this value directly against the defines for certainty but helper procs do exist in bulkier situations.
+ var/ready = PLAYER_NOT_READY
/// Referenced when you want to delete the new_player later on in the code.
var/spawning = FALSE
/// For instant transfer once the round is set up
@@ -64,6 +66,12 @@
var/datum/poll_question/poll = locate(href_list["votepollref"]) in GLOB.polls
vote_on_poll_handler(poll, href_list)
+/// Quickly gets a boolean of whether the new_player is ready to play or not in places where we would like the boolean logic.
+/// The assertion is that readiness must be an opted in TRUE, while all other states (e.g. not ready, broken, etc) are FALSE.
+/// We organize it this way to ensure the system is extensible for other possible ready states.
+/mob/dead/new_player/proc/is_ready_to_play()
+ return ready == PLAYER_READY_TO_PLAY
+
//When you cop out of the round (NB: this HAS A SLEEP FOR PLAYER INPUT IN IT)
/mob/dead/new_player/proc/make_me_an_observer()
if(QDELETED(src) || !src.client)
diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm
index 880885e964d..30df9028eb7 100644
--- a/code/modules/mob/emote.dm
+++ b/code/modules/mob/emote.dm
@@ -12,7 +12,7 @@
#define BEYBLADE_CONFUSION_LIMIT (40 SECONDS)
//The code execution of the emote datum is located at code/datums/emotes.dm
-/mob/proc/emote(act, m_type = null, message = null, intentional = FALSE, force_silence = FALSE, forced = FALSE)
+/mob/proc/emote(act, type_override = NONE, message = null, intentional = FALSE, force_silence = FALSE, forced = FALSE)
var/param = message
var/custom_param = findchar(act, " ")
if(custom_param)
@@ -33,11 +33,11 @@
continue
if(!forced && !emote.can_run_emote(src, TRUE, intentional, param))
continue
- if(SEND_SIGNAL(src, COMSIG_MOB_PRE_EMOTED, emote.key, param, m_type, intentional, emote) & COMPONENT_CANT_EMOTE)
+ if(SEND_SIGNAL(src, COMSIG_MOB_PRE_EMOTED, emote.key, param, type_override, intentional, emote) & COMPONENT_CANT_EMOTE)
silenced = TRUE
continue
- emote.run_emote(src, param, m_type, intentional)
- SEND_SIGNAL(src, COMSIG_MOB_EMOTE, emote, act, m_type, message, intentional)
+ emote.run_emote(src, param, type_override, intentional)
+ SEND_SIGNAL(src, COMSIG_MOB_EMOTE, emote, act, type_override, message, intentional)
SEND_SIGNAL(src, COMSIG_MOB_EMOTED(emote.key))
return TRUE
if(intentional && !silenced && !force_silence)
diff --git a/code/modules/mob/eye/eye.dm b/code/modules/mob/eye/eye.dm
index 74efd39f610..fb3c2a4fab7 100644
--- a/code/modules/mob/eye/eye.dm
+++ b/code/modules/mob/eye/eye.dm
@@ -44,7 +44,7 @@
z_move_flags |= ZMOVE_IGNORE_OBSTACLES //cameras do not respect these FLOORS you speak so much of
return ..()
-/mob/eye/emote(act, m_type=1, message = null, intentional = FALSE, force_silence = FALSE, forced = FALSE)
+/mob/eye/emote(act, type_override = EMOTE_VISIBLE, message = null, intentional = FALSE, force_silence = FALSE, forced = FALSE)
if(has_emotes)
return ..()
return FALSE
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index b4125910449..cf23b717a94 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -181,7 +181,7 @@
I.do_pickup_animation(src)
if(get_item_for_held_index(hand_index))
dropItemToGround(get_item_for_held_index(hand_index), force = TRUE)
- I.forceMove(src)
+ I.forceMove(src) //this has to come before has_equipped() is called
held_items[hand_index] = I
SET_PLANE_EXPLICIT(I, ABOVE_HUD_PLANE, src)
if(I.pulledby)
@@ -440,6 +440,7 @@
item_dropping.layer = initial(item_dropping.layer)
SET_PLANE_EXPLICIT(item_dropping, initial(item_dropping.plane), newloc)
item_dropping.appearance_flags &= ~NO_CLIENT_COLOR
+ item_dropping.item_flags &= ~IN_INVENTORY //This has to come before MoveToNullspace/forceMove is called
if(!no_move && !(item_dropping.item_flags & DROPDEL)) //item may be moved/qdel'd immedietely, don't bother moving it
if (isnull(newloc))
item_dropping.moveToNullspace()
@@ -458,7 +459,7 @@
* * Optional - include_flags, (see obj.flags.dm) describes which optional things to include or not (pockets, accessories, held items)
*/
-/mob/living/proc/get_equipped_items(include_flags = NONE)
+/mob/proc/get_equipped_items(include_flags = NONE)
var/list/items = list()
for(var/obj/item/item_contents in contents)
if(item_contents.item_flags & IN_INVENTORY)
@@ -472,6 +473,12 @@
return items
+///Get all items in our possession that should affect our movespeed
+/mob/proc/get_equipped_speed_mod_items()
+ . = get_equipped_items(INCLUDE_ABSTRACT|INCLUDE_PROSTHETICS)
+ for(var/obj/item/thing in held_items)
+ if(thing.item_flags & SLOWS_WHILE_IN_HAND)
+ . += thing
/**
* Returns the items that were successfully unequipped.
*/
@@ -532,12 +539,16 @@
/// This proc is called after an item has been successfully handled and equipped to a slot.
/mob/proc/has_equipped(obj/item/item, slot, initial = FALSE)
SHOULD_CALL_PARENT(TRUE)
- return item.on_equipped(src, slot, initial)
+ item.item_flags |= IN_INVENTORY
+ . = item.on_equipped(src, slot, initial)
+ if(.)
+ update_equipment_speed_mods()
/// This proc is called after an item has been removed from a mob but before it has been officially deslotted.
/mob/proc/has_unequipped(obj/item/item, silent = FALSE)
SHOULD_CALL_PARENT(TRUE)
item.dropped(src, silent)
+ update_equipment_speed_mods()
return TRUE
/**
diff --git a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
index 0fe0a6fab67..d52b70575c5 100644
--- a/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
+++ b/code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
@@ -8,6 +8,7 @@
health = 25
maxHealth = 25
light_color = "#99ccff"
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 2)
req_one_access = list(ACCESS_ROBOTICS, ACCESS_JANITOR)
radio_key = /obj/item/encryptionkey/headset_service
diff --git a/code/modules/mob/living/basic/bots/firebot/firebot.dm b/code/modules/mob/living/basic/bots/firebot/firebot.dm
index 1db37c6340b..b6ef076c629 100644
--- a/code/modules/mob/living/basic/bots/firebot/firebot.dm
+++ b/code/modules/mob/living/basic/bots/firebot/firebot.dm
@@ -7,6 +7,7 @@
icon_state = "firebot1"
light_color = "#8cffc9"
light_power = 0.8
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 0.9, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 2)
req_one_access = list(ACCESS_ROBOTICS, ACCESS_CONSTRUCTION)
radio_key = /obj/item/encryptionkey/headset_eng
diff --git a/code/modules/mob/living/basic/bots/honkbots/honkbot.dm b/code/modules/mob/living/basic/bots/honkbots/honkbot.dm
index cc4469f8971..eb57581ae16 100644
--- a/code/modules/mob/living/basic/bots/honkbots/honkbot.dm
+++ b/code/modules/mob/living/basic/bots/honkbots/honkbot.dm
@@ -4,6 +4,7 @@
icon_state = "honkbot"
base_icon_state = "honkbot"
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, STAMINA = 0, OXY = 0)
+ custom_materials = list(/datum/material/cardboard = SHEET_MATERIAL_AMOUNT, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 0.8, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 2)
req_access = list(ACCESS_ROBOTICS, ACCESS_THEATRE, ACCESS_JANITOR)
radio_key = /obj/item/encryptionkey/headset_service
ai_controller = /datum/ai_controller/basic_controller/bot/honkbot
diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm
index 80e1ce7b63b..eb4dd4565fd 100644
--- a/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm
+++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm
@@ -21,6 +21,8 @@
additional_access = /datum/id_trim/job/janitor
hackables = "cleaning service protocols"
ai_controller = /datum/ai_controller/basic_controller/bot/hygienebot
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 3.3, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 2)
+
///are we currently washing someone?
var/washing = FALSE
@@ -115,6 +117,7 @@
target.fire_act()
return
target.wash(CLEAN_WASH)
+ target.extinguish()
/mob/living/basic/bot/hygienebot/on_bot_movement(atom/movable/source, atom/oldloc, dir, forced)
diff --git a/code/modules/mob/living/basic/bots/medbot/medbot.dm b/code/modules/mob/living/basic/bots/medbot/medbot.dm
index 4d2cf13f4b0..9c705053143 100644
--- a/code/modules/mob/living/basic/bots/medbot/medbot.dm
+++ b/code/modules/mob/living/basic/bots/medbot/medbot.dm
@@ -14,6 +14,7 @@
pass_flags = PASSMOB | PASSFLAPS
status_flags = (CANPUSH | CANSTUN)
ai_controller = /datum/ai_controller/basic_controller/bot/medbot
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 2)
req_one_access = list(ACCESS_ROBOTICS, ACCESS_MEDICAL)
radio_key = /obj/item/encryptionkey/headset_med
diff --git a/code/modules/mob/living/basic/bots/repairbot/repairbot.dm b/code/modules/mob/living/basic/bots/repairbot/repairbot.dm
index 29583c9fca9..8cc3a90a336 100644
--- a/code/modules/mob/living/basic/bots/repairbot/repairbot.dm
+++ b/code/modules/mob/living/basic/bots/repairbot/repairbot.dm
@@ -8,9 +8,10 @@
pass_flags = parent_type::pass_flags | PASSTABLE
layer = BELOW_MOB_LAYER
anchored = FALSE
- health = 100
+ health = 35
can_be_held = TRUE
- maxHealth = 100
+ maxHealth = 35
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.3, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 2)
path_image_color = "#80dae7"
bot_ui = "RepairBot"
req_one_access = list(ACCESS_ROBOTICS, ACCESS_ENGINEERING)
diff --git a/code/modules/mob/living/basic/bots/vibebot/vibebot.dm b/code/modules/mob/living/basic/bots/vibebot/vibebot.dm
index b881e74fc92..ab89baea651 100644
--- a/code/modules/mob/living/basic/bots/vibebot/vibebot.dm
+++ b/code/modules/mob/living/basic/bots/vibebot/vibebot.dm
@@ -9,6 +9,8 @@
light_range = 6
ai_controller = /datum/ai_controller/basic_controller/bot/vibebot
light_power = 2
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 0.8, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 4)
+
hackables = "vibing scanners"
radio_key = /obj/item/encryptionkey/headset_service
diff --git a/code/modules/mob/living/basic/drone/drone_tools.dm b/code/modules/mob/living/basic/drone/drone_tools.dm
index 2941d867d6b..d1be8ab0cec 100644
--- a/code/modules/mob/living/basic/drone/drone_tools.dm
+++ b/code/modules/mob/living/basic/drone/drone_tools.dm
@@ -79,6 +79,8 @@
inhand_icon_state = "cutters"
item_flags = NO_MAT_REDEMPTION
random_color = FALSE
+ greyscale_config = null
+ greyscale_colors = null
/obj/item/multitool/drone
name = "built-in multitool"
diff --git a/code/modules/mob/living/basic/drone/inventory.dm b/code/modules/mob/living/basic/drone/inventory.dm
index c6c7aa5da95..acacdb0e265 100644
--- a/code/modules/mob/living/basic/drone/inventory.dm
+++ b/code/modules/mob/living/basic/drone/inventory.dm
@@ -59,7 +59,7 @@
equipping.pulledby.stop_pulling()
equipping.screen_loc = null // will get moved if inventory is visible
- equipping.forceMove(src)
+ equipping.forceMove(src) //This has to come before has_equipped is called.
SET_PLANE_EXPLICIT(equipping, ABOVE_HUD_PLANE, src)
switch(slot)
diff --git a/code/modules/mob/living/basic/illusion/illlusion_ai.dm b/code/modules/mob/living/basic/illusion/illlusion_ai.dm
index 5a7119b734c..48270fc2583 100644
--- a/code/modules/mob/living/basic/illusion/illlusion_ai.dm
+++ b/code/modules/mob/living/basic/illusion/illlusion_ai.dm
@@ -20,6 +20,7 @@
/datum/ai_controller/basic_controller/illusion/escape
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, // we don't need the special illusion one here
+ BB_FLEE_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
)
ai_traits = DEFAULT_AI_FLAGS
diff --git a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_instrument.dm b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_instrument.dm
index 1e2d42761b9..508728ffa73 100644
--- a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_instrument.dm
+++ b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_instrument.dm
@@ -6,6 +6,7 @@
icon_state = "bilehorn"
allowed_instrument_ids = "bilehorn"
inhand_icon_state = null
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 2)
/datum/crafting_recipe/bilehorn
name = "Bilehorn"
diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm
index debd43e4504..662bbe9fb69 100644
--- a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm
+++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm
@@ -17,6 +17,7 @@
icon_state = "oresensor"
icon = 'icons/obj/mining.dmi'
slot_flags = ITEM_SLOT_EARS
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT)
var/range = 5
var/cooldown = 4 SECONDS //between the standard and the advanced ore scanner in strength
COOLDOWN_DECLARE(ore_sensing_cooldown)
diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_color.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_color.dm
index 94f5f8a111e..4270155c429 100644
--- a/code/modules/mob/living/basic/lavaland/raptor/raptor_color.dm
+++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_color.dm
@@ -316,12 +316,17 @@ GLOBAL_LIST_INIT(raptor_colors, init_raptor_colors())
check_flags = AB_CHECK_CONSCIOUS | AB_CHECK_IMMOBILE | AB_CHECK_INCAPACITATED
button_icon = 'icons/mob/actions/actions_animal.dmi'
button_icon_state = "raptor_wings"
+ background_icon_state = "bg_default"
+ overlay_icon_state = "bg_default_border"
/datum/action/innate/raptor_wings/Activate()
var/obj/item/mob_holder/purple_raptor/holder = target
var/mob/living/carbon/human/user = holder.loc
- if (istype(user) && user.get_item_by_slot(ITEM_SLOT_BACK) == holder)
- holder.toggle_wings(user)
+ if (!istype(user) || user.get_item_by_slot(ITEM_SLOT_BACK) != holder)
+ return
+ holder.toggle_wings(user)
+ background_icon_state = "bg_default[holder.wings_open ? "_on" : ""]"
+ build_all_button_icons()
/datum/raptor_color/green
color = "green"
diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_food_trough.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_food_trough.dm
index 03289f2d0fe..efa600de5ae 100644
--- a/code/modules/mob/living/basic/lavaland/raptor/raptor_food_trough.dm
+++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_food_trough.dm
@@ -5,3 +5,4 @@
icon_state = "raptor_trough"
x_offsets = list(-5, 5)
y_offsets = list(-4, 5)
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 5)
diff --git a/code/modules/mob/living/basic/ruin_defender/stickman.dm b/code/modules/mob/living/basic/ruin_defender/stickman.dm
index 24be8389a5b..d51c0b4efe1 100644
--- a/code/modules/mob/living/basic/ruin_defender/stickman.dm
+++ b/code/modules/mob/living/basic/ruin_defender/stickman.dm
@@ -17,8 +17,7 @@
attack_sound = 'sound/items/weapons/punch1.ogg'
combat_mode = TRUE
faction = list(FACTION_STICKMAN)
- unsuitable_atmos_damage = 7.5
- unsuitable_cold_damage = 7.5
+ unsuitable_atmos_damage = 0
unsuitable_heat_damage = 7.5
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 0, OXY = 1)
diff --git a/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm b/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm
index f2bacdd514a..24980cc0110 100644
--- a/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm
+++ b/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm
@@ -22,6 +22,7 @@
attack_sound = 'sound/effects/hallucinations/growl1.ogg'
ai_controller = /datum/ai_controller/basic_controller/paper_wizard
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 0, OXY = 1)
+ unsuitable_atmos_damage = 0
///spell to summon minions
var/datum/action/cooldown/spell/conjure/wizard_summon_minions/summon
///spell to summon clones
diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_objectives.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_objectives.dm
index 7dd391c17e4..79e8ac90cb0 100644
--- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_objectives.dm
+++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_objectives.dm
@@ -25,11 +25,14 @@
"Ensure that any holy weapons are rendered unusable.",
"Heed and obey the requests of the dead, provided that carrying them out wouldn't be too inconvenient or self-destructive.",
"Impersonate or be worshipped as a God.",
- "Make the captain as miserable as possible.",
- "Make the clown as miserable as possible.",
"Make the crew as miserable as possible.",
"Prevent the use of energy weapons where possible.",
)
+ if(SSjob.assigned_captain)
+ explanation_texts += "Make the captain as miserable as possible."
+ var/datum/job/clown/clown_job = SSjob.get_job(JOB_CLOWN)
+ if(clown_job.current_positions)
+ explanation_texts += "Make the clown as miserable as possible."
explanation_text = pick(explanation_texts)
return ..()
diff --git a/code/modules/mob/living/basic/space_fauna/robot_customer.dm b/code/modules/mob/living/basic/space_fauna/robot_customer.dm
index c880be1fdb9..7ffe1fadd6a 100644
--- a/code/modules/mob/living/basic/space_fauna/robot_customer.dm
+++ b/code/modules/mob/living/basic/space_fauna/robot_customer.dm
@@ -118,3 +118,8 @@
order = attending_venue.order_food_line(wanted_item)
. += span_notice("Their order was: \"[order].\"")
+
+/mob/living/basic/robot_customer/death()
+ new /obj/effect/gibspawner/robot(drop_location())
+
+ return ..()
diff --git a/code/modules/mob/living/basic/trader/trader.dm b/code/modules/mob/living/basic/trader/trader.dm
index 9b01261fa51..c23726a562b 100644
--- a/code/modules/mob/living/basic/trader/trader.dm
+++ b/code/modules/mob/living/basic/trader/trader.dm
@@ -23,7 +23,7 @@
///Sound used when item sold/bought
var/sell_sound = 'sound/effects/cashregister.ogg'
///The currency name
- var/currency_name = "credits"
+ var/currency_name = MONEY_NAME
///The spawner we use to create our look
var/spawner_path = /obj/effect/mob_spawn/corpse/human/generic_assistant
///Our species to create our look
diff --git a/code/modules/mob/living/basic/trader/trader_data.dm b/code/modules/mob/living/basic/trader/trader_data.dm
index c47e200154f..6af2c85530e 100644
--- a/code/modules/mob/living/basic/trader/trader_data.dm
+++ b/code/modules/mob/living/basic/trader/trader_data.dm
@@ -8,7 +8,7 @@
///Sound used when item sold/bought
var/sell_sound = 'sound/effects/cashregister.ogg'
///The currency name
- var/currency_name = "credits"
+ var/currency_name = MONEY_NAME
///The initial products that the trader offers
var/list/initial_products = list(
/obj/item/food/burger/ghost = list(PAYCHECK_CREW * 4, INFINITY),
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 39280040b97..f99ce084fdb 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -108,6 +108,20 @@
SEND_SIGNAL(src, COMSIG_LIVING_UPDATE_BLOOD_STATUS, had_blood, has_blood, old_blood_volume, new_blood_volume)
+/// A one-time coagulating effect of the mob's bloodiest cut/stab
+/// Returns TRUE if a wound was affected, FALSE if no wound was found
+/mob/living/proc/coagulant_effect(amount_to_heal = 1)
+ return FALSE
+
+/mob/living/carbon/coagulant_effect(amount_to_heal = 1)
+ var/datum/wound/bloodiest_wound
+ for(var/datum/wound/iter_wound as anything in all_wounds)
+ if(iter_wound.blood_flow && iter_wound.blood_flow > bloodiest_wound?.blood_flow)
+ bloodiest_wound = iter_wound
+
+ bloodiest_wound?.adjust_blood_flow(-1 * abs(amount_to_heal))
+ return !!bloodiest_wound
+
// Takes care blood loss and regeneration
/mob/living/carbon/human/handle_blood(seconds_per_tick, times_fired)
// Under these circumstances blood handling is not necessary
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 0547d533725..d4c86984c70 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -655,34 +655,6 @@
else
hud_used.healths.icon_state = "health6"
-/mob/living/carbon/update_stamina_hud(shown_stamina_loss)
- if(!client || !hud_used?.stamina)
- return
-
- var/stam_crit_threshold = maxHealth - crit_threshold
-
- if(stat == DEAD)
- hud_used.stamina.icon_state = "stamina_dead"
- else
-
- if(shown_stamina_loss == null)
- shown_stamina_loss = get_stamina_loss()
-
- if(shown_stamina_loss >= stam_crit_threshold)
- hud_used.stamina.icon_state = "stamina_crit"
- else if(shown_stamina_loss > maxHealth*0.8)
- hud_used.stamina.icon_state = "stamina_5"
- else if(shown_stamina_loss > maxHealth*0.6)
- hud_used.stamina.icon_state = "stamina_4"
- else if(shown_stamina_loss > maxHealth*0.4)
- hud_used.stamina.icon_state = "stamina_3"
- else if(shown_stamina_loss > maxHealth*0.2)
- hud_used.stamina.icon_state = "stamina_2"
- else if(shown_stamina_loss > 0)
- hud_used.stamina.icon_state = "stamina_1"
- else
- hud_used.stamina.icon_state = "stamina_full"
-
/mob/living/carbon/proc/update_spacesuit_hud_icon(cell_state = "empty")
hud_used?.spacesuit?.icon_state = "spacesuit_[cell_state]"
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 365e4b9700e..744eb620955 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -593,7 +593,7 @@
*/
/mob/living/carbon/proc/check_passout()
var/mob_oxyloss = get_oxy_loss()
- if(mob_oxyloss >= OXYLOSS_PASSOUT_THRESHOLD)
+ if(mob_oxyloss >= OXYLOSS_PASSOUT_THRESHOLD && !HAS_TRAIT(src, TRAIT_NO_OXYLOSS_PASSOUT))
if(!HAS_TRAIT_FROM(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT))
ADD_TRAIT(src, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT)
else if(mob_oxyloss < OXYLOSS_PASSOUT_THRESHOLD)
diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm
index e5deaf8f163..f148c0cfe15 100644
--- a/code/modules/mob/living/carbon/carbon_update_icons.dm
+++ b/code/modules/mob/living/carbon/carbon_update_icons.dm
@@ -584,9 +584,10 @@
. += "-[facial_hairstyle]"
. += "-[override_hair_color || fixed_hair_color || facial_hair_color]"
. += "-[facial_hair_alpha]"
- if(gradient_styles?[GRADIENT_FACIAL_HAIR_KEY])
- . += "-[gradient_styles[GRADIENT_FACIAL_HAIR_KEY]]"
- . += "-[gradient_colors[GRADIENT_FACIAL_HAIR_KEY]]"
+ var/facial_hair_gradient_style = get_hair_gradient_style(GRADIENT_FACIAL_HAIR_KEY)
+ if(facial_hair_gradient_style)
+ . += "-[facial_hair_gradient_style]"
+ . += "-[get_hair_gradient_color(GRADIENT_FACIAL_HAIR_KEY)]"
if(show_eyeless)
. += "-SHOW_EYELESS"
@@ -600,9 +601,10 @@
. += "-[hairstyle]"
. += "-[override_hair_color || fixed_hair_color || hair_color]"
. += "-[hair_alpha]"
- if(gradient_styles?[GRADIENT_HAIR_KEY])
- . += "-[gradient_styles[GRADIENT_HAIR_KEY]]"
- . += "-[gradient_colors[GRADIENT_HAIR_KEY]]"
+ var/hair_gradient_style = get_hair_gradient_style(GRADIENT_HAIR_KEY)
+ if(hair_gradient_style)
+ . += "-[hair_gradient_style]"
+ . += "-[get_hair_gradient_color(GRADIENT_HAIR_KEY)]"
if(LAZYLEN(hair_masks))
. += "-[jointext(hair_masks, "-")]"
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index 64f828d7231..0261947f0bf 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -87,6 +87,89 @@
hands_use_check = TRUE
sound = 'sound/mobs/humanoids/human/salute/salute.ogg'
+/datum/emote/living/carbon/human/slit
+ key = "slit"
+ key_third_person = "slits"
+ message = "drags a finger across their neck."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/human/scratch_h
+ key = "scratch_h"
+ message = "scratches their head."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/human/thumb_up
+ key = "thumb_u"
+ message = "gives a thumbs up."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/human/thumb_down
+ key = "thumb_d"
+ message = "gives a thumbs down."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/human/time
+ key = "time"
+ message = "checks the time."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/human/tap
+ key = "tap"
+ key_third_person = "taps"
+ message = "taps their foot impatiently."
+
+/datum/emote/living/carbon/human/halt
+ key = "halt"
+ key_third_person = "halts"
+ message = "holds up their palm, signaling to stop."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/human/shush
+ key = "shush"
+ key_third_person = "shushes"
+ message = "holds a finger to their lips."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/human/listen
+ key = "listen"
+ key_third_person = "listens"
+ message = "cups a hand to their ear."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/human/think
+ key = "think"
+ key_third_person = "thinks"
+ message = "taps their head, thinking."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/human/beckon
+ key = "beckon"
+ key_third_person = "beckons"
+ message = "waves a hand for someone to come closer."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/human/airquote
+ key = "airquote"
+ key_third_person = "airquotes"
+ message = "makes air quotes."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/human/crazy
+ key = "crazy"
+ message = "twirls a finger next to their head."
+ hands_use_check = TRUE
+
+/datum/emote/living/carbon/human/squint
+ key = "squint"
+ key_third_person = "squints"
+ message = "squints."
+
+/datum/emote/living/carbon/human/rub
+ key = "rub"
+ key_third_person = "rubs"
+ message = "rubs their chin."
+ hands_use_check = TRUE
+
/datum/emote/living/carbon/human/shrug
key = "shrug"
key_third_person = "shrugs"
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 2b3bb5e63f1..be1e729654d 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -352,7 +352,7 @@
var/datum/crime/citation/new_citation = new(name = citation_name, author = allowed_access, fine = fine)
target_record.citations += new_citation
- new_citation.alert_owner(usr, src, target_record.name, "You have been fined [fine] credits for '[citation_name]'. Fines may be paid at security.")
+ new_citation.alert_owner(usr, src, target_record.name, "You have been fined [fine] [MONEY_NAME] for '[citation_name]'. Fines may be paid at security.")
investigate_log("New Citation: [citation_name] Fine: [fine] | Added to [target_record.name] by [key_name(human_user)]", INVESTIGATE_RECORDS)
SSblackbox.ReportCitation(REF(new_citation), human_user.ckey, human_user.real_name, target_record.name, citation_name, null, fine)
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index cbb7dace00d..e8e5abe9645 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -24,15 +24,9 @@
var/hairstyle = "Bald"
///Colours used for hair and facial hair gradients.
- var/list/grad_color = list(
- COLOR_BLACK, //Hair Gradient Color
- COLOR_BLACK, //Facial Hair Gradient Color
- )
+ var/list/grad_color
///Styles used for hair and facial hair gradients.
- var/list/grad_style = list(
- "None", //Hair Gradient Style
- "None", //Facial Hair Gradient Style
- )
+ var/list/grad_style
//Facial hair colour and style
var/facial_hair_color = COLOR_BLACK
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 68b325ada91..a17ce471381 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -81,15 +81,20 @@
return real_name
/mob/living/carbon/human/get_face_name(if_no_face = "Unknown")
- if(HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE))
- return if_no_face //We're Unknown, no face information for you
- if(obscured_slots & HIDEFACE)
- return if_no_face
- var/obj/item/bodypart/head = get_bodypart(BODY_ZONE_HEAD)
- if(isnull(head) || !real_name || HAS_TRAIT(src, TRAIT_DISFIGURED) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN)) //disfigured. use id-name if possible
+ if(!real_name || is_face_obscured())
return if_no_face
return real_name
+/mob/living/carbon/human/proc/is_face_obscured()
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN_APPEARANCE))
+ return TRUE //We're Unknown, no face information for you
+ if(obscured_slots & HIDEFACE)
+ return TRUE
+ var/obj/item/bodypart/head = get_bodypart(BODY_ZONE_HEAD)
+ if(isnull(head) || HAS_TRAIT(src, TRAIT_DISFIGURED) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN)) //disfigured. use id-name if possible
+ return TRUE
+ return FALSE
+
/**
* Gets whatever name is in our ID or PDA
*
diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm
index 037336b1a43..dfd918573dd 100644
--- a/code/modules/mob/living/carbon/human/human_update_icons.dm
+++ b/code/modules/mob/living/carbon/human/human_update_icons.dm
@@ -1198,7 +1198,7 @@ mutant_styles: The mutant style - taur bodytype, STYLE_TESHARI, etc. // SKYRAT E
/mob/living/carbon/human/proc/update_underwear() // OVERRIDDEN IN MODULAR_ZUBBERS
remove_overlay(BODY_LAYER)
- if(HAS_TRAIT(src, TRAIT_HUSK) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
+ if(HAS_TRAIT(src, TRAIT_HUSK) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN) || HAS_TRAIT(src, TRAIT_NO_UNDERWEAR))
return
// Underwear, Undershirts & Socks
var/list/standing = list()
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 967347c97f2..14bba5d38e7 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -214,9 +214,6 @@
return not_handled //For future deeper overrides
-/mob/living/carbon/human/get_equipped_speed_mod_items()
- return ..() - list(l_store, r_store, s_store)
-
/mob/living/carbon/human/doUnEquip(obj/item/item_dropping, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
. = ..() //See mob.dm for an explanation on this and some rage about people copypasting instead of calling ..() like they should.
if(!. || !item_dropping)
@@ -374,6 +371,9 @@
return
var/obj/item/thing = get_active_held_item()
var/obj/item/equipped_item = get_item_by_slot(slot_type)
+ var/thing_reject = NONE
+ if(thing)
+ thing_reject = SEND_SIGNAL(thing, COMSIG_HUMAN_NON_STORAGE_HOTKEY, src, equipped_item)
if(!equipped_item) // We also let you equip an item like this
if(!thing)
to_chat(src, span_warning("You have no [slot_item_name] to take something out of!"))
@@ -386,6 +386,8 @@
if(!thing)
equipped_item.attack_hand(src)
else
+ if(thing_reject & COMPONENT_STORAGE_HOTKEY_HANDLED)
+ return
to_chat(src, span_warning("You can't fit [thing] into your [equipped_item.name]!"))
return
if(!storage.supports_smart_equip)
diff --git a/code/modules/mob/living/carbon/human/monkey.dm b/code/modules/mob/living/carbon/human/monkey.dm
index de7eb94f39f..7a9ddc5ece2 100644
--- a/code/modules/mob/living/carbon/human/monkey.dm
+++ b/code/modules/mob/living/carbon/human/monkey.dm
@@ -48,32 +48,20 @@ GLOBAL_DATUM(the_one_and_only_punpun, /mob/living/carbon/human/species/monkey/pu
var/memory_saved = FALSE
/mob/living/carbon/human/species/monkey/punpun/Initialize(mapload)
- // 1 Pun Pun should exist
- REGISTER_REQUIRED_MAP_ITEM(1, 1)
+ . = ..()
+
+ REGISTER_REQUIRED_MAP_ITEM(1, 1) // pun pun is required on maps.
if(mapload && (locate(/datum/station_trait/job/pun_pun) in SSstation.station_traits))
new /obj/effect/landmark/start/pun_pun(loc) //Pun Pun is a crewmember, and may late-join.
return INITIALIZE_HINT_QDEL
+
Read_Memory()
-
- var/name_to_use = name
-
- if(ancestor_name)
- name_to_use = ancestor_name
- if(ancestor_chain > 1)
- name_to_use += " \Roman[ancestor_chain]"
- else if(prob(10))
- name_to_use = pick(list("Professor Bobo", "Deempisi's Revenge", "Furious George", "King Louie", "Dr. Zaius", "Jimmy Rustles", "Dinner", "Lanky"))
- if(name_to_use == "Furious George")
- ai_controller = /datum/ai_controller/monkey/angry //hes always mad
-
- . = ..()
-
if(!GLOB.the_one_and_only_punpun && mapload)
GLOB.the_one_and_only_punpun = src
else if(GLOB.the_one_and_only_punpun)
ADD_TRAIT(src, TRAIT_DONT_WRITE_MEMORY, INNATE_TRAIT) //faaaaaaake!
- fully_replace_character_name(real_name, name_to_use)
+ give_special_name()
//These have to be after the parent new to ensure that the monkey
//bodyparts are actually created before we try to equip things to
@@ -141,3 +129,20 @@ GLOBAL_DATUM(the_one_and_only_punpun, /mob/living/carbon/human/species/monkey/pu
file_data["relic_mask"] = wear_mask ? wear_mask.type : null
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
+
+
+/// Gives pun pun a special name based on various factors
+/mob/living/carbon/human/species/monkey/punpun/proc/give_special_name()
+ var/name_to_use = name
+
+ if(ancestor_name)
+ name_to_use = ancestor_name
+ if(ancestor_chain > 1)
+ name_to_use += " \Roman[ancestor_chain]"
+ else if(prob(10))
+ name_to_use = pick(list("Professor Bobo", "Deempisi's Revenge", "Furious George", "King Louie", "Dr. Zaius", "Jimmy Rustles", "Dinner", "Lanky"))
+ if(name_to_use == "Furious George")
+ qdel(ai_controller)
+ ai_controller = new /datum/ai_controller/monkey/angry(src) //hes always mad
+
+ fully_replace_character_name(real_name, name_to_use)
diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm
index 01a39ebf7b2..218e8544d9f 100644
--- a/code/modules/mob/living/carbon/inventory.dm
+++ b/code/modules/mob/living/carbon/inventory.dm
@@ -118,7 +118,7 @@
for(var/mob/dead/observe as anything in observers)
observe.client?.screen -= equipping
- equipping.forceMove(src)
+ equipping.forceMove(src) //This has to come before has_equipped is called.
SET_PLANE_EXPLICIT(equipping, ABOVE_HUD_PLANE, src)
equipping.appearance_flags |= NO_CLIENT_COLOR
var/not_handled = FALSE
@@ -163,15 +163,11 @@
return not_handled
-/mob/living/carbon/get_equipped_speed_mod_items()
- return ..() + get_equipped_items(INCLUDE_ABSTRACT)
-
/mob/living/carbon/has_equipped(obj/item/item, slot, initial = FALSE)
. = ..()
if(!.)
return
- update_equipment_speed_mods()
hud_used?.update_locked_slots()
if(!(slot & item.slot_flags)) // Things below only update if slotted in (ie: not held)
return
@@ -180,11 +176,10 @@
add_item_coverage(item)
/mob/living/carbon/has_unequipped(obj/item/item)
- . = ..() // NB: ATP the item is still in the slot, but no longer has the IN_INVENTORY flag (so is not returned by get_equipped_items)
+ . = ..()
if(!.)
return
- update_equipment_speed_mods()
hud_used?.update_locked_slots()
if(item.hair_mask)
update_body()
diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm
index 211ab89fd85..e6ccbb508fd 100644
--- a/code/modules/mob/living/emote.dm
+++ b/code/modules/mob/living/emote.dm
@@ -698,6 +698,7 @@
/datum/emote/living/custom
key = "me"
key_third_person = "custom"
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
message = null
/datum/emote/living/custom/can_run_emote(mob/user, status_check, intentional, params)
@@ -782,23 +783,15 @@
if(!emote_is_valid(user, our_message))
return FALSE
- if(type_override)
- emote_type = type_override
-
if(!params)
var/user_emote_type = get_custom_emote_type_from_user()
if(!user_emote_type)
return FALSE
- emote_type = user_emote_type
+ type_override = user_emote_type
- message = our_message
- . = ..()
-
- ///Reset the message and emote type after it's run.
- message = null
- emote_type = EMOTE_VISIBLE
+ . = ..(user = user, params = our_message, type_override = type_override, intentional = intentional)
/datum/emote/living/custom/replace_pronoun(mob/user, message)
return message
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 75f53eb92af..fc7ed11e6b4 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1508,6 +1508,34 @@
SEND_SIGNAL(src, COMSIG_LIVING_STAMINA_UPDATE)
update_stamina_hud()
+/mob/living/update_stamina_hud(shown_stamina_loss)
+ if(!client || !hud_used?.stamina)
+ return
+
+ var/stam_crit_threshold = maxHealth - crit_threshold
+
+ if(stat == DEAD)
+ hud_used.stamina.icon_state = "stamina_dead"
+ else
+
+ if(shown_stamina_loss == null)
+ shown_stamina_loss = get_stamina_loss()
+
+ if(shown_stamina_loss >= stam_crit_threshold)
+ hud_used.stamina.icon_state = "stamina_crit"
+ else if(shown_stamina_loss > maxHealth*0.8)
+ hud_used.stamina.icon_state = "stamina_5"
+ else if(shown_stamina_loss > maxHealth*0.6)
+ hud_used.stamina.icon_state = "stamina_4"
+ else if(shown_stamina_loss > maxHealth*0.4)
+ hud_used.stamina.icon_state = "stamina_3"
+ else if(shown_stamina_loss > maxHealth*0.2)
+ hud_used.stamina.icon_state = "stamina_2"
+ else if(shown_stamina_loss > 0)
+ hud_used.stamina.icon_state = "stamina_1"
+ else
+ hud_used.stamina.icon_state = "stamina_full"
+
/mob/living/carbon/alien/update_stamina()
return
@@ -1581,23 +1609,23 @@
var/static/list/robot_options = list(
/mob/living/silicon/robot = 200,
/mob/living/basic/drone/polymorphed = 200,
+ /mob/living/silicon/robot/model/syndicate = 100,
+ /mob/living/silicon/robot/model/syndicate/medical = 100,
+ /mob/living/silicon/robot/model/syndicate/saboteur = 100,
+ /mob/living/basic/hivebot/strong = 50,
+ /mob/living/basic/hivebot/mechanic = 50,
/mob/living/basic/bot/dedbot = 25,
/mob/living/basic/bot/cleanbot = 25,
/mob/living/basic/bot/firebot = 25,
/mob/living/basic/bot/honkbot = 25,
/mob/living/basic/bot/hygienebot = 25,
- /mob/living/basic/bot/medbot/mysterious = 12,
- /mob/living/basic/bot/medbot = 13,
/mob/living/basic/bot/vibebot = 25,
- /mob/living/basic/hivebot/strong = 50,
- /mob/living/basic/hivebot/mechanic = 50,
+ /mob/living/basic/bot/medbot = 13,
+ /mob/living/basic/bot/medbot/mysterious = 12,
/mob/living/basic/netguardian = 1,
- /mob/living/silicon/robot/model/syndicate = 1,
- /mob/living/silicon/robot/model/syndicate/medical = 1,
- /mob/living/silicon/robot/model/syndicate/saboteur = 1,
)
- var/picked_robot = pick(robot_options)
+ var/picked_robot = pick_weight(robot_options)
new_mob = new picked_robot(loc)
if(issilicon(new_mob))
var/mob/living/silicon/robot/created_robot = new_mob
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index ce6e36d5363..75000eb0c2d 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -805,22 +805,22 @@
if(!(shove_flags & SHOVE_DIRECTIONAL_BLOCKED) && (SEND_SIGNAL(target_shove_turf, COMSIG_LIVING_DISARM_COLLIDE, src, target, shove_flags, weapon) & COMSIG_LIVING_SHOVE_HANDLED))
return
if((shove_flags & SHOVE_BLOCKED) && !(shove_flags & (SHOVE_KNOCKDOWN_BLOCKED|SHOVE_CAN_KICK_SIDE)))
- target.Knockdown(SHOVE_KNOCKDOWN_SOLID, daze_amount = 3 SECONDS)
- target.visible_message(span_danger("[name] shoves [target.name], knocking [target.p_them()] down!"),
- span_userdanger("You're knocked down from a shove by [name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
- to_chat(src, span_danger("You shove [target.name], knocking [target.p_them()] down!"))
- log_combat(src, target, "shoved", "knocking them down[weapon ? " with [weapon]" : ""]")
+ var/knocked_down = target.Knockdown(SHOVE_KNOCKDOWN_SOLID, daze_amount = 3 SECONDS)
+ target.visible_message(span_danger("[name] shoves [target.name][knocked_down ? ", knocking [target.p_them()] down" : ""]!"),
+ span_userdanger("You[knocked_down ? "'re knocked down" : " resist falling down"] from a shove by [name]!"), span_hear("You hear aggressive shuffling [knocked_down ? "followed by a loud thud!" : ""]"), COMBAT_MESSAGE_RANGE, src)
+ to_chat(src, span_danger("You shove [target.name][knocked_down ? ", knocking [target.p_them()] down" : ""]!"))
+ log_combat(src, target, "shoved", "[knocked_down ? "knocking them down[weapon ? " with [weapon]" : ""]" : ""]")
return
if(shove_flags & SHOVE_CAN_KICK_SIDE) //KICK HIM IN THE NUTS
- target.Paralyze(SHOVE_CHAIN_PARALYZE)
- target.apply_status_effect(/datum/status_effect/no_side_kick)
- target.visible_message(span_danger("[name] kicks [target.name] onto [target.p_their()] side!"),
- span_userdanger("You're kicked onto your side by [name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
- to_chat(src, span_danger("You kick [target.name] onto [target.p_their()] side!"))
- addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living, SetKnockdown), 0), SHOVE_CHAIN_PARALYZE)
- log_combat(src, target, "kicks", "onto their side (paralyzing)")
- return
+ if(target.Paralyze(SHOVE_CHAIN_PARALYZE))
+ target.apply_status_effect(/datum/status_effect/no_side_kick)
+ target.visible_message(span_danger("[name] kicks [target.name] onto [target.p_their()] side!"),
+ span_userdanger("You're kicked onto your side by [name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
+ to_chat(src, span_danger("You kick [target.name] onto [target.p_their()] side!"))
+ addtimer(CALLBACK(target, TYPE_PROC_REF(/mob/living, SetKnockdown), 0), SHOVE_CHAIN_PARALYZE)
+ log_combat(src, target, "kicks", "onto their side (paralyzing)")
+ return
target.get_shoving_message(src, weapon, shove_flags)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index edb6bd530ae..487c4de4f9c 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -5,7 +5,7 @@
/mob/living/silicon/ai/Initialize(mapload, datum/ai_laws/L, mob/target_ai)
. = ..()
if(!target_ai) //If there is no player/brain inside.
- new/obj/structure/ai_core/deactivated(loc) //New empty terminal.
+ new/obj/structure/ai_core(loc, CORE_STATE_FINISHED) //New empty terminal.
return INITIALIZE_HINT_QDEL //Delete AI.
ADD_TRAIT(src, TRAIT_NO_TELEPORT, AI_ANCHOR_TRAIT)
@@ -152,7 +152,7 @@
current = null
bot_ref = null
controlled_equipment = null
- linked_core = null
+ break_core_link()
apc_override = null
if(ai_voicechanger)
ai_voicechanger.owner = null
@@ -363,53 +363,45 @@
status_flags &= ~CANPUSH //we dont want the core to be push-able when anchored
ADD_TRAIT(src, TRAIT_NO_TELEPORT, AI_ANCHOR_TRAIT)
+/// Creates an MMI of the AI based on its configuration.
+/mob/living/silicon/ai/proc/make_mmi(atom/destination) as /obj/item/mmi
+ RETURN_TYPE(/obj/item/mmi)
+ //FIXME: this code is really bad, we shouldn't be doing most of this ourselves. MMI code needs a good refactoring....
+ var/obj/item/mmi/copied_mmi
+ if(posibrain_inside)
+ copied_mmi = new /obj/item/mmi/posibrain(destination, FALSE)
+ copied_mmi.name = "[initial(copied_mmi.name)] ([real_name])"
+ else
+ copied_mmi = new /obj/item/mmi(destination)
+ copied_mmi.name = "[initial(copied_mmi.name)]: [real_name]"
+ copied_mmi.brain = new /obj/item/organ/brain(copied_mmi)
+ copied_mmi.brain.organ_flags |= ORGAN_FROZEN
+ copied_mmi.brain.name = "[real_name]'s brain"
+ copied_mmi.set_brainmob(new /mob/living/brain(copied_mmi))
+ copied_mmi.brainmob.container = copied_mmi
+
+ copied_mmi.brainmob.name = real_name
+ copied_mmi.brainmob.real_name = real_name
+ copied_mmi.brainmob.gender = gender
+
+ var/suicided = HAS_TRAIT(src, TRAIT_SUICIDED)
+ copied_mmi.brainmob.set_suicide(suicided)
+ copied_mmi.brain?.suicided = suicided // we can't guarantee that the MMI has a brain... sigh
+
+ if(copied_mmi.brainmob.stat == DEAD && !suicided)
+ copied_mmi.brainmob.set_stat(CONSCIOUS)
+
+ copied_mmi.update_appearance()
+ return copied_mmi
+
/mob/living/silicon/ai/proc/ai_mob_to_structure()
disconnect_shell()
ShutOffDoomsdayDevice()
- var/obj/structure/ai_core/deactivated/ai_core = new(get_turf(src), /* skip_mmi_creation = */ TRUE)
- if(make_mmi_drop_and_transfer(ai_core.core_mmi, the_core = ai_core))
- qdel(src)
+ var/obj/structure/ai_core/ai_core = new(get_turf(src), CORE_STATE_FINISHED, make_mmi())
+ mind?.transfer_to(ai_core.core_mmi.brainmob)
+ qdel(src)
return ai_core
-/mob/living/silicon/ai/proc/break_core_link()
- to_chat(src, span_danger("Your core has been destroyed!"))
- linked_core = null
-
-/mob/living/silicon/ai/proc/make_mmi_drop_and_transfer(obj/item/mmi/the_mmi, the_core)
- var/mmi_type
- if(posibrain_inside)
- mmi_type = new/obj/item/mmi/posibrain(src, /* autoping = */ FALSE)
- else
- mmi_type = new/obj/item/mmi(src)
- if(hack_software)
- new/obj/item/malf_upgrade(get_turf(src))
- the_mmi = mmi_type
- the_mmi.brain = new /obj/item/organ/brain(the_mmi)
- the_mmi.brain.organ_flags |= ORGAN_FROZEN
- the_mmi.brain.name = "[real_name]'s brain"
- the_mmi.name = "[initial(the_mmi.name)]: [real_name]"
- the_mmi.set_brainmob(new /mob/living/brain(the_mmi))
- the_mmi.brainmob.name = src.real_name
- the_mmi.brainmob.real_name = src.real_name
- the_mmi.brainmob.container = the_mmi
- the_mmi.brainmob.gender = src.gender
-
- var/has_suicided_trait = HAS_TRAIT(src, TRAIT_SUICIDED)
- the_mmi.brainmob.set_suicide(has_suicided_trait)
- the_mmi.brain.suicided = has_suicided_trait
- if(the_core)
- var/obj/structure/ai_core/core = the_core
- core.core_mmi = the_mmi
- the_mmi.forceMove(the_core)
- else
- the_mmi.forceMove(get_turf(src))
- if(the_mmi.brainmob.stat == DEAD && !has_suicided_trait)
- the_mmi.brainmob.set_stat(CONSCIOUS)
- if(mind)
- mind.transfer_to(the_mmi.brainmob)
- the_mmi.update_appearance()
- return TRUE
-
/mob/living/silicon/ai/Topic(href, href_list)
..()
if(usr != src)
@@ -703,7 +695,7 @@
if(!istype(apc))
to_chat(owner, span_notice("You are already in your Main Core."))
return
- if(SEND_SIGNAL(owner, COMSIG_SILICON_AI_CORE_STATUS) & COMPONENT_CORE_ALL_GOOD)
+ if(astype(owner, /mob/living/silicon/ai)?.linked_core)
apc.malfvacate()
else
to_chat(owner, span_danger("Linked core not detected!"))
@@ -787,7 +779,7 @@
balloon_alert(user, "no intelligence detected!") // average tg coder am i right
return
ShutOffDoomsdayDevice()
- var/obj/structure/ai_core/new_core = new /obj/structure/ai_core/deactivated(loc, posibrain_inside)//Spawns a deactivated terminal at AI location.
+ var/obj/structure/ai_core/new_core = new /obj/structure/ai_core(loc, CORE_STATE_FINISHED, make_mmi())
new_core.circuit.battery = battery
ai_restore_power()//So the AI initially has power.
set_control_disabled(TRUE) //Can't control things remotely if you're stuck in a card!
@@ -1110,6 +1102,99 @@
SEND_SIGNAL(src, COMSIG_SILICON_AI_SET_CONTROL_DISABLED, control_disabled)
src.control_disabled = control_disabled
+
+/// Establishes a "core link" with a supplied core structure.
+/// This will register multiple signals and give the AI a strong reference to it.
+/// See [proc/resolve_core_link] or [proc/break_core_link] for ways to end the connection.
+/mob/living/silicon/ai/proc/create_core_link(obj/structure/ai_core/core)
+ if(linked_core) //uh oh
+ break_core_link(linked_core)
+ linked_core = core
+
+ //this block is kind of sketchy, but I don't think this should cause any problems
+ qdel(core.core_mmi)
+ core.core_mmi = make_mmi(core)
+
+ RegisterSignals(linked_core, list(COMSIG_ATOM_DESTRUCTION, COMSIG_QDELETING), PROC_REF(on_core_destroyed))
+ RegisterSignals(linked_core, list(
+ COMSIG_ATOM_ITEM_INTERACTION,
+ COMSIG_ATOM_TOOL_ACT(TOOL_CROWBAR),
+ COMSIG_ATOM_TOOL_ACT(TOOL_WRENCH),
+ COMSIG_ATOM_TOOL_ACT(TOOL_WELDER),
+ COMSIG_ATOM_TOOL_ACT(TOOL_WIRECUTTER),
+ COMSIG_ATOM_TOOL_ACT(TOOL_SCREWDRIVER),
+ ), PROC_REF(on_core_item_interaction))
+ RegisterSignal(linked_core, COMSIG_ATOM_TAKE_DAMAGE, PROC_REF(on_core_take_damage))
+ RegisterSignal(linked_core, COMSIG_ATOM_EXITED, PROC_REF(on_core_exited))
+
+/// Elegantly closes the AI's link to a core structure,
+/// moving them to its location and cleaning it up. This is generally what you want to call.
+/// Prefer calling [proc/break_core_link] directly if the connection is meant to be suddenly severed.
+/mob/living/silicon/ai/proc/resolve_core_link()
+ if(!linked_core) //oh no bro
+ CRASH("tried to resolve a core link with no core!!!!")
+
+ forceMove(linked_core.loc)
+ var/obj/structure/ai_core/unlinked_core = linked_core
+ break_core_link()
+ qdel(unlinked_core)
+ cancel_camera()
+
+/// Handles unregistering the AI from its core. The core itself will not be cleaned up.
+/// Prefer calling [proc/resolve_core_link] if the connection is being closed elegantly.
+/mob/living/silicon/ai/proc/break_core_link()
+ if(!linked_core)
+ return
+
+ UnregisterSignal(linked_core, list(
+ COMSIG_QDELETING, COMSIG_ATOM_DESTRUCTION, //on_core_destroyed
+ //on_core_item_interaction
+ COMSIG_ATOM_ITEM_INTERACTION,
+ COMSIG_ATOM_TOOL_ACT(TOOL_CROWBAR),
+ COMSIG_ATOM_TOOL_ACT(TOOL_WRENCH),
+ COMSIG_ATOM_TOOL_ACT(TOOL_WELDER),
+ COMSIG_ATOM_TOOL_ACT(TOOL_WIRECUTTER),
+ COMSIG_ATOM_TOOL_ACT(TOOL_SCREWDRIVER),
+ COMSIG_ATOM_TAKE_DAMAGE, //on_core_take_damage
+ COMSIG_ATOM_EXITED, //on_core_exited
+ ))
+ linked_core = null
+
+/mob/living/silicon/ai/proc/on_core_item_interaction(datum/source, mob/living/user, obj/item/tool, list/processing_recipes)
+ SIGNAL_HANDLER
+ if(user.combat_mode)
+ return NONE
+
+ to_chat(src, span_danger("CORE TAMPERING DETECTED!"))
+ return NONE
+
+/mob/living/silicon/ai/proc/on_core_take_damage(datum/source, damage_taken, ...)
+ SIGNAL_HANDLER
+
+ if(damage_taken > 0)
+ to_chat(src, span_danger("CORE DAMAGE DETECTED!"))
+ return NONE
+
+/mob/living/silicon/ai/proc/on_core_destroyed(datum/source, damage_flag)
+ SIGNAL_HANDLER
+
+ to_chat(src, span_danger("Your core has been destroyed!"))
+ ShutOffDoomsdayDevice()
+ break_core_link()
+
+/mob/living/silicon/ai/proc/on_core_exited(datum/source, atom/movable/gone, direction)
+ SIGNAL_HANDLER
+
+ if(istype(gone, /obj/item/mmi))
+ var/obj/item/mmi/mmi_gone = gone
+ on_core_destroyed(source, NONE)
+ if(!IS_MALF_AI(src)) //don't pull back shunted malf AIs
+ death(gibbed = TRUE, drop_mmi = FALSE)
+ ///the drop_mmi param determines whether the MMI is dropped at their current location
+ ///which in this case would be somewhere else, so we drop their MMI at the core instead
+ mind?.transfer_to(mmi_gone.brainmob)
+ qdel(src)
+
#undef HOLOGRAM_CHOICE_CHARACTER
#undef CHARACTER_TYPE_SELF
#undef CHARACTER_TYPE_CREWMEMBER
diff --git a/code/modules/mob/living/silicon/ai/ai_defines.dm b/code/modules/mob/living/silicon/ai/ai_defines.dm
index de631e7b867..30201c9e703 100644
--- a/code/modules/mob/living/silicon/ai/ai_defines.dm
+++ b/code/modules/mob/living/silicon/ai/ai_defines.dm
@@ -122,8 +122,8 @@
/* REMOTE CONTROL */
/// Equipment that the AI is controlling remotely, to determine whether to relaymove or use the AI eye
VAR_FINAL/obj/controlled_equipment
- /// AI core that this AI is linked to, used when put into an exosuit
- VAR_FINAL/obj/structure/ai_core/deactivated/linked_core
+ /// AI core that this AI is linked to. See [proc/create_core_link] [proc/resolve_core_link] [proc/break_core_link]
+ VAR_FINAL/obj/structure/ai_core/linked_core
/// Robot that this AI is currently using
VAR_FINAL/mob/living/silicon/robot/deployed_shell
/// Action to deploy to a shell from a list of options
diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm
index aad3df39d80..6f76f3294a9 100644
--- a/code/modules/mob/living/silicon/ai/death.dm
+++ b/code/modules/mob/living/silicon/ai/death.dm
@@ -34,7 +34,8 @@
ShutOffDoomsdayDevice()
if(gibbed && drop_mmi)
- make_mmi_drop_and_transfer()
+ var/obj/item/mmi/loose_cpu = make_mmi(get_turf(src))
+ mind?.transfer_to(loose_cpu.brainmob)
if(explodes_on_death)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(explosion), loc, 3, 6, 12, null, 15), 1 SECONDS)
@@ -42,7 +43,5 @@
SSblackbox.ReportDeath(src)
/mob/living/silicon/ai/proc/ShutOffDoomsdayDevice()
- if(nuking)
- nuking = FALSE
- if(doomsday_device)
- qdel(doomsday_device)
+ nuking = FALSE
+ QDEL_NULL(doomsday_device)
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index c700c3106f2..52a7208f079 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -227,7 +227,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
return ITEM_INTERACT_BLOCKING
locked = !locked
update_icons()
- balloon_alert(user, "chassis cover lock [emagged ? "glitches" : "toggled"]")
+ balloon_alert(user, "chassis cover [emagged ? "lock glitches" : "[locked ? "locked" : "unlocked"]"]")
logevent("[emagged ? "ChÃ¥vÃis" : "Chassis"] cover lock has been [locked ? "engaged" : "released"]")
return ITEM_INTERACT_SUCCESS
@@ -458,7 +458,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
return TRUE
scrambledcodes = TRUE // BUBBER EDIT START
- SetEmagged(1)
+ SetEmagged(TRUE)
SetStun(10 SECONDS) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown
lawupdate = FALSE
set_connected_ai(null)
diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm
index 09695f78e65..19c7b0f721b 100644
--- a/code/modules/mob/living/simple_animal/bot/construction.dm
+++ b/code/modules/mob/living/simple_animal/bot/construction.dm
@@ -503,6 +503,7 @@
desc = "Clear out the swamp once and for all"
icon_state = "hygienebot"
created_name = "Hygienebot"
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2)
/obj/item/bot_assembly/hygienebot/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
index 4c492a545c9..5a615407f9a 100644
--- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
@@ -15,6 +15,8 @@
automated_announcements = list(ED209_VOICED_DOWN_WEAPONS = 'sound/mobs/non-humanoids/ed209/ed209_20sec.ogg')
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2.8, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 2.1)
+
var/lastfired = 0
var/shot_delay = 1.5 SECONDS
var/shoot_sound = 'sound/items/weapons/laser.ogg'
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index c98dcadd014..5095a1e4baa 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -164,46 +164,46 @@
return
if(!cell)
to_chat(user, span_warning("[src] doesn't have a power cell!"))
- return ITEM_INTERACT_SUCCESS
+ return ITEM_INTERACT_BLOCKING
cell.add_fingerprint(user)
- if(Adjacent(user) && !issilicon(user))
- user.put_in_hands(cell)
- else
- cell.forceMove(drop_location())
user.visible_message(
span_notice("[user] crowbars [cell] out from [src]."),
span_notice("You pry [cell] out of [src]."),
)
- cell = null
- diag_hud_set_mulebotcell()
+ if(Adjacent(user) && !issilicon(user))
+ user.put_in_hands(cell)
+ else
+ cell.forceMove(drop_location())
return ITEM_INTERACT_SUCCESS
-/mob/living/simple_animal/bot/mulebot/attackby(obj/item/I, mob/living/user, list/modifiers, list/attack_modifiers)
- if(istype(I, /obj/item/stock_parts/power_store/cell) && bot_cover_flags & BOT_COVER_MAINTS_OPEN)
+/mob/living/simple_animal/bot/mulebot/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(istype(tool, /obj/item/stock_parts/power_store/cell) && (bot_cover_flags & BOT_COVER_MAINTS_OPEN))
if(cell)
to_chat(user, span_warning("[src] already has a power cell!"))
- return TRUE
- if(!user.transferItemToLoc(I, src))
- return TRUE
- cell = I
+ return ITEM_INTERACT_BLOCKING
+ if(!user.transferItemToLoc(tool, src))
+ return ITEM_INTERACT_BLOCKING
+ cell = tool
diag_hud_set_mulebotcell()
user.visible_message(
span_notice("[user] inserts \a [cell] into [src]."),
span_notice("You insert [cell] into [src]."),
)
+ return ITEM_INTERACT_SUCCESS
+ if(is_wire_tool(tool) && (bot_cover_flags & BOT_COVER_MAINTS_OPEN))
+ attack_hand(user)
+ return ITEM_INTERACT_SUCCESS
+ return ..()
+
+/mob/living/simple_animal/bot/mulebot/attackby(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers)
+ . = ..()
+ if(ismob(load) && prob(1 + attacking_item.force * 2))
+ user.visible_message(
+ span_danger("[user] knocks [load] off [src] with \the [attacking_item]!"),
+ span_danger("You knock [load] off [src] with \the [attacking_item]!"),
+ )
+ unload(0)
return TRUE
- else if(is_wire_tool(I) && bot_cover_flags & BOT_COVER_MAINTS_OPEN)
- return attack_hand(user)
- else if(load && ismob(load)) // chance to knock off rider
- if(prob(1 + I.force * 2))
- unload(0)
- user.visible_message(span_danger("[user] knocks [load] off [src] with \the [I]!"),
- span_danger("You knock [load] off [src] with \the [I]!"))
- else
- to_chat(user, span_warning("You hit [src] with \the [I] but to no effect!"))
- return ..()
- else
- return ..()
/mob/living/simple_animal/bot/mulebot/emag_act(mob/user, obj/item/card/emag/emag_card)
if(!(bot_cover_flags & BOT_COVER_EMAGGED))
@@ -500,7 +500,7 @@
if(HAS_TRAIT(src, TRAIT_IMMOBILIZED))
return
- var/speed = (wires.is_cut(WIRE_MOTOR1) ? 0 : 1) + (wires.is_cut(WIRE_MOTOR2) ? 0 : 2)
+ var/speed = (wires.is_cut(WIRE_MOTOR1) ? 0 : 2) + (wires.is_cut(WIRE_MOTOR2) ? 0 : 1)
if(!speed)//Devide by zero man bad
return
num_steps = round(10/speed) //10, 5, or 3 steps, depending on how many wires we have cut
diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm
index eea52b6bb16..88bf95e7935 100644
--- a/code/modules/mob/living/simple_animal/bot/secbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/secbot.dm
@@ -34,6 +34,8 @@
BEEPSKY_VOICED_SECURE_DAY = 'sound/mobs/non-humanoids/beepsky/secureday.ogg',
)
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.2, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 3.2)
+
///Whether this secbot is considered 'commissioned' and given the trait on Initialize.
var/commissioned = FALSE
///The type of baton this Secbot will use
@@ -600,7 +602,7 @@
nap_violation(target)
return FALSE
var/datum/bank_account/beepsky_department_account = SSeconomy.get_dep_account(payment_department)
- say("Thank you for your compliance. Your account been charged [fair_market_price] credits.")
+ say("Thank you for your compliance. Your account been charged [fair_market_price] [MONEY_NAME].")
if(beepsky_department_account)
beepsky_department_account.adjust_money(fair_market_price)
return TRUE
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index bee7727ebd4..c328cb34fce 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -474,7 +474,7 @@
///Get the item on the mob in the storage slot identified by the id passed in
-/mob/proc/get_item_by_slot(slot_id)
+/mob/proc/get_item_by_slot(slot_id) as /obj/item
return null
/// Gets what slot the item on the mob is held in.
@@ -920,6 +920,15 @@
set category = "OOC"
reset_perspective(null)
+/**
+ * Helpful for when a players uplink window gets glitched to above their screen.
+ * preventing them from moving the UPLINK window.
+ */
+/mob/verb/reset_ui_positions_for_mob()
+ set name = "Reset UI Positions"
+ set category = "OOC"
+ SStgui.reset_ui_position(src)
+
//suppress the .click/dblclick macros so people can't use them to identify the location of items or aimbot
/mob/verb/DisClick(argu = null as anything, sec = "" as text, number1 = 0 as num , number2 = 0 as num)
set name = ".click"
@@ -1621,13 +1630,6 @@
else
remove_movespeed_modifier(/datum/movespeed_modifier/equipment_speedmod)
-///Get all items in our possession that should affect our movespeed
-/mob/proc/get_equipped_speed_mod_items()
- . = list()
- for(var/obj/item/thing in held_items)
- if(thing.item_flags & SLOWS_WHILE_IN_HAND)
- . += thing
-
/mob/proc/set_stat(new_stat)
if(new_stat == stat)
return
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index b637e33eb6f..d340bc5268c 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -174,9 +174,10 @@
if(C.prefs?.read_preference(/datum/preference/toggle/screen_shake_darken))
var/type = /atom/movable/screen/fullscreen/flash/black
+ var/shake_dur = max(duration, 2 SECONDS)
M.overlay_fullscreen("flash", type)
- addtimer(CALLBACK(M, TYPE_PROC_REF(/mob, clear_fullscreen), "flash", 3 SECONDS), 3 SECONDS)
+ addtimer(CALLBACK(M, TYPE_PROC_REF(/mob, clear_fullscreen), "flash", 1 SECONDS), shake_dur)
//How much time to allot for each pixel moved
var/time_scalar = (1 / ICON_SIZE_ALL) * TILES_PER_SECOND
@@ -206,6 +207,31 @@
#undef TILES_PER_SECOND
+/// Helper proc for a similar function to shake_camera, expect this one kicks the camera back at an opposite angle rather then skake_camera's irratic jittering.
+/proc/recoil_camera(mob/recoiled_mob, duration, backtime_duration, strength, angle)
+ if(!recoiled_mob || !recoiled_mob.client || duration < 1)
+ return
+ var/client/my_client = recoiled_mob.client
+ strength *= world.icon_size
+ var/client/client_to_shake = recoiled_mob.client
+ var/oldx = client_to_shake.pixel_x
+ var/oldy = client_to_shake.pixel_y
+
+ //get pixels to move the camera in an angle
+ var/mpx = sin(angle) * strength
+ var/mpy = cos(angle) * strength
+
+
+ if(my_client.prefs?.read_preference(/datum/preference/toggle/screen_shake_darken))
+ var/type = /atom/movable/screen/fullscreen/flash/black
+ var/shake_dur = max(duration, 2 SECONDS)
+ recoiled_mob.overlay_fullscreen("flash", type)
+ addtimer(CALLBACK(recoiled_mob, TYPE_PROC_REF(/mob, clear_fullscreen), "flash", 1 SECONDS), shake_dur)
+
+
+ animate(client_to_shake, pixel_x = oldx+mpx, pixel_y = oldy+mpy, time = duration, flags = ANIMATION_RELATIVE)
+ animate(pixel_x = oldx, pixel_y = oldy, time = backtime_duration, easing = BACK_EASING)
+
///Find if the message has the real name of any user mob in the mob_list
/proc/findname(msg)
if(!istext(msg))
diff --git a/code/modules/mob/mob_say.dm b/code/modules/mob/mob_say.dm
index 90f84cfe7da..7d1717cd496 100644
--- a/code/modules/mob/mob_say.dm
+++ b/code/modules/mob/mob_say.dm
@@ -50,7 +50,7 @@
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
- QUEUE_OR_CALL_VERB_FOR(VERB_CALLBACK(src, TYPE_PROC_REF(/mob, emote), "me", EMOTE_VISIBLE|EMOTE_AUDIBLE, message, TRUE), SSspeech_controller)
+ QUEUE_OR_CALL_VERB_FOR(VERB_CALLBACK(src, TYPE_PROC_REF(/mob, emote), "me", NONE, message, TRUE), SSspeech_controller)
/mob/try_speak(message, ignore_spam = FALSE, forced = null, filterproof = FALSE)
var/list/filter_result
diff --git a/code/modules/mob_spawn/corpses/mining_corpses.dm b/code/modules/mob_spawn/corpses/mining_corpses.dm
index da860fdb3b6..bece8ddb28b 100644
--- a/code/modules/mob_spawn/corpses/mining_corpses.dm
+++ b/code/modules/mob_spawn/corpses/mining_corpses.dm
@@ -350,7 +350,7 @@
dame.physique = FEMALE
dame.update_body()
if(prob(30))
- back = /obj/item/nullrod/vibro/talking
+ back = /obj/item/nullrod/claymore/talking
else
back = /obj/item/shield/buckler
belt = /obj/item/nullrod/claymore
@@ -388,8 +388,8 @@
/datum/outfit/consumed_heremoth
name = "Legion-Consumed Tribal Mothman"
uniform = /obj/item/clothing/under/costume/loincloth
- suit = /obj/item/clothing/suit/hooded/cultrobes/eldritch
- head = /obj/item/clothing/head/hooded/cult_hoodie/eldritch
+ suit = /obj/item/clothing/suit/chaplainsuit/armor/heretic
+ head = /obj/item/clothing/head/helmet/chaplain/heretic
/datum/outfit/consumed_heremoth/pre_equip(mob/living/carbon/human/moth, visuals_only = FALSE)
if(!visuals_only)
diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm
index 899792610b1..c8ce0f08f2d 100644
--- a/code/modules/mod/mod_activation.dm
+++ b/code/modules/mod/mod_activation.dm
@@ -259,6 +259,9 @@
part.heat_protection = initial(part.heat_protection)
part.cold_protection = initial(part.cold_protection)
part.alternate_worn_layer = part_datum.sealed_layer
+ if(part.slot_flags & ITEM_SLOT_HEAD)
+ var/datum/component/wearertargeting/protection = part.AddComponent(/datum/component/wearertargeting/earprotection, protection_amount = src.theme.hearing_protection)
+ protection.on_equip(src, wearer, ITEM_SLOT_HEAD)
else
part.icon_state = "[skin]-[part.base_icon_state]"
part.flags_cover &= ~part.visor_flags_cover
@@ -267,6 +270,8 @@
part.heat_protection = NONE
part.cold_protection = NONE
part.alternate_worn_layer = part_datum.unsealed_layer
+ if((part.slot_flags & ITEM_SLOT_HEAD) && istype(part, /obj/item/clothing/head/mod))
+ qdel(part.GetComponent(/datum/component/wearertargeting/earprotection))
update_speed()
wearer.update_clothing(part.slot_flags | slot_flags)
wearer.refresh_obscured()
diff --git a/code/modules/mod/mod_clothes.dm b/code/modules/mod/mod_clothes.dm
index bfebdb155e5..80a88ddb215 100644
--- a/code/modules/mod/mod_clothes.dm
+++ b/code/modules/mod/mod_clothes.dm
@@ -76,3 +76,35 @@
/obj/item/clothing/shoes/mod/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NO_SPEED_POTION, INNATE_TRAIT)
+
+/obj/item/clothing/glasses/mod
+ name = "MOD glasses"
+ desc = "A pair of glasses for a MODsuit."
+ icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ icon_state = "standard-glasses"
+ base_icon_state = "glasses"
+ worn_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ armor_type = /datum/armor/none
+ equip_sound = null
+ pickup_sound = null
+ drop_sound = null
+
+/obj/item/clothing/glasses/mod/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NO_SPEED_POTION, INNATE_TRAIT)
+
+/obj/item/clothing/neck/mod
+ name = "MOD tie"
+ desc = "An tie for a MODsuit."
+ icon = 'icons/obj/clothing/modsuit/mod_clothing.dmi'
+ icon_state = "standard-tie"
+ base_icon_state = "tie"
+ worn_icon = 'icons/mob/clothing/modsuit/mod_clothing.dmi'
+ armor_type = /datum/armor/none
+ equip_sound = null
+ pickup_sound = null
+ drop_sound = null
+
+/obj/item/clothing/neck/mod/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NO_SPEED_POTION, INNATE_TRAIT)
diff --git a/code/modules/mod/mod_construction.dm b/code/modules/mod/mod_construction.dm
index fd41c1c7f30..da27123ba92 100644
--- a/code/modules/mod/mod_construction.dm
+++ b/code/modules/mod/mod_construction.dm
@@ -95,6 +95,9 @@
/obj/item/mod/construction/plating/civilian
theme = /datum/mod_theme/civilian
+/obj/item/mod/construction/plating/portable_suit
+ theme = /datum/mod_theme/portable_suit
+
/obj/item/mod/construction/plating/engineering
theme = /datum/mod_theme/engineering
diff --git a/code/modules/mod/mod_core.dm b/code/modules/mod/mod_core.dm
index ab5cc6ef9aa..174299fdc67 100644
--- a/code/modules/mod/mod_core.dm
+++ b/code/modules/mod/mod_core.dm
@@ -6,6 +6,7 @@
inhand_icon_state = "electronic"
lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi'
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.05, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 1.05)
/// MOD unit we are powering.
var/obj/item/mod/control/mod
@@ -301,6 +302,7 @@
liquid electricity, this core makes it much more efficient, running all soft, hard, and wetware with several \
times less energy usage."
/// A modifier to all charge we use, ethereals don't need to spend as much energy as normal suits.
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.15, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 1.05)
var/charge_modifier = 0.1
/obj/item/mod/core/ethereal/charge_source()
diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm
index 2ad2fa889ca..54d6c7173da 100644
--- a/code/modules/mod/mod_theme.dm
+++ b/code/modules/mod/mod_theme.dm
@@ -25,6 +25,8 @@
var/datum/armor/armor_type = /datum/armor/mod_theme
/// Resistance flags shared across the MOD parts.
var/resistance_flags = NONE
+ /// Hearing protection level supplied by the modsuit head
+ var/hearing_protection
/// Atom flags shared across the MOD parts.
var/atom_flags = NONE
/// Max heat protection shared across the MOD parts.
@@ -114,7 +116,7 @@
if(!ispath(path))
continue
var/obj/item/mod_part = new path(mod)
- if(mod_part.slot_flags == ITEM_SLOT_OCLOTHING && isclothing(mod_part))
+ if(isclothing(mod_part) && mod_part.slot_flags == ITEM_SLOT_OCLOTHING)
var/obj/item/clothing/chestplate = mod_part
chestplate.allowed |= allowed_suit_storage
var/datum/mod_part/part_datum = new()
@@ -234,6 +236,42 @@
acid = 25
wound = 5
+/datum/mod_theme/portable_suit
+ name = "portable suit"
+ desc = "A one-piece three-piece suit designed for maximum negotiating power. Provides no meaningful protection."
+ extended_desc = "The \"secret weapon\" of the Moonrakers Conglomerate, designed for maximum bureaucratic efficiency. \
+ While giving practically no protection against any physical threat, the aura of affluence it exudes is said to ward off \
+ bear markets."
+ default_skin = "psuit"
+ armor_type = /datum/armor/mod_theme_portable_suit
+ charge_drain = DEFAULT_CHARGE_DRAIN / 2
+ max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT
+ min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT
+ complexity_max = DEFAULT_MAX_COMPLEXITY - 5
+ slot_flags = ITEM_SLOT_NECK
+ slowdown_deployed = 0
+ inbuilt_modules = list(/obj/item/mod/module/selfcleaner)
+ variants = list(
+ "psuit" = list(
+ /obj/item/clothing/glasses/mod = list(
+ UNSEALED_MESSAGE = GLASSES_UNSEAL_MESSAGE,
+ SEALED_MESSAGE = GLASSES_SEAL_MESSAGE,
+ ),
+ /obj/item/clothing/suit/mod = list(
+ UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE,
+ SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE,
+ ),
+ /obj/item/clothing/shoes/mod = list(
+ CAN_OVERSLOT = TRUE,
+ UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE,
+ SEALED_MESSAGE = BOOT_SEAL_MESSAGE,
+ ),
+ ),
+ )
+
+/datum/armor/mod_theme_portable_suit
+ bio = 50
+
/datum/mod_theme/engineering
name = "engineering"
desc = "An engineer-fit suit with heat and shock resistance. Nakamura Engineering's classic."
@@ -630,20 +668,21 @@
charge_drain = DEFAULT_CHARGE_DRAIN * 1.5
slowdown_deployed = 0.5
allowed_suit_storage = list(
+ /obj/item/crowbar/power/paramedic,
+ /obj/item/defibrillator/compact,
/obj/item/healthanalyzer,
- /obj/item/reagent_containers/dropper,
+ /obj/item/reagent_containers/applicator,
/obj/item/reagent_containers/cup/beaker,
/obj/item/reagent_containers/cup/bottle,
/obj/item/reagent_containers/cup/tube,
+ /obj/item/reagent_containers/dropper,
/obj/item/reagent_containers/hypospray,
- /obj/item/reagent_containers/applicator,
/obj/item/reagent_containers/syringe,
- /obj/item/stack/medical,
/obj/item/sensor_device,
- /obj/item/storage/pill_bottle,
- /obj/item/storage/bag/chemistry,
+ /obj/item/stack/medical,
/obj/item/storage/bag/bio,
- /obj/item/crowbar/power/paramedic,
+ /obj/item/storage/bag/chemistry,
+ /obj/item/storage/pill_bottle,
)
variants = list(
"medical" = list(
@@ -742,20 +781,22 @@
slowdown_deployed = 0.25
inbuilt_modules = list(/obj/item/mod/module/quick_carry/advanced)
allowed_suit_storage = list(
+ /obj/item/crowbar/power/paramedic,
+ /obj/item/defibrillator/compact,
/obj/item/healthanalyzer,
+ /obj/item/melee/baton/telescopic,
+ /obj/item/reagent_containers/applicator,
/obj/item/reagent_containers/dropper,
/obj/item/reagent_containers/cup/beaker,
/obj/item/reagent_containers/cup/bottle,
/obj/item/reagent_containers/cup/tube,
/obj/item/reagent_containers/hypospray,
- /obj/item/reagent_containers/applicator/pill,
/obj/item/reagent_containers/syringe,
- /obj/item/stack/medical,
/obj/item/sensor_device,
- /obj/item/storage/pill_bottle,
+ /obj/item/stack/medical,
/obj/item/storage/bag/chemistry,
/obj/item/storage/bag/bio,
- /obj/item/melee/baton/telescopic,
+ /obj/item/storage/pill_bottle,
)
variants = list(
"rescue" = list(
@@ -821,7 +862,8 @@
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
complexity_max = DEFAULT_MAX_COMPLEXITY + 5
slowdown_deployed = 1.25
- inbuilt_modules = list(/obj/item/mod/module/reagent_scanner/advanced, /obj/item/mod/module/hearing_protection)
+ inbuilt_modules = list(/obj/item/mod/module/reagent_scanner/advanced)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/analyzer,
/obj/item/dnainjector,
@@ -888,7 +930,7 @@
armor_type = /datum/armor/mod_theme_security
complexity_max = DEFAULT_MAX_COMPLEXITY - 2
slowdown_deployed = 0.5
- inbuilt_modules = list(/obj/item/mod/module/hearing_protection)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/reagent_containers/spray/pepper,
/obj/item/restraints/handcuffs,
@@ -955,7 +997,8 @@
armor_type = /datum/armor/mod_theme_safeguard
resistance_flags = FIRE_PROOF
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
- inbuilt_modules = list(/obj/item/mod/module/shove_blocker/locked, /obj/item/mod/module/hearing_protection)
+ inbuilt_modules = list(/obj/item/mod/module/shove_blocker/locked)
+ hearing_protection = EAR_PROTECTION_NORMAL
slowdown_deployed = 0.25
allowed_suit_storage = list(
/obj/item/reagent_containers/spray/pepper,
@@ -1028,7 +1071,7 @@
siemens_coefficient = 0
complexity_max = DEFAULT_MAX_COMPLEXITY + 5
slowdown_deployed = 0.25
- inbuilt_modules = list(/obj/item/mod/module/hearing_protection)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/restraints/handcuffs,
/obj/item/assembly/flash,
@@ -1167,7 +1210,8 @@
slowdown_deployed = 0
ui_theme = "syndicate"
resistance_flags = FIRE_PROOF
- inbuilt_modules = list(/obj/item/mod/module/welding/syndicate, /obj/item/mod/module/night, /obj/item/mod/module/hearing_protection)
+ inbuilt_modules = list(/obj/item/mod/module/welding/syndicate, /obj/item/mod/module/night)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/restraints/handcuffs,
/obj/item/assembly/flash,
@@ -1273,7 +1317,8 @@
siemens_coefficient = 0
slowdown_deployed = 0
ui_theme = "syndicate"
- inbuilt_modules = list(/obj/item/mod/module/welding/syndicate, /obj/item/mod/module/night, /obj/item/mod/module/hearing_protection)
+ inbuilt_modules = list(/obj/item/mod/module/welding/syndicate, /obj/item/mod/module/night)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/restraints/handcuffs,
/obj/item/assembly/flash,
@@ -1347,7 +1392,8 @@
activation_step_time = MOD_ACTIVATION_STEP_TIME * 0.5
ui_theme = "syndicate"
slot_flags = ITEM_SLOT_BELT
- inbuilt_modules = list(/obj/item/mod/module/infiltrator, /obj/item/mod/module/storage/belt, /obj/item/mod/module/demoralizer, /obj/item/mod/module/hearing_protection, /obj/item/mod/module/night)
+ inbuilt_modules = list(/obj/item/mod/module/infiltrator, /obj/item/mod/module/storage/belt, /obj/item/mod/module/demoralizer, /obj/item/mod/module/night)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/restraints/handcuffs,
/obj/item/assembly/flash,
@@ -1415,8 +1461,9 @@
resistance_flags = FIRE_PROOF|ACID_PROOF
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
charge_drain = DEFAULT_CHARGE_DRAIN * 2
- slowdown_deployed = 0 //bubber edit from -0.5. This suit was causing speed issues.
- inbuilt_modules = list(/obj/item/mod/module/quick_carry/advanced, /obj/item/mod/module/hearing_protection)
+ slowdown_deployed = -0.5
+ inbuilt_modules = list(/obj/item/mod/module/quick_carry/advanced)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/assembly/flash,
/obj/item/healthanalyzer,
@@ -1503,7 +1550,8 @@
complexity_max = DEFAULT_MAX_COMPLEXITY
slowdown_deployed = 0
ui_theme = "wizard"
- inbuilt_modules = list(/obj/item/mod/module/anti_magic/wizard, /obj/item/mod/module/hearing_protection)
+ inbuilt_modules = list(/obj/item/mod/module/anti_magic/wizard)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/teleportation_scroll,
/obj/item/highfrequencyblade/wizard,
@@ -1570,7 +1618,8 @@
siemens_coefficient = 0
slowdown_deployed = 0
ui_theme = "hackerman"
- inbuilt_modules = list(/obj/item/mod/module/welding/camera_vision, /obj/item/mod/module/hacker, /obj/item/mod/module/weapon_recall, /obj/item/mod/module/adrenaline_boost, /obj/item/mod/module/energy_net, /obj/item/mod/module/hearing_protection)
+ inbuilt_modules = list(/obj/item/mod/module/welding/camera_vision, /obj/item/mod/module/hacker, /obj/item/mod/module/weapon_recall, /obj/item/mod/module/adrenaline_boost, /obj/item/mod/module/energy_net)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/gun,
/obj/item/melee/baton,
@@ -1706,7 +1755,8 @@
siemens_coefficient = 0
slowdown_deployed = 0
ui_theme = "ntos_terminal"
- inbuilt_modules = list(/obj/item/mod/module/welding/syndicate, /obj/item/mod/module/hearing_protection)
+ inbuilt_modules = list(/obj/item/mod/module/welding/syndicate)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/ammo_box,
/obj/item/ammo_casing,
@@ -1774,7 +1824,7 @@
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
siemens_coefficient = 0
slowdown_deployed = 0
- inbuilt_modules = list(/obj/item/mod/module/hearing_protection)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/restraints/handcuffs,
/obj/item/assembly/flash,
@@ -1887,7 +1937,7 @@
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
siemens_coefficient = 0
complexity_max = DEFAULT_MAX_COMPLEXITY + 10
- inbuilt_modules = list(/obj/item/mod/module/hearing_protection)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/restraints/handcuffs,
/obj/item/assembly/flash,
@@ -1958,7 +2008,7 @@
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
siemens_coefficient = 0
slowdown_deployed = 0
- inbuilt_modules = list(/obj/item/mod/module/hearing_protection)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/restraints/handcuffs,
/obj/item/assembly/flash,
@@ -2023,7 +2073,7 @@
max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT
complexity_max = DEFAULT_MAX_COMPLEXITY - 10
slowdown_deployed = 0
- inbuilt_modules = list(/obj/item/mod/module/hearing_protection)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/restraints/handcuffs,
)
@@ -2089,7 +2139,7 @@
siemens_coefficient = 0
slowdown_deployed = 0
activation_step_time = MOD_ACTIVATION_STEP_TIME * 0.2
- inbuilt_modules = list(/obj/item/mod/module/hearing_protection)
+ hearing_protection = EAR_PROTECTION_NORMAL
allowed_suit_storage = list(
/obj/item/gun,
)
@@ -2156,7 +2206,7 @@
siemens_coefficient = 0
slowdown_deployed = 0
activation_step_time = MOD_ACTIVATION_STEP_TIME * 0.01
- inbuilt_modules = list(/obj/item/mod/module/hearing_protection)
+ hearing_protection = EAR_PROTECTION_FULL
allowed_suit_storage = list(
/obj/item/gun,
)
diff --git a/code/modules/mod/mod_types.dm b/code/modules/mod/mod_types.dm
index 6d624c702b5..217f0b2666e 100644
--- a/code/modules/mod/mod_types.dm
+++ b/code/modules/mod/mod_types.dm
@@ -54,6 +54,16 @@
/obj/item/mod/module/flashlight,
)
+/obj/item/mod/control/pre_equipped/portable_suit
+ theme = /datum/mod_theme/portable_suit
+ applied_modules = list(
+ /obj/item/mod/module/paper_dispenser,
+ /obj/item/mod/module/stamp,
+ )
+ default_pins = list(
+ /obj/item/mod/module/stamp,
+ )
+
/obj/item/mod/control/pre_equipped/engineering
theme = /datum/mod_theme/engineering
applied_modules = list(
@@ -264,6 +274,7 @@
/obj/item/mod/module/magnetic_harness,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/jump_jet,
+ /obj/item/mod/module/pathfinder,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/dna_lock,
/obj/item/mod/module/hat_stabilizer/syndicate,
@@ -322,6 +333,7 @@
/obj/item/mod/module/magnetic_harness,
/obj/item/mod/module/jetpack/advanced,
/obj/item/mod/module/jump_jet,
+ /obj/item/mod/module/pathfinder,
/obj/item/mod/module/flashlight,
/obj/item/mod/module/hat_stabilizer/syndicate,
/obj/item/mod/module/quick_cuff,
diff --git a/code/modules/mod/modules/module_pathfinder.dm b/code/modules/mod/modules/module_pathfinder.dm
index 4b948d6ed07..55263aa332d 100644
--- a/code/modules/mod/modules/module_pathfinder.dm
+++ b/code/modules/mod/modules/module_pathfinder.dm
@@ -1,3 +1,5 @@
+#define PATHFINDER_PRE_ANIMATE_TIME (2 SECONDS)
+
///Pathfinder - Can fly the suit from a long distance to an implant installed in someone.
/obj/item/mod/module/pathfinder
name = "MOD pathfinder module"
@@ -24,6 +26,8 @@
var/image/jet_icon
/// Allow suit activation - Lets this module be recalled from the MOD.
var/allow_suit_activation = FALSE // I'm not here to argue about balance
+ /// Are we currently travelling?
+ var/in_transit = FALSE
/obj/item/mod/module/pathfinder/Initialize(mapload)
@@ -88,7 +92,7 @@
return
balloon_alert(activator, "implanted")
if(!(activator == mod.wearer)) // someone else implanted you
- balloon_alert(mod.wearer, "pathfinder MOD tracker implanted!")
+ balloon_alert(mod.wearer, "tracker implanted!")
playsound(src, 'sound/effects/spray.ogg', 30, TRUE, -6)
/obj/item/mod/module/pathfinder/proc/attach(mob/living/user)
@@ -101,64 +105,86 @@
return
mod.quick_deploy(user)
human_user.update_action_buttons(TRUE)
- balloon_alert(human_user, "[mod] attached")
playsound(mod, 'sound/machines/ping.ogg', 50, TRUE)
drain_power(use_energy_cost)
/obj/item/mod/module/pathfinder/proc/recall(mob/recaller)
if(!implant)
- balloon_alert(recaller, "no target implant!")
+ balloon_alert(recaller, "no implant!")
return FALSE
if(recaller != implant.imp_in && !allow_suit_activation) // No pAI recalling
- balloon_alert(recaller, "sector safety regulations prevent MOD-side recalling!")
+ balloon_alert(recaller, "invalid user!")
return FALSE
if(mod.open)
balloon_alert(recaller, "cover open!")
return FALSE
- if(mod.ai_controller)
- balloon_alert(recaller, "already moving!")
+ if(in_transit)
+ balloon_alert(recaller, "suit in transit!")
return FALSE
- if(ismob(get_atom_on_turf(mod)))
- balloon_alert(recaller, "already on someone!")
+ var/atom_on_turf = get_atom_on_turf(mod)
+ if(ismob(atom_on_turf))
+ if(atom_on_turf == recaller)
+ balloon_alert(recaller, "already worn!")
+ else
+ recaller.balloon_alert(recaller, "suit is worn by somebody else!")
return FALSE
- if(mod.z != implant.imp_in.z || get_dist(implant.imp_in, mod) > MOD_AI_RANGE)
- balloon_alert(recaller, "too far!")
- return FALSE
- var/datum/ai_controller/mod_ai = new /datum/ai_controller/mod(mod)
- mod.ai_controller = mod_ai
- mod_ai.set_movement_target(type, implant.imp_in)
- mod_ai.set_blackboard_key(BB_MOD_TARGET, implant.imp_in)
- mod_ai.set_blackboard_key(BB_MOD_MODULE, src)
- mod.interaction_flags_item &= ~INTERACT_ITEM_ATTACK_HAND_PICKUP
- mod.AddElement(/datum/element/movetype_handler)
- ADD_TRAIT(mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
- animate(mod, 0.2 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y)
- mod.add_overlay(jet_icon)
- RegisterSignal(mod, COMSIG_MOVABLE_MOVED, PROC_REF(on_move))
+
+ in_transit = TRUE
+ animate(mod, 0.5 SECONDS, pixel_x = base_pixel_y, pixel_y = base_pixel_y)
+ mod.Shake(pixelshiftx = 1, pixelshifty = 1, duration = PATHFINDER_PRE_ANIMATE_TIME)
+ addtimer(CALLBACK(src, PROC_REF(do_recall), recaller), PATHFINDER_PRE_ANIMATE_TIME, TIMER_DELETE_ME)
+
balloon_alert(recaller, "suit recalled")
if(!(recaller == mod.wearer))
balloon_alert(mod.wearer, "suit recalled")
return TRUE
-/obj/item/mod/module/pathfinder/proc/on_move(atom/movable/source, atom/old_loc, dir, forced)
- SIGNAL_HANDLER
-
- var/matrix/mod_matrix = matrix()
- mod_matrix.Turn(get_angle(source, implant.imp_in))
- source.transform = mod_matrix
-
-/obj/item/mod/module/pathfinder/proc/end_recall(successful = TRUE)
- if(!mod)
+/// Pod-transport the suit to its owner
+/obj/item/mod/module/pathfinder/proc/do_recall(mob/recaller)
+ var/container = get_atom_on_turf(mod)
+ if(ismob(container))
+ balloon_alert(recaller, "launch interrupted!")
+ in_transit = FALSE
return
- QDEL_NULL(mod.ai_controller)
- mod.interaction_flags_item |= INTERACT_ITEM_ATTACK_HAND_PICKUP
- REMOVE_TRAIT(mod, TRAIT_MOVE_FLYING, MOD_TRAIT)
- mod.RemoveElement(/datum/element/movetype_handler)
+
+ if(iscloset(container))
+ var/obj/structure/closet/closet = container
+ if (!closet.opened)
+ if (!closet.open())
+ playsound(closet, 'sound/effects/bang.ogg', vol = 50, vary = TRUE)
+ closet.bust_open()
+
+
+ mod.add_overlay(jet_icon)
+ playsound(mod, 'sound/vehicles/rocketlaunch.ogg', vol = 80, vary = FALSE)
+ var/turf/land_target = get_turf(implant.imp_in)
+ var/obj/structure/closet/supplypod/pod = podspawn(list(
+ "target" = get_turf(mod),
+ "path" = /obj/structure/closet/supplypod/transport/module_pathfinder,
+ "reverse_dropoff_coords" = list(land_target.x, land_target.y, land_target.z),
+ ))
+
+ pod.insert(mod, pod)
+ RegisterSignal(pod, COMSIG_SUPPLYPOD_RETURNING, PROC_REF(pod_takeoff))
+
+ if (istype(container, /obj/machinery/suit_storage_unit))
+ var/obj/machinery/suit_storage_unit/storage = container
+ storage.locked = FALSE
+ storage.open_machine()
+
+/// Track when pod has taken off so we don't falsely report the initial landing
+/obj/item/mod/module/pathfinder/proc/pod_takeoff(datum/pod)
+ SIGNAL_HANDLER
+ RegisterSignal(pod, COMSIG_SUPPLYPOD_LANDED, PROC_REF(pod_landed))
+
+/// When the pod landed, we can recall again
+/obj/item/mod/module/pathfinder/proc/pod_landed()
+ SIGNAL_HANDLER
+ in_transit = FALSE
mod.cut_overlay(jet_icon)
- mod.transform = matrix()
- UnregisterSignal(mod, COMSIG_MOVABLE_MOVED)
- if(!successful)
- balloon_alert(implant.imp_in, "suit lost connection!")
+ playsound(mod, 'sound/items/handling/toolbox/toolbox_drop.ogg', vol = 80, vary = FALSE)
+ if (implant?.imp_in?.Adjacent(src))
+ INVOKE_ASYNC(src, PROC_REF(attach), implant.imp_in)
// ###########
// THE INPLANT
@@ -173,8 +199,6 @@
/// The pathfinder module we are linked to.
var/obj/item/mod/module/pathfinder/module
-
-
/obj/item/implant/mod/Initialize(mapload)
. = ..()
if(!istype(loc, /obj/item/mod/module/pathfinder))
@@ -182,8 +206,6 @@
module = loc
/obj/item/implant/mod/Destroy()
- if(module?.mod?.ai_controller)
- module.end_recall(successful = FALSE)
module = null
return ..()
@@ -192,7 +214,6 @@
Name: Nakamura Engineering Pathfinder Implant
\
Implant Details: Allows for the recall of a Modular Outerwear Device by the implant owner at any time.
"
-
/datum/action/item_action/mod_recall
name = "Recall MOD"
desc = "Recall a MODsuit anyplace, anytime."
@@ -205,7 +226,7 @@
COOLDOWN_DECLARE(recall_cooldown)
/datum/action/item_action/mod_recall/New(Target)
- ..()
+ . = ..()
if(!istype(Target, /obj/item/implant/mod))
qdel(src)
return
@@ -215,5 +236,14 @@
if(!COOLDOWN_FINISHED(src, recall_cooldown))
implant.balloon_alert(owner, "on cooldown!")
return
- if(implant.module.recall(owner)) // change this
- COOLDOWN_START(src, recall_cooldown, 15 SECONDS)
+ if(implant.module.recall(owner))
+ implant.balloon_alert(owner, "suit incoming...")
+ COOLDOWN_START(src, recall_cooldown, 5 SECONDS)
+
+/// Special pod subtype we use just to make insertion check easy
+/obj/structure/closet/supplypod/transport/module_pathfinder
+
+/obj/structure/closet/supplypod/transport/module_pathfinder/insertion_allowed(atom/to_insert)
+ return istype(to_insert, /obj/item/mod/control)
+
+#undef PATHFINDER_PRE_ANIMATE_TIME
diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm
index 6b7d814aa3e..44443c90fa2 100644
--- a/code/modules/mod/modules/modules_antag.dm
+++ b/code/modules/mod/modules/modules_antag.dm
@@ -29,6 +29,8 @@
var/shield_icon = "shield-red"
/// Charges the shield should start with.
var/charges
+ /// Whether or not we allow this shield to block overwhelming attacks, such as from mechs.
+ var/block_overwhelming_attacks = FALSE
/obj/item/mod/module/energy_shield/Initialize(mapload)
. = ..()
@@ -43,6 +45,7 @@
charge_recovery = charge_recovery, \
lose_multiple_charges = lose_multiple_charges, \
starting_charges = charges, \
+ can_block_overwhelming = block_overwhelming_attacks, \
shield_icon_file = shield_icon_file, \
shield_icon = shield_icon)
RegisterSignal(mod.wearer, COMSIG_LIVING_CHECK_BLOCK, PROC_REF(shield_reaction))
@@ -64,7 +67,7 @@
SIGNAL_HANDLER
if(mod.hit_reaction(owner, hitby, attack_text, 0, damage, attack_type))
- drain_power(use_energy_cost)
+ drain_power(use_energy_cost + use_energy_cost * (attack_type == OVERWHELMING_ATTACK ? (damage/100) : 0))
return SUCCESSFUL_BLOCK
return NONE
@@ -80,6 +83,7 @@
max_charges = 5
recharge_start_delay = 20 SECONDS
charge_increment_delay = 3 SECONDS
+ block_overwhelming_attacks = TRUE // It's magic, bitch
shield_icon_file = 'icons/effects/magic.dmi'
shield_icon = "mageshield"
required_slots = list()
diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm
index ab3e9366db6..9b24c5b7dc3 100644
--- a/code/modules/mod/modules/modules_engineering.dm
+++ b/code/modules/mod/modules/modules_engineering.dm
@@ -256,6 +256,7 @@
icon = 'icons/obj/clothing/modsuit/mod_modules.dmi'
max_integrity = 60
interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_UI_INTERACT
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 6.15, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 1.5)
/// MODsuit tether module that created our projectile
var/obj/item/mod/module/tether/parent_module
/// Should we reset our pixel positions next time we move?
diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm
index 66e3ace2a8c..4b72ea0ff37 100644
--- a/code/modules/mod/modules/modules_general.dm
+++ b/code/modules/mod/modules/modules_general.dm
@@ -1053,25 +1053,3 @@
var/datum/effect_system/lightning_spread/sparks = new /datum/effect_system/lightning_spread
sparks.set_up(number = 5, cardinals_only = TRUE, location = mod.wearer.loc)
sparks.start()
-
-/obj/item/mod/module/hearing_protection
- name = "MOD hearing protection module"
- desc = "A module that protects the users ears from loud sounds"
- complexity = 0
- removable = FALSE
- incompatible_modules = list(/obj/item/mod/module/hearing_protection)
- required_slots = list(ITEM_SLOT_HEAD)
-
-/obj/item/mod/module/hearing_protection/on_part_activation()
- var/obj/item/clothing/head_cover = mod.get_part_from_slot(ITEM_SLOT_HEAD) || mod.get_part_from_slot(ITEM_SLOT_MASK) || mod.get_part_from_slot(ITEM_SLOT_EYES)
- if(istype(head_cover))
- head_cover.AddComponent(/datum/component/wearertargeting/earprotection)
- var/datum/component/wearertargeting/earprotection/protection = head_cover.GetComponent(/datum/component/wearertargeting/earprotection)
- protection.on_equip(src, mod.wearer, ITEM_SLOT_HEAD)
-
-/obj/item/mod/module/hearing_protection/on_part_deactivation(deleting = FALSE)
- if(deleting)
- return
- var/obj/item/clothing/head_cover = mod.get_part_from_slot(ITEM_SLOT_HEAD) || mod.get_part_from_slot(ITEM_SLOT_MASK) || mod.get_part_from_slot(ITEM_SLOT_EYES)
- if(istype(head_cover))
- qdel(head_cover.GetComponent(/datum/component/wearertargeting/earprotection))
diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm
index e1169810723..f047cbfce34 100644
--- a/code/modules/mod/modules/modules_maint.dm
+++ b/code/modules/mod/modules/modules_maint.dm
@@ -243,7 +243,7 @@
use_energy_cost = DEFAULT_CHARGE_DRAIN * 0.5
incompatible_modules = list(/obj/item/mod/module/paper_dispenser)
cooldown_time = 5 SECONDS
- required_slots = list(ITEM_SLOT_GLOVES)
+ required_slots = list(ITEM_SLOT_GLOVES|ITEM_SLOT_NECK)
/// The total number of sheets created by this MOD. The more sheets, them more likely they set on fire.
var/num_sheets_dispensed = 0
@@ -286,7 +286,7 @@
device = /obj/item/stamp/mod
incompatible_modules = list(/obj/item/mod/module/stamp)
cooldown_time = 0.5 SECONDS
- required_slots = list(ITEM_SLOT_GLOVES)
+ required_slots = list(ITEM_SLOT_GLOVES|ITEM_SLOT_NECK)
/obj/item/stamp/mod
name = "MOD electronic stamp"
diff --git a/code/modules/mod/modules/modules_service.dm b/code/modules/mod/modules/modules_service.dm
index 9ca53939fb2..c40e3552bce 100644
--- a/code/modules/mod/modules/modules_service.dm
+++ b/code/modules/mod/modules/modules_service.dm
@@ -109,3 +109,18 @@
var/refill_add = min(volume - reagents.total_volume, 2 * seconds_per_tick)
if(refill_add > 0)
reagents.add_reagent(/datum/reagent/space_cleaner, refill_add)
+
+/obj/item/mod/module/selfcleaner
+ name = "MOD perfumer module"
+ desc = "A small spray to clean oneself up. Has a pleasant scent."
+ icon_state = "cleaner"
+ module_type = MODULE_USABLE
+ use_energy_cost = DEFAULT_CHARGE_DRAIN * 5
+ complexity = 1
+ incompatible_modules = list(/obj/item/mod/module/selfcleaner)
+ cooldown_time = 10 SECONDS
+
+/obj/item/mod/module/selfcleaner/on_use(mob/activator)
+ activator.wash(CLEAN_WASH)
+ drain_power(use_energy_cost)
+ playsound(activator, 'sound/effects/spray.ogg', 50, FALSE)
diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm
index f124b5f536e..c88a75f5468 100644
--- a/code/modules/modular_computers/computers/item/computer.dm
+++ b/code/modules/modular_computers/computers/item/computer.dm
@@ -149,6 +149,7 @@
if(internal_cell)
internal_cell = new internal_cell(src)
+ AddElement(/datum/element/drag_to_activate)
install_default_programs()
register_context()
update_appearance()
@@ -399,10 +400,6 @@
update_appearance()
return TRUE
-/obj/item/modular_computer/mouse_drop_dragged(atom/over_object, mob/user)
- if(!istype(over_object, /atom/movable/screen))
- return attack_self(user)
-
/obj/item/modular_computer/attack_ai(mob/user)
return attack_self(user)
diff --git a/code/modules/modular_computers/computers/item/role_tablet_presets.dm b/code/modules/modular_computers/computers/item/role_tablet_presets.dm
index da9f459769b..2ef6e082372 100644
--- a/code/modules/modular_computers/computers/item/role_tablet_presets.dm
+++ b/code/modules/modular_computers/computers/item/role_tablet_presets.dm
@@ -133,6 +133,7 @@
starting_programs = list(
/datum/computer_file/program/records/security,
/datum/computer_file/program/robocontrol,
+ /datum/computer_file/program/budgetorders,
)
/**
diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm
index 5f4b1f5dc6f..33199c8a92f 100644
--- a/code/modules/modular_computers/computers/machinery/modular_computer.dm
+++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm
@@ -10,6 +10,7 @@
density = TRUE
max_integrity = 300
integrity_failure = 0.5
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 10)
///A flag that describes this device type
var/hardware_flag = PROGRAM_CONSOLE
diff --git a/code/modules/modular_computers/file_system/programs/betting.dm b/code/modules/modular_computers/file_system/programs/betting.dm
index d368d15e8e7..98b827bcb4a 100644
--- a/code/modules/modular_computers/file_system/programs/betting.dm
+++ b/code/modules/modular_computers/file_system/programs/betting.dm
@@ -234,7 +234,7 @@ GLOBAL_LIST_EMPTY_TYPED(active_bets, /datum/active_bet)
//they only made their money back, don't tell them they won anything.
if((money_won - text2num(winner[2])) == 0)
continue
- winner_account.bank_card_talk("You won [money_won]cr from having a correct guess on [name]!")
+ winner_account.bank_card_talk("You won [money_won][MONEY_SYMBOL] from having a correct guess on [name]!")
///Puts a bank account's money bet on a given option.
/datum/active_bet/proc/bet_money(datum/bank_account/better, money_betting, option_betting)
@@ -255,7 +255,7 @@ GLOBAL_LIST_EMPTY_TYPED(active_bets, /datum/active_bet)
if(!better.adjust_money(-money_adding_in, "Gambling on [name]."))
return
total_amount_bet += money_adding_in
- better.bank_card_talk("Additional [money_adding_in]cr deducted for your bet on [name].")
+ better.bank_card_talk("Additional [money_adding_in][MONEY_SYMBOL] deducted for your bet on [name].")
existing_bets[2] = "[money_betting]"
return
//taking it all out, we remove them from the list so they aren't a winner with bets of 0.
@@ -269,7 +269,7 @@ GLOBAL_LIST_EMPTY_TYPED(active_bets, /datum/active_bet)
if(text2num(existing_bets[2]) > money_betting)
var/money_taking_out = text2num(existing_bets[2]) - money_betting
total_amount_bet -= money_taking_out
- better.bank_card_talk("Refunded [money_taking_out]cr for taking money out of your bet on [name].")
+ better.bank_card_talk("Refunded [money_taking_out][MONEY_SYMBOL] for taking money out of your bet on [name].")
better.adjust_money(money_taking_out, "Refund from gambling on [name].")
existing_bets[2] = "[money_betting]"
return
@@ -278,7 +278,7 @@ GLOBAL_LIST_EMPTY_TYPED(active_bets, /datum/active_bet)
return
total_amount_bet += money_betting
options[option_betting] += list(list(better, "[money_betting]"))
- better.bank_card_talk("Deducted [money_betting]cr for your bet on [name].")
+ better.bank_card_talk("Deducted [money_betting][MONEY_SYMBOL] for your bet on [name].")
///Cancels your bet, removing your bet and refunding your money.
/datum/active_bet/proc/cancel_bet(datum/bank_account/better)
@@ -287,7 +287,7 @@ GLOBAL_LIST_EMPTY_TYPED(active_bets, /datum/active_bet)
if(existing_bets[1] == better)
var/money_refunding = text2num(existing_bets[2])
total_amount_bet -= money_refunding
- better.bank_card_talk("Refunded [money_refunding]cr for cancelling your bet on [name].")
+ better.bank_card_talk("Refunded [money_refunding][MONEY_SYMBOL] for cancelling your bet on [name].")
better.adjust_money(money_refunding, "Refunded: changed bet for [name].")
options[option] -= list(existing_bets)
diff --git a/code/modules/modular_computers/file_system/programs/bounty_board.dm b/code/modules/modular_computers/file_system/programs/bounty_board.dm
index 1b132054473..37e9ddd7121 100644
--- a/code/modules/modular_computers/file_system/programs/bounty_board.dm
+++ b/code/modules/modular_computers/file_system/programs/bounty_board.dm
@@ -102,7 +102,7 @@
playsound(computer, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
return
request_target.transfer_money(current_user, active_request.value, "Bounties: Request Completed")
- computer.say("Paid out [active_request.value] credits.")
+ computer.say("Paid out [active_request.value] [MONEY_NAME].")
GLOB.request_list.Remove(active_request)
return TRUE
if("clear")
diff --git a/code/modules/modular_computers/file_system/programs/budgetordering.dm b/code/modules/modular_computers/file_system/programs/budgetordering.dm
index f85193dc615..65f78991f93 100644
--- a/code/modules/modular_computers/file_system/programs/budgetordering.dm
+++ b/code/modules/modular_computers/file_system/programs/budgetordering.dm
@@ -67,7 +67,7 @@
var/obj/item/card/id/id_card = computer.stored_id?.GetID()
if(id_card?.registered_account)
buyer = SSeconomy.get_dep_account(id_card?.registered_account.account_job.paycheck_department)
- if((ACCESS_COMMAND in id_card.access))
+ if((ACCESS_BUDGET in id_card.access))
requestonly = FALSE
can_approve_requests = TRUE
// If buyer is a departmental budget, replaces "Cargo" with that budget - we're not using the cargo budget here
@@ -89,14 +89,16 @@
data["supplies"] = list()
for(var/pack in SSshuttle.supply_packs)
var/datum/supply_pack/P = SSshuttle.supply_packs[pack]
- if(!is_visible_pack(user, P.access_view , null, P.contraband) || P.hidden)
+ if(P.order_flags & ORDER_INVISIBLE)
+ continue
+ if(!is_visible_pack(user, P.access_view , null, (P.order_flags & ORDER_CONTRABAND)) || (P.order_flags & ORDER_EMAG_ONLY))
continue
if(!data["supplies"][P.group])
data["supplies"][P.group] = list(
"name" = P.group,
"packs" = list()
)
- if((P.hidden && (P.contraband && !contraband) || (P.special && !P.special_enabled) || P.drop_pod_only))
+ if(((P.order_flags & ORDER_EMAG_ONLY) && ((P.order_flags & ORDER_CONTRABAND) && !contraband) || ((P.order_flags & ORDER_SPECIAL) && !(P.order_flags & ORDER_SPECIAL_ENABLED)) || (P.order_flags & ORDER_POD_ONLY)))
continue
var/obj/item/first_item = length(P.contains) > 0 ? P.contains[1] : null
@@ -107,7 +109,7 @@
"desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name.
"first_item_icon" = first_item?.icon,
"first_item_icon_state" = first_item?.icon_state,
- "goody" = P.goody,
+ "goody" = P.order_flags & ORDER_GOODY,
"access" = P.access,
"contains" = P.get_contents_ui_data(),
))
@@ -216,7 +218,7 @@
var/datum/supply_pack/pack = SSshuttle.supply_packs[id]
if(!istype(pack))
return
- if(pack.hidden || pack.contraband || pack.drop_pod_only || (pack.special && !pack.special_enabled))
+ if((pack.order_flags & (ORDER_EMAG_ONLY | ORDER_POD_ONLY | ORDER_CONTRABAND)) || ((pack.order_flags & ORDER_SPECIAL) && !(pack.order_flags & ORDER_SPECIAL_ENABLED)))
return
var/name = "*None Provided*"
@@ -267,7 +269,7 @@
if(dept_choice == "Cargo Budget")
personal_department = null
- if(pack.goody && !self_paid)
+ if((pack.order_flags & ORDER_GOODY) && !self_paid)
playsound(computer, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
computer.say("ERROR: Small crates may only be purchased by private accounts.")
return
diff --git a/code/modules/modular_computers/file_system/programs/dept_order.dm b/code/modules/modular_computers/file_system/programs/dept_order.dm
index 3677890dd0f..eede7b147f1 100644
--- a/code/modules/modular_computers/file_system/programs/dept_order.dm
+++ b/code/modules/modular_computers/file_system/programs/dept_order.dm
@@ -105,13 +105,11 @@ GLOBAL_VAR(department_cd_override)
/// Checks if we can "see" the passed supply pack
/datum/computer_file/program/department_order/proc/can_see_pack(datum/supply_pack/to_check)
PROTECTED_PROC(TRUE)
- if(to_check.hidden && !(computer.obj_flags & EMAGGED))
+ if((to_check.order_flags & ORDER_EMAG_ONLY) && !(computer.obj_flags & EMAGGED))
return FALSE
- if(to_check.special && !to_check.special_enabled)
+ if((to_check.order_flags & ORDER_SPECIAL) && !(to_check.order_flags & ORDER_SPECIAL_ENABLED))
return FALSE
- if(to_check.drop_pod_only)
- return FALSE
- if(to_check.goody)
+ if(to_check.order_flags & (ORDER_INVISIBLE | ORDER_POD_ONLY | ORDER_GOODY | ORDER_NOT_DEPARTMENTAL))
return FALSE
return TRUE
diff --git a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
index 5f21d1b0623..31d45e3c3c8 100644
--- a/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
+++ b/code/modules/modular_computers/file_system/programs/messenger/messenger_program.dm
@@ -606,6 +606,13 @@
for(var/datum/computer_file/program/messenger/messenger as anything in targets)
stringified_targets += get_messenger_name(messenger)
+ var/sent_prob = 0
+ if(ishuman(source))
+ var/mob/living/carbon/human/oldie = source
+ sent_prob = (0.025 * oldie.age) ** 3 // 25 y/o = ~0.25% chance, 85 y/o = ~10% chance
+ if (computer && prob(sent_prob))
+ message = "[message] [computer.get_messenger_ending()]"
+
var/datum/signal/subspace/messaging/tablet_message/signal = new(computer, list(
"ref" = REF(src),
"message" = message,
diff --git a/code/modules/modular_computers/file_system/programs/nt_pay.dm b/code/modules/modular_computers/file_system/programs/nt_pay.dm
index 3138567d448..19f3c7bb198 100644
--- a/code/modules/modular_computers/file_system/programs/nt_pay.dm
+++ b/code/modules/modular_computers/file_system/programs/nt_pay.dm
@@ -83,7 +83,7 @@
return NT_PAY_STATUS_INVALID_MONEY
if(token == current_user.pay_token)
if(user)
- to_chat(user, span_notice("You can't send credits to yourself."))
+ to_chat(user, span_notice("You can't send [MONEY_NAME] to yourself."))
return NT_PAY_SATUS_SENDER_IS_RECEIVER
for(var/account in SSeconomy.bank_accounts_by_id)
@@ -100,12 +100,12 @@
current_user.bank_card_talk("You cannot afford it.")
return NT_PAY_STATUS_INVALID_MONEY
- recipient.bank_card_talk("You received [money_to_send] credit(s). Reason: transfer from [current_user.account_holder]")
+ recipient.bank_card_talk("You received [money_to_send] [MONEY_NAME](s). Reason: transfer from [current_user.account_holder]")
recipient.transfer_money(current_user, money_to_send)
for(var/obj/item/card/id/id_card as anything in recipient.bank_cards)
SEND_SIGNAL(id_card, COMSIG_ID_CARD_NTPAY_MONEY_RECEIVED, computer, money_to_send)
- current_user.bank_card_talk("You send [money_to_send] credit(s) to [recipient.account_holder]. Now you have [current_user.account_balance] credit(s)")
+ current_user.bank_card_talk("You send [money_to_send] [MONEY_NAME](s) to [recipient.account_holder]. Now you have [current_user.account_balance] [MONEY_NAME](s)")
return NT_PAY_STATUS_SUCCESS
diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm
index f17f3244c0a..bea7ec2e543 100644
--- a/code/modules/paperwork/clipboard.dm
+++ b/code/modules/paperwork/clipboard.dm
@@ -1,3 +1,20 @@
+// Clipboard skins
+/datum/atom_skin/clipboard
+ abstract_type = /datum/atom_skin/clipboard
+ change_inhand_icon_state = TRUE
+
+/datum/atom_skin/clipboard/brown
+ preview_name = "Brown"
+ new_icon_state = "clipboard"
+
+/datum/atom_skin/clipboard/black
+ preview_name = "Black"
+ new_icon_state = "clipboard_black"
+
+/datum/atom_skin/clipboard/white
+ preview_name = "White"
+ new_icon_state = "clipboard_white"
+
/**
* Clipboard
*/
@@ -14,12 +31,7 @@
slot_flags = ITEM_SLOT_BELT
resistance_flags = FLAMMABLE
- unique_reskin = list(
- "Brown" = "clipboard",
- "Black" = "clipboard_black",
- "White" = "clipboard_white",
- )
- unique_reskin_changes_inhand = TRUE
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT, /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT)
/// The stored pen
var/obj/item/pen/pen
@@ -39,6 +51,7 @@
/obj/item/clipboard/Initialize(mapload)
update_appearance()
. = ..()
+ AddComponent(/datum/component/reskinable_item, /datum/atom_skin/clipboard)
/obj/item/clipboard/Destroy()
QDEL_NULL(pen)
diff --git a/code/modules/paperwork/desk_bell.dm b/code/modules/paperwork/desk_bell.dm
index 0f37a458729..860becaece0 100644
--- a/code/modules/paperwork/desk_bell.dm
+++ b/code/modules/paperwork/desk_bell.dm
@@ -11,7 +11,7 @@
max_integrity = 5000 // To make attacking it not instantly break it
throwforce = 2
interaction_flags_mouse_drop = NEED_HANDS
-
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2)
/// The amount of times this bell has been rang, used to check the chance it breaks
var/times_rang = 0
/// Is this bell broken?
diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm
index ecd0ca40029..7f51baea891 100644
--- a/code/modules/paperwork/filingcabinet.dm
+++ b/code/modules/paperwork/filingcabinet.dm
@@ -17,6 +17,7 @@
icon_state = "filingcabinet"
density = TRUE
anchored = TRUE
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2)
/obj/structure/filingcabinet/chestdrawer
name = "chest drawer"
diff --git a/code/modules/paperwork/folders.dm b/code/modules/paperwork/folders.dm
index 944132545a5..382e6ea8212 100644
--- a/code/modules/paperwork/folders.dm
+++ b/code/modules/paperwork/folders.dm
@@ -7,6 +7,7 @@
pressure_resistance = 2
resistance_flags = FLAMMABLE
obj_flags = UNIQUE_RENAME | RENAME_NO_DESC
+ custom_materials = list(/datum/material/cardboard = SHEET_MATERIAL_AMOUNT)
/// The background color for tgui in hex (with a `#`)
var/bg_color = "#7f7f7f"
/// A typecache of the objects that can be inserted into a folder
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index d8493feea2b..838019ef1e9 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -27,6 +27,7 @@
drop_sound = 'sound/items/handling/paper_drop.ogg'
pickup_sound = 'sound/items/handling/paper_pickup.ogg'
grind_results = list(/datum/reagent/cellulose = 3)
+ custom_materials = list(/datum/material/paper = HALF_SHEET_MATERIAL_AMOUNT / 2)
color = COLOR_WHITE
item_flags = SKIP_FANTASY_ON_SPAWN
interaction_flags_click = NEED_DEXTERITY|NEED_HANDS|ALLOW_RESTING
diff --git a/code/modules/paperwork/paper_cutter.dm b/code/modules/paperwork/paper_cutter.dm
index 6e24123bfa1..df55320a435 100644
--- a/code/modules/paperwork/paper_cutter.dm
+++ b/code/modules/paperwork/paper_cutter.dm
@@ -204,6 +204,7 @@
desc = "A plastic card for confidential corporate matters. Can be written on with pen somehow."
icon_state = "corppaperslip"
grind_results = list(/datum/reagent/plastic_polymers = 1.5) //It's a plastic card after all
+ custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 3, /datum/material/paper = HALF_SHEET_MATERIAL_AMOUNT / 2)
max_integrity = 130 //Slightly more sturdy because of being made out of a plastic
drop_sound = 'sound/items/handling/disk_drop.ogg'
pickup_sound = 'sound/items/handling/disk_pickup.ogg'
diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm
index f301ba3a29c..2e02ff0fdce 100644
--- a/code/modules/paperwork/paperbin.dm
+++ b/code/modules/paperwork/paperbin.dm
@@ -215,6 +215,7 @@
papertype = /obj/item/paper/natural
resistance_flags = FLAMMABLE
bin_overlay_string = "paper_bundle_overlay"
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT)
///Cable this bundle is held together with.
var/obj/item/stack/cable_coil/binding_cable
diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm
index 9bd4324a583..572939355b8 100644
--- a/code/modules/paperwork/pen.dm
+++ b/code/modules/paperwork/pen.dm
@@ -165,7 +165,7 @@
icon_state = "pen-charcoal"
colour = "#696969"
font = CHARCOAL_FONT
- custom_materials = null
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT)
grind_results = list(/datum/reagent/ash = 5, /datum/reagent/cellulose = 10)
requires_gravity = FALSE // this is technically a pencil
can_click = FALSE
@@ -177,6 +177,38 @@
time = 3 SECONDS
category = CAT_TOOLS
+// Skins for captain's fountain pen
+/datum/atom_skin/cap_pen
+ abstract_type = /datum/atom_skin/cap_pen
+
+/datum/atom_skin/cap_pen/apply(atom/apply_to)
+ . = ..()
+ apply_to.desc = "It's an expensive [preview_name] fountain pen. The nib is quite sharp."
+
+/datum/atom_skin/cap_pen/clear_skin(atom/clear_from)
+ . = ..()
+ clear_from.desc = initial(clear_from.desc)
+
+/datum/atom_skin/cap_pen/oak
+ preview_name = "Oak"
+ new_icon_state = "pen-fountain-o"
+
+/datum/atom_skin/cap_pen/gold
+ preview_name = "Gold"
+ new_icon_state = "pen-fountain-g"
+
+/datum/atom_skin/cap_pen/rosewood
+ preview_name = "Rosewood"
+ new_icon_state = "pen-fountain-r"
+
+/datum/atom_skin/cap_pen/black_silver
+ preview_name = "Black and Silver"
+ new_icon_state = "pen-fountain-b"
+
+/datum/atom_skin/cap_pen/command_blue
+ preview_name = "Command Blue"
+ new_icon_state = "pen-fountain-cb"
+
/obj/item/pen/fountain/captain
name = "captain's fountain pen"
desc = "It's an expensive Oak fountain pen. The nib is quite sharp."
@@ -187,22 +219,15 @@
custom_materials = list(/datum/material/gold = SMALL_MATERIAL_AMOUNT*7.5)
sharpness = SHARP_EDGED
resistance_flags = FIRE_PROOF
- unique_reskin = list(
- "Oak" = "pen-fountain-o",
- "Gold" = "pen-fountain-g",
- "Rosewood" = "pen-fountain-r",
- "Black and Silver" = "pen-fountain-b",
- "Command Blue" = "pen-fountain-cb"
- )
embed_type = /datum/embedding/pen/captain
dart_insert_casing_icon_state = "overlay_fountainpen_gold"
dart_insert_projectile_icon_state = "overlay_fountainpen_gold_proj"
var/list/overlay_reskin = list(
- "Oak" = "overlay_fountainpen_gold",
- "Gold" = "overlay_fountainpen_gold",
- "Rosewood" = "overlay_fountainpen_gold",
- "Black and Silver" = "overlay_fountainpen",
- "Command Blue" = "overlay_fountainpen_gold"
+ /datum/atom_skin/cap_pen/black_silver::preview_name = "overlay_fountainpen",
+ /datum/atom_skin/cap_pen/command_blue::preview_name = "overlay_fountainpen_gold",
+ /datum/atom_skin/cap_pen/gold::preview_name = "overlay_fountainpen_gold",
+ /datum/atom_skin/cap_pen/oak::preview_name = "overlay_fountainpen_gold",
+ /datum/atom_skin/cap_pen/rosewood::preview_name = "overlay_fountainpen_gold",
)
/datum/embedding/pen/captain
@@ -210,24 +235,21 @@
/obj/item/pen/fountain/captain/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/butchering, \
- speed = 20 SECONDS, \
- effectiveness = 115, \
+ AddComponent( \
+ /datum/component/butchering, \
+ speed = 20 SECONDS, \
+ effectiveness = 115, \
)
+ AddComponent(/datum/component/reskinable_item, /datum/atom_skin/cap_pen)
//the pen is mightier than the sword
RegisterSignal(src, COMSIG_DART_INSERT_PARENT_RESKINNED, PROC_REF(reskin_dart_insert))
-/obj/item/pen/fountain/captain/reskin_obj(mob/M)
- ..()
- if(current_skin)
- desc = "It's an expensive [current_skin] fountain pen. The nib is quite sharp."
-
-
-/obj/item/pen/fountain/captain/proc/reskin_dart_insert(datum/component/dart_insert/insert_comp)
+/obj/item/pen/fountain/captain/proc/reskin_dart_insert(datum/component/dart_insert/insert_comp, skin)
+ SIGNAL_HANDLER
if(!istype(insert_comp)) //You really shouldn't be sending this signal from anything other than a dart_insert component
return
- insert_comp.casing_overlay_icon_state = overlay_reskin[current_skin]
- insert_comp.projectile_overlay_icon_state = "[overlay_reskin[current_skin]]_proj"
+ insert_comp.casing_overlay_icon_state = overlay_reskin[skin]
+ insert_comp.projectile_overlay_icon_state = "[overlay_reskin[skin]]_proj"
/obj/item/pen/item_ctrl_click(mob/living/carbon/user)
if(loc != user)
diff --git a/code/modules/paperwork/stamps.dm b/code/modules/paperwork/stamps.dm
index 504bb89fe4e..a20b65f7aca 100644
--- a/code/modules/paperwork/stamps.dm
+++ b/code/modules/paperwork/stamps.dm
@@ -1,8 +1,8 @@
/obj/item/stamp
- name = "\improper GRANTED rubber stamp"
+ name = "rubber stamp"
desc = "A rubber stamp for stamping important documents."
icon = 'icons/obj/service/bureaucracy.dmi'
- icon_state = "stamp-ok"
+ abstract_type = /obj/item/stamp
worn_icon_state = "nothing"
inhand_icon_state = "stamp"
throwforce = 0
@@ -34,6 +34,7 @@
dye_color = DYE_LAW
/obj/item/stamp/head
+ abstract_type = /obj/item/stamp/head
/obj/item/stamp/head/Initialize(mapload)
. = ..()
@@ -75,6 +76,11 @@
icon_state = "stamp-qm"
dye_color = DYE_QM
+/obj/item/stamp/granted
+ name = "\improper GRANTED rubber stamp"
+ icon_state = "stamp-ok"
+ dye_color = DYE_GREEN
+
/obj/item/stamp/denied
name = "\improper DENIED rubber stamp"
icon_state = "stamp-deny"
diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm
index 66f745129be..fb85fe054ba 100644
--- a/code/modules/plumbing/ducts.dm
+++ b/code/modules/plumbing/ducts.dm
@@ -157,7 +157,7 @@ All the important duct code:
if(ispath(drop_on_wrench))
var/obj/item/stack/ducts/duct_stack = new drop_on_wrench(drop_location())
duct_stack.duct_color = GLOB.pipe_color_name[duct_color] || DUCT_COLOR_OMNI
- duct_stack.duct_layer = GLOB.plumbing_layer_names["[duct_layer]"] || GLOB.plumbing_layer_names["[DUCT_LAYER_DEFAULT]"]
+ duct_stack.duct_layer = GLOB.plumbing_layer_names["[duct_layer]"] || GLOB.plumbing_layer_names[DUCT_LAYER_DEFAULT]
duct_stack.add_atom_colour(duct_color, FIXED_COLOUR_PRIORITY)
drop_on_wrench = null
if(!QDELING(src))
diff --git a/code/modules/plumbing/plumbers/grinder_chemical.dm b/code/modules/plumbing/plumbers/grinder_chemical.dm
index c631e26def6..7f5e3dc5c2d 100644
--- a/code/modules/plumbing/plumbers/grinder_chemical.dm
+++ b/code/modules/plumbing/plumbers/grinder_chemical.dm
@@ -120,7 +120,7 @@
* Arguments
* * [AM][atom] - the atom to grind or juice
*/
-/obj/machinery/plumbing/grinder_chemical/proc/blend(obj/item/I)
+/obj/machinery/plumbing/grinder_chemical/proc/blend(obj/item/item_to_blend)
PRIVATE_PROC(TRUE)
if(!is_operational || !anchored)
@@ -128,14 +128,14 @@
if(reagents.holder_full())
return
- if((I.item_flags & ABSTRACT) || (I.flags_1 & HOLOGRAM_1))
+ if((item_to_blend.item_flags & ABSTRACT) || (item_to_blend.flags_1 & HOLOGRAM_1))
return
- if(!I.blend_requirements(src))
+ if(!item_to_blend.blend_requirements(src))
return
if(!grinding)
- I.juice(reagents, usr, src)
- else if(length(I.grind_results) || I.reagents?.total_volume)
- I.grind(reagents, usr, src)
+ item_to_blend.juice(reagents, usr, src)
+ else if(LAZYLEN(item_to_blend.grind_results) || item_to_blend.reagents?.total_volume)
+ item_to_blend.grind(reagents, usr, src)
use_energy(active_power_usage)
diff --git a/code/modules/power/apc/apc_malf.dm b/code/modules/power/apc/apc_malf.dm
index c4cafb9d5e7..eb0526e32ca 100644
--- a/code/modules/power/apc/apc_malf.dm
+++ b/code/modules/power/apc/apc_malf.dm
@@ -45,8 +45,7 @@
malf.ShutOffDoomsdayDevice()
occupier = malf
if (isturf(malf.loc)) // create a deactivated AI core if the AI isn't coming from an emergency mech shunt
- malf.linked_core = new /obj/structure/ai_core/deactivated(malf.loc)
- malf.linked_core.remote_ai = malf // note that we do not set the deactivated core's core_mmi.brainmob
+ malf.create_core_link(new /obj/structure/ai_core(malf.loc, CORE_STATE_FINISHED, malf.make_mmi()))
malf.forceMove(src) // move INTO the APC, not to its tile
if(!findtext(occupier.name, "APC Copy"))
occupier.name = "[malf.name] APC Copy"
@@ -74,9 +73,7 @@
return
if(occupier.linked_core)
occupier.shunted = FALSE
- occupier.forceMove(occupier.linked_core.loc)
- qdel(occupier.linked_core)
- occupier.cancel_camera()
+ occupier.resolve_core_link()
occupier = null
else
stack_trace("An AI: [occupier] has vacated an APC with no linked core and without being gibbed.")
diff --git a/code/modules/power/apc/apc_tool_act.dm b/code/modules/power/apc/apc_tool_act.dm
index 71bd3f226d3..f74860662bd 100644
--- a/code/modules/power/apc/apc_tool_act.dm
+++ b/code/modules/power/apc/apc_tool_act.dm
@@ -407,7 +407,7 @@
return FALSE
/obj/machinery/power/apc/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, list/rcd_data)
- if(!(the_rcd.construction_upgrades & RCD_UPGRADE_SIMPLE_CIRCUITS) || rcd_data["[RCD_DESIGN_MODE]"] != RCD_WALLFRAME)
+ if(!(the_rcd.construction_upgrades & RCD_UPGRADE_SIMPLE_CIRCUITS) || rcd_data[RCD_DESIGN_MODE] != RCD_WALLFRAME)
return FALSE
if(!has_electronics)
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index 9067aa49b78..55cfc9eb79d 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -101,7 +101,7 @@
/obj/item/stock_parts/power_store/cell/laser_pistol
name = "laser pistol power cell"
- chargerate = STANDARD_CELL_RATE * 3
+ chargerate = STANDARD_CELL_RATE * 0.15
/obj/item/stock_parts/power_store/cell/ninja
name = "black power cell"
@@ -260,6 +260,7 @@
custom_materials = null
grind_results = null
emp_damage_modifier = 0
+ abstract_type = /obj/item/stock_parts/power_store/cell/ethereal
/obj/item/stock_parts/power_store/cell/ethereal/examine(mob/user)
. = ..()
diff --git a/code/modules/power/floodlight.dm b/code/modules/power/floodlight.dm
index 762272ebd64..1b5a8692743 100644
--- a/code/modules/power/floodlight.dm
+++ b/code/modules/power/floodlight.dm
@@ -11,7 +11,7 @@
icon = 'icons/obj/lighting.dmi'
icon_state = "floodlight_c1"
density = TRUE
-
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5)
var/state = FLOODLIGHT_NEEDS_WIRES
/obj/structure/floodlight_frame/Initialize(mapload)
diff --git a/code/modules/power/supermatter/supermatter_variants.dm b/code/modules/power/supermatter/supermatter_variants.dm
index 270994d0a10..146c5b34545 100644
--- a/code/modules/power/supermatter/supermatter_variants.dm
+++ b/code/modules/power/supermatter/supermatter_variants.dm
@@ -61,6 +61,7 @@
icon_state = "sm_small"
moveable = TRUE
anchored = FALSE
+ custom_materials = list(/datum/material/adamantine = SHEET_MATERIAL_AMOUNT * 20, /datum/material/iron = SHEET_MATERIAL_AMOUNT)
/obj/machinery/power/supermatter_crystal/small/Initialize(mapload)
. = ..()
@@ -76,3 +77,4 @@
throw_speed = 1
throw_range = 1
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ custom_materials = list(/datum/material/adamantine = SHEET_MATERIAL_AMOUNT * 20)
diff --git a/code/modules/projectiles/ammunition/_ammunition.dm b/code/modules/projectiles/ammunition/_ammunition.dm
index e0cc6e08920..fa3aa1e1300 100644
--- a/code/modules/projectiles/ammunition/_ammunition.dm
+++ b/code/modules/projectiles/ammunition/_ammunition.dm
@@ -16,6 +16,8 @@
var/caliber = null
///The bullet type to create when New() is called
var/projectile_type = null
+ ///Muzzle flash color based on ammo casing.
+ var/muzzle_flash_color = LIGHT_COLOR_ORANGE
///the loaded projectile in this ammo casing
var/obj/projectile/loaded_projectile = null
///Pellets for spreadshot
diff --git a/code/modules/projectiles/ammunition/ballistic/junk.dm b/code/modules/projectiles/ammunition/ballistic/junk.dm
index 99a9b637923..2ec346b7d21 100644
--- a/code/modules/projectiles/ammunition/ballistic/junk.dm
+++ b/code/modules/projectiles/ammunition/ballistic/junk.dm
@@ -6,7 +6,7 @@
icon_state = "improvshell"
caliber = CALIBER_JUNK
projectile_type = /obj/projectile/bullet/junk
- custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*2, /datum/material/glass=SMALL_MATERIAL_AMOUNT*1)
+ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 2, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 1)
// Junk Shell Spawner; used to spawn in our random shells upon crafting
diff --git a/code/modules/projectiles/ammunition/ballistic/rifle.dm b/code/modules/projectiles/ammunition/ballistic/rifle.dm
index fdef40f5f0f..ceef56677b0 100644
--- a/code/modules/projectiles/ammunition/ballistic/rifle.dm
+++ b/code/modules/projectiles/ammunition/ballistic/rifle.dm
@@ -64,6 +64,7 @@
base_icon_state = "rod_sharp"
projectile_type = /obj/projectile/bullet/rebar
newtonian_force = 1.5
+ custom_materials = list(/datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT)
/obj/item/ammo_casing/rebar/Initialize(mapload)
. = ..()
@@ -104,6 +105,7 @@
icon_state = "rod_healium"
base_icon_state = "rod_healium"
projectile_type = /obj/projectile/bullet/rebar/healium
+ custom_materials = null
/// How many seconds of healing/sleeping action we have left, once all are spent the bolt dissolves
var/heals_left = 6 SECONDS
@@ -181,3 +183,4 @@
base_icon_state = "paperball"
projectile_type = /obj/projectile/bullet/paperball
newtonian_force = 0.5
+ custom_materials = list(/datum/material/paper = HALF_SHEET_MATERIAL_AMOUNT / 2)
diff --git a/code/modules/projectiles/ammunition/ballistic/shotgun.dm b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
index ea13dd878b8..8df354201f9 100644
--- a/code/modules/projectiles/ammunition/ballistic/shotgun.dm
+++ b/code/modules/projectiles/ammunition/ballistic/shotgun.dm
@@ -66,6 +66,7 @@
desc = "A shotgun shell rigged with CMC technology, which launches a massive slug when fired."
icon_state = "mshell"
projectile_type = /obj/projectile/bullet/cannonball/meteorslug
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 8, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 4)
/obj/item/ammo_casing/shotgun/pulseslug
name = "pulse slug"
@@ -74,6 +75,7 @@
would have difficulty with."
icon_state = "pshell"
projectile_type = /obj/projectile/beam/pulse/shotgun
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2.1, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 1.2)
/obj/item/ammo_casing/shotgun/frag12
name = "FRAG-12 slug"
@@ -161,6 +163,7 @@
pellets = 4
variance = 15
randomspread = TRUE
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 0.7)
/obj/item/ammo_casing/shotgun/scatterlaser
name = "scatter laser shell"
diff --git a/code/modules/projectiles/ammunition/energy/_energy.dm b/code/modules/projectiles/ammunition/energy/_energy.dm
index ad79840a716..1254dfa8cba 100644
--- a/code/modules/projectiles/ammunition/energy/_energy.dm
+++ b/code/modules/projectiles/ammunition/energy/_energy.dm
@@ -10,3 +10,4 @@
firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/red
var/select_color = FALSE //SKYRAT EDIT ADDITION - This is the color that shows up when selecting an ammo type. Disabled by default
newtonian_force = 0.5
+ muzzle_flash_color = LIGHT_COLOR_CYAN
diff --git a/code/modules/projectiles/ammunition/energy/laser.dm b/code/modules/projectiles/ammunition/energy/laser.dm
index 4ec34288ee6..9e601a63517 100644
--- a/code/modules/projectiles/ammunition/energy/laser.dm
+++ b/code/modules/projectiles/ammunition/energy/laser.dm
@@ -2,8 +2,8 @@
projectile_type = /obj/projectile/beam/laser
e_cost = LASER_SHOTS(16, STANDARD_CELL_CHARGE)
select_name = "kill"
-/obj/item/ammo_casing/energy/laser/revolver
- e_cost = LASER_SHOTS(12, STANDARD_CELL_CHARGE)
+ muzzle_flash_color = COLOR_SOFT_RED
+
/obj/item/ammo_casing/energy/laser/hellfire
projectile_type = /obj/projectile/beam/laser/hellfire
e_cost = LASER_SHOTS(12, STANDARD_CELL_CHARGE)
@@ -18,6 +18,7 @@
projectile_type = /obj/projectile/beam/laser
e_cost = LASER_SHOTS(20, STANDARD_CELL_CHARGE)
select_name = "kill"
+ muzzle_flash_color = COLOR_SOFT_RED
/obj/item/ammo_casing/energy/lasergun/pistol
e_cost = LASER_SHOTS(10, STANDARD_CELL_CHARGE)
@@ -113,6 +114,7 @@
select_name = "DESTROY"
fire_sound = 'sound/items/weapons/pulse.ogg'
firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/blue
+ muzzle_flash_color = LIGHT_COLOR_BLUE
/obj/item/ammo_casing/energy/laser/bluetag
projectile_type = /obj/projectile/beam/lasertag/bluetag
diff --git a/code/modules/projectiles/ammunition/energy/special.dm b/code/modules/projectiles/ammunition/energy/special.dm
index 7ed56c0ddd0..7cf649b393e 100644
--- a/code/modules/projectiles/ammunition/energy/special.dm
+++ b/code/modules/projectiles/ammunition/energy/special.dm
@@ -3,6 +3,7 @@
select_name = "ion"
fire_sound = 'sound/items/weapons/ionrifle.ogg'
firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/blue
+ muzzle_flash_color = LIGHT_COLOR_BLUE
/obj/item/ammo_casing/energy/ion/hos
projectile_type = /obj/projectile/ion/weak
diff --git a/code/modules/projectiles/ammunition/energy/stun.dm b/code/modules/projectiles/ammunition/energy/stun.dm
index cbf2f3f3f3a..1c659d081d6 100644
--- a/code/modules/projectiles/ammunition/energy/stun.dm
+++ b/code/modules/projectiles/ammunition/energy/stun.dm
@@ -5,6 +5,7 @@
e_cost = LASER_SHOTS(5, STANDARD_CELL_CHARGE)
harmful = FALSE
firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect
+ muzzle_flash_color = LIGHT_COLOR_DIM_YELLOW
//BUBBER EDIT START
/obj/item/ammo_casing/energy/electrode/sec
@@ -32,6 +33,7 @@
fire_sound = 'sound/items/weapons/taser2.ogg'
harmful = FALSE
firing_effect_type = /obj/effect/temp_visual/dir_setting/firing_effect/blue
+ muzzle_flash_color = LIGHT_COLOR_CYAN
/obj/item/ammo_casing/energy/disabler/smg
projectile_type = /obj/projectile/beam/disabler/weak
diff --git a/code/modules/projectiles/boxes_magazines/_box_magazine.dm b/code/modules/projectiles/boxes_magazines/_box_magazine.dm
index f5c123866b3..bcc551bdd17 100644
--- a/code/modules/projectiles/boxes_magazines/_box_magazine.dm
+++ b/code/modules/projectiles/boxes_magazines/_box_magazine.dm
@@ -45,9 +45,16 @@
/obj/item/ammo_box/Initialize(mapload)
. = ..()
- custom_materials = SSmaterials.FindOrCreateMaterialCombo(custom_materials, 0.1)
if(!start_empty)
top_off(starting=TRUE)
+ else if(custom_materials && !(item_flags & ABSTRACT)) //internal magazines are abstract
+ var/obj/item/ammo_casing/prototype = new ammo_type
+ var/list/new_materials = custom_materials?.Copy()
+ for(var/mat in prototype.custom_materials)
+ new_materials[mat] -= prototype.custom_materials[mat] * max_ammo
+ qdel(prototype)
+ set_custom_materials(new_materials)
+
update_icon_state()
/obj/item/ammo_box/Destroy(force)
@@ -64,6 +71,11 @@
/obj/item/ammo_box/proc/remove_from_stored_ammo(atom/movable/gone)
stored_ammo -= gone
+ if(gone.custom_materials && custom_materials && !(item_flags & ABSTRACT))
+ var/list/new_materials = custom_materials?.Copy()
+ for(var/mat in gone.custom_materials)
+ new_materials[mat] -= gone.custom_materials[mat]
+ set_custom_materials(new_materials)
update_appearance()
/obj/item/ammo_box/add_weapon_description()
@@ -108,8 +120,21 @@
stack_trace("Tried loading unsupported ammocasing type [load_type] into ammo box [type].")
return
+ var/list/new_materials = null
+ if(!(item_flags & ABSTRACT))
+ new_materials = custom_materials?.Copy() || list()
for(var/i in max(1, stored_ammo.len + 1) to max_ammo)
- stored_ammo += starting ? round_check : new round_check(src)
+ var/obj/item/ammo_casing/casing = round_check
+ if(!starting)
+ casing = new round_check(src)
+ if(new_materials)
+ for(var/mat in casing.custom_materials)
+ new_materials[mat] += casing.custom_materials[mat]
+ stored_ammo += casing
+
+ if(!starting && length(new_materials))
+ set_custom_materials(new_materials)
+
update_appearance()
///gets a round from the magazine
@@ -141,6 +166,11 @@
if (stored_ammo.len < max_ammo)
stored_ammo += new_round
new_round.forceMove(src)
+ if(new_round.custom_materials && !(item_flags & ABSTRACT))
+ var/list/new_materials = custom_materials?.Copy() || list()
+ for(var/mat in new_round.custom_materials)
+ new_materials[mat] += new_round.custom_materials[mat]
+ set_custom_materials(new_materials)
return TRUE
if(!replace_spent)
diff --git a/code/modules/projectiles/boxes_magazines/external/pistol.dm b/code/modules/projectiles/boxes_magazines/external/pistol.dm
index d67adb8c759..afde03e6924 100644
--- a/code/modules/projectiles/boxes_magazines/external/pistol.dm
+++ b/code/modules/projectiles/boxes_magazines/external/pistol.dm
@@ -123,6 +123,12 @@
max_ammo = 8
multiple_sprites = AMMO_BOX_PER_BULLET
multiple_sprite_use_base = TRUE
+ custom_materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 10,
+ /datum/material/gold = SHEET_MATERIAL_AMOUNT * 10,
+ /datum/material/silver = SHEET_MATERIAL_AMOUNT * 10,
+ /datum/material/plasma = SHEET_MATERIAL_AMOUNT * 10,
+ )
// M1911 (.45) //
diff --git a/code/modules/projectiles/boxes_magazines/external/rifle.dm b/code/modules/projectiles/boxes_magazines/external/rifle.dm
index b716b8d1811..41f9ec15492 100644
--- a/code/modules/projectiles/boxes_magazines/external/rifle.dm
+++ b/code/modules/projectiles/boxes_magazines/external/rifle.dm
@@ -24,7 +24,7 @@
ammo_type = /obj/item/ammo_casing/c38
caliber = CALIBER_38
custom_materials = list(
- /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 3,
+ /datum/material/iron = HALF_SHEET_MATERIAL_AMOUNT * 4,
/datum/material/plastic = HALF_SHEET_MATERIAL_AMOUNT * 1,
)
max_ammo = 15
diff --git a/code/modules/projectiles/boxes_magazines/internal/_internal.dm b/code/modules/projectiles/boxes_magazines/internal/_internal.dm
index 43b34979e44..238ac94406d 100644
--- a/code/modules/projectiles/boxes_magazines/internal/_internal.dm
+++ b/code/modules/projectiles/boxes_magazines/internal/_internal.dm
@@ -4,6 +4,7 @@
spawn_blacklisted = TRUE
obj_flags = CONDUCTS_ELECTRICITY
item_flags = ABSTRACT
+ custom_materials = null //we are never gonna be recycled anyway
//internals magazines are accessible, so replace spent ammo if full when trying to put a live one in
/obj/item/ammo_box/magazine/internal/give_round(obj/item/ammo_casing/R)
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 18e5c505316..188d1407d1c 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -23,7 +23,18 @@
attack_verb_simple = list("strike", "hit", "bash")
action_slots = ALL
+ // Muzzle Flash
+ light_on = FALSE
+ light_system = OVERLAY_LIGHT_DIRECTIONAL
+ light_range = 3
+ light_color = LIGHT_COLOR_ORANGE
+ light_power = 0.5
+ var/can_muzzle_flash = TRUE
+ /// Muzzle Flash Duration
+ var/light_time = 0.1 SECONDS
+
var/gun_flags = NONE
+
var/fire_sound = 'sound/items/weapons/gun/pistol/shot.ogg'
var/vary_fire_sound = TRUE
var/fire_sound_volume = 50
@@ -36,7 +47,7 @@
var/suppressed_volume = 60
/// Whether a gun can be unsuppressed. for ballistics, also determines if it generates a suppressor overlay
var/can_unsuppress = TRUE
- var/recoil = 0 //boom boom shake the room
+
var/clumsy_check = TRUE
var/obj/item/ammo_casing/chambered = null
trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers
@@ -51,7 +62,6 @@
/// firing cooldown, true if this gun shouldn't be allowed to manually fire
var/fire_cd = 0
var/weapon_weight = WEAPON_LIGHT
- var/dual_wield_spread = 24 //additional spread when dual wielding
///Can we hold up our target with this? Default to yes
var/can_hold_up = FALSE // SKYRAT EDIT - DISABLED ORIGINAL: TRUE
/// If TRUE, and we aim at ourselves, it will initiate a do after to fire at ourselves.
@@ -74,6 +84,18 @@
var/spread = 0 //Spread induced by the gun itself.
var/randomspread = 1 //Set to 0 for shotguns. This is used for weapons that don't fire all their bullets at once.
+ var/dual_wield_spread = 24 //additional spread when dual wielding
+
+ ///Screen shake when the weapon is fired
+ var/recoil = 0
+ ///a multiplier of the duration the recoil takes to go back to normal view, this is (recoil*recoil_backtime_multiplier)+1
+ var/recoil_backtime_multiplier = 1.5
+ ///this is how much deviation the gun recoil can have, recoil pushes the screen towards the reverse angle you shot + some deviation which this is the max.
+ var/recoil_deviation = 20
+ /// Used as the min value when calculating recoil
+ /// Affected by a player's min_recoil_multiplier preference, so keep in mind it can ultimately be 0 regardless
+ /// Often utilized as a "purely visual" form of recoil (as it can be disabled)
+ var/min_recoil = 0
lefthand_file = 'icons/mob/inhands/weapons/guns_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/guns_righthand.dmi'
@@ -208,10 +230,22 @@
else
playsound(src, fire_sound, fire_sound_volume, vary_fire_sound)
+/obj/item/gun/proc/muzzle_flash_on()
+ if (can_muzzle_flash)
+ set_light_on(TRUE)
+ addtimer(CALLBACK(src, PROC_REF(muzzle_flash_off)), light_time, TIMER_UNIQUE | TIMER_OVERRIDE)
+ else
+ muzzle_flash_off()
+
+/obj/item/gun/proc/muzzle_flash_off()
+ set_light_on(FALSE)
+
/obj/item/gun/proc/shoot_live_shot(mob/living/user, pointblank = FALSE, atom/pbtarget = null, message = TRUE)
- if(recoil && !tk_firing(user))
- shake_camera(user, recoil + 1, recoil)
+ if(!tk_firing(user))
+ var/actual_angle = get_angle((user || get_turf(src)), pbtarget)
+ simulate_recoil(user, recoil, actual_angle)
fire_sounds()
+ muzzle_flash_on()
if(suppressed || !message)
return FALSE
if(tk_firing(user))
@@ -307,11 +341,10 @@
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
/obj/item/gun/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- if(try_fire_gun(interacting_with, user, list2params(modifiers)))
- return ITEM_INTERACT_SUCCESS
- if(chambered_attack_block == TRUE && can_shoot() && isliving(interacting_with))
- return ITEM_INTERACT_BLOCKING // block melee (etc), usually if waiting on fire delay
- return NONE
+ var/fired = try_fire_gun(interacting_with, user, list2params(modifiers))
+ if(!fired && chambered_attack_block == TRUE && can_shoot() && isliving(interacting_with))
+ return ITEM_INTERACT_BLOCKING
+ return fired
/obj/item/gun/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
if(user.combat_mode && isliving(interacting_with))
@@ -333,9 +366,7 @@
return ITEM_INTERACT_SUCCESS
/obj/item/gun/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- if(try_fire_gun(interacting_with, user, list2params(modifiers)))
- return ITEM_INTERACT_SUCCESS
- return ITEM_INTERACT_BLOCKING
+ return try_fire_gun(interacting_with, user, list2params(modifiers))
/obj/item/gun/ranged_interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
if(IN_GIVEN_RANGE(user, interacting_with, GUNPOINT_SHOOTER_STRAY_RANGE))
@@ -347,43 +378,42 @@
/obj/item/gun/proc/fire_gun(atom/target, mob/living/user, flag, params)
if(QDELETED(target))
- return
+ return NONE
if(firing_burst)
- return
+ return NONE
if(SEND_SIGNAL(user, COMSIG_MOB_TRYING_TO_FIRE_GUN, src, target, flag, params) & COMPONENT_CANCEL_GUN_FIRE)
- return
+ return NONE
if(SEND_SIGNAL(src, COMSIG_GUN_TRY_FIRE, user, target, flag, params) & COMPONENT_CANCEL_GUN_FIRE)
- return
+ return NONE
if(flag) //It's adjacent, is the user, or is on the user's person
if(target in user.contents) //can't shoot stuff inside us.
- return
+ return NONE
if(!ismob(target)) //melee attack
- return
+ return NONE
if(target == user && (user.zone_selected != BODY_ZONE_PRECISE_MOUTH && doafter_self_shoot)) //so we can't shoot ourselves (unless mouth selected)
- return
+ return NONE
if(istype(user))//Check if the user can use the gun, if the user isn't alive(turrets) assume it can.
var/mob/living/L = user
if(!can_trigger_gun(L))
- return
+ return NONE
if(flag && doafter_self_shoot && user.zone_selected == BODY_ZONE_PRECISE_MOUTH)
- handle_suicide(user, target, params)
- return
+ return handle_suicide(user, target, params)
if(!can_shoot()) //Just because you can pull the trigger doesn't mean it can shoot.
shoot_with_empty_chamber(user)
- return
+ return ITEM_INTERACT_BLOCKING
if(check_botched(user, target))
- return
+ return NONE
var/obj/item/bodypart/other_hand = user.has_hand_for_held_index(user.get_inactive_hand_index()) //returns non-disabled inactive hands
if(weapon_weight == WEAPON_HEAVY && (user.get_inactive_held_item() || !other_hand))
balloon_alert(user, "use both hands!")
- return
+ return ITEM_INTERACT_BLOCKING
//DUAL (or more!) WIELDING
var/bonus_spread = 0
var/loop_counter = 0
@@ -473,6 +503,39 @@
update_appearance()
return TRUE
+/**
+ * Calculates the final recoil value applied when firing a gun.
+ *
+ * Arguments:
+ * * user - The living mob attempting to fire the gun. Used for preference lookups.
+ * * recoil_amount - The raw recoil value to be processed before clamping.
+ *
+ * Returns:
+ * The clamped recoil value after applying all modifiers.
+ */
+/obj/item/gun/proc/calculate_recoil(mob/living/user, recoil_amount = 0)
+ var/used_min_recoil = min_recoil
+ if(user.client)
+ used_min_recoil *= (user.client.prefs.read_preference(/datum/preference/numeric/min_recoil_multiplier) / 100)
+ return clamp(recoil_amount, used_min_recoil, INFINITY)
+
+/**
+ * Simulates firearm recoil and applies camera feedback when firing.
+ *
+ * Arguments:
+ * * user - The mob firing the gun. Used for recoil calculation and camera shake.
+ * * recoil_amount - The base recoil value before modifiers.
+ * * firing_angle - The firing direction used to determine camera kick direction.
+ */
+/obj/item/gun/proc/simulate_recoil(mob/living/user, recoil_amount = 0, firing_angle)
+ var/total_recoil = calculate_recoil(user, recoil_amount)
+
+ var/actual_angle = firing_angle + rand(-recoil_deviation, recoil_deviation) + 180
+ if(actual_angle > 360)
+ actual_angle -= 360
+ if(total_recoil > 0)
+ recoil_camera(user, total_recoil + 1, (total_recoil * recoil_backtime_multiplier)+1, total_recoil, actual_angle)
+
///returns true if the gun successfully fires
/obj/item/gun/proc/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
var/base_bonus_spread = 0
@@ -487,7 +550,7 @@
add_fingerprint(user)
if(fire_cd)
- return
+ return NONE
//Vary by at least this much
var/randomized_bonus_spread = rand(base_bonus_spread, bonus_spread)
@@ -512,12 +575,12 @@
if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal.
if(chambered.harmful) // Is the bullet chambered harmful?
to_chat(user, span_warning("[src] is lethally chambered! You don't want to risk harming anyone..."))
- return
+ return NONE
var/sprd = round((rand(0, 1) - 0.5) * DUALWIELD_PENALTY_EXTRA_MULTIPLIER * total_random_spread)
before_firing(target,user)
if(!chambered.fire_casing(target, user, params, 0, suppressed, zone_override, sprd, src))
shoot_with_empty_chamber(user)
- return
+ return NONE
else
if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot
shoot_live_shot(user, TRUE, target, message)
@@ -525,7 +588,7 @@
shoot_live_shot(user, FALSE, target, message)
else
shoot_with_empty_chamber(user)
- return
+ return NONE
// If gun gets destroyed as a result of firing
if (!QDELETED(src))
process_chamber()
@@ -598,10 +661,10 @@
/obj/item/gun/proc/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params, bypass_timer)
if(!ishuman(user) || !ishuman(target))
- return
+ return NONE
if(fire_cd)
- return
+ return NONE
if(user == target)
target.visible_message(span_warning("[user] sticks [src] in [user.p_their()] mouth, ready to pull the trigger..."), \
@@ -619,22 +682,27 @@
else if(target?.Adjacent(user))
target.visible_message(span_notice("[user] has decided to spare [target]"), span_notice("[user] has decided to spare your life!"))
fire_cd = FALSE
- return
+ return ITEM_INTERACT_BLOCKING
fire_cd = FALSE
target.visible_message(span_warning("[user] pulls the trigger!"), span_userdanger("[(user == target) ? "You pull" : "[user] pulls"] the trigger!"))
- if(chambered?.loaded_projectile)
- chambered.loaded_projectile.damage *= 5
- if(chambered.loaded_projectile.wound_bonus != CANT_WOUND)
- chambered.loaded_projectile.wound_bonus += 5 // much more dramatic on multiple pellet'd projectiles really
+ if(!chambered?.loaded_projectile)
+ shoot_with_empty_chamber()
+ return ITEM_INTERACT_BLOCKING
+
+ chambered.loaded_projectile.damage *= 5
+ if(chambered.loaded_projectile.wound_bonus != CANT_WOUND)
+ chambered.loaded_projectile.wound_bonus += 5 // much more dramatic on multiple pellet'd projectiles really
var/fired = process_fire(target, user, TRUE, params, BODY_ZONE_HEAD)
if(!fired && chambered?.loaded_projectile)
chambered.loaded_projectile.damage /= 5
if(chambered.loaded_projectile.wound_bonus != CANT_WOUND)
chambered.loaded_projectile.wound_bonus -= 5
+ return NONE
+ return ITEM_INTERACT_SUCCESS
/obj/item/gun/proc/unlock() //used in summon guns and as a convience for admins
if(pin)
diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm
index 042d56537bd..5511f6a6ca5 100644
--- a/code/modules/projectiles/guns/ballistic.dm
+++ b/code/modules/projectiles/guns/ballistic.dm
@@ -9,7 +9,8 @@
pickup_sound = 'sound/items/handling/gun/gun_pick_up.ogg'
drop_sound = 'sound/items/handling/gun/gun_drop.ogg'
sound_vary = TRUE
- unique_reskin_changes_base_icon_state = TRUE
+
+ min_recoil = 0.1
///sound when inserting magazine
var/load_sound = 'sound/items/weapons/gun/general/magazine_insert_full.ogg'
@@ -611,6 +612,7 @@
suppressor = new_suppressor
suppressed = suppressor.suppression
update_weight_class(w_class + suppressor.w_class) //so pistols do not fit in pockets when suppressed
+ can_muzzle_flash = FALSE
update_appearance()
/obj/item/gun/ballistic/clear_suppressor()
@@ -620,6 +622,7 @@
if(suppressor)
update_weight_class(w_class - suppressor.w_class)
suppressor = null
+ can_muzzle_flash = initial(can_muzzle_flash)
update_appearance()
/obj/item/gun/ballistic/click_alt(mob/user)
diff --git a/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm b/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm
index a0986614352..c50694aaf61 100644
--- a/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm
+++ b/code/modules/projectiles/guns/ballistic/bows/bow_arrows.dm
@@ -11,6 +11,7 @@
throwforce = 1
firing_effect_type = null
caliber = CALIBER_ARROW
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT, /datum/material/iron = SHEET_MATERIAL_AMOUNT)
///Whether the bullet type spawns another casing of the same type or not.
var/reusable = TRUE
@@ -131,6 +132,7 @@
base_icon_state = "plastic_arrow"
projectile_type = /obj/projectile/bullet/arrow/plastic
reusable = FALSE //cheap shit
+ custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT)
/// plastic arrow projectile
/obj/projectile/bullet/arrow/plastic
@@ -179,6 +181,7 @@
inhand_icon_state = "ashen_arrow"
base_icon_state = "ashen_arrow"
projectile_type = /obj/projectile/bullet/arrow/ashen
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT)
/// ashen arrow projectile
/obj/projectile/bullet/arrow/ashen
diff --git a/code/modules/projectiles/guns/ballistic/bows/bow_types.dm b/code/modules/projectiles/guns/ballistic/bows/bow_types.dm
index 54d0bd6d947..ee8725f71df 100644
--- a/code/modules/projectiles/guns/ballistic/bows/bow_types.dm
+++ b/code/modules/projectiles/guns/ballistic/bows/bow_types.dm
@@ -10,6 +10,7 @@
desc = "A simple homemade shortbow. Great for LARPing. Or poking out someones eye."
obj_flags = UNIQUE_RENAME
projectile_damage_multiplier = 0.5
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 4, /datum/material/iron = SHEET_MATERIAL_AMOUNT)
///chaplain's divine archer bow
/obj/item/gun/ballistic/bow/divine
@@ -49,3 +50,4 @@
slot_flags = ITEM_SLOT_BACK
obj_flags = UNIQUE_RENAME
projectile_damage_multiplier = 0.5
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 6)
diff --git a/code/modules/projectiles/guns/ballistic/launchers.dm b/code/modules/projectiles/guns/ballistic/launchers.dm
index cf56c2874ad..738ffc41820 100644
--- a/code/modules/projectiles/guns/ballistic/launchers.dm
+++ b/code/modules/projectiles/guns/ballistic/launchers.dm
@@ -11,6 +11,7 @@
w_class = WEIGHT_CLASS_NORMAL
pin = /obj/item/firing_pin/implant/pindicate
bolt_type = BOLT_TYPE_NO_BOLT
+ can_muzzle_flash = FALSE
/obj/item/gun/ballistic/revolver/grenadelauncher/unrestricted
pin = /obj/item/firing_pin
@@ -90,6 +91,7 @@
cartridge_wording = "rocket"
empty_indicator = TRUE
tac_reloads = FALSE
+ can_muzzle_flash = FALSE
/// Do we shit flames behind us when we fire?
var/backblast = TRUE
diff --git a/code/modules/projectiles/guns/ballistic/pistol.dm b/code/modules/projectiles/guns/ballistic/pistol.dm
index 457130eddff..77dfe713df8 100644
--- a/code/modules/projectiles/guns/ballistic/pistol.dm
+++ b/code/modules/projectiles/guns/ballistic/pistol.dm
@@ -26,6 +26,7 @@
bolt_wording = "slide"
suppressor_x_offset = 10
suppressor_y_offset = -1
+ recoil_backtime_multiplier = 1
/obj/item/gun/ballistic/automatic/pistol/no_mag
spawnwithmagazine = FALSE
@@ -100,6 +101,8 @@
return ..()
/obj/item/gun/ballistic/automatic/pistol/clandestine/fisher/afterattack(atom/target, mob/user, list/modifiers, list/attack_modifiers)
+ if(QDELETED(target))
+ return
var/obj/projectile/energy/fisher/melee/simulated_hit = new
simulated_hit.firer = user
simulated_hit.on_hit(target)
@@ -191,6 +194,7 @@
accepted_magazine_type = /obj/item/ammo_box/magazine/r10mm
actions_types = list(/datum/action/item_action/toggle_firemode)
obj_flags = UNIQUE_RENAME // if you did the sidequest, you get the customization
+ custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT * 30, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 25, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 11.5)
/obj/item/gun/ballistic/automatic/pistol/aps
name = "\improper Stechkin APS machine pistol"
diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm
index 06a8b50c566..6f4b9be9330 100644
--- a/code/modules/projectiles/guns/ballistic/revolver.dm
+++ b/code/modules/projectiles/guns/ballistic/revolver.dm
@@ -97,8 +97,7 @@
. = ..()
var/live_ammo = get_ammo(FALSE, FALSE)
. += "[live_ammo ? live_ammo : "None"] of those are live rounds."
- if (current_skin)
- . += span_notice("It can be spun with [EXAMINE_HINT("alt-click")].")
+ . += span_notice("It can be spun with [EXAMINE_HINT("alt-click")].")
/obj/item/gun/ballistic/revolver/ignition_effect(atom/A, mob/user)
if(last_fire && last_fire + 15 SECONDS > world.time)
@@ -112,6 +111,47 @@
base_icon_state = "c38"
fire_sound = 'sound/items/weapons/gun/revolver/shot.ogg'
+// 38 special skins
+/datum/atom_skin/det_revolver
+ abstract_type = /datum/atom_skin/det_revolver
+ change_base_icon_state = TRUE
+
+/datum/atom_skin/det_revolver/default
+ preview_name = "Default"
+ new_icon_state = "c38"
+
+/datum/atom_skin/det_revolver/fitz_special
+ preview_name = "Fitz Special"
+ new_icon_state = "c38_fitz"
+
+/datum/atom_skin/det_revolver/police_positive_special
+ preview_name = "Police Positive Special"
+ new_icon_state = "c38_police"
+
+/datum/atom_skin/det_revolver/blued_steel
+ preview_name = "Blued Steel"
+ new_icon_state = "c38_blued"
+
+/datum/atom_skin/det_revolver/stainless_steel
+ preview_name = "Stainless Steel"
+ new_icon_state = "c38_stainless"
+
+/datum/atom_skin/det_revolver/gold_trim
+ preview_name = "Gold Trim"
+ new_icon_state = "c38_trim"
+
+/datum/atom_skin/det_revolver/golden
+ preview_name = "Golden"
+ new_icon_state = "c38_gold"
+
+/datum/atom_skin/det_revolver/peacemaker
+ preview_name = "The Peacemaker"
+ new_icon_state = "c38_peacemaker"
+
+/datum/atom_skin/det_revolver/black_panther
+ preview_name = "Black Panther"
+ new_icon_state = "c38_panther"
+
/obj/item/gun/ballistic/revolver/c38/detective
name = "\improper Colt Detective Special"
desc = "A classic, if not outdated, law enforcement firearm. Uses .38 Special rounds. \nSome spread rumors that if you loosen the barrel with a wrench, you can \"improve\" it."
@@ -126,17 +166,10 @@
misfire_percentage_increment = 25 //about 1 in 4 rounds, which increases rapidly every shot
obj_flags = UNIQUE_RENAME
- unique_reskin = list(
- "Default" = "c38",
- "Fitz Special" = "c38_fitz",
- "Police Positive Special" = "c38_police",
- "Blued Steel" = "c38_blued",
- "Stainless Steel" = "c38_stainless",
- "Gold Trim" = "c38_trim",
- "Golden" = "c38_gold",
- "The Peacemaker" = "c38_peacemaker",
- "Black Panther" = "c38_panther"
- )
+
+/obj/item/gun/ballistic/revolver/c38/detective/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/reskinable_item, /datum/atom_skin/det_revolver)
/obj/item/gun/ballistic/revolver/badass
name = "\improper Badass Revolver"
diff --git a/code/modules/projectiles/guns/ballistic/rifle.dm b/code/modules/projectiles/guns/ballistic/rifle.dm
index 2a18fe54d70..db6567e2767 100644
--- a/code/modules/projectiles/guns/ballistic/rifle.dm
+++ b/code/modules/projectiles/guns/ballistic/rifle.dm
@@ -217,6 +217,7 @@
fire_sound = 'sound/items/xbow_lock.ogg'
can_be_sawn_off = FALSE
tac_reloads = FALSE
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 3.1, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 1.2)
var/draw_time = 3 SECONDS
SET_BASE_PIXEL(0, 0)
@@ -298,6 +299,7 @@
inhand_icon_state = "pipegun"
worn_icon_state = "pipegun"
fire_sound = 'sound/items/weapons/gun/sniper/shot.ogg'
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 8, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 8, /datum/material/cardboard = SHEET_MATERIAL_AMOUNT)
accepted_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/pipegun
projectile_damage_multiplier = 1.35
@@ -336,6 +338,7 @@
icon_state = "pipepistol"
inhand_icon_state = "pipepistol"
worn_icon_state = "gun"
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 4, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 7, /datum/material/cardboard = SHEET_MATERIAL_AMOUNT)
accepted_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/pipegun/pistol
projectile_damage_multiplier = 0.50
spread = 15 //kinda inaccurate
@@ -360,6 +363,13 @@
worn_icon_state = "regal_pipegun"
accepted_magazine_type = /obj/item/ammo_box/magazine/internal/boltaction/pipegun/prime
projectile_damage_multiplier = 2
+ custom_materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 9.15,
+ /datum/material/wood = SHEET_MATERIAL_AMOUNT *8,
+ /datum/material/gold = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/glass = SHEET_MATERIAL_AMOUNT * 1.15,
+ /datum/material/cardboard = SHEET_MATERIAL_AMOUNT,
+ )
/obj/item/gun/ballistic/rifle/boltaction/pipegun/pistol/prime
name = "regal pipe pistol"
@@ -485,6 +495,7 @@
semi_auto = TRUE
slot_flags = ITEM_SLOT_BACK
projectile_damage_multiplier = 0.5
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 8, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 5.5, /datum/material/cardboard = SHEET_MATERIAL_AMOUNT)
SET_BASE_PIXEL(-8, 0)
diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm
index 437b406b296..2d85dbee063 100644
--- a/code/modules/projectiles/guns/ballistic/shotgun.dm
+++ b/code/modules/projectiles/guns/ballistic/shotgun.dm
@@ -285,6 +285,35 @@
/obj/item/gun/ballistic/shotgun/bulldog/unrestricted
pin = /obj/item/firing_pin
+// Double barrel shotgun skins
+/datum/atom_skin/bar_shotgun
+ abstract_type = /datum/atom_skin/bar_shotgun
+ change_base_icon_state = TRUE
+
+/datum/atom_skin/bar_shotgun/default
+ preview_name = "Standard Finish"
+ new_icon_state = "dshotgun"
+
+/datum/atom_skin/bar_shotgun/dark_red
+ preview_name = "Dark Red Finish"
+ new_icon_state = "dshotgun_d"
+
+/datum/atom_skin/bar_shotgun/ash
+ preview_name = "Ash"
+ new_icon_state = "dshotgun_f"
+
+/datum/atom_skin/bar_shotgun/faded_grey
+ preview_name = "Faded Grey"
+ new_icon_state = "dshotgun_g"
+
+/datum/atom_skin/bar_shotgun/maple
+ preview_name = "Maple"
+ new_icon_state = "dshotgun_l"
+
+/datum/atom_skin/bar_shotgun/rosewood
+ preview_name = "Rosewood"
+ new_icon_state = "dshotgun_p"
+
/////////////////////////////
// DOUBLE BARRELED SHOTGUN //
/////////////////////////////
@@ -304,18 +333,15 @@
sawn_desc = "Omar's coming!"
obj_flags = UNIQUE_RENAME
rack_sound_volume = 0
- unique_reskin = list("Default" = "dshotgun",
- "Dark Red Finish" = "dshotgun_d",
- "Ash" = "dshotgun_f",
- "Faded Grey" = "dshotgun_g",
- "Maple" = "dshotgun_l",
- "Rosewood" = "dshotgun_p"
- )
semi_auto = TRUE
bolt_type = BOLT_TYPE_NO_BOLT
can_be_sawn_off = TRUE
pb_knockback = 3 // it's a super shotgun!
+/obj/item/gun/ballistic/shotgun/doublebarrel/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/reskinable_item, /datum/atom_skin/bar_shotgun)
+
/obj/item/gun/ballistic/shotgun/doublebarrel/sawoff(mob/user)
. = ..()
if(.)
diff --git a/code/modules/projectiles/guns/ballistic/toy.dm b/code/modules/projectiles/guns/ballistic/toy.dm
index dae77b09368..6d43ecbcea2 100644
--- a/code/modules/projectiles/guns/ballistic/toy.dm
+++ b/code/modules/projectiles/guns/ballistic/toy.dm
@@ -14,6 +14,7 @@
item_flags = NONE
gun_flags = TOY_FIREARM_OVERLAY | NOT_A_REAL_GUN
casing_ejector = FALSE
+ can_muzzle_flash = FALSE
/obj/item/gun/ballistic/automatic/toy/riot
spawn_magazine_type = /obj/item/ammo_box/magazine/toy/smg/riot
@@ -49,6 +50,7 @@
weapon_weight = WEAPON_LIGHT
pb_knockback = 0
gun_flags = TOY_FIREARM_OVERLAY | NOT_A_REAL_GUN
+ can_muzzle_flash = FALSE
/obj/item/gun/ballistic/shotgun/toy/handle_chamber(empty_chamber = TRUE, from_firing = TRUE, chamber_next_round = TRUE)
. = ..()
@@ -75,6 +77,7 @@
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
gun_flags = NONE
+ can_muzzle_flash = FALSE
/obj/item/gun/ballistic/shotgun/toy/crossbow/riot
spawn_magazine_type = /obj/item/ammo_box/magazine/internal/shot/toy/crossbow/riot
@@ -89,6 +92,7 @@
casing_ejector = FALSE
clumsy_check = FALSE
gun_flags = TOY_FIREARM_OVERLAY | NOT_A_REAL_GUN
+ can_muzzle_flash = FALSE
/obj/item/gun/ballistic/automatic/c20r/toy/unrestricted //Use this for actual toys
pin = /obj/item/firing_pin
@@ -108,6 +112,7 @@
casing_ejector = FALSE
clumsy_check = FALSE
gun_flags = TOY_FIREARM_OVERLAY | NOT_A_REAL_GUN
+ can_muzzle_flash = FALSE
/obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted //Use this for actual toys
pin = /obj/item/firing_pin
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index 404884282bc..66406652857 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -238,6 +238,8 @@
var/obj/item/ammo_casing/energy/shot = ammo_type[select]
fire_sound = shot.fire_sound
fire_delay = shot.delay
+ if (shot.muzzle_flash_color)
+ set_light_color(shot.muzzle_flash_color)
if (shot.select_name && user)
balloon_alert(user, "set to [shot.select_name]")
chambered = null
diff --git a/code/modules/projectiles/guns/energy/beam_rifle.dm b/code/modules/projectiles/guns/energy/beam_rifle.dm
index 9f94cf02ddb..273637612fc 100644
--- a/code/modules/projectiles/guns/energy/beam_rifle.dm
+++ b/code/modules/projectiles/guns/energy/beam_rifle.dm
@@ -19,7 +19,16 @@
w_class = WEIGHT_CLASS_BULKY
ammo_type = list(/obj/item/ammo_casing/energy/event_horizon)
selfcharge = TRUE
+ light_color = COLOR_STRONG_BLUE
self_charge_amount = STANDARD_ENERGY_GUN_SELF_CHARGE_RATE * 10
+ custom_materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/uranium = SHEET_MATERIAL_AMOUNT * 4,
+ /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2.5,
+ /datum/material/diamond = SHEET_MATERIAL_AMOUNT * 2.5,
+ /datum/material/gold = SHEET_MATERIAL_AMOUNT * 2.5,
+ /datum/material/silver = SHEET_MATERIAL_AMOUNT * 2.25,
+ )
/obj/item/gun/energy/event_horizon/Initialize(mapload)
. = ..()
diff --git a/code/modules/projectiles/guns/energy/crank_guns.dm b/code/modules/projectiles/guns/energy/crank_guns.dm
index 4d64602ae44..4110874abfb 100644
--- a/code/modules/projectiles/guns/energy/crank_guns.dm
+++ b/code/modules/projectiles/guns/energy/crank_guns.dm
@@ -7,6 +7,8 @@
ammo_type = list(/obj/item/ammo_casing/energy/laser/musket)
slot_flags = ITEM_SLOT_BACK
obj_flags = UNIQUE_RENAME
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 8, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 1.2, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.2)
+ light_color = COLOR_PURPLE
/obj/item/gun/energy/laser/musket/add_bayonet_point()
AddComponent(/datum/component/bayonet_attachable, offset_x = 22, offset_y = 11)
@@ -34,6 +36,13 @@
inhand_icon_state = "musket_prime"
worn_icon_state = "las_musket_prime"
ammo_type = list(/obj/item/ammo_casing/energy/laser/musket/prime)
+ custom_materials = list(
+ /datum/material/wood = SHEET_MATERIAL_AMOUNT * 8,
+ /datum/material/silver = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.4,
+ /datum/material/glass = SHEET_MATERIAL_AMOUNT * 1.35,
+ /datum/material/plastic = SMALL_MATERIAL_AMOUNT * 2,
+ )
/obj/item/gun/energy/disabler/smoothbore
@@ -45,6 +54,12 @@
charge_sections = 1
spread = 22.5
obj_flags = UNIQUE_RENAME
+ custom_materials = list(
+ /datum/material/wood = SHEET_MATERIAL_AMOUNT * 8,
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 2.25,
+ /datum/material/cardboard = SHEET_MATERIAL_AMOUNT,
+ /datum/material/glass = SMALL_MATERIAL_AMOUNT * 1.2,
+ )
/obj/item/gun/energy/disabler/smoothbore/Initialize(mapload)
. = ..()
@@ -74,6 +89,12 @@
ammo_type = list(/obj/item/ammo_casing/energy/disabler/smoothbore/prime)
charge_sections = 2
spread = 0 //could be like 5, but having just very tiny spread kinda feels like bullshit
+ custom_materials = list(
+ /datum/material/wood = SHEET_MATERIAL_AMOUNT * 8,
+ /datum/material/gold = SHEET_MATERIAL_AMOUNT * 5,
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 2.25,
+ /datum/material/cardboard = SHEET_MATERIAL_AMOUNT,
+ /datum/material/glass = SMALL_MATERIAL_AMOUNT * 5.2)
//Inferno and Cryo Pistols
@@ -118,6 +139,7 @@
While it doesn't manipulate temperature in and of itself, it does cause an violent eruption in anyone who is severely cold. Able to generate \
ammunition by manually spinning the weapon's nanite canister."
icon_state = "infernopistol"
+ light_color = COLOR_RED
ammo_type = list(/obj/item/ammo_casing/energy/nanite/inferno)
/obj/item/gun/energy/laser/thermal/cryo //the ice gun
@@ -126,6 +148,7 @@
While it doesn't manipulate temperature in and of itself, it does cause an internal explosion in anyone who is severely hot. Able to generate \
ammunition by manually spinning the weapon's nanite canister."
icon_state = "cryopistol"
+ light_color = COLOR_BLUE
ammo_type = list(/obj/item/ammo_casing/energy/nanite/cryo)
// The Deep Lore //
diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm
index 183cb612ada..0968d648979 100644
--- a/code/modules/projectiles/guns/energy/energy_gun.dm
+++ b/code/modules/projectiles/guns/energy/energy_gun.dm
@@ -8,6 +8,7 @@
modifystate = TRUE
ammo_x_offset = 3
dual_wield_spread = 60
+ light_color = LIGHT_COLOR_CYAN
/obj/item/gun/energy/e_gun/Initialize(mapload)
. = ..()
@@ -156,6 +157,12 @@
ammo_x_offset = 1
ammo_type = list(/obj/item/ammo_casing/energy/laser, /obj/item/ammo_casing/energy/disabler)
selfcharge = 1
+ custom_materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 6,
+ /datum/material/uranium = SHEET_MATERIAL_AMOUNT * 1.5,
+ /datum/material/glass = SHEET_MATERIAL_AMOUNT,
+ /datum/material/titanium = HALF_SHEET_MATERIAL_AMOUNT,
+ )
var/reactor_overloaded
var/fail_tick = 0
var/fail_chance = 0
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index 4337699651e..f3989e16cad 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -7,19 +7,20 @@
custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT)
ammo_type = list(/obj/item/ammo_casing/energy/lasergun)
shaded_charge = TRUE
+ light_color = COLOR_SOFT_RED
/obj/item/gun/energy/laser/Initialize(mapload)
. = ..()
- // Only actual lasguns can be converted
+ add_deep_lore()
+
+ // Only regular lasguns can be slapcrafted
if(type != /obj/item/gun/energy/laser)
return
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/xraylaser, /datum/crafting_recipe/hellgun, /datum/crafting_recipe/ioncarbine)
-
AddElement(
/datum/element/slapcrafting,\
slapcraft_recipes = slapcraft_recipe_list,\
)
- add_deep_lore()
/obj/item/gun/energy/laser/add_seclight_point()
AddComponent(/datum/component/seclite_attachable, \
@@ -29,8 +30,9 @@
overlay_y = 12)
/obj/item/gun/energy/laser/pistol
- name = "\improper Type 5C laser pistol"
- desc = "The Type 5 Heat Delivery System Compact Variant, developed by Nanotrasen. The workhorse of Nanotrasen's security forces, but in a more portable size."
+ name = "\improper Type 5/C laser pistol"
+ desc = "The Type 5 Heat Delivery System, Compact Variant, developed by Nanotrasen. The workhorse of Nanotrasen's security forces, but in a more portable size. \
+ Sacrifices some stopping power and capacity for ease of carry and faster charging."
icon_state = "laser_pistol"
w_class = WEIGHT_CLASS_NORMAL
projectile_damage_multiplier = 0.8
@@ -41,8 +43,9 @@
return
/obj/item/gun/energy/laser/assault
- name = "\improper Type 5A assault laser rifle"
- desc = "The Type 5 Heat Delivery System Assault Variant, developed by Nanotrasen. The workhorse of Nanotrasen's security forces and paramilitary organizations."
+ name = "\improper Type 5/A assault laser rifle"
+ desc = "The Type 5 Heat Delivery System, Assault Variant, developed by Nanotrasen. The workhorse of Nanotrasen's security forces and paramilitary organizations. \
+ While it sacrifices some stopping power and ease of use, its laser system is remarkably efficient and it boasts some resistance against electromagnetic interference."
icon = 'icons/obj/weapons/guns/wide_guns.dmi'
icon_state = "assault_laser"
inhand_icon_state = "assault_laser"
@@ -83,14 +86,15 @@
/obj/item/gun/energy/laser/soul
name ="\improper Type 3 laser gun"
- desc = "The Type 3 Heat Delivery System, developed by Nanotrasen. Quite possibly the most popular model of HDS ever made by Nanotrasen. They don't make them like they used too."
+ desc = "The Type 3 Heat Delivery System, developed by Nanotrasen. Quite possibly the most popular model of HDS ever made by Nanotrasen. \
+ They don't make them like they used to."
icon_state = "laser_soulful"
inhand_icon_state = "laser_soulful"
ammo_x_offset = 1
/obj/item/gun/energy/laser/carbine
- name = "\improper Type 5R laser carbine"
- desc = "The burst fire Type 5R Rapid Heat Delivery System, developed by Nanotrasen. Capable of firing a sustained volley of directed energy projectiles, though each individual projectile lacks the punch of the Type 5."
+ name = "\improper Type 5/R laser carbine"
+ desc = "The burst fire Type 5/R Rapid Heat Delivery System, developed by Nanotrasen. Capable of firing a sustained volley of directed energy projectiles, though each individual projectile lacks the punch of the Type 5."
icon_state = "laser_carbine"
burst_size = 2
fire_delay = 2
@@ -121,7 +125,7 @@
/obj/item/gun/energy/laser/carbine/practice
name = "practice laser carbine"
- desc = "A modified version of the Type 5R laser carbine. Fires entirely harmless bolts of directed energy. Safe AND entertaining to fire with abandon."
+ desc = "A modified version of the Type 5/R laser carbine. Fires entirely harmless bolts of directed energy. Safe AND entertaining to fire with abandon."
ammo_type = list(/obj/item/ammo_casing/energy/lasergun/carbine/practice)
clumsy_check = FALSE
item_flags = NONE
@@ -131,23 +135,27 @@
return
/obj/item/gun/energy/laser/retro/old
- desc = "The NT Type 1 Heat Delivery System, developed by Nanotrasen. Although, this one looks downright ancient. What the hell happened to it?"
+ desc = "The NT Type 1 Heat Delivery System, developed by Nanotrasen. This one looks downright ancient. What the hell happened to it?"
ammo_type = list(/obj/item/ammo_casing/energy/lasergun/old)
/obj/item/gun/energy/laser/retro/old/add_deep_lore()
return
/obj/item/gun/energy/laser/hellgun
- name = "\improper Type 2 'hellfire' laser gun"
- desc = "The Type 2 Heat Delivery System, developed by Nanotrasen. Technically speaking, it is an improvement. Legally speaking, possession of this weapon is restricted in most occupied sectors of space. \
- The Type 2 is notorious for its ability to render victims a carbonized husk with ease, melting flesh and bone as easily as butter. A painful, gruesome death awaits anyone on the wrong ends of this gun."
+ name = "\improper Type 4 'hellfire' laser gun"
+ desc = "The Type 4 Heat Delivery System, developed by Nanotrasen. Technically speaking, it is an improvement. \
+ Legally speaking, possession of this weapon is restricted in most occupied sectors of space. \
+ The Type 4 is notorious for its ability to render victims a carbonized husk with ease, melting flesh and bone as easily as butter. \
+ A painful, gruesome death awaits anyone on the wrong end of this gun."
icon_state = "hellgun"
ammo_type = list(/obj/item/ammo_casing/energy/laser/hellfire)
ammo_x_offset = 1
+ light_color = COLOR_AMMO_HELLFIRE
/obj/item/gun/energy/laser/captain
name = "antique laser gun"
- desc = "This is an antique laser gun. All craftsmanship is of the highest quality. It is decorated with assistant leather and chrome. The object menaces with spikes of energy. On the item is an image of Space Station 13. The station is exploding."
+ desc = "This is an antique laser gun. All craftsmanship is of the highest quality. It is decorated with assistant leather and chrome. \
+ The object menaces with spikes of energy. On the item is an image of Space Station 13. The station is exploding."
icon_state = "caplaser"
w_class = WEIGHT_CLASS_NORMAL
inhand_icon_state = null
@@ -157,10 +165,12 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
flags_1 = PREVENT_CONTENTS_EXPLOSION_1
ammo_type = list(/obj/item/ammo_casing/energy/laser/hellfire)
+ light_color = COLOR_AMMO_HELLFIRE
/obj/item/gun/energy/laser/captain/scattershot
name = "scatter shot laser rifle"
- desc = "An industrial-grade heavy-duty laser rifle with a modified laser lens to scatter its shot into multiple smaller lasers. The inner-core can self-charge for theoretically infinite use."
+ desc = "An industrial-grade heavy-duty laser rifle with a modified laser lens to scatter its shot into multiple smaller lasers. \
+ The inner-core can self-charge for theoretically infinite use."
icon_state = "lasercannon"
w_class = WEIGHT_CLASS_BULKY
inhand_icon_state = "laser"
@@ -199,7 +209,7 @@
icon = 'icons/obj/weapons/guns/ballistic.dmi'
icon_state = "cshotgun"
inhand_icon_state = "shotgun"
- desc = "A combat shotgun gutted and refitted with an internal laser system. Can switch between taser and scattered disabler shots."
+ desc = "A combat shotgun gutted and refitted with an internal energy emission system. Can switch between scattered disabler shots and taser electrodes."
shaded_charge = FALSE
pin = /obj/item/firing_pin/implant/mindshield
ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter/disabler, /obj/item/ammo_casing/energy/electrode)
@@ -226,13 +236,22 @@
/obj/item/gun/energy/laser/xray
name = "\improper Type 6 X-ray laser gun"
- desc = "The Type 6 Heat Delivery System, developed by Nanotrasen. Capable of expelling concentrated 'X-ray' blasts that pass through multiple soft targets and heavier materials."
+ desc = "The Type 6 Heat Delivery System, developed by Nanotrasen. \
+ Capable of expelling concentrated 'X-ray' blasts that pass through multiple soft targets and heavier materials."
icon_state = "xray"
w_class = WEIGHT_CLASS_BULKY
inhand_icon_state = null
ammo_type = list(/obj/item/ammo_casing/energy/xray)
ammo_x_offset = 3
+ custom_materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 3.5,
+ /datum/material/gold = SHEET_MATERIAL_AMOUNT * 2.5,
+ /datum/material/uranium = SHEET_MATERIAL_AMOUNT * 2,
+ /datum/material/titanium = SHEET_MATERIAL_AMOUNT,
+ /datum/material/bluespace = SHEET_MATERIAL_AMOUNT,
+ )
shaded_charge = FALSE
+ light_color = LIGHT_COLOR_GREEN
////////Laser Tag////////////////////
@@ -282,7 +301,6 @@
desc = "A laser gun modified to cost 20 credits to fire. Point towards poor people."
pin = /obj/item/firing_pin/paywall/luxury
-
// The Deep Lore //
// Laser Gun
@@ -290,17 +308,17 @@
/obj/item/gun/energy/laser/proc/add_deep_lore()
AddElement(/datum/element/examine_lore, \
lore_hint = span_notice("You can [EXAMINE_HINT("look closer")] to learn a little more about [src]."), \
- lore = "The NT Type 5 Heat Delivery System (sometimes referred to as the HDS5 in promotional material) is what truly put Nanotrasen \
+ lore = "The NT Type 5 Heat Delivery System (sometimes referred to as the HDS-5 in promotional material) is what truly put Nanotrasen \
head and shoulders above most weapon manufacturers in the modern era. All modern energy weaponry offered by the company have \
the success of the Type 5 to thank for setting the standard for energy-based weapon platforms.
\
\
Adopted as the standard infantry firearm for Nanotrasen military forces, as well as private security lethal armaments, few can deny \
- the weapon's reliability. And at an affordable price!
\
+ the weapon's reliability, and at an affordable price!
\
\
However, the weapon platform still possesses many of the vulnerabilities of previous energy-based weaponry. Onboard power supplies \
cannot be adequately shielded from external electromagnetic pulses that might interfere with the weapon's functionality without \
also severely jeopardizing thermal distribution into the weapon's heatsink. The Type 4, which never saw wider adoption, remains a \
- haunting example to Nanotrasen's weapons division as to the consequences when a HDS is unable to expel thermal buildup safely.
\
+ haunting example to Nanotrasen's weapons division as to the consequences when a HDS is unable to expel thermal buildup safely.
\
\
Certainly, the Melted Veterans of Galpha 5 advocacy group will never let them forget it." \
)
@@ -310,39 +328,39 @@
/obj/item/gun/energy/laser/retro/add_deep_lore()
AddElement(/datum/element/examine_lore, \
lore_hint = span_notice("You can [EXAMINE_HINT("look closer")] to learn a little more about [src]."), \
- lore = "The NT Type 1 Heat Delivery System (sometimes referred to as the HDS1 in older weapon catalogs) was a weapon that \
- marked the beginning of a new era of firearm development.
\
+ lore = "The NT Type 1 Heat Delivery System (sometimes referred to as the HDS-1 in older weapon catalogs) was a weapon that \
+ marked the beginning of a new era of firearm development.
\
\
- Invented in the thinktank laboritories of Nanotrasen's weapon development team towards the end of the 24th century, the Type 1 found \
+ Invented in the think-tank laboratories of Nanotrasen's weapon development team towards the end of the 24th century, the Type 1 found \
itself adopted broadly by various factions and military entities vying for control over the frontier once it hit the market. One \
- hallmark of those who stood successful in these conflicts were those groups that had adopted the Type 1 as a standard infantry \
- weapon. The logistics required to maintain the operational peak of the HDS1 allowed most quartermasters to merely dump a half \
+ hallmark of those who stood successful in these conflicts was the adoption of the Type 1 as a standard infantry \
+ weapon. The logistics required to maintain the operational peak of the HDS-1 allowed most quartermasters to merely dump a half \
dozen of the weapons into the hands of bloodythirsty marines, knowing full well the weapons were rugged enough to survive \
- most anything thrown at them, and only needing a recharging station with a power supply to keep operational once they run empty.
\
+ most anything thrown at them, only needing a recharging station with a power supply to become operational again once they ran empty.
\
\
- So many of these weapons exist today that even modern conflicts may see more usage of the HDS1 than the updated and equally \
+ So many of these weapons exist today that even modern conflicts may see more usage of the HDS-1 than the updated and equally \
reliable HDS5 employed by Nanotrasen's modern combat forces. Nanotrasen, despite their best efforts, still have not managed \
to encourage potential customers to swap for the new model despite a generous exchange discount." \
)
// Soulful Laser Gun
-/obj/item/gun/energy/laser/soulful/add_deep_lore()
+/obj/item/gun/energy/laser/soul/add_deep_lore()
AddElement(/datum/element/examine_lore, \
lore_hint = span_notice("You can [EXAMINE_HINT("look closer")] to learn a little more about [src]."), \
- lore = "The NT Type 3 Heat Delivery System (sometimes referred to as the HDS3 in the memories of security officers) is quite possibly \
+ lore = "The NT Type 3 Heat Delivery System (sometimes referred to as the HDS-3 in the memories of security officers) is quite possibly \
the most common type of HDS still available on the market. Fondly regarded, with quite a few diehard fans still clinging to their \
- Type 3's like their lives depended on it, the weapon has its own place in history as the 'gun that could do it all'.
\
+ Type 3s like their lives depended on it, the weapon has its own place in history as the 'gun that could do it all'.
\
\
The Type 3 line ran for several decades before attempts to replace it ever even crossed Nanotrasen's minds. When people think \
'laser gun', the Type 3 is usually what comes to mind.
\
\
When Nanotrasen announced its replacement, the Type 4, skeptics were quick to pan the weapon, claiming that it lacked several notable \
features that users of the Type 3 had enjoyed for years. As it turns out, most of those critics would end up vindicated after word of \
- Gulpha 5 and the terrible, terrible consequences of the Type 4's volatile nature came to light. Most stuck to the Type 3 and never \
+ Galpha 5 and the terrible, terrible consequences of the Type 4's volatile nature came to light. Most stuck to the Type 3 and never \
looked back, even when the Type 5 rolled out to considerable success in its own right.
\
\
- Nanotrasen still services Type 3's, with many of the parts used in the weapon sharing compatible cousins in the Type 5. Most \
+ Nanotrasen still services Type 3s, with many of the parts used in the weapon sharing compatible cousins in the Type 5. Most \
examples of the Type 3 today may actually be closer in function and form to the Type 5 than they were during their original \
construction, depending on how often it is serviced." \
)
@@ -352,25 +370,26 @@
/obj/item/gun/energy/laser/hellgun/add_deep_lore()
AddElement(/datum/element/examine_lore, \
lore_hint = span_notice("You can [EXAMINE_HINT("look closer")] to learn a little more about [src]."), \
- lore = "The NT Type 2 Heat Delivery System (sometimes referred to as the HDS2 in legal documentation) is considered a notable \
- example of Nanotrasen flying too close to the sun for its own good.
\
+ lore = "The NT Type 4 Heat Delivery System (sometimes referred to as the HDS-4 in legal documentation) is considered a notable \
+ example of Nanotrasen's weapons development teams flying too close to the sun.
\
\
- The success of the Type 1 resulted in shareholders urging marketing to bring out the 'next best thing' in energy-based weaponry. \
- At the time, Nanotrasen's weapons division had a prototype still in the works. However, there were some concerns raised amongst \
- researchers as to the 'moral implications' that might result from unleashing 'that much directed radioactive material' towards \
- a living being. Executives at the time brushed off such concerns, as there was money to be made and already ultrawealthy shareholders \
- to feed the earnings.
\
+ The success of the Type 3 resulted in shareholders urging marketing to bring out the 'next best thing' in energy-based weaponry. \
+ At the time, Nanotrasen's weapons division had a prototype still in the works, with recently-learned lessons \
+ from the failure-prone Type 2 in mind after it had more than a few catastrophic failures in testing. \
+ However, there were some concerns raised amongst researchers as to the 'moral implications' \
+ that might result from unleashing 'that much directed radioactive material' towards a living being. \
+ Executives at the time brushed off such concerns, as there was money to be made and already ultrawealthy shareholders to feed the earnings.
\
\
- The Type 2 was rushed onto the market within the next quarter, even before most common safety mechanisms had been properly tested and implemented.\
- Reports immediately began flooding in of horrific accidental dischargess, battlefield atrocities and unexpected spontaneous combustion \
+ The Type 4 was rushed onto the market within the next quarter, even before most common safety mechanisms had been properly tested and implemented. \
+ Reports immediately began flooding in of horrific accidental discharges, battlefield atrocities, and unexpected spontaneous combustion \
from excessive exposure to the untested experimental heat distribution systems 'taking its pound of flesh' for the 'hell it unleashed'.
\
\
News outlets and tabloids alike railed against the company for creating what was now being called the 'hellfire' laser gun. In response, most \
legal bodies rushed to ban the firearm from sales within their region of space, and the weapon became infamous for its unethical means of ending \
sentient life. Laws were passed to ensure power regulators were installed in all future energy-based weaponry sold by Nanotrasen. Nanotrasen quickly \
- discontinued the Type 2 in response, and it never saw production from that day forth. However, retrofit kits still exist \
- on the black market and in some of Nanotrasen's own warehouses. While legally, it is unlawful to sell and possess a Type 2, Nanotrasen itself \
- does not regulate possession of the firearm aboard its own stations. Nor does any legal body intend on preventing them from utilizing it in defense \
+ discontinued the Type 4 in response, and it never saw production from that day forth. However, retrofit kits still exist \
+ on the black market and in some of Nanotrasen's own warehouses. While, legally, it is unlawful to sell and possess a Type 4, Nanotrasen itself \
+ does not regulate possession of the firearm aboard its own stations, nor does any legal body intend on preventing them from utilizing it in defense \
of its own assets." \
)
@@ -379,25 +398,25 @@
/obj/item/gun/energy/laser/captain/add_deep_lore()
AddElement(/datum/element/examine_lore, \
lore_hint = span_notice("You can [EXAMINE_HINT("look closer")] to learn a little more about [src]."), \
- lore = "For a brief period, Nanotrasen produced a series of custom-made Type 2 laser guns for a select group of \
- clients. Largely, various wealthy starship captains, politicians and military leaders looking to demonstrate prestige before \
- the common folk.
\
+ lore = "For a brief period, Nanotrasen produced a series of custom-made Type 4 laser guns for a select group of \
+ clients, primarily composed of wealthy starship captains, politicians, and military leaders looking to demonstrate prestige before \
+ the common folk.
\
\
- The Type 2 was a commercial failure, but this particular variant earned its own infamy, linked to narratives of crazed \
- despots using it to put down political rivals and dissidents. As well as tales of mad generals marching ahead of their \
- forces, this weapon brandished, running hot, in an outstretched arm towards any moving target they could find on the \
- battlefield.
\
+ The Type 4 was a commercial failure, but this particular variant earned its own infamy, linked to narratives of crazed \
+ despots using it to put down political rivals and dissidents, as well as tales of mad generals marching ahead of their \
+ forces, this weapon brandished and running hot in an outstretched arm, pointed towards any moving target they could find on the \
+ battlefield.
\
\
Copies of this firearm are now prohibited within TerraGov space, and any captured are quickly decommissioned.\
- This is largely why Nanotrasen insists that any examples held by ranking officers be kept under lock and key. \
- All records of the schematics surrounding this variant of the Type 2 were seized and destroyed, and the creator behind \
- it detained in a maximum security TerraGov sanitorium. When they found her, she appeared to have smeared the walls in her \
- own blood, claiming that 'She' was coming, and that she had paid dearly for the knowledge of how to make the weapon.
insists that any examples held by ranking officers be kept under lock and key. \
+ All records of the schematics surrounding this variant of the Type 4 were seized and destroyed, and the creator behind \
+ it was detained in a maximum security TerraGov sanitorium. When they found her again, she appeared to have smeared the walls in her \
+ own blood, claiming that 'She' was coming, and that she had paid dearly for the knowledge of how to make the weapon.
\
\
Even the microfusion breeder cell housed inside the weapon is practically a lost technology, and Nanotrasen have been unable \
to reverse engineer the devices exact means of functionality.
\
\
- The Syndicate are obviously just as interested in exactly how this weapon is capable of self-perpetuation. Hence why the collective \
+ The Syndicate are obviously just as interested in exactly how this weapon is capable of self-perpetuation, hence why the collective \
seem hell-bent on capturing them whenever possible. Maybe keep this somewhere safe. Or don't." \
)
@@ -407,18 +426,18 @@
AddElement(/datum/element/examine_lore, \
lore_hint = span_notice("You can [EXAMINE_HINT("look closer")] to learn a little more about [src]."), \
lore = "The NT Type 6 Heat Delivery System (sometimes referred to as the HDS6 in research notes) is a breakthrough in the \
- development of directed energy weaponry.
\
+ development of man-portable directed energy weaponry.
\
\
- Very little is known about the Type 6, as it is a relatively new experimental weapon only accessible to Nanotrasen security forces.\
+ Very little is known about the Type 6, as it is a relatively new experimental weapon only accessible to Nanotrasen security forces. \
Somehow, Nanotrasen has found a means to 'slip' the energy beams produced by the Type 6 through unintended targets, only impacting \
once it has made contact with a pre-designated target by the weapon's user. It appears to be unable to slip past organic matter reliably, \
which hampers its potential for eliminating friendly-fire. However, inorganic targets are left unscathed unless the weapon is directed towards \
- firing upon the object. This makes the weapon exceptional for asset recovery and defense entrenchment and assaults on defensive structures.
\
+ firing upon the object. This makes the weapon exceptional for asset recovery, defense of entrenched positions, and assaults on defensive structures.
\
\
Nanotrasen claims that this phenomenon is achieved 'through the power of X-rays'. Most critics have highlighted that this is total nonsense. Some claim \
- that Nanotrasen has discovered a yet uknown state of matter that the company is exploiting for weapons development and manufacturing. The most \
- conspiratorially minded of Nanotrasen's critics have even gone as far as to claim it is 'proof of ectoplasm as the sixth element'.\
- And that, perhaps, the weapon may be operating through supernatural means. Maybe even powered by the 'spirits of the damned'.
\
+ that Nanotrasen has discovered a yet-unknown state of matter that the company is exploiting for weapons development and manufacturing. The most \
+ conspiratorially minded of Nanotrasen's critics have even gone as far as to claim it is 'proof of ectoplasm as the sixth element,' \
+ perhaps even allowing the weapon to operate through supernatural means: perhaps even powered by the 'spirits of the damned'.
\
\
Whatever the truth may be, the weapon seems to function as advertized, and is even more energy efficient than the Type 5. Nanotrasen \
expects full commercial rollout sometime in the next quarter." \
@@ -429,20 +448,20 @@
/obj/item/gun/energy/laser/carbine/add_deep_lore()
AddElement(/datum/element/examine_lore, \
lore_hint = span_notice("You can [EXAMINE_HINT("look closer")] to learn a little more about [src]."), \
- lore = "The NT Type 5R Rapid Heat Delivery System (sometimes referred to as the RHDS5R in briefing manuals, and 'that piece of shit flashlight' amongst \
- TGMC troopers) was a shaky first step into automatic directed energy weaponry.
\
+ lore = "The NT Type 5/R Rapid Heat Delivery System (sometimes referred to as the HDS-5/R in briefing manuals, and 'that piece of shit flashlight' \
+ amongst TGMC troopers) was a shaky first step into automatic directed energy weaponry.
\
\
- Intended for use in special operations, particularly in the hands of orbital drop shock troopers, the Type 5R was foreseen to be an excellent \
+ Intended for use in special operations, particularly in the hands of orbital drop shock troopers, the Type 5/R was foreseen to be an excellent \
addition to Nanotrasen's arsenal of offerings to military forces across occupied space. However, field performance proved grim.
\
\
The advantages of directed energy weapons is the lightweight impacts felt on the supply chain for logistical officers and quartermasters due to the \
only necessary upkeep for the weapons being a consistent power supply, either established or brought to the front, and the occassional cleaning.
\
\
This, however, is not a benefit that soldiers operating behind enemy lines or during tactical deployments are capable of exploiting. As a result, \
- operators often chaffed against the limited ammunition supply compared to conventional ballistic firearms, and the weapon quickly was abandoned by \
+ operators often chafed against the limited ammunition supply compared to conventional ballistic firearms, and the weapon quickly was abandoned by \
most special forces units.
\
\
Instead, the weapon found favour in the hands of private security teams, who enjoyed the volume of fire it provided, while maintaining \
- exceptional accuracy even at long ranges, as well as being compact enough to allow a high of discretion compared to a full sized rifle. \
+ exceptional accuracy even at long ranges, along with being compact enough to allow a high degree of discretion compared to a full sized rifle. \
The weapon is also often utilized by rim pirates and marauders, giving the weapon something of an ill reputation." \
)
diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm
index 839689144c2..973904873a6 100644
--- a/code/modules/projectiles/guns/energy/pulse.dm
+++ b/code/modules/projectiles/guns/energy/pulse.dm
@@ -9,6 +9,7 @@
modifystate = TRUE
obj_flags = CONDUCTS_ELECTRICITY
slot_flags = ITEM_SLOT_BACK
+ light_color = COLOR_BLUE
ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser)
cell_type = /obj/item/stock_parts/power_store/cell/pulse
diff --git a/code/modules/projectiles/guns/energy/recharge.dm b/code/modules/projectiles/guns/energy/recharge.dm
index 7ed3e9f9816..7c47e7b3b83 100644
--- a/code/modules/projectiles/guns/energy/recharge.dm
+++ b/code/modules/projectiles/guns/energy/recharge.dm
@@ -105,7 +105,12 @@
inhand_icon_state = "crossbow"
no_charge_state = "crossbow_empty"
w_class = WEIGHT_CLASS_SMALL
- custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT)
+ custom_materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT,
+ /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT * 0.5,
+ /datum/material/uranium = HALF_SHEET_MATERIAL_AMOUNT * 0.5,
+ /datum/material/silver = HALF_SHEET_MATERIAL_AMOUNT * 0.5,
+ )
suppressed = SUPPRESSED_QUIET
ammo_type = list(/obj/item/ammo_casing/energy/bolt)
recharge_time = 2 SECONDS
@@ -130,7 +135,12 @@
base_icon_state = "crossbowlarge"
no_charge_state = "crossbowlarge_empty"
w_class = WEIGHT_CLASS_BULKY
- custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT*2)
+ custom_materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 3.5,
+ /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT * 1.5,
+ /datum/material/uranium = HALF_SHEET_MATERIAL_AMOUNT * 1.5,
+ /datum/material/silver = HALF_SHEET_MATERIAL_AMOUNT * 1.5,
+ )
suppressed = SUPPRESSED_NONE
ammo_type = list(/obj/item/ammo_casing/energy/bolt/large)
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index c06d5bf68c2..20560880fea 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -9,6 +9,8 @@
obj_flags = CONDUCTS_ELECTRICITY
slot_flags = ITEM_SLOT_BACK
ammo_type = list(/obj/item/ammo_casing/energy/ion)
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 3, /datum/material/uranium = SHEET_MATERIAL_AMOUNT)
+ light_color = LIGHT_COLOR_BLUE
/obj/item/gun/energy/ionrifle/Initialize(mapload)
. = ..()
@@ -322,6 +324,7 @@
ammo_type = list(/obj/item/ammo_casing/energy/temp, /obj/item/ammo_casing/energy/temp/hot)
cell_type = /obj/item/stock_parts/power_store/cell/high
pin = null
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 3.5, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 1.5, /datum/material/glass = HALF_SHEET_MATERIAL_AMOUNT)
/obj/item/gun/energy/temperature/security
name = "security temperature gun"
@@ -388,6 +391,7 @@ it is often confused with the mech weapon of the same name, since it is a bit mo
weapon_weight = WEAPON_HEAVY
w_class = WEIGHT_CLASS_BULKY
///if our stpck is extended and we are ready to fire.
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 5, /datum/material/silver = SHEET_MATERIAL_AMOUNT * 5)
var/ready_to_fire = FALSE
/obj/item/gun/energy/tesla_cannon/Initialize(mapload)
@@ -502,6 +506,7 @@ it is often confused with the mech weapon of the same name, since it is a bit mo
light_system = OVERLAY_LIGHT
light_power = 2
light_range = 1
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 3, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 7, /datum/material/gold = SHEET_MATERIAL_AMOUNT * 5)
/obj/item/gun/energy/photon/Initialize(mapload)
. = ..()
diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm
index 51b54b36196..22f6646804e 100644
--- a/code/modules/projectiles/guns/energy/stun.dm
+++ b/code/modules/projectiles/guns/energy/stun.dm
@@ -3,6 +3,7 @@
desc = "A low-capacity, energy-based stun gun used by security teams to subdue targets at range."
icon_state = "taser"
inhand_icon_state = null //so the human update icon uses the icon_state instead.
+ light_color = LIGHT_COLOR_DIM_YELLOW
ammo_type = list(/obj/item/ammo_casing/energy/electrode)
ammo_x_offset = 3
diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm
index 62bda8a9a32..510d815d79d 100644
--- a/code/modules/projectiles/guns/magic.dm
+++ b/code/modules/projectiles/guns/magic.dm
@@ -9,6 +9,7 @@
fire_sound = 'sound/items/weapons/emitter.ogg'
obj_flags = CONDUCTS_ELECTRICITY
w_class = WEIGHT_CLASS_HUGE
+ can_muzzle_flash = FALSE
///what kind of magic is this
var/school = SCHOOL_EVOCATION
var/antimagic_flags = MAGIC_RESISTANCE
diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm
index 244b34abcdf..9e9094afe8a 100644
--- a/code/modules/projectiles/guns/magic/staff.dm
+++ b/code/modules/projectiles/guns/magic/staff.dm
@@ -264,7 +264,7 @@
)
/obj/item/gun/magic/staff/spellblade/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 || LEAP_ATTACK || OVERWHELMING_ATTACK))
+ if(attack_type == PROJECTILE_ATTACK || attack_type == LEAP_ATTACK || attack_type == OVERWHELMING_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. Or a road roller, if one happened to hit you.
return ..()
diff --git a/code/modules/projectiles/guns/special/chem_gun.dm b/code/modules/projectiles/guns/special/chem_gun.dm
index b085a20026e..5bd6db713d8 100644
--- a/code/modules/projectiles/guns/special/chem_gun.dm
+++ b/code/modules/projectiles/guns/special/chem_gun.dm
@@ -6,6 +6,7 @@
icon_state = "chemgun"
inhand_icon_state = "chemgun"
w_class = WEIGHT_CLASS_NORMAL
+ can_muzzle_flash = FALSE
throw_speed = 3
throw_range = 7
force = 4
diff --git a/code/modules/projectiles/guns/special/grenade_launcher.dm b/code/modules/projectiles/guns/special/grenade_launcher.dm
index 9a290459389..1b120992f66 100644
--- a/code/modules/projectiles/guns/special/grenade_launcher.dm
+++ b/code/modules/projectiles/guns/special/grenade_launcher.dm
@@ -5,6 +5,7 @@
icon_state = "riotgun"
inhand_icon_state = "riotgun"
w_class = WEIGHT_CLASS_BULKY
+ can_muzzle_flash = FALSE
throw_speed = 2
throw_range = 7
force = 5
diff --git a/code/modules/projectiles/guns/special/meat_hook.dm b/code/modules/projectiles/guns/special/meat_hook.dm
index e9937bff99f..57685c5f91a 100644
--- a/code/modules/projectiles/guns/special/meat_hook.dm
+++ b/code/modules/projectiles/guns/special/meat_hook.dm
@@ -18,6 +18,7 @@
sharpness = SHARP_POINTY
force = 18
antimagic_flags = NONE
+ can_muzzle_flash = FALSE
/obj/item/gun/magic/hook/shoot_with_empty_chamber(mob/living/user)
balloon_alert(user, "not ready yet!")
diff --git a/code/modules/projectiles/guns/special/medbeam.dm b/code/modules/projectiles/guns/special/medbeam.dm
index 0631425f0a7..45c9de8e186 100644
--- a/code/modules/projectiles/guns/special/medbeam.dm
+++ b/code/modules/projectiles/guns/special/medbeam.dm
@@ -6,6 +6,7 @@
inhand_icon_state = "chronogun"
w_class = WEIGHT_CLASS_NORMAL
item_flags = parent_type::item_flags & ~NEEDS_PERMIT
+ can_muzzle_flash = FALSE
var/mob/living/current_target
var/last_check = 0
diff --git a/code/modules/projectiles/guns/special/syringe_gun.dm b/code/modules/projectiles/guns/special/syringe_gun.dm
index 1742567355a..7390c769d03 100644
--- a/code/modules/projectiles/guns/special/syringe_gun.dm
+++ b/code/modules/projectiles/guns/special/syringe_gun.dm
@@ -19,6 +19,7 @@
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT)
clumsy_check = FALSE
fire_sound = 'sound/items/syringeproj.ogg'
+ can_muzzle_flash = FALSE
gun_flags = NOT_A_REAL_GUN
var/load_sound = 'sound/items/weapons/gun/shotgun/insert_shell.ogg'
var/list/syringes = list()
@@ -228,6 +229,7 @@
pixel_x = 0
force = 4
trigger_guard = TRIGGER_GUARD_ALLOW_ALL
+ custom_materials = list(/datum/material/bamboo = SHEET_MATERIAL_AMOUNT * 10)
/obj/item/gun/syringe/blowgun/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
. = ..()
diff --git a/code/modules/projectiles/pins.dm b/code/modules/projectiles/pins.dm
index d3059ac3fda..d00dc32ac1c 100644
--- a/code/modules/projectiles/pins.dm
+++ b/code/modules/projectiles/pins.dm
@@ -255,7 +255,7 @@
/obj/item/firing_pin/paywall/gun_insert(mob/living/user, obj/item/gun/new_gun, starting = FALSE)
if(pin_owner || starting)
. = ..()
- gun.desc += span_notice("This [gun.name] has a [multi_payment ? "per-shot" : "license permit"] cost of [payment_amount] credit[payment_amount > 1 ? "s" : ""].")
+ gun.desc += span_notice("This [gun.name] has a [multi_payment ? "per-shot" : "license permit"] cost of [payment_amount] [MONEY_NAME_AUTOPURAL(payment_amount)].")
return
if(isnull(user))
@@ -314,7 +314,7 @@
if(active_prompt_user == user)
return FALSE
active_prompt_user = user
- var/license_request = tgui_alert(user, "Do you wish to pay [payment_amount] credit[( payment_amount > 1 ) ? "s" : ""] for [( multi_payment ) ? "each shot of [gun.name]" : "usage license of [gun.name]"]?", "Weapon Purchase", list("Yes", "No"), 15 SECONDS)
+ var/license_request = tgui_alert(user, "Do you wish to pay [payment_amount] [MONEY_NAME_AUTOPURAL(payment_amount)] for [( multi_payment ) ? "each shot of [gun.name]" : "usage license of [gun.name]"]?", "Weapon Purchase", list("Yes", "No"), 15 SECONDS)
if(!user.can_perform_action(src))
active_prompt_user = null
return FALSE
diff --git a/code/modules/reagents/chemistry/holder/reactions.dm b/code/modules/reagents/chemistry/holder/reactions.dm
index d2688e1debe..1a63d45429b 100644
--- a/code/modules/reagents/chemistry/holder/reactions.dm
+++ b/code/modules/reagents/chemistry/holder/reactions.dm
@@ -340,7 +340,7 @@
my_atom.visible_message(span_notice("[iconhtml] \The [my_atom]'s power is consumed in the reaction."))
extract.name = "used slime extract"
extract.desc = "This extract has been used up."
- extract.grind_results.Cut()
+ LAZYNULL(extract.grind_results)
//finish the reaction
selected_reaction.on_reaction(src, null, multiplier)
diff --git a/code/modules/reagents/chemistry/items.dm b/code/modules/reagents/chemistry/items.dm
index 735fc037486..0500a0b3e4c 100644
--- a/code/modules/reagents/chemistry/items.dm
+++ b/code/modules/reagents/chemistry/items.dm
@@ -15,6 +15,7 @@
resistance_flags = FLAMMABLE
w_class = WEIGHT_CLASS_TINY
interaction_flags_mouse_drop = NEED_HANDS
+ custom_materials = list(/datum/material/paper = HALF_SHEET_MATERIAL_AMOUNT / 2)
///How many pages the booklet holds
var/number_of_pages = 50
@@ -151,6 +152,7 @@
icon = 'icons/obj/medical/chemical.dmi'
icon_state = "burner"
grind_results = list(/datum/reagent/consumable/ethanol = 5, /datum/reagent/silicon = 10)
+ custom_materials = list(/datum/material/paper = HALF_SHEET_MATERIAL_AMOUNT / 2)
item_flags = NOBLUDGEON
resistance_flags = FLAMMABLE
w_class = WEIGHT_CLASS_TINY
@@ -295,6 +297,7 @@
item_flags = NOBLUDGEON
w_class = WEIGHT_CLASS_TINY
grind_results = list(/datum/reagent/mercury = 5)
+ custom_materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT)
///The reagents datum that this object is attached to, so we know where we are when it's added to something.
var/datum/reagents/attached_to_reagents
@@ -352,6 +355,7 @@
/obj/item/thermometer/pen
color = "#888888"
+ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 0.1)
#undef DETAILED_CHEM_OUTPUT
#undef SHORTENED_CHEM_OUTPUT
diff --git a/code/modules/reagents/chemistry/machinery/chem_separator.dm b/code/modules/reagents/chemistry/machinery/chem_separator.dm
index f9b98f67ce0..c0611a7e0ec 100644
--- a/code/modules/reagents/chemistry/machinery/chem_separator.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_separator.dm
@@ -7,6 +7,7 @@
icon = 'icons/obj/medical/chemical.dmi'
icon_state = "separator"
light_power = 1
+ custom_materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT * 2, /datum/material/wood = SHEET_MATERIAL_AMOUNT, /datum/material/paper = HALF_SHEET_MATERIAL_AMOUNT / 2)
///Is the mixture currently boiling
var/boiling = FALSE
diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm
index 124dc5cfc86..947400e9834 100644
--- a/code/modules/reagents/chemistry/machinery/pandemic.dm
+++ b/code/modules/reagents/chemistry/machinery/pandemic.dm
@@ -99,12 +99,12 @@
if(machine_stat & (NOPOWER|BROKEN))
return ..()
if(beaker)
- balloon_alert(user, "pandemic full!")
- return ..()
- if(!user.transferItemToLoc(held_item, src))
- return ..()
+ balloon_alert(user, "beaker swapped")
+ try_put_in_hand(beaker, usr)
+ else
+ balloon_alert(user, "beaker loaded")
+ user.transferItemToLoc(held_item, src)
beaker = held_item
- balloon_alert(user, "beaker loaded")
update_appearance()
SStgui.update_uis(src)
@@ -363,6 +363,7 @@
traits["resistance"] = adv_disease.totalResistance()
traits["stage_speed"] = adv_disease.totalStageSpeed()
traits["stealth"] = adv_disease.totalStealth()
+ traits["severity"] = adv_disease.totalSeverity()
traits["symptoms"] = list()
for(var/datum/symptom/symptom as anything in adv_disease.symptoms)
var/list/this_symptom = list()
diff --git a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
index fc6ee328aec..adc556b18ec 100644
--- a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
@@ -672,7 +672,7 @@
/datum/reagent/consumable/ethanol/bloody_mary/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired)
. = ..()
- drinker.adjust_blood_volume(3 * REM * seconds_per_tick, maximum = BLOOD_VOLUME_NORMAL) // Bloody Mary quickly restores blood loss.
+ drinker.adjust_blood_volume((0.25 + round(2 * drinker.get_drunk_amount() / 40, 0.1)) * REM * seconds_per_tick, maximum = BLOOD_VOLUME_NORMAL) // Bloody Mary restores blood loss based on how drunk you are
/datum/reagent/consumable/ethanol/brave_bull
name = "Brave Bull"
diff --git a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm
index d769ceb3354..355a94a3a9f 100644
--- a/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/impure_reagents/impure_medicine_reagents.dm
@@ -492,6 +492,7 @@ Basically, we fill the time between now and 2s from now with hands based off the
TRAIT_NOHARDCRIT,
TRAIT_NOSOFTCRIT,
TRAIT_STABLEHEART,
+ TRAIT_NO_OXYLOSS_PASSOUT,
)
/datum/reagent/inverse/penthrite/on_mob_dead(mob/living/carbon/affected_mob, seconds_per_tick)
@@ -508,7 +509,7 @@ Basically, we fill the time between now and 2s from now with hands based off the
affected_mob.update_sight()
REMOVE_TRAIT(affected_mob, TRAIT_KNOCKEDOUT, STAT_TRAIT)
REMOVE_TRAIT(affected_mob, TRAIT_KNOCKEDOUT, CRIT_HEALTH_TRAIT) //Because these are normally updated using set_health() - but we don't want to adjust health, and the addition of NOHARDCRIT blocks it being added after, but doesn't remove it if it was added before
- REMOVE_TRAIT(affected_mob, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) //Prevents the user from being knocked out by oxyloss
+ REMOVE_TRAIT(affected_mob, TRAIT_KNOCKEDOUT, OXYLOSS_TRAIT) //As above, removes unconsciousness if it was added before the reagent was administered
affected_mob.set_resting(FALSE) //Please get up, no one wants a deaththrows juggernaught that lies on the floor all the time
affected_mob.SetAllImmobility(0)
affected_mob.grab_ghost(force = FALSE) //Shoves them back into their freshly reanimated corpse.
@@ -1023,7 +1024,8 @@ Basically, we fill the time between now and 2s from now with hands based off the
/datum/reagent/inverse/aranesp
name = "Epoetin Alfa"
- description = "Synthetic medication that induces blood regeneration, liver healing and wound clotting in patients. Causes adverse side effects when excessively used over time."
+ description = "Synthetic medication that induces blood regeneration and wound clotting in patients. \
+ Causes adverse side effects, including arterial damage and migraines when excessively used over time."
color = "#dee4ff"
metabolization_rate = 0.25 * REM
overdose_threshold = 20
@@ -1034,16 +1036,10 @@ Basically, we fill the time between now and 2s from now with hands based off the
. = ..()
if(overdosed)
return
- var/need_mob_update
- need_mob_update = affected_mob.adjust_organ_loss(ORGAN_SLOT_LIVER, -1 * REM * seconds_per_tick)
- for(var/datum/wound/iter_wound as anything in affected_mob.all_wounds)
- if(iter_wound.blood_flow)
- if(holder.has_reagent(/datum/reagent/medicine/coagulant, 3))
- return
- else
- holder.add_reagent(/datum/reagent/medicine/coagulant, 0.2 * REM * seconds_per_tick)
- affected_mob.adjust_blood_volume(3 * seconds_per_tick, maximum = BLOOD_VOLUME_NORMAL)
+ affected_mob.coagulant_effect(0.1 * REM * seconds_per_tick)
+ affected_mob.adjust_blood_volume(1 * seconds_per_tick, maximum = BLOOD_VOLUME_NORMAL)
+ affected_mob.adjust_organ_loss(ORGAN_SLOT_HEART, 0.2 * REM * seconds_per_tick)
switch(current_cycle)
if(10)
@@ -1086,10 +1082,6 @@ Basically, we fill the time between now and 2s from now with hands based off the
to_chat(affected_mob, span_warning("Your breathing becomes weak and raspy, you can barely stay conscious!"))
holder.add_reagent(/datum/reagent/toxin/histamine, 6 * REM * seconds_per_tick)
affected_mob.losebreath += 3
- need_mob_update = TRUE
-
- if(need_mob_update)
- return UPDATE_MOB_HEALTH
/datum/reagent/inverse/aranesp/overdose_process(mob/living/affected_mob, seconds_per_tick, times_fired)
. = ..()
diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index b41917e9837..a514c81911e 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -1837,22 +1837,11 @@
/datum/reagent/medicine/coagulant/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
. = ..()
- if(!CAN_HAVE_BLOOD(affected_mob) || !affected_mob.all_wounds)
- return
-
- var/datum/wound/bloodiest_wound
-
- for(var/i in affected_mob.all_wounds)
- var/datum/wound/iter_wound = i
- if(iter_wound.blood_flow)
- if(iter_wound.blood_flow > bloodiest_wound?.blood_flow)
- bloodiest_wound = iter_wound
-
- if(bloodiest_wound)
+ if(affected_mob.coagulant_effect(clot_rate * REM * seconds_per_tick))
if(!was_working)
to_chat(affected_mob, span_green("You can feel your flowing blood start thickening!"))
was_working = TRUE
- bloodiest_wound.adjust_blood_flow(-clot_rate * REM * seconds_per_tick)
+
else if(was_working)
was_working = FALSE
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index f8acd8f2168..a921e1077d6 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -250,6 +250,15 @@
//You don't belong in this world, monster!
mytray.reagents.remove_reagent(type, volume)
+/datum/reagent/water/mineral
+ name = "Mineral Water"
+ chemical_flags = REAGENT_CAN_BE_SYNTHESIZED|REAGENT_NO_RANDOM_RECIPE|REAGENT_CLEANS
+
+/datum/reagent/water/mineral/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, times_fired)
+ . = ..()
+ affected_mob.adjust_tox_loss(-0.1 * REM * seconds_per_tick, updating_health = FALSE)
+ return UPDATE_MOB_HEALTH
+
/datum/reagent/water/salt
name = "Saltwater"
description = "Water, but salty. Smells like... the station infirmary?"
@@ -471,17 +480,8 @@
need_mob_update += affected_mob.adjust_brute_loss(-2 * REM * seconds_per_tick, updating_health = FALSE)
need_mob_update += affected_mob.adjust_fire_loss(-2 * REM * seconds_per_tick, updating_health = FALSE)
need_mob_update = TRUE
- if(ishuman(affected_mob))
- affected_mob.adjust_blood_volume(3 * REM * seconds_per_tick, maximum = BLOOD_VOLUME_NORMAL)
-
- var/datum/wound/bloodiest_wound
-
- for(var/datum/wound/iter_wound as anything in affected_mob.all_wounds)
- if(iter_wound.blood_flow && iter_wound.blood_flow > bloodiest_wound?.blood_flow)
- bloodiest_wound = iter_wound
-
- if(bloodiest_wound)
- bloodiest_wound.adjust_blood_flow(-2 * REM * seconds_per_tick)
+ affected_mob.adjust_blood_volume(3 * REM * seconds_per_tick, maximum = BLOOD_VOLUME_NORMAL)
+ affected_mob.coagulant_effect(2 * REM * seconds_per_tick)
else // Will deal about 90 damage when 50 units are thrown
need_mob_update += affected_mob.adjust_organ_loss(ORGAN_SLOT_BRAIN, 3 * REM * seconds_per_tick, 150)
@@ -2500,13 +2500,19 @@
/datum/reagent/romerol/expose_mob(mob/living/carbon/human/exposed_mob, methods=TOUCH, reac_volume, show_message = TRUE, touch_protection = 0)
. = ..()
// Silently add the zombie infection organ to be activated upon death
- if(exposed_mob.get_organ_slot(ORGAN_SLOT_ZOMBIE))
- return
-
- if((methods & (PATCH|INGEST|INJECT|INHALE)) || ((methods & (VAPOR|TOUCH)) && prob(min(reac_volume,100)*(1 - touch_protection))))
+ if(can_infect(exposed_mob, reac_volume, methods))
var/obj/item/organ/zombie_infection/nodamage/zombie_infection = new()
zombie_infection.Insert(exposed_mob)
+/datum/reagent/romerol/proc/can_infect(mob/living/carbon/human/exposed_mob, reac_volume = 5, methods = INGEST)
+ if(exposed_mob.get_organ_slot(ORGAN_SLOT_ZOMBIE))
+ return FALSE
+ if(methods & (PATCH|INGEST|INJECT|INHALE))
+ return TRUE
+ if(reac_volume >= 1)
+ return TRUE
+ return FALSE
+
/datum/reagent/magillitis
name = "Magillitis"
description = "An experimental serum which causes rapid muscular growth in Hominidae. Side effects may include hypertrichosis, violent outbursts, and an unending affinity for bananas."
diff --git a/code/modules/reagents/reagent_containers/applicator.dm b/code/modules/reagents/reagent_containers/applicator.dm
index c70b49891c4..e183e9338e8 100644
--- a/code/modules/reagents/reagent_containers/applicator.dm
+++ b/code/modules/reagents/reagent_containers/applicator.dm
@@ -4,7 +4,6 @@
desc = "Report this please."
abstract_type = /obj/item/reagent_containers/applicator
has_variable_transfer_amount = FALSE
- grind_results = list()
/// Action string displayed in vis_message
var/apply_method = "swallow"
/// Does the item get its name changed as volume when its produced
diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm
index fa5249e4e73..d2c03b73a88 100644
--- a/code/modules/reagents/reagent_containers/cups/_cup.dm
+++ b/code/modules/reagents/reagent_containers/cups/_cup.dm
@@ -23,10 +23,13 @@
var/reagent_consumption_method = INGEST
///What sound does our consumption play on consuming from the container?
var/consumption_sound = 'sound/items/drink.ogg'
+ ///Whether to allow heating up the contents with a source of flame.
+ var/heatable = TRUE
/obj/item/reagent_containers/cup/Initialize(mapload, vol)
. = ..()
- AddElement(/datum/element/reagents_item_heatable)
+ if(heatable)
+ AddElement(/datum/element/reagents_item_heatable)
/obj/item/reagent_containers/cup/examine(mob/user)
. = ..()
@@ -316,7 +319,7 @@
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
fill_icon_state = "bucket"
fill_icon_thresholds = list(50, 90)
- custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT * 2)
+ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 2)
w_class = WEIGHT_CLASS_NORMAL
amount_per_transfer_from_this = 20
possible_transfer_amounts = list(5,10,15,20,25,30,50,100) //SKYRAT EDIT CHANGE
@@ -349,7 +352,7 @@
name = "wooden bucket"
icon_state = "woodbucket"
inhand_icon_state = "woodbucket"
- custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 2)
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 3)
resistance_flags = FLAMMABLE
armor_type = /datum/armor/bucket_wooden
@@ -421,6 +424,7 @@
icon = 'icons/obj/medical/chemical.dmi'
icon_state = "pestle"
force = 7
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT)
/obj/item/reagent_containers/cup/mortar
name = "mortar"
@@ -430,7 +434,7 @@
amount_per_transfer_from_this = 10
possible_transfer_amounts = list(5, 10, 15, 20, 25, 30, 50, 100)
volume = 100
- custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT)
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 3)
resistance_flags = FLAMMABLE
initial_reagent_flags = OPENCONTAINER
var/obj/item/grinded
diff --git a/code/modules/reagents/reagent_containers/cups/drinks.dm b/code/modules/reagents/reagent_containers/cups/drinks.dm
index 4fc28f66d37..c36611254f6 100644
--- a/code/modules/reagents/reagent_containers/cups/drinks.dm
+++ b/code/modules/reagents/reagent_containers/cups/drinks.dm
@@ -29,7 +29,7 @@
return
if(bartender_check(target, thrower) && throwingdatum)
return
- splash_reagents(target, thrower || throwingdatum?.get_thrower(), allow_closed_splash = TRUE)
+ splash_reagents(QDELETED(target) ? target.drop_location() : target, thrower || throwingdatum?.get_thrower(), allow_closed_splash = TRUE)
var/obj/item/broken_bottle/B = new (loc)
B.mimic_broken(src, target, break_top)
qdel(src)
@@ -236,7 +236,7 @@
icon_state = "smallbottle"
inhand_icon_state = null
list_reagents = list(/datum/reagent/water = 49.5, /datum/reagent/fluorine = 0.5)//see desc, don't think about it too hard
- custom_materials = list(/datum/material/plastic=HALF_SHEET_MATERIAL_AMOUNT)
+ custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT)
volume = 50
amount_per_transfer_from_this = 10
fill_icon_thresholds = list(0, 10, 25, 50, 75, 80, 90)
@@ -321,7 +321,7 @@
/obj/item/reagent_containers/cup/glass/waterbottle/large
desc = "A fresh commercial-sized bottle of water."
icon_state = "largebottle"
- custom_materials = list(/datum/material/plastic=SHEET_MATERIAL_AMOUNT * 1.5)
+ custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 3)
list_reagents = list(/datum/reagent/water = 100)
volume = 100
amount_per_transfer_from_this = 10
@@ -354,6 +354,7 @@
possible_transfer_amounts = list(10)
volume = 10
isGlass = FALSE
+ custom_materials = list(/datum/material/paper = HALF_SHEET_MATERIAL_AMOUNT)
/obj/item/reagent_containers/cup/glass/sillycup/update_icon_state()
icon_state = reagents.total_volume ? "water_cup" : "water_cup_e"
@@ -366,6 +367,7 @@
icon_state = "juicebox"
volume = 15
drink_type = NONE
+ custom_materials = list(/datum/material/cardboard = SHEET_MATERIAL_AMOUNT)
/obj/item/reagent_containers/cup/glass/bottle/juice/smallcarton/Initialize(mapload, vol)
. = ..()
@@ -379,11 +381,10 @@
/obj/item/reagent_containers/cup/glass/bottle/juice/smallcarton/smash(atom/target, mob/thrower, datum/thrownthing/throwingdatum, break_top)
if(bartender_check(target, thrower) && throwingdatum)
return
- splash_reagents(target, thrower || throwingdatum?.get_thrower(), allow_closed_splash = TRUE)
+ splash_reagents(QDELETED(target) ? target.drop_location() : target, thrower || throwingdatum?.get_thrower(), allow_closed_splash = TRUE)
var/obj/item/broken_bottle/bottle_shard = new(drop_location())
bottle_shard.mimic_broken(src, target)
qdel(src)
- target.Bumped(bottle_shard)
/obj/item/reagent_containers/cup/glass/colocup
name = "colo cup"
@@ -391,7 +392,7 @@
icon = 'icons/obj/drinks/colo.dmi'
icon_state = "colocup"
inhand_icon_state = "colocup"
- custom_materials = list(/datum/material/plastic =HALF_SHEET_MATERIAL_AMOUNT)
+ custom_materials = list(/datum/material/plastic = HALF_SHEET_MATERIAL_AMOUNT)
possible_transfer_amounts = list(5, 10, 15, 20)
volume = 20
amount_per_transfer_from_this = 5
diff --git a/code/modules/reagents/reagent_containers/cups/glassbottle.dm b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
index bcd1d91c2eb..b1686c774ef 100644
--- a/code/modules/reagents/reagent_containers/cups/glassbottle.dm
+++ b/code/modules/reagents/reagent_containers/cups/glassbottle.dm
@@ -139,7 +139,6 @@
message_in_a_bottle.forceMove(drop_location())
qdel(src)
- target.Bumped(broken)
return TRUE
/obj/item/reagent_containers/cup/glass/bottle/try_splash(mob/user, atom/target)
@@ -152,25 +151,25 @@
return
var/head_hitter = user.zone_selected == BODY_ZONE_HEAD && isliving(target)
-
- // An attack that targets the head of a living mob will attempt to knock them down
- if(head_hitter)
- var/mob/living/living_target = target
- var/knockdown_effectiveness = 0
- if(!HAS_TRAIT(target, TRAIT_HEAD_INJURY_BLOCKED))
- knockdown_effectiveness = bottle_knockdown_duration + ((force / 10) * 1 SECONDS) - living_target.getarmor(BODY_ZONE_HEAD, MELEE)
- if(prob(knockdown_effectiveness))
- living_target.Knockdown(min(knockdown_effectiveness, 20 SECONDS))
+ if(!QDELETED(target))
+ // An attack that targets the head of a living mob will attempt to knock them down
+ if(head_hitter)
+ var/mob/living/living_target = target
+ var/knockdown_effectiveness = 0
+ if(!HAS_TRAIT(target, TRAIT_HEAD_INJURY_BLOCKED))
+ knockdown_effectiveness = bottle_knockdown_duration + ((force / 10) * 1 SECONDS) - living_target.getarmor(BODY_ZONE_HEAD, MELEE)
+ if(prob(knockdown_effectiveness))
+ living_target.Knockdown(min(knockdown_effectiveness, 20 SECONDS))
// Displays a custom message which follows the attack
if(target == user)
- target.visible_message(
+ user.visible_message(
span_warning("[user] smashes [src] [head_hitter ? "over [user.p_their()] head" : "against [user.p_them()]selves"]!"),
span_warning("You smash [src] [head_hitter ? "over your head" : "against yourself"]!"),
)
else
- target.visible_message(
+ user.visible_message(
span_warning("[user] smashes [src] [head_hitter ? "over [target]'s head" : "against [target]"]!"),
span_warning("[user] smashes [src] [head_hitter ? "over your head" : "against you"]!"),
)
@@ -263,8 +262,7 @@
desc = "A carton with the bottom half burst open. Might give you a papercut."
else
if(prob(33))
- var/obj/item/shard/stab_with = new(to_mimic.drop_location())
- target.Bumped(stab_with)
+ new /obj/item/shard(to_mimic.drop_location())
playsound(src, SFX_SHATTER, 70, TRUE)
name = "broken [to_mimic.name]"
to_mimic.transfer_fingerprints_to(src)
@@ -882,6 +880,7 @@
desc = "A throwing weapon used to ignite things, typically filled with an accelerant. Recommended highly by rioters and revolutionaries. Light and toss."
icon_state = "vodkabottle"
list_reagents = list()
+ heatable = FALSE
var/active = FALSE
var/list/accelerants = list(
/datum/reagent/consumable/ethanol,
@@ -909,15 +908,16 @@
..(hit_atom, throwingdatum, do_splash = FALSE)
/obj/item/reagent_containers/cup/glass/bottle/molotov/smash(atom/target, mob/thrower, datum/thrownthing/throwingdatum, break_top)
- var/firestarter = 0
+ var/firestarter = FALSE
for(var/datum/reagent/contained_reagent in reagents.reagent_list)
for(var/accelerant_type in accelerants)
if(istype(contained_reagent, accelerant_type))
- firestarter = 1
+ firestarter = TRUE
break
..()
if(firestarter && active)
- target.fire_act()
+ if(!QDELETED(target))
+ target.fire_act()
new /obj/effect/hotspot(get_turf(target))
/obj/item/reagent_containers/cup/glass/bottle/molotov/item_interaction(mob/living/user, obj/item/item, list/modifiers)
@@ -1022,6 +1022,7 @@
righthand_file = 'icons/mob/inhands/items/drinks_righthand.dmi'
isGlass = FALSE
age_restricted = FALSE
+ custom_materials = list(/datum/material/cardboard = SHEET_MATERIAL_AMOUNT * 1.5)
/obj/item/reagent_containers/cup/glass/bottle/juice/orangejuice
name = "orange juice"
diff --git a/code/modules/reagents/reagent_containers/dropper.dm b/code/modules/reagents/reagent_containers/dropper.dm
index 05ab3ac88e2..6702fcf4fe6 100644
--- a/code/modules/reagents/reagent_containers/dropper.dm
+++ b/code/modules/reagents/reagent_containers/dropper.dm
@@ -10,6 +10,7 @@
volume = 5
initial_reagent_flags = TRANSPARENT
custom_price = PAYCHECK_CREW
+ custom_materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT)
/obj/item/reagent_containers/dropper/interact_with_atom(atom/target, mob/living/user, list/modifiers)
if(!target.reagents)
diff --git a/code/modules/reagents/reagent_containers/inhaler.dm b/code/modules/reagents/reagent_containers/inhaler.dm
index 329bca489b6..181a7e1d6e1 100644
--- a/code/modules/reagents/reagent_containers/inhaler.dm
+++ b/code/modules/reagents/reagent_containers/inhaler.dm
@@ -74,7 +74,7 @@
var/mob/living/target_mob = interacting_with
if (!can_puff(target_mob, user))
- return NONE
+ return ITEM_INTERACT_BLOCKING
var/puff_timer = 0
@@ -110,10 +110,10 @@
to_chat(user, pre_use_self_message)
if (pre_use_target_message)
to_chat(target_mob, pre_use_target_message)
- if (!do_after(user, puff_timer, src))
- return NONE
+ if (!do_after(user, puff_timer, target_mob))
+ return ITEM_INTERACT_BLOCKING
if (!can_puff(target_mob, user)) // sanity
- return NONE
+ return ITEM_INTERACT_BLOCKING
user.visible_message(post_use_visible_message, ignored_mobs = list(user, target_mob))
to_chat(user, post_use_self_message)
@@ -121,6 +121,7 @@
to_chat(target_mob, post_use_target_message)
canister.puff(user, target_mob)
+ return ITEM_INTERACT_SUCCESS
/obj/item/inhaler/attack_self(mob/user, modifiers)
try_remove_canister(user, modifiers)
diff --git a/code/modules/reagents/reagent_containers/medigel.dm b/code/modules/reagents/reagent_containers/medigel.dm
index f21c08cdb0c..d88a427277c 100644
--- a/code/modules/reagents/reagent_containers/medigel.dm
+++ b/code/modules/reagents/reagent_containers/medigel.dm
@@ -1,3 +1,31 @@
+// Gel skins
+/datum/atom_skin/med_gel
+ abstract_type = /datum/atom_skin/med_gel
+
+/datum/atom_skin/med_gel/blue
+ preview_name = "Blue"
+ new_icon_state = "medigel_blue"
+
+/datum/atom_skin/med_gel/cyan
+ preview_name = "Cyan"
+ new_icon_state = "medigel_cyan"
+
+/datum/atom_skin/med_gel/green
+ preview_name = "Green"
+ new_icon_state = "medigel_green"
+
+/datum/atom_skin/med_gel/red
+ preview_name = "Red"
+ new_icon_state = "medigel_red"
+
+/datum/atom_skin/med_gel/orange
+ preview_name = "Orange"
+ new_icon_state = "medigel_orange"
+
+/datum/atom_skin/med_gel/purple
+ preview_name = "Purple"
+ new_icon_state = "medigel_purple"
+
/obj/item/reagent_containers/medigel
name = "medical gel"
desc = "A medical gel applicator bottle, designed for precision application, with an unscrewable cap."
@@ -23,15 +51,11 @@
var/apply_method = "spray" //the thick gel is sprayed and then dries into patch like film.
var/self_delay = 30
custom_price = PAYCHECK_CREW * 2
- unique_reskin = list(
- "Blue" = "medigel_blue",
- "Cyan" = "medigel_cyan",
- "Green" = "medigel_green",
- "Red" = "medigel_red",
- "Orange" = "medigel_orange",
- "Purple" = "medigel_purple"
- )
+/obj/item/reagent_containers/medigel/Initialize(mapload)
+ . = ..()
+ if(icon_state == "medigel") // oh yeah baby raw icon state check to make sure we can't reskin preset gels
+ AddComponent(/datum/component/reskinable_item, /datum/atom_skin/med_gel)
/obj/item/reagent_containers/medigel/mode_change_message(mob/user)
var/squirt_mode = amount_per_transfer_from_this == initial(amount_per_transfer_from_this)
@@ -78,21 +102,18 @@
name = "medical gel (libital)"
desc = "A medical gel applicator bottle, designed for precision application, with an unscrewable cap. This one contains libital, for treating cuts and bruises. Libital does minor liver damage. Diluted with granibitaluri."
icon_state = "brutegel"
- current_skin = "brutegel"
list_reagents = list(/datum/reagent/medicine/c2/libital = 24, /datum/reagent/medicine/granibitaluri = 36)
/obj/item/reagent_containers/medigel/aiuri
name = "medical gel (aiuri)"
desc = "A medical gel applicator bottle, designed for precision application, with an unscrewable cap. This one contains aiuri, useful for treating burns. Aiuri does minor eye damage. Diluted with granibitaluri."
icon_state = "burngel"
- current_skin = "burngel"
list_reagents = list(/datum/reagent/medicine/c2/aiuri = 24, /datum/reagent/medicine/granibitaluri = 36)
/obj/item/reagent_containers/medigel/synthflesh
name = "medical gel (synthflesh)"
desc = "A medical gel applicator bottle, designed for precision application, with an unscrewable cap. This one contains synthflesh, a slightly toxic medicine capable of healing bruises, burns, and husks."
icon_state = "synthgel"
- current_skin = "synthgel"
list_reagents = list(/datum/reagent/medicine/c2/synthflesh = 60)
list_reagents_purity = 1
amount_per_transfer_from_this = 60
@@ -117,6 +138,5 @@
name = "sterilizer gel"
desc = "gel bottle loaded with non-toxic sterilizer. Useful in preparation for surgery."
icon_state = "medigel_blue"
- current_skin = "medigel_blue"
list_reagents = list(/datum/reagent/space_cleaner/sterilizine = 60)
custom_price = PAYCHECK_CREW * 2
diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm
index 869131dd5e8..4c4f0dbcbf9 100644
--- a/code/modules/reagents/reagent_containers/spray.dm
+++ b/code/modules/reagents/reagent_containers/spray.dm
@@ -420,6 +420,23 @@
. = ..()
icon_state = pick("sprayer_sus_1", "sprayer_sus_2", "sprayer_sus_3", "sprayer_sus_4", "sprayer_sus_5","sprayer_sus_6", "sprayer_sus_7", "sprayer_sus_8")
+// Spray bottle skins
+/datum/atom_skin/med_spray
+ abstract_type = /datum/atom_skin/med_spray
+ change_inhand_icon_state = TRUE
+
+/datum/atom_skin/med_spray/red
+ preview_name = "Red"
+ new_icon_state = "sprayer_med_red"
+
+/datum/atom_skin/med_spray/yellow
+ preview_name = "Yellow"
+ new_icon_state = "sprayer_med_yellow"
+
+/datum/atom_skin/med_spray/blue
+ preview_name = "Blue"
+ new_icon_state = "sprayer_med_blue"
+
/obj/item/reagent_containers/spray/medical
name = "medical spray bottle"
icon = 'icons/obj/medical/chemical.dmi'
@@ -428,20 +445,10 @@
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
volume = 100
- unique_reskin = list("Red" = "sprayer_med_red",
- "Yellow" = "sprayer_med_yellow",
- "Blue" = "sprayer_med_blue")
-/obj/item/reagent_containers/spray/medical/reskin_obj(mob/M)
- ..()
- switch(icon_state)
- if("sprayer_med_red")
- inhand_icon_state = "sprayer_med_red"
- if("sprayer_med_yellow")
- inhand_icon_state = "sprayer_med_yellow"
- if("sprayer_med_blue")
- inhand_icon_state = "sprayer_med_blue"
- M.update_held_items()
+/obj/item/reagent_containers/spray/medical/Initialize(mapload, vol)
+ . = ..()
+ AddComponent(/datum/component/reskinable_item, /datum/atom_skin/med_spray)
/obj/item/reagent_containers/spray/hercuri
name = "medical spray (hercuri)"
diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm
index 94a0d39b2ff..e32b60e9a2b 100644
--- a/code/modules/reagents/reagent_containers/syringes.dm
+++ b/code/modules/reagents/reagent_containers/syringes.dm
@@ -352,6 +352,7 @@
dart_insert_casing_icon_state = "overlay_syringe_crude"
dart_insert_projectile_icon_state = "overlay_syringe_crude_proj"
embed_type = /datum/embedding/syringe/crude
+ custom_materials = list(/datum/material/bamboo = SHEET_MATERIAL_AMOUNT * 5)
/datum/embedding/syringe/crude
embed_chance = 75
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index b07f7fc2629..25f442456b8 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -380,6 +380,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/reagent_dispensers/wall/peppertank, 3
tank_volume = 200
can_be_tanked = FALSE
max_integrity = 150
+ custom_materials = list(/datum/material/plastic = SHEET_MATERIAL_AMOUNT * 25)
///Paper cups left from the cooler.
var/paper_cups = 25
///Reference to our jug.
diff --git a/code/modules/recycling/conveyor.dm b/code/modules/recycling/conveyor.dm
index 91cfb1c19f4..d050b974573 100644
--- a/code/modules/recycling/conveyor.dm
+++ b/code/modules/recycling/conveyor.dm
@@ -63,6 +63,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON = PROC_REF(conveyable_enter)
)
AddElement(/datum/element/connect_loc, loc_connections)
+ AddElement(/datum/element/force_move_pulled)
update_move_direction()
LAZYADD(GLOB.conveyors_by_id[id], src)
if(wire_mode)
@@ -362,14 +363,6 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-
-// attack with hand, move pulled object onto conveyor
-/obj/machinery/conveyor/attack_hand(mob/user, list/modifiers)
- . = ..()
- if(.)
- return
- user.Move_Pulled(src)
-
/obj/machinery/conveyor/powered(chan = power_channel, ignore_use_power = FALSE)
if(!wire_mode)
return ..()
@@ -447,9 +440,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
update_appearance()
LAZYADD(GLOB.conveyors_by_id[id], src)
set_wires(new /datum/wires/conveyor(src))
- AddComponent(/datum/component/usb_port, list(
- /obj/item/circuit_component/conveyor_switch,
- ))
+ AddComponent(/datum/component/usb_port, typecacheof(list(/obj/item/circuit_component/conveyor_switch), only_root_path = TRUE))
register_context()
/obj/machinery/conveyor_switch/Destroy()
diff --git a/code/modules/recycling/disposal/bin.dm b/code/modules/recycling/disposal/bin.dm
index 507d19af168..36c0fb9c3f9 100644
--- a/code/modules/recycling/disposal/bin.dm
+++ b/code/modules/recycling/disposal/bin.dm
@@ -74,6 +74,9 @@ GLOBAL_VAR_INIT(disposals_animals_spawned, 0)
return INITIALIZE_HINT_LATELOAD //we need turfs to have air
+/obj/machinery/disposal/AllowDrop()
+ return TRUE
+
/// Checks if there a connecting trunk diposal pipe under the disposal
/obj/machinery/disposal/proc/trunk_check()
var/obj/structure/disposalpipe/trunk/found_trunk = locate() in loc
@@ -363,6 +366,8 @@ GLOBAL_VAR_INIT(disposals_animals_spawned, 0)
to_dump.pixel_x = to_dump.base_pixel_x + rand(-5, 5)
to_dump.pixel_y = to_dump.base_pixel_y + rand(-5, 5)
+ update_appearance()
+
/obj/machinery/disposal/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
. = ..()
visible_message(span_warning("[src] is ripped free from the floor!"))
diff --git a/code/modules/religion/religion_sects.dm b/code/modules/religion/religion_sects.dm
index ba8024fffe2..2f68a0153cf 100644
--- a/code/modules/religion/religion_sects.dm
+++ b/code/modules/religion/religion_sects.dm
@@ -276,7 +276,7 @@
return BLESSING_IGNORED
if(account.account_balance < GREEDY_HEAL_COST)
- to_chat(chap, span_warning("Healing from [GLOB.deity] costs [GREEDY_HEAL_COST] credits for 30 health!"))
+ to_chat(chap, span_warning("Healing from [GLOB.deity] costs [GREEDY_HEAL_COST] [MONEY_NAME] for 30 health!"))
return BLESSING_IGNORED
var/mob/living/carbon/human/blessed = blessed_living
diff --git a/code/modules/research/designs/mecha_designs.dm b/code/modules/research/designs/mecha_designs.dm
index 4b76de95f12..eff9f35ecf3 100644
--- a/code/modules/research/designs/mecha_designs.dm
+++ b/code/modules/research/designs/mecha_designs.dm
@@ -233,7 +233,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -251,7 +252,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -269,7 +271,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -287,7 +290,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -305,7 +309,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -323,7 +328,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -341,7 +347,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -359,7 +366,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -377,7 +385,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -395,7 +404,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -413,7 +423,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -431,7 +442,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -449,7 +461,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -467,7 +480,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -485,7 +499,8 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -506,7 +521,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -527,7 +543,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -548,7 +565,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -568,7 +586,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -589,7 +608,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -610,7 +630,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -631,7 +652,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -652,7 +674,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -673,7 +696,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -708,7 +732,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -728,7 +753,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -746,6 +772,7 @@
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -762,7 +789,8 @@
RND_CATEGORY_MECHFAB_PADDY + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_GYGAX + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index 8f2a06dba2d..eccbca85669 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -1042,7 +1042,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -1094,7 +1095,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -1176,7 +1178,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -1194,7 +1197,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -1811,7 +1815,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_SUPPORTED_EQUIPMENT
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -1833,7 +1838,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -1857,7 +1863,8 @@
RND_CATEGORY_MECHFAB_DURAND + RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES,
RND_CATEGORY_MECHFAB_HONK + RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES,
RND_CATEGORY_MECHFAB_PHAZON + RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES,
- RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES
+ RND_CATEGORY_MECHFAB_CLARKE + RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES,
+ RND_CATEGORY_MECHFAB_SAVANNAH_IVANOV + RND_SUBCATEGORY_MECHFAB_CONTROL_INTERFACES
)
departmental_flags = DEPARTMENT_BITFLAG_SCIENCE
@@ -1980,6 +1987,18 @@
)
research_icon_state = "civilian-plating"
+/datum/design/mod_plating/portable_suit
+ name = "MOD Portable Suit Plating"
+ id = "mod_plating_portable_suit"
+ build_path = /obj/item/mod/construction/plating/portable_suit
+ materials = list(
+ /datum/material/iron = SHEET_MATERIAL_AMOUNT * 3,
+ /datum/material/plastic = SHEET_MATERIAL_AMOUNT * 1,
+ /datum/material/plasma = HALF_SHEET_MATERIAL_AMOUNT,
+ /datum/material/plastic = SHEET_MATERIAL_AMOUNT,
+ )
+ research_icon_state = "psuit-plating"
+
/datum/design/mod_plating/engineering
name = "MOD Engineering Plating"
id = "mod_plating_engineering"
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index 4260a6f4685..d2bca7ccce5 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -188,18 +188,6 @@
)
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
-/datum/design/defibrillator
- name = "Defibrillator"
- desc = "A portable defibrillator, used for resuscitating recently deceased crew."
- id = "defibrillator"
- build_type = PROTOLATHE | AWAY_LATHE
- build_path = /obj/item/defibrillator
- materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*4, /datum/material/glass = SHEET_MATERIAL_AMOUNT*2, /datum/material/silver =SHEET_MATERIAL_AMOUNT * 1.5, /datum/material/gold =HALF_SHEET_MATERIAL_AMOUNT * 1.5)
- category = list(
- RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_MEDICAL
- )
- departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
-
/datum/design/defibrillator_mount
name = "Defibrillator Wall Mount"
desc = "A mounted frame for holding defibrillators, providing easy security."
@@ -224,18 +212,6 @@
)
departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
-/datum/design/defibrillator_compact
- name = "Compact Defibrillator"
- desc = "A compact defibrillator that can be worn on a belt."
- id = "defibrillator_compact"
- build_type = PROTOLATHE | AWAY_LATHE
- build_path = /obj/item/defibrillator/compact
- materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*6, /datum/material/glass = SHEET_MATERIAL_AMOUNT*4, /datum/material/silver = SHEET_MATERIAL_AMOUNT*3, /datum/material/gold =SHEET_MATERIAL_AMOUNT * 1.5)
- category = list(
- RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_MEDICAL
- )
- departmental_flags = DEPARTMENT_BITFLAG_MEDICAL
-
/datum/design/genescanner
name = "Genetic Sequence Analyzer"
desc = "A handy hand-held analyzers for quickly determining mutations and collecting the full sequence."
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index e40bf4299c5..310e13f73af 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -67,24 +67,20 @@
item_reactions = list()
valid_items = list()
- for(var/I in typesof(/obj/item))
- if(ispath(I, /obj/item/relic))
- item_reactions["[I]"] = SCANTYPE_DISCOVER
+ for(var/obj/item/item_path as anything in valid_subtypesof(/obj/item))
+ if(ispath(item_path, /obj/item/relic))
+ item_reactions["[item_path]"] = SCANTYPE_DISCOVER
else
- item_reactions["[I]"] = pick(SCANTYPE_POKE,SCANTYPE_IRRADIATE,SCANTYPE_GAS,SCANTYPE_HEAT,SCANTYPE_COLD,SCANTYPE_OBLITERATE)
+ item_reactions["[item_path]"] = pick(SCANTYPE_POKE,SCANTYPE_IRRADIATE,SCANTYPE_GAS,SCANTYPE_HEAT,SCANTYPE_COLD,SCANTYPE_OBLITERATE)
- if(is_type_in_typecache(I, banned_typecache))
+ if(is_type_in_typecache(item_path, banned_typecache))
continue
- if(ispath(I, /obj/item/stock_parts) || ispath(I, /obj/item/grenade/chem_grenade) || ispath(I, /obj/item/knife))
- var/obj/item/tempCheck = I
- if(initial(tempCheck.icon_state) != null) //check it's an actual usable item, in a hacky way
- valid_items["[I]"] += 15
+ if(ispath(item_path, /obj/item/stock_parts) || ispath(item_path, /obj/item/grenade/chem_grenade) || ispath(item_path, /obj/item/knife))
+ valid_items["[item_path]"] += 15
- if(ispath(I, /obj/item/food))
- var/obj/item/tempCheck = I
- if(initial(tempCheck.icon_state) != null) //check it's an actual usable item, in a hacky way
- valid_items["[I]"] += rand(1,4)
+ if(ispath(item_path, /obj/item/food))
+ valid_items["[item_path]"] += rand(1,4)
/obj/machinery/rnd/experimentor/Initialize(mapload)
. = ..()
@@ -690,7 +686,8 @@
)
for(var/counter in 1 to rand(1, 25))
var/animal_spawn = pick(valid_animals)
- new animal_spawn(get_turf(src))
+ var/mob/living/animal = new animal_spawn(get_turf(src))
+ ADD_TRAIT(animal, TRAIT_SPAWNED_MOB, INNATE_TRAIT)
warn_admins(user, "Mass Mob Spawn")
if(prob(60))
to_chat(user, span_warning("[src] falls apart!"))
diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm
index 9a481d6f362..3a0a4cad642 100644
--- a/code/modules/research/rdconsole.dm
+++ b/code/modules/research/rdconsole.dm
@@ -38,6 +38,10 @@ Nothing else in the console has ID requirements.
/// Cooldown that prevents hanging the MC when tech disks are copied
STATIC_COOLDOWN_DECLARE(cooldowncopy)
+// An unlocked subtype of the console for mapping.
+/obj/machinery/computer/rdconsole/unlocked
+ circuit = /obj/item/circuitboard/computer/rdconsole/unlocked
+
/proc/CallMaterialName(ID)
if (istype(ID, /datum/material))
var/datum/material/material = ID
@@ -166,7 +170,7 @@ Nothing else in the console has ID requirements.
var/obj/item/circuitboard/computer/rdconsole/board = circuit
if(!(board.obj_flags & EMAGGED))
board.silence_announcements = TRUE
- locked = FALSE
+ board.locked = FALSE
return TRUE
/obj/machinery/computer/rdconsole/ui_interact(mob/user, datum/tgui/ui = null)
@@ -184,8 +188,11 @@ Nothing else in the console has ID requirements.
// heavy data from this proc should be moved to static data when possible
/obj/machinery/computer/rdconsole/ui_data(mob/user)
var/list/data = list()
+
+ var/obj/item/circuitboard/computer/rdconsole/board = circuit
+
data["stored_research"] = !!stored_research
- data["locked"] = locked
+ data["locked"] = board.locked
if(!stored_research) //lack of a research node is all we care about.
return data
data += list(
@@ -331,8 +338,10 @@ Nothing else in the console has ID requirements.
add_fingerprint(usr)
+ var/obj/item/circuitboard/computer/rdconsole/board = circuit
+
// Check if the console is locked to block any actions occuring
- if (locked && action != "toggleLock")
+ if (board.locked && action != "toggleLock")
say("Console is locked, cannot perform further actions.")
return TRUE
@@ -342,7 +351,7 @@ Nothing else in the console has ID requirements.
to_chat(usr, span_boldwarning("Security protocol error: Unable to access locking protocols."))
return TRUE
if(allowed(usr))
- locked = !locked
+ board.locked = !board.locked
else
to_chat(usr, span_boldwarning("Unauthorized Access."))
return TRUE
diff --git a/code/modules/research/server_control.dm b/code/modules/research/server_control.dm
index 73596925a2d..1b792339d4a 100644
--- a/code/modules/research/server_control.dm
+++ b/code/modules/research/server_control.dm
@@ -52,10 +52,11 @@
))
for(var/obj/machinery/computer/rdconsole/console as anything in stored_research.consoles_accessing)
+ var/obj/item/circuitboard/computer/rdconsole/console_board = console.circuit
data["consoles"] += list(list(
"console_name" = console,
"console_location" = get_area(console),
- "console_locked" = console.locked,
+ "console_locked" = console_board.locked,
"console_ref" = REF(console),
))
@@ -79,7 +80,8 @@
return TRUE
if("lock_console")
var/obj/machinery/computer/rdconsole/console_selected = locate(params["selected_console"]) in stored_research.consoles_accessing
+ var/obj/item/circuitboard/computer/rdconsole/console_board = console_selected.circuit
if(!console_selected)
return FALSE
- console_selected.locked = !console_selected.locked
+ console_board.locked = !console_board.locked
return TRUE
diff --git a/code/modules/research/techweb/nodes/medbay_nodes.dm b/code/modules/research/techweb/nodes/medbay_nodes.dm
index 09f404e97ec..c79bad013ef 100644
--- a/code/modules/research/techweb/nodes/medbay_nodes.dm
+++ b/code/modules/research/techweb/nodes/medbay_nodes.dm
@@ -7,7 +7,6 @@
"operating",
"medicalbed",
"defibmountdefault",
- "defibrillator",
"surgical_drapes",
"scalpel",
"retractor",
@@ -80,7 +79,6 @@
"healthanalyzer_advanced",
"mod_health_analyzer",
"crewpinpointer",
- "defibrillator_compact",
"defibmount",
"medicalbed_emergency",
"piercesyringe",
diff --git a/code/modules/research/techweb/nodes/modsuit_nodes.dm b/code/modules/research/techweb/nodes/modsuit_nodes.dm
index 63cc115125d..6c33271dc03 100644
--- a/code/modules/research/techweb/nodes/modsuit_nodes.dm
+++ b/code/modules/research/techweb/nodes/modsuit_nodes.dm
@@ -1,7 +1,7 @@
/datum/techweb_node/mod_suit
id = TECHWEB_NODE_MOD_SUIT
starting_node = TRUE
- display_name = "Modular Suit"
+ display_name = "Modular Suits"
description = "Specialized back mounted power suits with various different modules."
prereq_ids = list(TECHWEB_NODE_ROBOTICS)
design_ids = list(
@@ -26,22 +26,33 @@
prereq_ids = list(TECHWEB_NODE_MOD_SUIT)
design_ids = list(
"modlink_scryer",
- "mod_clamp",
"mod_tether",
"mod_welding",
- "mod_safety",
- "mod_mouthhole",
"mod_longfall",
"mod_thermal_regulator",
"mod_sign_radio",
- "mod_mister_janitor",
)
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS)
announce_channels = list(RADIO_CHANNEL_SCIENCE)
+/datum/techweb_node/mod_service
+ id = TECHWEB_NODE_MOD_SERVICE
+ display_name = "Civilian Modular Suits"
+ description = "Civilian MODsuits for dignified living."
+ prereq_ids = list(TECHWEB_NODE_MOD_SUIT)
+ design_ids = list(
+ "mod_clamp",
+ "mod_safety",
+ "mod_mouthhole",
+ "mod_mister_janitor",
+ "mod_plating_portable_suit"
+ )
+ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_1_POINTS / 2)
+ announce_channels = list(RADIO_CHANNEL_SCIENCE, RADIO_CHANNEL_SERVICE)
+
/datum/techweb_node/mod_entertainment
id = TECHWEB_NODE_MOD_ENTERTAINMENT
- display_name = "Entertainment Modular Suit"
+ display_name = "Entertainment Modular Suits"
description = "Powered suits for protection against low-humor environments."
prereq_ids = list(TECHWEB_NODE_MOD_SUIT)
design_ids = list(
@@ -55,7 +66,7 @@
/datum/techweb_node/mod_medical
id = TECHWEB_NODE_MOD_MEDICAL
- display_name = "Medical Modular Suit"
+ display_name = "Medical Modular Suits"
description = "Medical MODsuits for quick rescue purposes."
prereq_ids = list(TECHWEB_NODE_MOD_SUIT, TECHWEB_NODE_CHEM_SYNTHESIS)
design_ids = list(
@@ -117,7 +128,7 @@
/datum/techweb_node/mod_engi_adv
id = TECHWEB_NODE_MOD_ENGI_ADV
- display_name = "Advanced Engineering Modular Suit"
+ display_name = "Advanced Engineering Modular Suits"
description = "Advanced Engineering suits, for advanced powered engineers."
prereq_ids = list(TECHWEB_NODE_MOD_ENGI)
design_ids = list(
@@ -137,7 +148,7 @@
/datum/techweb_node/mod_anomaly
id = TECHWEB_NODE_MOD_ANOMALY
- display_name = "Anomalock Modular Suit"
+ display_name = "Anomalock Modular Suits"
description = "Modules for MODsuits that require anomaly cores to function."
prereq_ids = list(TECHWEB_NODE_MOD_ENGI_ADV, TECHWEB_NODE_ANOMALY_RESEARCH)
design_ids = list(
diff --git a/code/modules/research/techweb/nodes/syndicate_nodes.dm b/code/modules/research/techweb/nodes/syndicate_nodes.dm
index f4317669597..d031cf7c848 100644
--- a/code/modules/research/techweb/nodes/syndicate_nodes.dm
+++ b/code/modules/research/techweb/nodes/syndicate_nodes.dm
@@ -30,9 +30,8 @@
SIGNAL_HANDLER
UnregisterSignal(SSearly_assets, COMSIG_SUBSYSTEM_POST_INITIALIZE)
required_items_to_unlock = list()
- for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type)
- var/datum/uplink_item/item = SStraitor.uplink_items_by_type[item_path]
- if(!item.item || !(item.uplink_item_flags & SYNDIE_ILLEGAL_TECH))
+ for(var/datum/uplink_item/item as anything in SStraitor.uplink_items)
+ if(isnull(item.item) || item.item == ABSTRACT_UPLINK_ITEM || !(item.uplink_item_flags & SYNDIE_ILLEGAL_TECH))
continue
required_items_to_unlock |= item.item //allows deconning to unlock.
diff --git a/code/modules/research/xenobiology/vatgrowing/microscope.dm b/code/modules/research/xenobiology/vatgrowing/microscope.dm
index d96f577db66..9eb78f17a5a 100644
--- a/code/modules/research/xenobiology/vatgrowing/microscope.dm
+++ b/code/modules/research/xenobiology/vatgrowing/microscope.dm
@@ -3,6 +3,7 @@
desc = "A simple microscope, allowing you to examine micro-organisms."
icon = 'icons/obj/science/vatgrowing.dmi'
icon_state = "microscope"
+ custom_materials = list(/datum/material/glass = SHEET_MATERIAL_AMOUNT, /datum/material/plastic = SHEET_MATERIAL_AMOUNT)
///Analyzed dish
var/obj/item/petri_dish/current_dish
diff --git a/code/modules/shuttle/misc/special.dm b/code/modules/shuttle/misc/special.dm
index c38e4c40a38..6b635fdec83 100644
--- a/code/modules/shuttle/misc/special.dm
+++ b/code/modules/shuttle/misc/special.dm
@@ -384,7 +384,7 @@
for(var/obj/I in counted_money)
qdel(I)
if(!check_times[AM] || check_times[AM] < world.time) //Let's not spam the message
- to_chat(AM, span_notice("[payees[AM]] cr received. You need [threshold-payees[AM]] cr more."))
+ to_chat(AM, span_notice("[payees[AM]] [MONEY_SYMBOL] received. You need [threshold-payees[AM]] [MONEY_SYMBOL] more."))
check_times[AM] = world.time + LUXURY_MESSAGE_COOLDOWN
alarm_beep()
return ..()
diff --git a/code/modules/shuttle/mobile_port/variants/custom/blueprints.dm b/code/modules/shuttle/mobile_port/variants/custom/blueprints.dm
index db7d3aa1ecc..c37cb6cef7d 100644
--- a/code/modules/shuttle/mobile_port/variants/custom/blueprints.dm
+++ b/code/modules/shuttle/mobile_port/variants/custom/blueprints.dm
@@ -700,6 +700,7 @@
base_icon_state = "shuttle_blueprints_crude"
base_desc = "This is just a sheet of paper thoroughly covered in what could either be crayon or spraypaint."
linked_desc = "This is just a crude doodle of a shuttle drawn on a background of what could either be crayon or spraypaint."
+ custom_materials = list(/datum/material/paper = HALF_SHEET_MATERIAL_AMOUNT / 2)
/obj/item/shuttle_blueprints/borg
name = "shuttle blueprint database"
diff --git a/code/modules/shuttle/mobile_port/variants/emergency/emergency_console.dm b/code/modules/shuttle/mobile_port/variants/emergency/emergency_console.dm
index ff070188da6..08eeef8be76 100644
--- a/code/modules/shuttle/mobile_port/variants/emergency/emergency_console.dm
+++ b/code/modules/shuttle/mobile_port/variants/emergency/emergency_console.dm
@@ -104,16 +104,16 @@
if(!ID)
to_chat(user, span_warning("You don't have an ID."))
- return
+ return .
if(!(ACCESS_COMMAND in ID.access))
to_chat(user, span_warning("The access level of your card is not high enough."))
- return
+ return .
- if (user in acted_recently)
- return
+ if(user in acted_recently)
+ return .
- var/old_len = authorized.len
+ var/old_len = length(authorized)
addtimer(CALLBACK(src, PROC_REF(clear_recent_action), user), SHUTTLE_CONSOLE_ACTION_DELAY)
switch(action)
@@ -124,23 +124,36 @@
authorized -= ID
if("abort")
- if(authorized.len)
- // Abort. The action for when heads are fighting over whether
- // to launch early.
+ if(length(authorized))
+ // Abort. The action for when heads are fighting over whether to launch early.
authorized.Cut()
. = TRUE
- if((old_len != authorized.len) && !ENGINES_STARTED)
- var/alert = (authorized.len > old_len)
- var/repeal = (authorized.len < old_len)
- var/remaining = max(0, auth_need - authorized.len)
- if(authorized.len && remaining)
- minor_announce("[remaining] authorizations needed until shuttle is launched early", null, alert)
+ var/new_len = length(authorized)
+ if((old_len != new_len) && !ENGINES_STARTED)
+ var/repeal = (new_len < old_len)
+ var/remaining = max(0, auth_need - new_len)
+ if(new_len && remaining)
+ priority_announce(
+ "[remaining] authorization\s needed until shuttle is launched early.",
+ "Emergency Shuttle Status",
+ sound = 'sound/announcer/notice/notice1.ogg',
+ type = ANNOUNCEMENT_TYPE_PRIORITY,
+ has_important_message = TRUE,
+ color_override = "red",
+ )
if(repeal)
- minor_announce("Early launch authorization revoked, [remaining] authorizations needed")
+ priority_announce(
+ "Early launch authorization revoked, [remaining] authorization\s needed.",
+ "Emergency Shuttle Status",
+ sound = 'sound/announcer/notice/notice2.ogg',
+ type = ANNOUNCEMENT_TYPE_PRIORITY,
+ color_override = "blue",
+ )
acted_recently += user
SStgui.update_user_uis(user, src)
+ return .
/obj/machinery/computer/emergency_shuttle/proc/authorize(mob/living/user, source)
var/obj/item/card/id/ID = user.get_idcard(TRUE)
diff --git a/code/modules/shuttle/mobile_port/variants/supply.dm b/code/modules/shuttle/mobile_port/variants/supply.dm
index cb853fb1c8e..d3eb5ffa7cb 100644
--- a/code/modules/shuttle/mobile_port/variants/supply.dm
+++ b/code/modules/shuttle/mobile_port/variants/supply.dm
@@ -165,7 +165,6 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
var/pack_cost
var/list/goodies_by_buyer = list() // if someone orders more than GOODY_FREE_SHIPPING_MAX goodies, we upcharge to a normal crate so they can't carry around 20 combat shotties
var/list/clean_up_orders = list() // orders to remove since we are done with them
- var/list/forced_briefcases = list() // SKYRAT EDIT ADDITION
for(var/datum/supply_order/spawning_order in SSshuttle.shopping_list)
if(!empty_turfs.len)
@@ -193,18 +192,18 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
if(spawning_order.paying_account) //Someone paid out of pocket
paying_for_this = spawning_order.paying_account
// note this is before we increment, so this is the GOODY_FREE_SHIPPING_MAX + 1th goody to ship. also note we only increment off this step if they successfully pay the fee, so there's no way around it
- if(spawning_order.pack.goody)
+ if(spawning_order.pack.order_flags & ORDER_GOODY)
var/list/current_buyer_orders = goodies_by_buyer[spawning_order.paying_account]
if(LAZYLEN(current_buyer_orders) == GOODY_FREE_SHIPPING_MAX)
price = round(price + CRATE_TAX)
- paying_for_this.bank_card_talk("Goody order size exceeds free shipping limit: Assessing [CRATE_TAX] credit S&H fee.")
+ paying_for_this.bank_card_talk("Goody order size exceeds free shipping limit: Assessing [CRATE_TAX] [MONEY_NAME_SINGULAR] S&H fee.")
else
paying_for_this = SSeconomy.get_dep_account(ACCOUNT_CAR)
if(paying_for_this)
if(!paying_for_this.adjust_money(-price, "Cargo: [spawning_order.pack.name]"))
if(spawning_order.paying_account)
- paying_for_this.bank_card_talk("Cargo order #[spawning_order.id] rejected due to lack of funds. Credits required: [price]")
+ paying_for_this.bank_card_talk("Cargo order #[spawning_order.id] rejected due to lack of funds. [MONEY_NAME_CAPITALIZED] required: [price]")
if(!spawning_order.can_be_cancelled) //only if it absolutly cannot be canceled by the player do we cancel it for them
SSshuttle.shopping_list -= spawning_order
clean_up_orders += spawning_order
@@ -213,18 +212,18 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
pack_cost = spawning_order.pack.get_cost()
if(spawning_order.paying_account && spawning_order.charge_on_purchase) // SKYRAT EDIT CHANGE - ORIGINAL: if(spawning_order.paying_account)
paying_for_this = spawning_order.paying_account
- if(spawning_order.pack.goody)
+ if(spawning_order.pack.order_flags & ORDER_GOODY)
LAZYADD(goodies_by_buyer[spawning_order.paying_account], spawning_order)
var/receiver_message = "Cargo order #[spawning_order.id] has shipped."
if(spawning_order.charge_on_purchase)
- receiver_message += " [price] credits have been charged to your bank account"
+ receiver_message += " [price] [MONEY_NAME] have been charged to your bank account"
paying_for_this.bank_card_talk(receiver_message)
SSeconomy.add_audit_entry(paying_for_this, price, spawning_order.pack.name)
var/datum/bank_account/department/cargo = SSeconomy.get_dep_account(ACCOUNT_CAR)
cargo.adjust_money(price - pack_cost) //Cargo gets the handling fee
value += pack_cost
- if(!spawning_order.pack.goody && !(spawning_order?.paying_account in forced_briefcases)) // SKYRAT EDIT CHANGE - ORIGINAL : if(!spawning_order.pack.goody)
+ if(!(spawning_order.pack.order_flags & ORDER_GOODY)) //we handle goody crates below
var/obj/structure/closet/crate = spawning_order.generate(pick_n_take(empty_turfs))
crate.name += " - #[spawning_order.id]"
@@ -232,7 +231,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
var/from_whom = paying_for_this?.account_holder || "nobody (department order)"
investigate_log("Order #[spawning_order.id] ([spawning_order.pack.name], placed by [key_name(spawning_order.orderer_ckey)]), paid by [from_whom] has shipped.", INVESTIGATE_CARGO)
- if(spawning_order.pack.dangerous)
+ if(spawning_order.pack.order_flags & ORDER_DANGEROUS)
message_admins("\A [spawning_order.pack.name] ordered by [ADMIN_LOOKUPFLW(spawning_order.orderer_ckey)], paid by [from_whom] has shipped.")
purchases++
@@ -286,7 +285,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
qdel(completed_order)
var/datum/bank_account/cargo_budget = SSeconomy.get_dep_account(ACCOUNT_CAR)
- investigate_log("[purchases] orders in this shipment, worth [value] credits. [cargo_budget.account_balance] credits left.", INVESTIGATE_CARGO)
+ investigate_log("[purchases] orders in this shipment, worth [value] [MONEY_NAME]. [cargo_budget.account_balance] [MONEY_NAME] left.", INVESTIGATE_CARGO)
/// Deletes and sells the items on the shuttle
/obj/docking_port/mobile/supply/proc/sell()
@@ -317,7 +316,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
SSshuttle.centcom_message = msg
if(report.exported_atoms.len)
- investigate_log("contents sold for [cargo_budget.account_balance - presale_points] credits. Contents: [report.exported_atoms.Join(",")]. Message: [msg]", INVESTIGATE_CARGO)
+ investigate_log("contents sold for [cargo_budget.account_balance - presale_points] [MONEY_NAME]. Contents: [report.exported_atoms.Join(",")]. Message: [msg]", INVESTIGATE_CARGO)
/*
Generates a box of mail depending on our exports and imports.
diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm
index 72a22e60fd9..36b0363ed12 100644
--- a/code/modules/station_goals/bsa.dm
+++ b/code/modules/station_goals/bsa.dm
@@ -23,7 +23,7 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE)
/datum/station_goal/bluespace_cannon/on_report()
//Unlock BSA parts
var/datum/supply_pack/engineering/bsa/P = SSshuttle.supply_packs[/datum/supply_pack/engineering/bsa]
- P.special_enabled = TRUE
+ P.order_flags |= ORDER_SPECIAL_ENABLED
/datum/station_goal/bluespace_cannon/check_completion()
if(..())
diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm
index 001419406cc..169a105aa75 100644
--- a/code/modules/station_goals/dna_vault.dm
+++ b/code/modules/station_goals/dna_vault.dm
@@ -47,10 +47,10 @@
/datum/station_goal/dna_vault/on_report()
var/datum/supply_pack/P = SSshuttle.supply_packs[/datum/supply_pack/engineering/dna_vault]
- P.special_enabled = TRUE
+ P.order_flags |= ORDER_SPECIAL_ENABLED
P = SSshuttle.supply_packs[/datum/supply_pack/engineering/dna_probes]
- P.special_enabled = TRUE
+ P.order_flags |= ORDER_SPECIAL_ENABLED
/datum/station_goal/dna_vault/check_completion()
if(..())
diff --git a/code/modules/station_goals/meteor_shield.dm b/code/modules/station_goals/meteor_shield.dm
index 8d5e2dbf409..2ea25b1a7bf 100644
--- a/code/modules/station_goals/meteor_shield.dm
+++ b/code/modules/station_goals/meteor_shield.dm
@@ -30,10 +30,10 @@
/datum/station_goal/station_shield/on_report()
//Unlock
var/datum/supply_pack/P = SSshuttle.supply_packs[/datum/supply_pack/engineering/shield_sat]
- P.special_enabled = TRUE
+ P.order_flags |= ORDER_SPECIAL_ENABLED
P = SSshuttle.supply_packs[/datum/supply_pack/engineering/shield_sat_control]
- P.special_enabled = TRUE
+ P.order_flags |= ORDER_SPECIAL_ENABLED
/datum/station_goal/station_shield/check_completion()
if(..())
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 119d238472b..219b8d9bb55 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -378,8 +378,8 @@
sexy_chad.hair_color = hair_color
sexy_chad.facial_hairstyle = facial_hairstyle
sexy_chad.facial_hair_color = facial_hair_color
- sexy_chad.grad_style = gradient_styles.Copy()
- sexy_chad.grad_color = gradient_colors.Copy()
+ sexy_chad.grad_style = LAZYCOPY(gradient_styles)
+ sexy_chad.grad_color = LAZYCOPY(gradient_colors)
sexy_chad.lip_style = lip_style
sexy_chad.lip_color = lip_color
diff --git a/code/modules/surgery/bodyparts/ghetto_parts.dm b/code/modules/surgery/bodyparts/ghetto_parts.dm
index 4835bba0300..5e8858c68c2 100644
--- a/code/modules/surgery/bodyparts/ghetto_parts.dm
+++ b/code/modules/surgery/bodyparts/ghetto_parts.dm
@@ -17,6 +17,7 @@
disabling_threshold_percentage = 1
bodypart_flags = BODYPART_UNHUSKABLE
biological_state = (BIO_WOOD|BIO_JOINTED)
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 2)
/obj/item/bodypart/arm/left/ghetto/Initialize(mapload, ...)
. = ..()
@@ -41,6 +42,7 @@
disabling_threshold_percentage = 1
bodypart_flags = BODYPART_UNHUSKABLE
biological_state = (BIO_WOOD|BIO_JOINTED)
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 2)
/obj/item/bodypart/arm/right/ghetto/Initialize(mapload, ...)
. = ..()
@@ -63,6 +65,7 @@
disabling_threshold_percentage = 1
bodypart_flags = BODYPART_UNHUSKABLE
biological_state = (BIO_WOOD|BIO_JOINTED)
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 2)
/obj/item/bodypart/leg/left/ghetto/Initialize(mapload, ...)
. = ..()
@@ -85,6 +88,7 @@
disabling_threshold_percentage = 1
bodypart_flags = BODYPART_UNHUSKABLE
biological_state = (BIO_WOOD|BIO_JOINTED)
+ custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT * 2)
/obj/item/bodypart/leg/right/ghetto/Initialize(mapload, ...)
. = ..()
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 5e909eeee83..a814f2719cb 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -57,15 +57,9 @@
var/facial_hair_hidden = FALSE
/// Gradient styles, if any
- var/list/gradient_styles = list(
- "None", //Hair gradient style
- "None", //Facial hair gradient style
- )
+ var/list/gradient_styles
/// Gradient colors, if any
- var/list/gradient_colors = list(
- COLOR_BLACK, //Hair gradient color
- COLOR_BLACK, //Facial hair gradient color
- )
+ var/list/gradient_colors
/// An override color that can be cleared later, affects both hair and facial hair
var/override_hair_color = null
@@ -182,7 +176,7 @@
if(!eyes)
if (!(head_flags & HEAD_EYEHOLES))
return
- var/image/no_eyes = image('icons/mob/human/human_face.dmi', "eyes_missing", -EYES_LAYER, SOUTH)
+ var/image/no_eyes = image('icons/mob/human/human_eyes.dmi', "eyes_missing", -EYES_LAYER, SOUTH)
worn_face_offset?.apply_offset(no_eyes)
. += no_eyes
return
@@ -191,8 +185,8 @@
return
// This is a bit of copy/paste code from eyes.dm:generate_body_overlay
- var/image/eye_left = image('icons/mob/human/human_face.dmi', "[eyes.eye_icon_state]_l", -EYES_LAYER, SOUTH)
- var/image/eye_right = image('icons/mob/human/human_face.dmi', "[eyes.eye_icon_state]_r", -EYES_LAYER, SOUTH)
+ var/image/eye_left = image(eyes.eye_icon, "[eyes.eye_icon_state]_l", -EYES_LAYER, SOUTH)
+ var/image/eye_right = image(eyes.eye_icon, "[eyes.eye_icon_state]_r", -EYES_LAYER, SOUTH)
if(head_flags & HEAD_EYECOLOR)
if(eyes.eye_color_left)
eye_left.color = eyes.eye_color_left
diff --git a/code/modules/surgery/bodyparts/head_hair_and_lips.dm b/code/modules/surgery/bodyparts/head_hair_and_lips.dm
index 77766197233..45025158b90 100644
--- a/code/modules/surgery/bodyparts/head_hair_and_lips.dm
+++ b/code/modules/surgery/bodyparts/head_hair_and_lips.dm
@@ -56,8 +56,8 @@
facial_hair_alpha = target_species.facial_hair_alpha
facial_hair_color = target.facial_hair_color
fixed_hair_color = target_species.get_fixed_hair_color(target) //Can be null
- gradient_styles = target.grad_style.Copy()
- gradient_colors = target.grad_color.Copy()
+ gradient_styles = LAZYCOPY(target.grad_style)
+ gradient_colors = LAZYCOPY(target.grad_color)
var/obj/item/organ/eyes/peepers = locate() in src
if(peepers)
if(overwrite_eyes || isnull(initial(peepers.eye_color_left)))
@@ -114,9 +114,9 @@
worn_face_offset?.apply_offset(facial_hair_overlay)
. += facial_hair_overlay
//Gradients
- var/facial_hair_gradient_style = gradient_styles[GRADIENT_FACIAL_HAIR_KEY]
+ var/facial_hair_gradient_style = get_hair_gradient_style(GRADIENT_FACIAL_HAIR_KEY)
if(facial_hair_gradient_style != "None")
- var/facial_hair_gradient_color = gradient_colors[GRADIENT_FACIAL_HAIR_KEY]
+ var/facial_hair_gradient_color = get_hair_gradient_color(GRADIENT_FACIAL_HAIR_KEY)
var/image/facial_hair_gradient_overlay = get_gradient_overlay(icon(sprite_accessory.icon, sprite_accessory.icon_state), -HAIR_LAYER, SSaccessories.facial_hair_gradients_list[facial_hair_gradient_style], facial_hair_gradient_color, dropped)
. += facial_hair_gradient_overlay
@@ -155,9 +155,9 @@
worn_face_offset?.apply_offset(hair_overlay)
. += hair_overlay
//Gradients
- var/hair_gradient_style = gradient_styles[GRADIENT_HAIR_KEY]
+ var/hair_gradient_style = get_hair_gradient_style(GRADIENT_HAIR_KEY)
if(hair_gradient_style != "None")
- var/hair_gradient_color = gradient_colors[GRADIENT_HAIR_KEY]
+ var/hair_gradient_color = get_hair_gradient_color(GRADIENT_HAIR_KEY)
var/image/hair_gradient_overlay = get_gradient_overlay(base_icon, hair_overlay.layer, SSaccessories.hair_gradients_list[hair_gradient_style], hair_gradient_color, dropped)
hair_gradient_overlay.pixel_z = hair_sprite_accessory.y_offset
// SKYRAT EDIT ADDITION - Species hair offset
@@ -216,7 +216,7 @@
/// Returns an appropriate missing eyes overlay
/obj/item/bodypart/head/proc/get_eyeless_overlay(dropped)
RETURN_TYPE(/image)
- var/eyeless_icon = 'icons/mob/human/human_face.dmi'
+ var/eyeless_icon = 'icons/mob/human/human_eyes.dmi'
var/eyeless_icon_state = "eyes_missing"
var/image/eyeless_overlay = mutable_appearance(eyeless_icon, eyeless_icon_state, -HAIR_LAYER)
@@ -312,6 +312,39 @@
if(update)
update_body_parts()
+/**
+ * Get the hair gradient style of a human.
+ * Defaults to "None".
+ * arguments:
+ * * key (optional) - corresponds to hair or facial hair index. If no key is provided returns whole list.
+ **/
+/mob/living/proc/get_hair_gradient_style(key)
+ return
+
+/mob/living/carbon/human/get_hair_gradient_style(key)
+ if(key)
+ return LAZYACCESS(grad_style, key) || "None"
+
+ return grad_style || list(
+ "None", //Hair Gradient Style
+ "None", //Facial Hair Gradient Style
+ )
+
+/**
+ * Get the hair gradient style of a head.
+ * Defaults to "None".
+ * arguments:
+ * * key (optional) - corresponds to hair or facial hair index. If no key is provided returns whole list.
+ **/
+/obj/item/bodypart/head/proc/get_hair_gradient_style(key)
+ if(key)
+ return LAZYACCESS(gradient_styles, key) || "None"
+
+ return gradient_styles || list(
+ "None", //Hair Gradient Style
+ "None", //Facial Hair Gradient Style
+ )
+
/**
* Set the hair gradient style of a human.
* Update calls update_body_parts().
@@ -320,17 +353,52 @@
return
/mob/living/carbon/human/set_hair_gradient_style(new_style, update = TRUE)
- if(grad_style[GRADIENT_HAIR_KEY] == new_style)
+ if(LAZYACCESS(grad_style, GRADIENT_HAIR_KEY) == new_style)
return
var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD)
- grad_style[GRADIENT_HAIR_KEY] = new_style
+ LAZYSET(grad_style, GRADIENT_HAIR_KEY, new_style)
if(my_head)
- my_head.gradient_styles[GRADIENT_HAIR_KEY] = new_style
+ LAZYSET(my_head.gradient_styles, GRADIENT_HAIR_KEY, new_style)
if(update)
update_body_parts()
+/**
+ * Get the hair gradient color of a human.
+ * Defaults to black.
+ *
+ * arguments:
+ * * key (optional) - corresponds to hair or facial hair index. If no key is provided returns whole list.
+ **/
+/mob/living/proc/get_hair_gradient_color(key)
+ return
+
+/mob/living/carbon/human/get_hair_gradient_color(key)
+ if(key)
+ return LAZYACCESS(grad_color, key) || COLOR_BLACK
+
+ return grad_color || list(
+ COLOR_BLACK, //Hair Gradient Color
+ COLOR_BLACK, //Facial Hair Gradient Color
+ )
+
+/**
+ * Get the hair gradient color of a head.
+ * Defaults to black.
+ *
+ * arguments:
+ * * key (optional) - corresponds to hair or facial hair index. If no key is provided returns whole list.
+ **/
+/obj/item/bodypart/head/proc/get_hair_gradient_color(key)
+ if(key)
+ return LAZYACCESS(gradient_colors, key) || COLOR_BLACK
+
+ return gradient_colors || list(
+ COLOR_BLACK, //Hair Gradient Color
+ COLOR_BLACK, //Facial Hair Gradient Color
+ )
+
/**
* Set the hair gradient color of a human.
* Update calls update_body_parts().
@@ -339,13 +407,13 @@
return
/mob/living/carbon/human/set_hair_gradient_color(new_color, update = TRUE)
- if(grad_color[GRADIENT_HAIR_KEY] == new_color)
+ if(LAZYACCESS(grad_color, GRADIENT_HAIR_KEY) == new_color)
return
var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD)
- grad_color[GRADIENT_HAIR_KEY] = new_color
+ LAZYSET(grad_color, GRADIENT_HAIR_KEY, new_color)
if(my_head)
- my_head.gradient_colors[GRADIENT_HAIR_KEY] = new_color
+ LAZYSET(my_head.gradient_colors, GRADIENT_HAIR_KEY, new_color)
if(update)
update_body_parts()
@@ -395,13 +463,13 @@
return
/mob/living/carbon/human/set_facial_hair_gradient_style(new_style, update = TRUE)
- if(grad_style[GRADIENT_FACIAL_HAIR_KEY] == new_style)
+ if(LAZYACCESS(grad_style, GRADIENT_FACIAL_HAIR_KEY) == new_style)
return
var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD)
- grad_style[GRADIENT_FACIAL_HAIR_KEY] = new_style
+ LAZYSET(grad_style, GRADIENT_FACIAL_HAIR_KEY, new_style)
if(my_head)
- my_head.gradient_styles[GRADIENT_FACIAL_HAIR_KEY] = new_style
+ LAZYSET(my_head.gradient_styles, GRADIENT_FACIAL_HAIR_KEY, new_style)
if(update)
update_body_parts()
@@ -414,13 +482,13 @@
return
/mob/living/carbon/human/set_facial_hair_gradient_color(new_color, update = TRUE)
- if(grad_color[GRADIENT_FACIAL_HAIR_KEY] == new_color)
+ if(LAZYACCESS(grad_color, GRADIENT_FACIAL_HAIR_KEY) == new_color)
return
var/obj/item/bodypart/head/my_head = get_bodypart(BODY_ZONE_HEAD)
- grad_color[GRADIENT_FACIAL_HAIR_KEY] = new_color
+ LAZYSET(grad_color, GRADIENT_FACIAL_HAIR_KEY, new_color)
if(my_head)
- my_head.gradient_colors[GRADIENT_FACIAL_HAIR_KEY] = new_color
+ LAZYSET(my_head.gradient_colors, GRADIENT_FACIAL_HAIR_KEY, new_color)
if(update)
update_body_parts()
diff --git a/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm b/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm
index 9d83f3c7cff..1d9244fb619 100644
--- a/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm
+++ b/code/modules/surgery/organs/internal/cyberimp/augments_arms.dm
@@ -287,7 +287,7 @@
/obj/item/taperecorder,
/obj/item/lighter,
/obj/item/laser_pointer,
- /obj/item/stamp,
+ /obj/item/stamp/granted,
/obj/item/stamp/denied,
)
diff --git a/code/modules/surgery/organs/internal/eyes/_eyes.dm b/code/modules/surgery/organs/internal/eyes/_eyes.dm
index 5496e21b0e7..da9584988ef 100644
--- a/code/modules/surgery/organs/internal/eyes/_eyes.dm
+++ b/code/modules/surgery/organs/internal/eyes/_eyes.dm
@@ -37,6 +37,9 @@
var/eye_color_left = null // set to a hex code to override a mob's left eye color
var/eye_color_right = null // set to a hex code to override a mob's right eye color
+ /// The icon file of that eyes as its applied to the mob
+ var/eye_icon = 'icons/mob/human/human_eyes.dmi'
+ /// The icon state of that eyes as its applied to the mob
var/eye_icon_state = "eyes"
/// Do these eyes have blinking animations
var/blink_animation = TRUE
@@ -127,11 +130,6 @@
if(CONFIG_GET(flag/native_fov) && native_fov)
affected_human.add_fov_trait(type, native_fov)
- // SKYRAT EDIT ADDITION - EMISSIVES
- if (affected_human.emissive_eyes)
- is_emissive = TRUE
- // SKYRAT EDIT END
-
if(call_update)
affected_human.update_body()
@@ -166,7 +164,6 @@
organ_owner.update_tint()
organ_owner.update_sight()
- is_emissive = FALSE // SKYRAT EDIT ADDITION
UnregisterSignal(organ_owner, list(
COMSIG_ATOM_BULLET_ACT,
COMSIG_COMPONENT_CLEAN_FACE_ACT,
@@ -293,28 +290,11 @@
if(!istype(parent) || parent.get_organ_by_type(/obj/item/organ/eyes) != src)
CRASH("Generating a body overlay for [src] targeting an invalid parent '[parent]'.")
- if(isnull(eye_icon_state) || eye_icon_state == "None") // SKYRAT EDIT - Synths, adds eye_icon_state == "None"
+ if(isnull(eye_icon_state))
return list()
- var/eye_icon = parent.dna?.species.eyes_icon || 'icons/mob/human/human_face.dmi' // SKYRAT EDIT ADDITION
-
- var/mutable_appearance/eye_left = mutable_appearance(eye_icon, "[eye_icon_state]_l", -eyes_layer, parent) // SKYRAT EDIT CHANGE - Customization - ORIGINAL: var/mutable_appearance/eye_left = mutable_appearance('icons/mob/human/human_face.dmi', "[eye_icon_state]_l", -EYES_LAYER, parent)
- var/mutable_appearance/eye_right = mutable_appearance(eye_icon, "[eye_icon_state]_r", -eyes_layer, parent) // SKYRAT EDIT CHANGE - Customization - ORIGINAL: var/mutable_appearance/eye_right = mutable_appearance('icons/mob/human/human_face.dmi', "[eye_icon_state]_r", -EYES_LAYER, parent)
-
- // BEGIN BUBBER ADDITION:
- if(HAS_TRAIT(owner, TRAIT_QUAD_EYES)) // (*) (*) v (*) (*)
- var/mutable_appearance/eye_left_2 = new /mutable_appearance(eye_left)
- eye_left_2.pixel_x -= 1
- eye_left_2.pixel_y += parent.quad_eyes_offset
- var/mutable_appearance/eye_right_2 = new /mutable_appearance(eye_right)
- eye_right_2.pixel_x += 1
- eye_right_2.pixel_y += parent.quad_eyes_offset
-
- eye_left.underlays.Add(eye_left_2)
- eye_right.underlays.Add(eye_right_2)
-
- // END BUBBER ADDITION
-
+ var/mutable_appearance/eye_left = mutable_appearance(eye_icon, "[eye_icon_state]_l", -EYES_LAYER, parent)
+ var/mutable_appearance/eye_right = mutable_appearance(eye_icon, "[eye_icon_state]_r", -EYES_LAYER, parent)
var/list/overlays = list(eye_left, eye_right)
if(!(parent.obscured_slots & HIDEEYES))
@@ -333,24 +313,15 @@
overlays += eyelids
if (scarring & RIGHT_EYE_SCAR)
- var/mutable_appearance/right_scar = mutable_appearance('icons/mob/human/human_face.dmi', "eye_scar_right", -EYES_LAYER, parent)
+ var/mutable_appearance/right_scar = mutable_appearance('icons/mob/human/human_eyes.dmi', "eye_scar_right", -EYES_LAYER, parent)
right_scar.color = my_head.draw_color
overlays += right_scar
if (scarring & LEFT_EYE_SCAR)
- var/mutable_appearance/left_scar = mutable_appearance('icons/mob/human/human_face.dmi', "eye_scar_left", -EYES_LAYER, parent)
+ var/mutable_appearance/left_scar = mutable_appearance('icons/mob/human/human_eyes.dmi', "eye_scar_left", -EYES_LAYER, parent)
left_scar.color = my_head.draw_color
overlays += left_scar
- // SKYRAT EDIT START - Customization Emissives
- if(is_emissive)
- var/mutable_appearance/emissive_left = emissive_appearance_copy(eye_left, owner)
- var/mutable_appearance/emissive_right = emissive_appearance_copy(eye_right, owner)
-
- overlays += emissive_left
- overlays += emissive_right
- // SKYRAT EDIT END - Customization Emissives
-
if(my_head.worn_face_offset)
for (var/mutable_appearance/overlay as anything in overlays)
my_head.worn_face_offset.apply_offset(overlay)
@@ -561,9 +532,7 @@
addtimer(CALLBACK(src, PROC_REF(animate_eyelids), owner), blink_delay + duration)
/obj/item/organ/eyes/proc/animate_eyelids(mob/living/carbon/human/parent)
- if(CONFIG_GET(flag/disable_blinking)) return // BUBBER EDIT - CONFIG BLINKING
-
- var/sync_blinking = TRUE // synchronized_blinking && (parent.get_organ_loss(ORGAN_SLOT_BRAIN) < ASYNC_BLINKING_BRAIN_DAMAGE) // BUBBER EDIT - REMOVE ASYNC BLINKING UNTIL https://github.com/tgstation/tgstation/issues/90269 is fixed
+ var/sync_blinking = synchronized_blinking && (parent.get_organ_loss(ORGAN_SLOT_BRAIN) < BRAIN_DAMAGE_ASYNC_BLINKING)
// Randomize order for unsynched animations
if (sync_blinking || prob(50))
var/list/anim_times = animate_eyelid(eyelid_left, parent, sync_blinking)
@@ -574,7 +543,7 @@
/obj/effect/abstract/eyelid_effect
name = "eyelid"
- icon = 'icons/mob/human/human_face.dmi'
+ icon = 'icons/mob/human/human_eyes.dmi'
layer = -EYES_LAYER
vis_flags = VIS_INHERIT_DIR | VIS_INHERIT_PLANE | VIS_INHERIT_ID
@@ -597,6 +566,7 @@
#define NIGHTVISION_LIGHT_HIG 3
/obj/item/organ/eyes/night_vision
+ abstract_type = /obj/item/organ/eyes/night_vision
actions_types = list(/datum/action/item_action/organ_action/use)
// These lists are used as the color cutoff for the eye
@@ -763,6 +733,7 @@
iris_overlay = null
flash_protect = FLASH_PROTECTION_WELDER
tint = INFINITY
+ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 2.5, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 1.9)
var/obj/item/flashlight/eyelight/eye
light_reactive = FALSE
pupils_name = "flashlights"
@@ -850,20 +821,8 @@
/// Set the initial color of the eyes on insert to be the mob's previous eye color.
/obj/item/organ/eyes/robotic/glow/on_mob_insert(mob/living/carbon/eye_recipient, special = FALSE, movement_flags)
. = ..()
- //BUBBER EDIT CHANGE BEGIN
- //left_eye_color_string = eye_color_left
- //right_eye_color_string = eye_color_right
- if (ishuman(eye_recipient))
- var/mob/living/carbon/human/H = eye_recipient
- left_eye_color_string = H.eye_color_left
- right_eye_color_string = H.eye_color_right
- light_color_string = H.eye_color_left
- else
- left_eye_color_string = eye_color_left
- right_eye_color_string = eye_color_right
- light_color_string = eye_color_left
- eye.set_light_color(light_color_string)
- //BUBBER EDIT CHANGE END
+ left_eye_color_string = eye_color_left
+ right_eye_color_string = eye_color_right
update_mob_eye_color(eye_recipient)
deactivate(close_ui = TRUE)
eye.forceMove(eye_recipient)
@@ -919,13 +878,12 @@
set_beam_range(new_range)
return TRUE
if("pick_color")
- // BUBBERSTATION EDIT START: TGUI COLOR PICKER
- var/new_color = tgui_color_picker(
+ var/new_color = input(
usr,
"Choose eye color color:",
"High Luminosity Eyes Menu",
light_color_string
- ) // BUBBERSTATION EDIT END: TGUI COLOR PICKER
+ ) as color|null
if(new_color)
var/to_update = params["to_update"]
set_beam_color(new_color, to_update)
@@ -1198,7 +1156,7 @@
name = "reptile eyes"
desc = "A pair of reptile eyes with thin vertical slits for pupils."
icon_state = "lizard_eyes"
- // synchronized_blinking = FALSE // BUBBER EDIT - REMOVE ASYNC BLINKING UNTIL https://github.com/tgstation/tgstation/issues/90269 is fixed
+ synchronized_blinking = FALSE
pupils_name = "slit pupils"
penlight_message = "have vertically slit pupils and tinted whites"
@@ -1236,9 +1194,9 @@
name = "pod eyes"
desc = "Strangest salad you've ever seen."
icon_state = "eyes_pod"
- // eye_color_left = "#375846" BUBBER EDIT - ALLOWS EYE COLOR TO BE CHANGED AGAIN
- // eye_color_right = "#375846" BUBBER EDIT - ALLOWS EYE COLOR TO BE CHANGED AGAIN
- // iris_overlay = null BUBBER EDIT - ALLOWS EYE COLOR TO BE CHANGED AGAIN
+ eye_color_left = "#375846"
+ eye_color_right = "#375846"
+ iris_overlay = null
foodtype_flags = PODPERSON_ORGAN_FOODTYPES
penlight_message = "are green and plant-like"
diff --git a/code/modules/surgery/organs/internal/heart/_heart.dm b/code/modules/surgery/organs/internal/heart/_heart.dm
index 48d52f7c1e0..6f272b57d58 100644
--- a/code/modules/surgery/organs/internal/heart/_heart.dm
+++ b/code/modules/surgery/organs/internal/heart/_heart.dm
@@ -256,14 +256,7 @@
if(toxification_probability && prob(toxification_probability))
wounded_owner.adjust_tox_loss(1 * seconds_per_tick, updating_health = FALSE)
- var/datum/wound/bloodiest_wound
-
- for(var/datum/wound/iter_wound as anything in wounded_owner.all_wounds)
- if(iter_wound.blood_flow && iter_wound.blood_flow > bloodiest_wound?.blood_flow)
- bloodiest_wound = iter_wound
-
- if(bloodiest_wound)
- bloodiest_wound.adjust_blood_flow(-1 * seconds_per_tick)
+ wounded_owner.coagulant_effect(1 * seconds_per_tick)
/obj/item/organ/heart/cybernetic/proc/stabilize_heart()
ADD_TRAIT(owner, TRAIT_NOSOFTCRIT, ORGAN_TRAIT)
diff --git a/code/modules/surgery/organs/internal/heart/heart_anomalock.dm b/code/modules/surgery/organs/internal/heart/heart_anomalock.dm
index 843317e2902..78d3d64b715 100644
--- a/code/modules/surgery/organs/internal/heart/heart_anomalock.dm
+++ b/code/modules/surgery/organs/internal/heart/heart_anomalock.dm
@@ -33,6 +33,10 @@
QDEL_NULL(core)
return ..()
+/obj/item/organ/heart/cybernetic/anomalock/examine(mob/user)
+ . = ..()
+ . += span_info("The voltaic boost will avoid healing toxin damage at all in slime-based humanoids, to prevent harmful side effects.")
+
/obj/item/organ/heart/cybernetic/anomalock/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags)
. = ..()
if(!core)
@@ -182,14 +186,19 @@
duration = 30 SECONDS
alert_type = /atom/movable/screen/alert/status_effect/anomalock_active
show_duration = TRUE
+ processing_speed = STATUS_EFFECT_PRIORITY
/datum/status_effect/voltaic_overdrive/tick(seconds_between_ticks)
. = ..()
-
- if(owner.health <= owner.crit_threshold)
- owner.heal_overall_damage(5, 5)
- owner.adjust_oxy_loss(-5)
- owner.adjust_tox_loss(-5)
+ if(owner.health > owner.crit_threshold)
+ return
+ var/needs_update = FALSE
+ needs_update += owner.heal_overall_damage(brute = 5, burn = 5, updating_health = FALSE)
+ needs_update += owner.adjust_oxy_loss(-5, updating_health = FALSE)
+ if(!HAS_TRAIT(owner, TRAIT_TOXINLOVER))
+ needs_update += owner.adjust_tox_loss(-5, updating_health = FALSE)
+ if(needs_update)
+ owner.updatehealth()
/datum/status_effect/voltaic_overdrive/on_apply()
. = ..()
diff --git a/code/modules/surgery/organs/internal/lungs/_lungs.dm b/code/modules/surgery/organs/internal/lungs/_lungs.dm
index e74abd583f4..74cad2abf28 100644
--- a/code/modules/surgery/organs/internal/lungs/_lungs.dm
+++ b/code/modules/surgery/organs/internal/lungs/_lungs.dm
@@ -1131,7 +1131,7 @@
icon_state = "lungs-evolved"
safe_plasma_max = 8
- safe_co2_max = 8
+ safe_co2_max = 20
maxHealth = 1.2 * STANDARD_ORGAN_THRESHOLD
safe_oxygen_min = 8
diff --git a/code/modules/surgery/organs/internal/stomach/_stomach.dm b/code/modules/surgery/organs/internal/stomach/_stomach.dm
index b03fa221e3a..000501a86fe 100644
--- a/code/modules/surgery/organs/internal/stomach/_stomach.dm
+++ b/code/modules/surgery/organs/internal/stomach/_stomach.dm
@@ -39,7 +39,7 @@
/// Whether the stomach's been repaired with surgery and can be fixed again or not
var/operated = FALSE
/// List of all atoms within the stomach
- var/list/atom/movable/stomach_contents = list()
+ var/list/atom/movable/stomach_contents
/// Have we been cut open with a scalpel? If so, how much damage from it we still have from it and can be recovered with a cauterizing tool.
/// All healing goes towards recovering this.
var/cut_open_damage = 0
@@ -53,7 +53,7 @@
reagents.flags |= REAGENT_HOLDER_ALIVE
/obj/item/organ/stomach/Destroy()
- QDEL_LIST(stomach_contents)
+ QDEL_LAZYLIST(stomach_contents)
return ..()
/obj/item/organ/stomach/on_life(seconds_per_tick, times_fired)
@@ -216,19 +216,19 @@
/obj/item/organ/stomach/proc/consume_thing(atom/movable/thing)
RegisterSignal(thing, COMSIG_MOVABLE_MOVED, PROC_REF(content_moved))
RegisterSignal(thing, COMSIG_QDELETING, PROC_REF(content_deleted))
- stomach_contents += thing
+ LAZYADD(stomach_contents, thing)
thing.forceMove(owner || src) // We assert that if we have no owner, we will not be nullspaced
return TRUE
/obj/item/organ/stomach/proc/content_deleted(atom/movable/source)
SIGNAL_HANDLER
- stomach_contents -= source
+ LAZYREMOVE(stomach_contents, source)
/obj/item/organ/stomach/proc/content_moved(atom/movable/source)
SIGNAL_HANDLER
if(source.loc == src || source.loc == owner) // not in us? out da list then
return
- stomach_contents -= source
+ LAZYREMOVE(stomach_contents, source)
UnregisterSignal(source, list(COMSIG_MOVABLE_MOVED, COMSIG_QDELETING))
/obj/item/organ/stomach/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
@@ -249,7 +249,7 @@
var/total_chance = chance
// If min_amount is set, make sure that we vomit at least some of our contents
if (min_amount)
- total_chance += 100 / (length(stomach_contents) + 1 - min_amount)
+ total_chance += 100 / (LAZYLEN(stomach_contents) + 1 - min_amount)
if (!prob(total_chance))
continue
nugget.forceMove(drop_loc)
@@ -272,7 +272,7 @@
if (!owner || SSmobs.times_fired % 3 != 0)
return
- if (!length(stomach_contents))
+ if (!LAZYLEN(stomach_contents))
return
var/obj/item/bodypart/chest/chest = owner.get_bodypart(zone)
@@ -306,7 +306,7 @@
if (chest && !chest.cavity_item && as_item.w_class <= WEIGHT_CLASS_NORMAL)
// Oopsie!
chest.cavity_item = as_item
- stomach_contents -= as_item
+ LAZYREMOVE(stomach_contents, as_item)
continue
owner.apply_damage(as_item.w_class * (as_item.sharpness ? 2 : 1), BRUTE, BODY_ZONE_CHEST, wound_bonus = CANT_WOUND,
@@ -413,7 +413,7 @@
/// If damage is high enough, we may end up vomiting out whatever we had stored
/obj/item/organ/stomach/proc/on_punched(datum/source, mob/living/carbon/human/attacker, damage, attack_type, obj/item/bodypart/affecting, final_armor_block, kicking, limb_sharpness)
SIGNAL_HANDLER
- if (!length(stomach_contents) || damage < 9 || final_armor_block || kicking)
+ if (!LAZYLEN(stomach_contents) || damage < 9 || final_armor_block || kicking)
return
if (owner.vomit(MOB_VOMIT_MESSAGE | MOB_VOMIT_FORCE))
// Since we vomited with a force flag, we should've vomited out at least one item
diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm
index f9b94949f18..37bbeca0e85 100644
--- a/code/modules/tgui/tgui.dm
+++ b/code/modules/tgui/tgui.dm
@@ -57,7 +57,7 @@
*/
/datum/tgui/New(mob/user, datum/src_object, interface, title, ui_x, ui_y)
log_tgui(user,
- "new [interface] fancy [user?.client?.prefs.read_preference(/datum/preference/toggle/tgui_fancy)]",
+ "new [interface]",
src_object = src_object)
src.user = user
src.src_object = src_object
@@ -98,7 +98,6 @@
if(!window.is_ready())
window.initialize(
strict_mode = TRUE,
- fancy = user.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy),
assets = list(
get_asset_datum(/datum/asset/simple/tgui),
))
@@ -152,11 +151,25 @@
state = null
qdel(src)
+/**
+ * public
+ *
+ * Sends a message to the front end to push the UI window to position 0,0
+ *
+ * optional can_be_suspended bool
+ */
+/datum/tgui/proc/reset_ui_position()
+ if(window)
+ // Windows you want to keep are usually blue screens of death
+ // and we want to keep them around, to allow user to read
+ // the error message properly.
+ window.send_message("resetposition")
+
/**
* public
*
* Closes all ByondUI elements, left dangling by a forceful TGUI exit,
- * such as via Alt+F4, closing in non-fancy mode, or terminating the process
+ * such as via Alt+F4, closing or terminating the process
*
*/
/datum/tgui/proc/terminate_byondui_elements()
@@ -258,7 +271,6 @@
"window" = list(
"key" = window_key,
"size" = window_size,
- "fancy" = user.client.prefs.read_preference(/datum/preference/toggle/tgui_fancy),
"locked" = user.client.prefs.read_preference(/datum/preference/toggle/tgui_lock),
"scale" = user.client.prefs.read_preference(/datum/preference/toggle/ui_scale),
),
diff --git a/code/modules/tgui/tgui_window.dm b/code/modules/tgui/tgui_window.dm
index 38c49548b64..c99ab314344 100644
--- a/code/modules/tgui/tgui_window.dm
+++ b/code/modules/tgui/tgui_window.dm
@@ -20,7 +20,6 @@
var/sent_assets = list()
// Vars passed to initialize proc (and saved for later)
var/initial_strict_mode
- var/initial_fancy
var/initial_assets
var/initial_inline_html
var/initial_inline_js
@@ -52,7 +51,6 @@
* will be put into the queue until the window finishes loading.
*
* optional strict_mode bool - Enables strict error handling and BSOD.
- * optional fancy bool - If TRUE and if this is NOT a panel, will hide the window titlebar.
* optional assets list - List of assets to load during initialization.
* optional inline_html string - Custom HTML to inject.
* optional inline_js string - Custom JS to inject.
@@ -60,7 +58,6 @@
*/
/datum/tgui_window/proc/initialize(
strict_mode = FALSE,
- fancy = FALSE,
assets = list(),
inline_html = "",
inline_js = "",
@@ -70,7 +67,6 @@
window = src)
if(!client)
return
- src.initial_fancy = fancy
src.initial_assets = assets
src.initial_inline_html = inline_html
src.initial_inline_js = inline_js
@@ -78,12 +74,7 @@
status = TGUI_WINDOW_LOADING
fatally_errored = FALSE
// Build window options
- var/options = "file=[id].html;can_minimize=0;auto_format=0;"
- // Remove titlebar and resize handles for a fancy window
- if(fancy)
- options += "titlebar=0;can_resize=0;"
- else
- options += "titlebar=1;can_resize=1;"
+ var/options = "file=[id].html;can_minimize=0;auto_format=0;titlebar=0;can_resize=0;"
// Generate page html
var/html = SStgui.basehtml
html = replacetextEx(html, "\[tgui:windowId]", id)
@@ -130,7 +121,6 @@
/datum/tgui_window/proc/reinitialize()
initialize(
strict_mode = initial_strict_mode,
- fancy = initial_fancy,
assets = initial_assets,
inline_html = initial_inline_html,
inline_js = initial_inline_js,
diff --git a/code/modules/tgui_input/say_modal/modal.dm b/code/modules/tgui_input/say_modal/modal.dm
index d7548e9612f..48cad74c639 100644
--- a/code/modules/tgui_input/say_modal/modal.dm
+++ b/code/modules/tgui_input/say_modal/modal.dm
@@ -52,7 +52,6 @@
sleep(3 SECONDS)
window.initialize(
strict_mode = TRUE,
- fancy = TRUE,
inline_css = file("tgui/public/tgui-say.bundle.css"),
inline_js = file("tgui/public/tgui-say.bundle.js"),
);
diff --git a/code/modules/tgui_panel/tgui_panel.dm b/code/modules/tgui_panel/tgui_panel.dm
index 24b1e865ffc..ecb3ad626a8 100644
--- a/code/modules/tgui_panel/tgui_panel.dm
+++ b/code/modules/tgui_panel/tgui_panel.dm
@@ -82,7 +82,6 @@
"computer_id" = client.computer_id,
),
"window" = list(
- "fancy" = FALSE,
"locked" = FALSE,
),
),
diff --git a/code/modules/transport/tram/tram_controller.dm b/code/modules/transport/tram/tram_controller.dm
index 5af24ef5a64..ffe1b12e45c 100644
--- a/code/modules/transport/tram/tram_controller.dm
+++ b/code/modules/transport/tram/tram_controller.dm
@@ -1195,7 +1195,7 @@
desc = "A box that contains the equipment to control a tram. Just secure to the tram wall."
icon = 'icons/obj/tram/tram_controllers.dmi'
icon_state = "tram-controller"
- custom_materials = list(/datum/material/titanium = SHEET_MATERIAL_AMOUNT * 4, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 2, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2)
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 20)
result_path = /obj/machinery/transport/tram_controller
pixel_shift = 32
diff --git a/code/modules/transport/tram/tram_displays.dm b/code/modules/transport/tram/tram_displays.dm
index 81b73398a15..4e850abb2a8 100644
--- a/code/modules/transport/tram/tram_displays.dm
+++ b/code/modules/transport/tram/tram_displays.dm
@@ -39,7 +39,7 @@
desc = "Used to build tram indicator displays, just secure to the wall."
icon_state = "indi_blank"
icon = 'icons/obj/tram/tram_indicator.dmi'
- custom_materials = list(/datum/material/titanium = SHEET_MATERIAL_AMOUNT * 4, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 2, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 2)
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 7)
result_path = /obj/machinery/transport/destination_sign/indicator
pixel_shift = 32
diff --git a/code/modules/transport/tram/tram_floors.dm b/code/modules/transport/tram/tram_floors.dm
index 623b13896f7..dd091e4e3ae 100644
--- a/code/modules/transport/tram/tram_floors.dm
+++ b/code/modules/transport/tram/tram_floors.dm
@@ -186,10 +186,15 @@
plane = GAME_PLANE
obj_flags = BLOCK_Z_OUT_DOWN | BLOCK_Z_OUT_UP
appearance_flags = PIXEL_SCALE|KEEP_TOGETHER
+ custom_materials = list(/datum/material/plastic = HALF_SHEET_MATERIAL_AMOUNT / 2)
var/secured = TRUE
var/floor_tile = /obj/item/stack/thermoplastic
var/mutable_appearance/damage_overlay
+/obj/structure/thermoplastic/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/force_move_pulled)
+
/datum/armor/tram_floor
melee = 40
bullet = 10
@@ -294,6 +299,7 @@
max_amount = 60
novariants = TRUE
merge_type = /obj/item/stack/thermoplastic
+ mats_per_unit = list(/datum/material/plastic = HALF_SHEET_MATERIAL_AMOUNT / 2)
var/tile_type = /obj/structure/thermoplastic
/obj/item/stack/thermoplastic/light
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 23b3c28c1d5..963ff89ceb6 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -166,6 +166,7 @@
#include "explosion_action.dm"
#include "firedoor_regions.dm"
#include "fish_unit_tests.dm"
+#include "flyperson.dm"
#include "focus_only_tests.dm"
#include "font_awesome_icons.dm"
#include "food_edibility_check.dm"
@@ -176,6 +177,7 @@
#include "gloves_and_shoes_armor.dm"
#include "greyscale_config.dm"
#include "hallucination_icons.dm"
+#include "held_slowdown.dm"
#include "heretic_knowledge.dm"
#include "heretic_rituals.dm"
#include "high_five.dm"
@@ -214,6 +216,7 @@
#include "mapping.dm"
#include "mapping_nearstation_test.dm"
#include "market.dm"
+#include "mecha_build.dm"
#include "mecha_damage.dm"
#include "medical_wounds.dm"
#include "merge_type.dm"
@@ -229,6 +232,7 @@
#include "modular_map_loader.dm"
#include "monkey_business.dm"
#include "mouse_bite_cable.dm"
+#include "move_pulled.dm"
#include "movement_order_sanity.dm"
#include "mutant_hands_consistency.dm"
#include "mutant_organs.dm"
@@ -266,6 +270,7 @@
#include "recycle_recycling.dm"
#include "required_map_items.dm"
#include "resist.dm"
+#include "reskin_validation.dm"
#include "reta_system.dm"
#include "say.dm"
#include "screenshot_airlocks.dm"
diff --git a/code/modules/unit_tests/area_contents.dm b/code/modules/unit_tests/area_contents.dm
index c53a9143e6a..dea3c0a3057 100644
--- a/code/modules/unit_tests/area_contents.dm
+++ b/code/modules/unit_tests/area_contents.dm
@@ -19,16 +19,16 @@
if (turf_to_check.in_contents_of)
var/area/existing = turf_to_check.in_contents_of
if (existing == turf_to_check.loc)
- TEST_FAIL("Found a duplicate turf [turf_to_check.type] inside [area_to_test.type]'s turf listing")
+ TEST_FAIL("Found a duplicate turf [turf_to_check.type] [COORD(turf_to_check)] inside [area_to_test.type]'s turf listing")
else
- TEST_FAIL("Found a shared turf [turf_to_check.type] between [area_to_test.type] and [existing.type]'s turf listings")
+ TEST_FAIL("Found a shared turf [turf_to_check.type] [COORD(turf_to_check)] between [area_to_test.type] and [existing.type]'s turf listings")
var/area/turfs_actual_area = turf_to_check.loc
if (turfs_actual_area != area_to_test)
- TEST_FAIL("Found a turf [turf_to_check.type] which is IN [turfs_actual_area.type], but is registered as being in [area_to_test.type]")
+ TEST_FAIL("Found a turf [turf_to_check.type] [COORD(turf_to_check)] which is IN [turfs_actual_area.type], but is registered as being in [area_to_test.type]")
turf_to_check.in_contents_of = turfs_actual_area
for(var/turf/position in ALL_TURFS())
if(!position.in_contents_of)
- TEST_FAIL("Found a turf [position.type] inside [position.loc.type] that is NOT stored in any area's turf listing")
+ TEST_FAIL("Found a turf [position.type] [COORD(position)] inside [position.loc.type] that is NOT stored in any area's turf listing")
diff --git a/code/modules/unit_tests/blindness.dm b/code/modules/unit_tests/blindness.dm
index 9d47e45ca5a..20a8349302b 100644
--- a/code/modules/unit_tests/blindness.dm
+++ b/code/modules/unit_tests/blindness.dm
@@ -52,14 +52,14 @@
// Check for the status effect, duh
TEST_ASSERT(dummy.is_blind(), "Dummy, [status_message], did not have the blind status effect.")
// Being more technical, we need to check for client color and screen overlays
- TEST_ASSERT(HAS_CLIENT_COLOR(dummy, /datum/client_colour/monochrome), "Dummy, [status_message], did not have the monochrome client color.")
+ TEST_ASSERT(HAS_CLIENT_COLOR(dummy, /datum/client_colour/blindness), "Dummy, [status_message], did not have the monochrome client color.")
TEST_ASSERT(HAS_SCREEN_OVERLAY(dummy, /atom/movable/screen/fullscreen/blind), "Dummy, [status_message], did not have a blind screen overlay in their list of screens.")
/datum/unit_test/blindness/proc/check_if_not_blind(mob/living/carbon/human/dummy, status_message = "after being cured of blindness")
// Check for no status effect
TEST_ASSERT(!dummy.is_blind(), "Dummy, [status_message], still had the blindness status effect.")
// Check that the client color and screen overlay are gone
- TEST_ASSERT(!HAS_CLIENT_COLOR(dummy, /datum/client_colour/monochrome), "Dummy, [status_message], still had the monochrome client color.")
+ TEST_ASSERT(!HAS_CLIENT_COLOR(dummy, /datum/client_colour/blindness), "Dummy, [status_message], still had the monochrome client color.")
TEST_ASSERT(!HAS_SCREEN_OVERLAY(dummy, /atom/movable/screen/fullscreen/blind), "Dummy, [status_message], still had the blind sceen overlay.")
/**
diff --git a/code/modules/unit_tests/crafting.dm b/code/modules/unit_tests/crafting.dm
index ed4ee4f631c..d6da10b732c 100644
--- a/code/modules/unit_tests/crafting.dm
+++ b/code/modules/unit_tests/crafting.dm
@@ -1,34 +1,37 @@
/**
* Check if a generic atom (because both mobs and the crafter machinery can do it) can potentially craft all recipes,
- * with the exact same types required in the recipe, and also compare the materials of crafted result with one of the same type
- * to ansure they match if the recipe has the CRAFT_ENFORCE_MATERIALS_PARITY flag.
+ * with the exact same types required in the recipe.
+ * Then, unless the recipe has the CRAFT_SKIP_MATERIALS_PARITY flag, compare the materials of the
+ * crafted result with a spawned instance of the same type to ensure that they match.
*/
/datum/unit_test/crafting
+ //The object responsible for using the crafting component
+ var/atom/movable/crafter
+ //The reagent holder responsible for holding reagents that may be used in a recipe.
+ var/obj/item/reagent_containers/cup/bottomless_cup
+ ///The tools that have been spawned so far, to be reused in other recipes as well.
+ var/list/tools = list()
/datum/unit_test/crafting/Run()
- var/atom/movable/crafter = allocate(__IMPLIED_TYPE__)
+ crafter = allocate(__IMPLIED_TYPE__)
- ///Clear the area around our crafting movable of objects that may mess with the unit test
- for(var/atom/movable/trash in (range(1, crafter) - crafter))
- qdel(trash)
+ clear_trash()
var/turf/turf = crafter.loc
var/old_turf_type = turf.type
var/datum/component/personal_crafting/unit_test/craft_comp = crafter.AddComponent(__IMPLIED_TYPE__)
- var/obj/item/reagent_containers/cup/bottomless_cup = allocate_bottomless_cup()
-
- var/list/tools = list()
+ bottomless_cup = allocate_bottomless_cup()
var/list/all_recipes = GLOB.crafting_recipes + GLOB.cooking_recipes
for(var/datum/crafting_recipe/recipe as anything in all_recipes)
if(recipe.non_craftable)
continue
//split into a different proc, so if something fails it's both easier to track and doesn't halt the loop.
- process_recipe(crafter, craft_comp, recipe, bottomless_cup, tools)
+ process_recipe(craft_comp, recipe)
if(QDELETED(bottomless_cup) || bottomless_cup.loc != turf) //The cup itself was used in a recipe, rather than its contents.
bottomless_cup = allocate_bottomless_cup()
- // We have one or two recipes that generate turf (from stacks, like snow walls), which shouldn't be carried between tests
+ // We have one or two recipes that generate turfs (from stacks, like snow walls), which shouldn't be carried between tests
if(turf.type != old_turf_type)
turf.ChangeTurf(old_turf_type)
@@ -39,16 +42,8 @@
bottomless_cup.reagents.maximum_volume = INFINITY
return bottomless_cup
-/datum/unit_test/crafting/proc/process_recipe(
- atom/crafter,
- datum/component/personal_crafting/unit_test/craft_comp,
- datum/crafting_recipe/recipe,
- obj/item/reagent_containers/bottomless_cup,
- list/tools
-)
+/datum/unit_test/crafting/proc/process_recipe(datum/component/personal_crafting/unit_test/craft_comp, datum/crafting_recipe/recipe)
var/turf/turf = crafter.loc
- //Components that have to be deleted later so they don't mess up with other recipes
- var/list/spawned_components = list()
//Warn if uncreatables were found in the recipe if it fails
//If it doesn't fail, then it was already handled, maybe through `unit_test_spawn_extras`
var/list/uncreatables_found
@@ -56,10 +51,10 @@
for(var/spawn_path in recipe.unit_test_spawn_extras)
var/amount = recipe.unit_test_spawn_extras[spawn_path]
if(ispath(spawn_path, /obj/item/stack))
- spawned_components += new spawn_path(turf, /*new_amount =*/ amount, /*merge =*/ FALSE)
+ new spawn_path(turf, /*new_amount =*/ amount, /*merge =*/ FALSE)
continue
for(var/index in 1 to amount)
- spawned_components += new spawn_path(turf)
+ new spawn_path(turf)
for(var/req_path in recipe.reqs) //spawn items and reagents
var/amount = recipe.reqs[req_path]
@@ -74,12 +69,12 @@
continue
if(ispath(req_path, /obj/item/stack)) //it's a stack
- spawned_components += new req_path(turf, /*new_amount =*/ amount, /*merge =*/ FALSE)
+ new req_path(turf, /*new_amount =*/ amount, /*merge =*/ FALSE)
continue
//it's any other item
for(var/iteration in 1 to amount)
- spawned_components += new req_path(turf)
+ new req_path(turf)
for(var/req_path in recipe.chem_catalysts) // spawn catalysts
var/amount = recipe.chem_catalysts[req_path]
@@ -93,7 +88,7 @@
if(req_path in uncreatables)
LAZYADD(uncreatables_found, req_path)
continue
- spawned_components += new req_path(turf)
+ new req_path(turf)
var/list/needed_tools = list()
needed_tools += recipe.tool_behaviors + recipe.tool_paths //either tool_behaviors and tool_paths could be null
@@ -124,48 +119,63 @@
if(istext(result) || isnull(result)) //construct_item() returned a text string telling us why it failed.
TEST_FAIL("[recipe.type] couldn't be crafted during unit test[result || ", result is null for some reason!"]")
if(uncreatables_found)
- TEST_FAIL("The following objects that shouldn't initialize during unit tests were found in [recipe]: [english_list(uncreatables_found)]")
- delete_components(spawned_components)
+ TEST_FAIL("The following objects that shouldn't be instantiated during unit tests were found in [recipe]: [english_list(uncreatables_found)]")
+ clear_trash()
return
- //enforcing materials parity between crafted and spawned for turfs would be more trouble than worth right now
- if(isturf(result))
- delete_components(spawned_components)
+ //enforcing materials parity between crafted and spawned for turfs would be more trouble than worth here
+ if((recipe.crafting_flags & (CRAFT_NO_MATERIALS|CRAFT_SKIP_MATERIALS_PARITY)) || isturf(result))
+ clear_trash()
return
- spawned_components += result
+ var/atom/copycat
+ if(isstack(result))
+ var/obj/item/stack/stack_result = result
+ copycat = new result.type(turf, /*new_amount =*/ stack_result.amount, /*merge =*/ FALSE)
+ else
+ copycat = new result.type(turf)
- if(!(recipe.crafting_flags & CRAFT_ENFORCE_MATERIALS_PARITY))
- delete_components(spawned_components)
- return
-
- var/atom/copycat = new result.type(turf)
- spawned_components += copycat
-
- // SSmaterials caches the combinations so we don't have to run more complex checks
- if(result.custom_materials == copycat.custom_materials)
- delete_components(spawned_components)
- return
if(!result.compare_materials(copycat))
- var/warning = "custom_materials of [result.type] when crafted compared to just spawned don't match"
- var/what_it_should_be = result.get_materials_english_list()
+ var/mats_varname = NAMEOF(result, custom_materials)
+
+ var/warning = "[mats_varname] of [result.type] when crafted compared to only spawned don't match"
+
+ ///Added right between the first half of the warning and the second half.
+ var/other_info = ""
+
+ var/target_var = mats_varname
+ var/list/result_mats = result.custom_materials
+ var/list/copycat_mats = copycat.custom_materials
+ if(isstack(result))
+ var/obj/item/stack/stack_result = result
+ var/obj/item/stack/stack_copy = copycat
+ target_var = NAMEOF(stack_result, mats_per_unit)
+ result_mats = stack_result.mats_per_unit
+ copycat_mats = stack_copy.mats_per_unit
+ other_info = " (size of resulting stack: [stack_result.amount])"
+ var/what_it_should_be = result.transcribe_materials_list(result_mats)
+ var/what_it_is = copycat.transcribe_materials_list(copycat_mats)
//compose a text string containing the syntax and paths to use for editing the custom_materials var
if(result.custom_materials)
- what_it_should_be += " (you can round values a bit)"
- TEST_FAIL("[warning]. custom_materials should be [what_it_should_be]. \
- Otherwise set the requirements_mats_blacklist variable for [recipe] \
- or remove the CRAFT_ENFORCE_MATERIALS_PARITY crafting flag from it")
+ what_it_should_be += " (you can round a bit for values above 100)"
- delete_components(spawned_components)
+ ///This tells you about other ways to deal with the issue, if you can't just change the materials of the object. For example, if there are two different recipes for it.
+ var/add_info = ""
-/**
- * Clear the area of the components that have been spawned as either the requirements of a recipe or its result
- * so they don't mess up with recipes that come after it.
- */
-/datum/unit_test/crafting/proc/delete_components(list/comps)
- for(var/atom/movable/used as anything in comps)
- if(!QDELETED(used))
- qdel(used)
+ if(istype(recipe, /datum/crafting_recipe/stack))
+ add_info = "add the CRAFT_SKIP_MATERIALS_PARITY crafting flag to its stack_recipe datum"
+ else
+ add_info = "set the [NAMEOF(recipe, requirements_mats_blacklist)] or [NAMEOF(recipe, removed_mats)] var of [recipe.type], or add the CRAFT_SKIP_MATERIALS_PARITY crafting flag to it"
+
+ TEST_FAIL("[warning]. should be: [target_var] = [what_it_should_be] (current value: [what_it_is])[other_info]. \
+ Fix that. Otherwise, [add_info]")
+
+ clear_trash()
+
+///Clear the area around our crafting movable of objects that may mess with the unit test
+/datum/unit_test/crafting/proc/clear_trash()
+ for(var/atom/movable/trash in (range(1, crafter) - list(crafter, bottomless_cup)))
+ qdel(trash)
/datum/component/personal_crafting/unit_test
ignored_flags = CRAFT_MUST_BE_LEARNED|CRAFT_ONE_PER_TURF|CRAFT_CHECK_DIRECTION|CRAFT_CHECK_DENSITY|CRAFT_ON_SOLID_GROUND|CRAFT_IGNORE_DO_AFTER
diff --git a/code/modules/unit_tests/flyperson.dm b/code/modules/unit_tests/flyperson.dm
new file mode 100644
index 00000000000..4625e989386
--- /dev/null
+++ b/code/modules/unit_tests/flyperson.dm
@@ -0,0 +1,14 @@
+/// Test that flypeople can get nutriment from vomit decals
+/datum/unit_test/flyperson
+
+/datum/unit_test/flyperson/Run()
+ var/mob/living/carbon/human/consistent/fly = allocate(__IMPLIED_TYPE__)
+ fly.set_species(/datum/species/fly)
+
+ var/obj/effect/decal/cleanable/vomit/gross = allocate(__IMPLIED_TYPE__)
+ gross.create_reagents(10)
+ gross.reagents.add_reagent(/datum/reagent/consumable/nutriment, 10)
+ click_wrapper(fly, gross)
+
+ TEST_ASSERT(QDELETED(gross), "The vomit was not deleted by the flyperson")
+ TEST_ASSERT(fly.has_reagent(/datum/reagent/consumable/nutriment, 10), "The flyperson did not gain the reagents present in the vomit")
diff --git a/code/modules/unit_tests/held_slowdown.dm b/code/modules/unit_tests/held_slowdown.dm
new file mode 100644
index 00000000000..0dbacd51246
--- /dev/null
+++ b/code/modules/unit_tests/held_slowdown.dm
@@ -0,0 +1,11 @@
+/// Tests SLOWS_WHILE_IN_HAND
+/datum/unit_test/held_slowdown
+
+/datum/unit_test/held_slowdown/Run()
+ var/mob/living/carbon/human/consistent/dummy = EASY_ALLOCATE()
+ var/obj/item/restraints/legcuffs/bola/bola = EASY_ALLOCATE()
+ dummy.put_in_hands(bola)
+ TEST_ASSERT(!(bola in dummy.get_equipped_speed_mod_items()), "Bola slows while held, when it shouldn't.")
+
+ bola.item_flags |= SLOWS_WHILE_IN_HAND
+ TEST_ASSERT((bola in dummy.get_equipped_speed_mod_items()), "Bola should slow while held now that it has the SLOWS_WHILE_IN_HAND flag.")
diff --git a/code/modules/unit_tests/mecha_build.dm b/code/modules/unit_tests/mecha_build.dm
new file mode 100644
index 00000000000..812974f12cc
--- /dev/null
+++ b/code/modules/unit_tests/mecha_build.dm
@@ -0,0 +1,39 @@
+/datum/unit_test/mecha_construction_icons
+
+/datum/unit_test/mecha_construction_icons/Run()
+ for(var/chassis_type in subtypesof(/obj/item/mecha_parts/chassis))
+ var/obj/item/mecha_parts/chassis/chassis = allocate(chassis_type)
+ var/datum/component/construction/unordered/mecha_chassis/chassis_comp = chassis.GetComponent(/datum/component/construction/unordered/mecha_chassis)
+ if(isnull(chassis_comp))
+ TEST_FAIL("[chassis_type]: Mecha chassis without a construction component")
+ continue
+ chassis_comp.spawn_result()
+
+ var/datum/component/construction/mecha/construction_comp = chassis.GetComponent(/datum/component/construction/mecha)
+ if(isnull(construction_comp))
+ TEST_FAIL("[chassis_type]: Finished chassis without a mech construction component")
+ continue
+ if(!QDELETED(chassis_comp))
+ TEST_FAIL("[chassis_type]: Chassis construction component was not deleted after applying the mecha construction component")
+ continue
+ if(isnull(construction_comp.base_icon))
+ continue // apparently valid, for construction which don't have *any* icon states
+
+ var/list/all_chassis_icon_states = icon_states_fast(chassis.icon)
+ var/list/step_icon_states = list()
+ for(var/list/step_data as anything in construction_comp.steps)
+ var/icon_state = step_data["icon_state"]
+ if(isnull(icon_state))
+ continue // valid, it just means the step doesn't change the icon
+
+ if(!(icon_state in all_chassis_icon_states))
+ TEST_FAIL("[chassis_type]: Mecha construction step has invalid icon_state '[icon_state]'")
+ continue
+
+ step_icon_states += icon_state
+
+ for(var/icon_state in all_chassis_icon_states - step_icon_states)
+ // little extra logic here to avoid false positives like finding "mech" in "darkmech"
+ if(!findtext("test-[icon_state]", "test-[construction_comp.base_icon]"))
+ continue
+ TEST_FAIL("[chassis_type]: Mecha construction has an unused icon state '[icon_state]'")
diff --git a/code/modules/unit_tests/modsuit.dm b/code/modules/unit_tests/modsuit.dm
index 33aedb9ce49..7e340d86378 100644
--- a/code/modules/unit_tests/modsuit.dm
+++ b/code/modules/unit_tests/modsuit.dm
@@ -20,3 +20,13 @@
TEST_ASSERT(!is_type_in_list(module_to_check, module.incompatible_modules), "[modpath] initial module [module.type] is incompatible with initial module [module_to_check.type]")
modules += module
+/datum/unit_test/mod_storage
+
+/datum/unit_test/mod_storage/Run()
+ var/mob/living/carbon/human/consistent/dummy = allocate(__IMPLIED_TYPE__)
+ dummy.equipOutfit(/datum/outfit/admin)
+ TEST_ASSERT(istype(dummy.back, /obj/item/mod/control/pre_equipped), "Admin outfit did not equip a MODsuit.")
+ // mod storage is owned by the modsuit but the actual items are stored in the module itseslf
+ var/obj/item/mod/module/storage/storage = locate() in dummy.back
+ TEST_ASSERT_NOTNULL(storage, "Admin outfit MODsuit did not have a storage module.")
+ TEST_ASSERT(length(storage.contents) > 0, "Admin outfit's storage module was empty.")
diff --git a/code/modules/unit_tests/move_pulled.dm b/code/modules/unit_tests/move_pulled.dm
new file mode 100644
index 00000000000..29b30d47064
--- /dev/null
+++ b/code/modules/unit_tests/move_pulled.dm
@@ -0,0 +1,46 @@
+/// Test that people can move pulled objects by clicking distant turfs or decals
+/datum/unit_test/move_pulled
+ abstract_type = /datum/unit_test/move_pulled
+
+/datum/unit_test/move_pulled/Run()
+ var/mob/living/carbon/human/consistent/puller = allocate(__IMPLIED_TYPE__)
+ var/obj/structure/closet/crate/crate = allocate(__IMPLIED_TYPE__)
+
+ puller.start_pulling(crate)
+
+ TEST_ASSERT(puller.pulling == crate, "The puller is not pulling the crate.")
+
+ var/atom/first_target = get_first_target(puller)
+ click_wrapper(puller, first_target)
+
+ TEST_ASSERT(crate.loc == run_loc_floor_bottom_left, "The crate should not have moved from clicking on the crate's turf.")
+
+ var/atom/second_target = get_second_target(puller)
+ click_wrapper(puller, second_target)
+
+ TEST_ASSERT(crate.loc != run_loc_floor_bottom_left, "The crate should have moved in the direction of the top right turf.")
+ TEST_ASSERT(crate.loc == get_turf(second_target), "The crate should be located at the northeast of the puller.")
+
+/datum/unit_test/move_pulled/proc/get_first_target(mob/living/puller)
+ CRASH("Unimplemented get_first_target in move_pulled unit test")
+
+/datum/unit_test/move_pulled/proc/get_second_target(mob/living/puller)
+ CRASH("Unimplemented get_second_target in move_pulled unit test")
+
+/// Try to move a pulled object to the turf below us, then to the opposite corner
+/datum/unit_test/move_pulled/to_turf
+
+/datum/unit_test/move_pulled/to_turf/get_first_target(mob/living/puller)
+ return run_loc_floor_bottom_left
+
+/datum/unit_test/move_pulled/to_turf/get_second_target(mob/living/puller)
+ return get_step(puller, NORTHEAST)
+
+/// Try to move a pulled object to a decal below us, then to a decal in the opposite corner
+/datum/unit_test/move_pulled/to_decal
+
+/datum/unit_test/move_pulled/to_decal/get_first_target(mob/living/puller)
+ return allocate(/obj/effect/decal/cleanable/blood, run_loc_floor_bottom_left)
+
+/datum/unit_test/move_pulled/to_decal/get_second_target(mob/living/puller)
+ return allocate(/obj/effect/decal/cleanable/blood, get_step(puller, NORTHEAST))
diff --git a/code/modules/unit_tests/reskin_validation.dm b/code/modules/unit_tests/reskin_validation.dm
new file mode 100644
index 00000000000..85e454058ce
--- /dev/null
+++ b/code/modules/unit_tests/reskin_validation.dm
@@ -0,0 +1,15 @@
+/datum/unit_test/reskin_validation
+
+/datum/unit_test/reskin_validation/Run()
+ var/list/known_names = list()
+ for(var/datum/atom_skin/skin as anything in valid_subtypesof(/datum/atom_skin))
+ if(isnull(skin::preview_name))
+ TEST_FAIL("Reskin [skin] is missing a preview_name.")
+ // preview names are bundled by abstract types
+ else if(known_names["[skin::preview_name]-[skin::abstract_type]"])
+ TEST_FAIL("Reskin [skin] has a duplicate preview_name [skin::preview_name].")
+ else
+ known_names["[skin::preview_name]-[skin::abstract_type]"] = TRUE
+
+ if(skin::new_icon && skin::new_icon_state && !icon_exists(skin::new_icon, skin::new_icon_state))
+ TEST_FAIL("Reskin [skin] has a new_icon_state [skin::new_icon_state] that does not exist in file [skin::new_icon].")
diff --git a/code/modules/unit_tests/screenshots/screenshot_antag_icons_heretic.png b/code/modules/unit_tests/screenshots/screenshot_antag_icons_heretic.png
index 8f8d0524667..1ebecaccc97 100644
Binary files a/code/modules/unit_tests/screenshots/screenshot_antag_icons_heretic.png and b/code/modules/unit_tests/screenshots/screenshot_antag_icons_heretic.png differ
diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm
index b89fff956df..5644e058244 100644
--- a/code/modules/uplink/uplink_items.dm
+++ b/code/modules/uplink/uplink_items.dm
@@ -109,6 +109,9 @@
/// Uses the purchase log, so items purchased that are not visible in the purchase log will not count towards this.
/// However, they won't be purchasable afterwards.
var/lock_other_purchases = FALSE
+ /// A lazylist of typepaths to uplink items relevant to this this item
+ /// EX: a pistol would list its magazines or modifications here
+ var/list/relevant_child_items
/datum/uplink_item/New()
. = ..()
diff --git a/code/modules/uplink/uplink_items/bundle.dm b/code/modules/uplink/uplink_items/bundle.dm
index b6cdc2fd3d6..6247e32e3b8 100644
--- a/code/modules/uplink/uplink_items/bundle.dm
+++ b/code/modules/uplink/uplink_items/bundle.dm
@@ -18,8 +18,7 @@
/datum/uplink_item/bundles_tc/random/purchase(mob/user, datum/uplink_handler/handler, atom/movable/source)
var/list/possible_items = list()
- for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type)
- var/datum/uplink_item/uplink_item = SStraitor.uplink_items_by_type[item_path]
+ for(var/datum/uplink_item/uplink_item as anything in SStraitor.uplink_items)
if(src == uplink_item || !uplink_item.item)
continue
if(!handler.can_purchase_item(user, uplink_item))
@@ -81,8 +80,7 @@
/// generates items that can go inside crates, edit this proc to change what items could go inside your specialized crate
/datum/uplink_item/bundles_tc/surplus/proc/generate_possible_items(mob/user, datum/uplink_handler/handler)
var/list/possible_items = list()
- for(var/datum/uplink_item/item_path as anything in SStraitor.uplink_items_by_type)
- var/datum/uplink_item/uplink_item = SStraitor.uplink_items_by_type[item_path]
+ for(var/datum/uplink_item/uplink_item as anything in SStraitor.uplink_items)
if(src == uplink_item || !uplink_item.item)
continue
if(!handler.check_if_restricted(uplink_item))
diff --git a/code/modules/uplink/uplink_items/dangerous.dm b/code/modules/uplink/uplink_items/dangerous.dm
index 59d5644778d..ad8509a3e7c 100644
--- a/code/modules/uplink/uplink_items/dangerous.dm
+++ b/code/modules/uplink/uplink_items/dangerous.dm
@@ -15,7 +15,10 @@
item = /obj/item/storage/toolbox/guncase/traitor/donksoft
cost = 6
surplus = 10
- purchasable_from = ~UPLINK_SERIOUS_OPS
+ purchasable_from = ~(UPLINK_SERIOUS_OPS | UPLINK_SPY)
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/toydarts,
+ )
/datum/uplink_item/dangerous/pistol
name = "Makarov Pistol Case"
@@ -24,7 +27,13 @@
While not included in the kit, the pistol is compatible with suppressors, which can be purchased separately."
item = /obj/item/storage/toolbox/guncase/traitor
cost = 7
- purchasable_from = ~UPLINK_ALL_SYNDIE_OPS
+ purchasable_from = ~(UPLINK_ALL_SYNDIE_OPS | UPLINK_SPY)
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/pistol,
+ /datum/uplink_item/ammo/pistolap,
+ /datum/uplink_item/ammo/pistolhp,
+ /datum/uplink_item/ammo/pistolfire,
+ )
/datum/uplink_item/dangerous/throwingweapons
name = "Box of Throwing Weapons"
@@ -96,3 +105,9 @@
cost = 13
surplus = 50
purchasable_from = ~UPLINK_ALL_SYNDIE_OPS //only traitors get the original revolver
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/revolver,
+ /datum/uplink_item/ammo_nuclear/basic/revolver,
+ /datum/uplink_item/ammo_nuclear/special/revolver/phasic,
+ /datum/uplink_item/ammo_nuclear/special/revolver/heartseeker,
+ )
diff --git a/code/modules/uplink/uplink_items/nukeops.dm b/code/modules/uplink/uplink_items/nukeops.dm
index e6b7a639ffe..dd18b63eeeb 100644
--- a/code/modules/uplink/uplink_items/nukeops.dm
+++ b/code/modules/uplink/uplink_items/nukeops.dm
@@ -72,6 +72,17 @@
desc = "A fully-loaded 2-round burst fire drum-fed shotgun, complete with a secondary magazine you can hotswap. The gun has a handy label to explain how. \
Compatible with all 12g rounds. Designed for close quarter anti-personnel engagements. Comes with three spare magazines."
item = /obj/item/storage/toolbox/guncase/bulldog
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/basic/buck,
+ /datum/uplink_item/ammo_nuclear/basic/donk,
+ /datum/uplink_item/ammo_nuclear/basic/flechette,
+ /datum/uplink_item/ammo_nuclear/basic/slug,
+ /datum/uplink_item/ammo_nuclear/incendiary/dragon,
+ /datum/uplink_item/ammo_nuclear/special/meteor,
+ /datum/uplink_item/spy_unique/shotgun_ammo,
+ /datum/uplink_item/spy_unique/shotgun_ammo/breacher_slug,
+ /datum/uplink_item/spy_unique/shotgun_ammo/slugs,
+ )
/datum/uplink_item/ammo_nuclear/basic/buck
name = "12g Buckshot Drum (Bulldog)"
@@ -122,6 +133,12 @@
desc = "A small, easily concealable handgun that uses 10mm auto rounds in 8-round magazines and is compatible \
with suppressors. Comes with three spare magazines."
item = /obj/item/storage/toolbox/guncase/clandestine
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/basic/m10mm,
+ /datum/uplink_item/ammo_nuclear/ap/m10mm,
+ /datum/uplink_item/ammo_nuclear/hp/m10mm,
+ /datum/uplink_item/ammo_nuclear/incendiary/m10mm,
+ )
/datum/uplink_item/ammo_nuclear/basic/m10mm
name = "10mm Handgun Magazine (Ansem)"
@@ -164,6 +181,12 @@
desc = "A fully-loaded Scarborough Arms bullpup submachine gun. The C-20r fires .45 rounds with a \
24-round magazine and is compatible with suppressors. Comes with spare three magazines."
item = /obj/item/storage/toolbox/guncase/c20r
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/ap/smg,
+ /datum/uplink_item/ammo_nuclear/basic/smg,
+ /datum/uplink_item/ammo_nuclear/hp/smg,
+ /datum/uplink_item/ammo_nuclear/incendiary/smg,
+ )
/datum/uplink_item/ammo_nuclear/basic/smg
name = ".45 SMG Magazine (C-20r)"
@@ -213,6 +236,12 @@
desc = "Waffle Corp's modernized Syndicate revolver. Fires 7 brutal rounds of .357 Magnum. \
A classic operative weapon, improved for the modern era. Comes with 3 additional speedloaders of .357."
item = /obj/item/storage/toolbox/guncase/revolver
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/revolver,
+ /datum/uplink_item/ammo_nuclear/basic/revolver,
+ /datum/uplink_item/ammo_nuclear/special/revolver/phasic,
+ /datum/uplink_item/ammo_nuclear/special/revolver/heartseeker,
+ )
/datum/uplink_item/ammo_nuclear/basic/revolver
name = ".357 Speed Loader (Revolver)"
@@ -246,6 +275,10 @@
desc = "A reusable rocket propelled grenade launcher preloaded with a low-yield 84mm HE round. \
Guaranteed to take your target out with a bang, or your money back! Comes with a bouquet of additional rockets!"
item = /obj/item/storage/toolbox/guncase/rocketlauncher
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/ap/rocket,
+ /datum/uplink_item/ammo_nuclear/basic/rocket,
+ )
/datum/uplink_item/ammo_nuclear/basic/rocket
name = "84mm HE Rocket Bouquet (Rocket Launcher)"
@@ -272,6 +305,13 @@
desc = "A fully-loaded Scarborough Arms belt-fed machine gun. \
This deadly weapon has a 50-round box chambered for 7.12x82mm ammunition." //SKYRAT EDIT - AUSSEC TO SCARBOROUGH
item = /obj/item/gun/ballistic/automatic/l6_saw
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/ap/machinegun,
+ /datum/uplink_item/ammo_nuclear/basic/machinegun,
+ /datum/uplink_item/ammo_nuclear/hp/machinegun,
+ /datum/uplink_item/ammo_nuclear/incendiary/machinegun,
+ /datum/uplink_item/ammo_nuclear/special/machinegun,
+ )
/datum/uplink_item/ammo_nuclear/basic/machinegun
name = "7mm Box Magazine (L6 SAW)"
@@ -310,6 +350,11 @@
desc = "A fully-loaded, specialized three-round burst carbine that fires .223 ammunition from a 30 round magazine.\
Comes with a 40mm underbarrel grenade launcher. Use secondary-fire to fire the grenade launcher. Also comes with two spare magazines."
item = /obj/item/storage/toolbox/guncase/m90gl
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/basic/carbine,
+ /datum/uplink_item/ammo_nuclear/special/carbine,
+ /datum/uplink_item/ammo_nuclear/basic/carbine/a40mm,
+ )
/datum/uplink_item/ammo_nuclear/basic/carbine
name = ".223 Toploader Magazine (M-90gl)"
@@ -339,6 +384,13 @@
Can be fitted with a suppressor. If anyone asks how that even works, tell them it's Nanotrasen's fault. Comes with \
3 spare magazines; 2 regular magazines and 1 disruptor magazine. Also comes with a suit and tie."
item = /obj/item/storage/briefcase/sniper
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/ap/sniper/penetrator,
+ /datum/uplink_item/ammo_nuclear/basic/sniper,
+ /datum/uplink_item/ammo_nuclear/basic/sniper/disruptor,
+ /datum/uplink_item/ammo_nuclear/incendiary/sniper,
+ /datum/uplink_item/ammo_nuclear/special/sniper/marksman,
+ )
/datum/uplink_item/ammo_nuclear/basic/sniper
name = ".50 BMG Magazine (AMSR)"
@@ -397,6 +449,9 @@
item = /obj/item/gun/ballistic/automatic/smartgun
cost = 2
purchasable_from = UPLINK_SERIOUS_OPS
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/surplus_smg,
+ )
/datum/uplink_item/ammo_nuclear/surplus_smg
name = "Surplus Smart-SMG Magazine (Smartgun)"
@@ -628,6 +683,12 @@
item = /obj/item/storage/box/syndie_kit/cowboy
cost = 18
purchasable_from = UPLINK_SERIOUS_OPS
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/revolver,
+ /datum/uplink_item/ammo_nuclear/basic/revolver,
+ /datum/uplink_item/ammo_nuclear/special/revolver/phasic,
+ /datum/uplink_item/ammo_nuclear/special/revolver/heartseeker,
+ )
// Mech related gear
@@ -649,6 +710,9 @@
for hit-and-run style attacks. Features a scattershot shotgun, armor boosters against melee and ranged attacks, and ion thrusters."
item = /obj/vehicle/sealed/mecha/gygax/dark/loaded
cost = 60
+ relevant_child_items = list(
+ /datum/uplink_item/mech/support_bag/mauler,
+ )
/datum/uplink_item/mech/mauler
name = "Mauler Exosuit"
@@ -656,6 +720,9 @@
and deployable smoke. Comes equipped with an LMG, scattershot carbine, missile rack, and an antiprojectile armor booster."
item = /obj/vehicle/sealed/mecha/marauder/mauler/loaded
cost = 100
+ relevant_child_items = list(
+ /datum/uplink_item/mech/support_bag/mauler,
+ )
// ~~ Mech Support ~~
diff --git a/code/modules/uplink/uplink_items/spy_unique.dm b/code/modules/uplink/uplink_items/spy_unique.dm
index e201b804df5..abc7449e567 100644
--- a/code/modules/uplink/uplink_items/spy_unique.dm
+++ b/code/modules/uplink/uplink_items/spy_unique.dm
@@ -76,6 +76,11 @@
item = /obj/item/gun/ballistic/shotgun/automatic/dual_tube/deadly
cost = SPY_UPPER_COST_THRESHOLD
uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
+ relevant_child_items = list(
+ /datum/uplink_item/spy_unique/shotgun_ammo,
+ /datum/uplink_item/spy_unique/shotgun_ammo/breacher_slug,
+ /datum/uplink_item/spy_unique/shotgun_ammo/slugs,
+ )
/datum/uplink_item/spy_unique/bulldog_shotgun
name = "Bulldog Shotgun"
@@ -83,6 +88,17 @@
item = /obj/item/gun/ballistic/shotgun/bulldog/unrestricted
cost = SPY_UPPER_COST_THRESHOLD
uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/basic/buck,
+ /datum/uplink_item/ammo_nuclear/basic/donk,
+ /datum/uplink_item/ammo_nuclear/basic/flechette,
+ /datum/uplink_item/ammo_nuclear/basic/slug,
+ /datum/uplink_item/ammo_nuclear/incendiary/dragon,
+ /datum/uplink_item/ammo_nuclear/special/meteor,
+ /datum/uplink_item/spy_unique/shotgun_ammo,
+ /datum/uplink_item/spy_unique/shotgun_ammo/breacher_slug,
+ /datum/uplink_item/spy_unique/shotgun_ammo/slugs,
+ )
/datum/uplink_item/spy_unique/ansem_pistol
name = "Ansem Pistol"
@@ -90,6 +106,36 @@
item = /obj/item/gun/ballistic/automatic/pistol/clandestine
cost = SPY_UPPER_COST_THRESHOLD
uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/basic/m10mm,
+ /datum/uplink_item/ammo_nuclear/ap/m10mm,
+ /datum/uplink_item/ammo_nuclear/hp/m10mm,
+ /datum/uplink_item/ammo_nuclear/incendiary/m10mm,
+ )
+
+/datum/uplink_item/spy_unique/makarov_pistol
+ name = "Makarov Pistol"
+ desc = "A Makarov pistol - Reliable and sturdy."
+ item = /obj/item/gun/ballistic/automatic/pistol
+ cost = SPY_LOWER_COST_THRESHOLD
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/pistol,
+ /datum/uplink_item/ammo/pistolap,
+ /datum/uplink_item/ammo/pistolhp,
+ /datum/uplink_item/ammo/pistolfire,
+ )
+
+/datum/uplink_item/spy_unique/toy_pistol
+ name = "Donksoft Riot Pistol"
+ desc = "A Donksoft Riot Pistol - A toy pistol that fires foam darts at higher than normal velocity."
+ item = /obj/item/gun/ballistic/automatic/pistol/toy/riot/clandestine
+ cost = SPY_LOWER_COST_THRESHOLD
+ cost = SPY_LOWER_COST_THRESHOLD
+ uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
+ relevant_child_items = list(
+ /datum/uplink_item/ammo/toydarts,
+ )
/datum/uplink_item/spy_unique/rocket_launcher
name = "Rocket Launcher"
@@ -97,6 +143,10 @@
item = /obj/item/gun/ballistic/rocketlauncher
cost = SPY_UPPER_COST_THRESHOLD - 1 // It's a meme item
uplink_item_flags = SYNDIE_ILLEGAL_TECH | SYNDIE_TRIPS_CONTRABAND
+ relevant_child_items = list(
+ /datum/uplink_item/ammo_nuclear/ap/rocket,
+ /datum/uplink_item/ammo_nuclear/basic/rocket,
+ )
/datum/uplink_item/spy_unique/shotgun_ammo
name = "Box of Buckshot"
diff --git a/code/modules/vehicles/cars/vim.dm b/code/modules/vehicles/cars/vim.dm
index 7ae87590d0e..9bfa5c323ab 100644
--- a/code/modules/vehicles/cars/vim.dm
+++ b/code/modules/vehicles/cars/vim.dm
@@ -19,6 +19,7 @@
light_on = FALSE
engine_sound = 'sound/effects/servostep.ogg'
interaction_flags_mouse_drop = NONE
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 0.55, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 0.7)
///Maximum size of a mob trying to enter the mech
var/maximum_mob_size = MOB_SIZE_SMALL
COOLDOWN_DECLARE(sound_cooldown)
diff --git a/code/modules/vehicles/lavaboat.dm b/code/modules/vehicles/lavaboat.dm
index 3a8d86879bc..f1a6333c8d5 100644
--- a/code/modules/vehicles/lavaboat.dm
+++ b/code/modules/vehicles/lavaboat.dm
@@ -28,6 +28,7 @@
force = 12
w_class = WEIGHT_CLASS_NORMAL
resistance_flags = LAVA_PROOF | FIRE_PROOF
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT * 2)
/datum/crafting_recipe/oar
name = "Goliath Bone Oar"
diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm
index cb4a987e5fe..44690288175 100644
--- a/code/modules/vehicles/mecha/_mecha.dm
+++ b/code/modules/vehicles/mecha/_mecha.dm
@@ -332,6 +332,7 @@
var/mob/living/silicon/ai/unlucky_ai
for(var/mob/living/occupant as anything in occupants)
if(isAI(occupant))
+ //FIXME: Nothiing about this block works
var/mob/living/silicon/ai/ai = occupant
if(!ai.linked_core && !ai.can_shunt) // we probably shouldnt gib AIs with a core or shunting abilities
unlucky_ai = occupant
@@ -549,20 +550,54 @@
. += span_warning("It's missing a capacitor.")
if(!scanmod)
. += span_warning("It's missing a scanning module.")
- if(mecha_flags & IS_ENCLOSED)
- return
- if(mecha_flags & SILICON_PILOT)
- . += span_notice("[src] appears to be piloting itself...")
- else
- for(var/occupante in occupants)
- . += span_notice("You can see [occupante] inside.")
- if(ishuman(user))
- var/mob/living/carbon/human/H = user
- for(var/held_item in H.held_items)
- if(!isgun(held_item))
- continue
- . += span_warning("It looks like you can hit the pilot directly if you target the center or above.")
- break //in case user is holding two guns
+ if(!(mecha_flags & IS_ENCLOSED))
+ if(mecha_flags & SILICON_PILOT)
+ . += span_notice("[src] appears to be piloting itself...")
+ else
+ for(var/occupante in occupants)
+ . += span_notice("You can see [occupante] inside.")
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ for(var/held_item in H.held_items)
+ if(!isgun(held_item))
+ continue
+ . += span_warning("It looks like you can hit the pilot directly if you target the center or above.")
+ break //in case user is holding two guns
+ . += span_notice("It has a tag listing its protection classes.")
+
+/obj/vehicle/sealed/mecha/Topic(href, href_list)
+ . = ..()
+
+ if(href_list["list_armor"])
+ var/list/readout = list()
+
+ var/datum/armor/armor = get_armor()
+ var/added_damage_header = FALSE
+ for(var/damage_key in ARMOR_LIST_DAMAGE())
+ var/rating = armor.get_rating(damage_key)
+ if(!rating)
+ continue
+ if(!added_damage_header)
+ readout += "ARMOR (I-X)"
+ added_damage_header = TRUE
+ readout += "[armor_to_protection_name(damage_key)] [armor_to_protection_class(rating)]"
+
+ var/added_durability_header = FALSE
+ for(var/durability_key in ARMOR_LIST_DURABILITY())
+ var/rating = armor.get_rating(durability_key)
+ if(!rating)
+ continue
+ if(!added_durability_header)
+ readout += "DURABILITY (I-X)"
+ added_durability_header = TRUE
+ readout += "[armor_to_protection_name(durability_key)] [armor_to_protection_class(rating)]"
+
+ readout += "It can withstand temperatures up to [max_temperature]K."
+ if(mecha_flags & IS_ENCLOSED)
+ readout += "It fully encloses its occupants, protecting them from the atmosphere or lack thereof."
+
+ var/formatted_readout = span_notice("PROTECTION CLASSES
[jointext(readout, "\n")]")
+ to_chat(usr, boxed_message(formatted_readout))
/obj/vehicle/sealed/mecha/generate_integrity_message()
var/examine_text = ""
diff --git a/code/modules/vehicles/mecha/equipment/tools/work_tools.dm b/code/modules/vehicles/mecha/equipment/tools/work_tools.dm
index f8ccec3e202..76a23c28159 100644
--- a/code/modules/vehicles/mecha/equipment/tools/work_tools.dm
+++ b/code/modules/vehicles/mecha/equipment/tools/work_tools.dm
@@ -108,8 +108,6 @@
return ..()
if(victim.check_block(chassis, clamp_damage, name, attack_type = OVERWHELMING_ATTACK))
- source.visible_message(span_danger("[chassis] attempts to squeeze [victim] with [src], but the [name] is blocked!"), span_userdanger("You attempt to squeeze [victim] with [src], but [victim.p_They()] managed to block the attempt!"), ignored_mobs = victim)
- to_chat(victim, span_userdanger("You block [chassis]'s attempt to squeeze you with [src]!"))
return ..()
if(iscarbon(victim) && killer_clamp)//meme clamp here
diff --git a/code/modules/vehicles/mecha/mech_melee_attack.dm b/code/modules/vehicles/mecha/mech_melee_attack.dm
index 3aee7762b76..ed16bb8f60d 100644
--- a/code/modules/vehicles/mecha/mech_melee_attack.dm
+++ b/code/modules/vehicles/mecha/mech_melee_attack.dm
@@ -68,9 +68,26 @@
return take_damage(mecha_attacker.force * 3, mecha_attacker.damtype, "melee", FALSE, get_dir(src, mecha_attacker)) // multiplied by 3 so we can hit objs hard but not be overpowered against mobs.
/obj/structure/window/mech_melee_attack(obj/vehicle/sealed/mecha/mecha_attacker, mob/living/user)
+ if(!user.combat_mode)
+ return
if(!can_be_reached())
return
- return ..()
+
+ mecha_attacker.do_attack_animation(src)
+ switch(mecha_attacker.damtype)
+ if(BRUTE)
+ playsound(src, mecha_attacker.brute_attack_sound, 50, TRUE)
+ if(BURN)
+ playsound(src, mecha_attacker.burn_attack_sound, 50, TRUE)
+ else
+ return
+ mecha_attacker.visible_message(span_danger("[mecha_attacker] smashes [src]!"), span_danger("You smash [src]!"), null, COMBAT_MESSAGE_RANGE)
+ // Additionally destroy any grilles
+ for(var/obj/structure/grille/grille in src.loc)
+ if(istype(grille))
+ grille.take_damage(mecha_attacker.force * 10, mecha_attacker.damtype, "melee", FALSE, get_dir(src, mecha_attacker))
+ ..()
+ return take_damage(mecha_attacker.force * 100, mecha_attacker.damtype, "melee", FALSE, get_dir(src, mecha_attacker))
/obj/vehicle/mech_melee_attack(obj/vehicle/sealed/mecha/mecha_attacker, mob/living/user)
if(!user.combat_mode)
@@ -100,9 +117,14 @@
if(!isnull(user) && HAS_TRAIT(user, TRAIT_PACIFISM))
to_chat(user, span_warning("You don't want to harm other living beings!"))
return
+
mecha_attacker.do_attack_animation(src)
if(mecha_attacker.damtype == BRUTE)
step_away(src, mecha_attacker, 15)
+
+ if(check_block(mecha_attacker, mecha_attacker.force * 3, "the [mecha_attacker.attack_verbs[1]]", attack_type = OVERWHELMING_ATTACK))
+ return
+
switch(mecha_attacker.damtype)
if(BRUTE)
if(mecha_attacker.force > 35) // durand and other heavy mechas
diff --git a/code/modules/vehicles/mecha/mecha_ai_interaction.dm b/code/modules/vehicles/mecha/mecha_ai_interaction.dm
index 02bc2c89953..7a13e423bb4 100644
--- a/code/modules/vehicles/mecha/mecha_ai_interaction.dm
+++ b/code/modules/vehicles/mecha/mecha_ai_interaction.dm
@@ -78,10 +78,7 @@
return
if(AI_MECH_HACK) //Called by AIs on the mech
- var/obj/structure/ai_core/deactivated/deactivated_core = new(AI.loc, FALSE, FALSE, AI)
- AI.linked_core = deactivated_core
- AI.linked_core.RegisterSignal(deactivated_core, COMSIG_ATOM_DESTRUCTION, TYPE_PROC_REF(/obj/structure/ai_core/deactivated, disable_doomsday)) //Protect that core! The structure goes bye-bye when we re-shunt back in so no need for cleanup.
- AI.linked_core.remote_ai = AI
+ AI.create_core_link(new /obj/structure/ai_core(AI.loc, CORE_STATE_FINISHED, AI.make_mmi()))
if(AI.can_dominate_mechs && LAZYLEN(occupants)) //Oh, I am sorry, were you using that?
to_chat(AI, span_warning("Occupants detected! Forced ejection initiated!"))
to_chat(occupants, span_danger("You have been forcibly ejected!"))
diff --git a/code/modules/vehicles/mecha/mecha_construction_paths.dm b/code/modules/vehicles/mecha/mecha_construction_paths.dm
index 35e394f5062..631d61c7ba6 100644
--- a/code/modules/vehicles/mecha/mecha_construction_paths.dm
+++ b/code/modules/vehicles/mecha/mecha_construction_paths.dm
@@ -1,7 +1,4 @@
-
-////////////////////////////////
-///// Construction datums //////
-////////////////////////////////
+/// Mecha construction
/datum/component/construction/mecha
var/base_icon
@@ -42,17 +39,35 @@
// Override if the mech needs an entirely custom process (See HONK mech)
// Otherwise override specific steps as needed (Ripley, Clarke, Phazon)
/datum/component/construction/mecha/proc/get_steps()
- return get_frame_steps() + get_circuit_steps() + (circuit_weapon ? get_circuit_weapon_steps() : list()) + get_stockpart_steps() + get_inner_plating_steps() + get_outer_plating_steps()
+ var/list/all_steps = \
+ get_frame_steps() + \
+ get_circuit_steps() + \
+ get_circuit_weapon_steps() + \
+ get_stockpart_steps() + \
+ get_inner_plating_steps() + \
+ get_outer_plating_steps()
-/datum/component/construction/mecha/update_parent(step_index)
- steps = get_steps()
- ..()
- // By default, each step in mech construction has a single icon_state:
- // "[base_icon][index - 1]"
- // For example, Ripley's step 1 icon_state is "ripley0"
- var/atom/parent_atom = parent
- if(!steps[index]["icon_state"] && base_icon)
- parent_atom.icon_state = "[base_icon][index - 1]"
+ // If you don't set a construction icon state, one will automatically be assigned
+ // based on the index of the step in the step list
+ //
+ // If you do set a custom icon state, it will not be overridden, but the state will still increment.
+ //
+ // You can use skip_state to prevent a step from increasing the index,
+ // useful for steps which don't affect the mech's appearance at all
+ // or for steps which have custom icon states that don't follow the normal pattern.
+ var/state = 0
+ for(var/list/step_data as anything in all_steps)
+ if(step_data["skip_state"])
+ continue
+
+ step_data["icon_state"] ||= "[base_icon][state]"
+ state += 1
+
+ return all_steps
+
+/datum/component/construction/mecha/Initialize()
+ steps ||= get_steps()
+ return ..()
/datum/component/construction/unordered/mecha_chassis/custom_action(obj/item/I, mob/living/user, typepath)
. = user.transferItemToLoc(I, parent)
@@ -67,7 +82,7 @@
parent_atom.icon = 'icons/mob/rideables/mech_construction.dmi'
parent_atom.set_density(TRUE)
parent_atom.cut_overlays()
- ..()
+ return ..()
// Default proc for the first steps of mech construction.
/datum/component/construction/mecha/proc/get_frame_steps()
@@ -82,7 +97,7 @@
"back_key" = TOOL_WRENCH,
"desc" = "The hydraulic systems are connected, and can be activated with a screwdriver.",
"forward_message" = "activated the hydraulic systems",
- "backward_message" = "disconnected the hydraulic systems"
+ "backward_message" = "disconnected the hydraulic systems",
),
list(
"key" = /obj/item/stack/cable_coil,
@@ -90,14 +105,14 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "The hydraulic systems are active, and the frame can be wired.",
"forward_message" = "added wiring",
- "backward_message" = "deactivated the hydraulic systems"
+ "backward_message" = "deactivated the hydraulic systems",
),
list(
"key" = TOOL_WIRECUTTER,
"back_key" = TOOL_SCREWDRIVER,
"desc" = "The wiring is added, and can be adjusted with wirecutters.",
"forward_message" = "adjusted wiring",
- "backward_message" = "removed wiring"
+ "backward_message" = "removed wiring",
)
)
@@ -111,14 +126,14 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "The wiring is adjusted, and the central control module slot has opened.",
"forward_message" = "added central control module",
- "backward_message" = "disconnected wiring"
+ "backward_message" = "disconnected wiring",
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
"desc" = "Central control module is installed, and can be screwed into place.",
"forward_message" = "secured central control module",
- "backward_message" = "removed central control module"
+ "backward_message" = "removed central control module",
),
list(
"key" = circuit_periph,
@@ -126,38 +141,42 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "Central control module is secured, and the peripheral control module slot has opened.",
"forward_message" = "added peripheral control module",
- "backward_message" = "unsecured central control module"
+ "backward_message" = "unsecured central control module",
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
"desc" = "Peripheral control module is installed, and can be screwed into place.",
"forward_message" = "secured peripheral control module",
- "backward_message" = "removed peripheral control module"
+ "backward_message" = "removed peripheral control module",
)
)
// Default proc for weapon circuitboard steps
// Used by combat mechs
/datum/component/construction/mecha/proc/get_circuit_weapon_steps()
+ if(!circuit_weapon)
+ return list()
+
return list(
list(
"key" = circuit_weapon,
"action" = ITEM_DELETE,
"back_key" = TOOL_SCREWDRIVER,
- "desc" = "Peripherals control module is secured, and the weapon control module slot has opened.",
+ "desc" = "Peripherals control module is secured, and the weapon control module slot has opened.",
"forward_message" = "added weapon control module",
- "backward_message" = "unsecured peripheral control module"
+ "backward_message" = "unsecured peripheral control module",
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
"desc" = "Weapon control module is installed, and can be screwed into place.",
"forward_message" = "secured weapon control module",
- "backward_message" = "removed weapon control module"
+ "backward_message" = "removed weapon control module",
)
)
+
// Default proc for stock part installation
// Third set of steps by default
/datum/component/construction/mecha/proc/get_stockpart_steps()
@@ -171,14 +190,14 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = prevstep_text,
"forward_message" = "added scanning module",
- "backward_message" = backward_text
+ "backward_message" = backward_text,
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
"desc" = "Scanning module is installed, and can be screwed into place.",
"forward_message" = "secured scanning module",
- "backward_message" = "removed scanning module"
+ "backward_message" = "removed scanning module",
),
list(
"key" = /obj/item/stock_parts/capacitor,
@@ -186,14 +205,14 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "Scanning module is secured, the capacitor can be added.",
"forward_message" = "added capacitor",
- "backward_message" = "unscecured scanning module"
+ "backward_message" = "unscecured scanning module",
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
"desc" = "Capacitor is installed, and can be screwed into place.",
"forward_message" = "secured capacitor",
- "backward_message" = "removed capacitor"
+ "backward_message" = "removed capacitor",
),
list(
"key" = /obj/item/stock_parts/servo,
@@ -201,14 +220,14 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "Scanning module is secured, the servo can be added.",
"forward_message" = "added servo",
- "backward_message" = "unsecured capacitor"
+ "backward_message" = "unsecured capacitor",
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
"desc" = "Servo is installed, and can be screwed into place.",
"forward_message" = "secured servo",
- "backward_message" = "removed servo"
+ "backward_message" = "removed servo",
),
list(
"key" = /obj/item/stock_parts/power_store/cell,
@@ -216,14 +235,14 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "Servo is secured, and the power cell can be added.",
"forward_message" = "added power cell",
- "backward_message" = "unsecured servo"
+ "backward_message" = "unsecured servo",
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
"desc" = "The power cell is installed, and can be screwed into place.",
"forward_message" = "secured power cell",
- "backward_message" = "removed power cell"
+ "backward_message" = "removed power cell",
)
)
@@ -239,7 +258,7 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "The power cell is secured, [inner_plating_amount] sheets of [initial(inner_plating.name)] can be used as inner plating.",
"forward_message" = "installed internal armor layer",
- "backward_message" = "unsecured power cell"
+ "backward_message" = "unsecured power cell",
)
)
else
@@ -250,7 +269,7 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "The power cell is secured, [initial(inner_plating.name)] can be used as inner plating.",
"forward_message" = "installed internal armor layer",
- "backward_message" = "unsecured power cell"
+ "backward_message" = "unsecured power cell",
)
)
@@ -260,14 +279,14 @@
"back_key" = TOOL_CROWBAR,
"desc" = "Inner plating is installed, and can be wrenched into place.",
"forward_message" = "secured internal armor layer",
- "backward_message" = "pried off internal armor layer"
+ "backward_message" = "pried off internal armor layer",
),
list(
"key" = TOOL_WELDER,
"back_key" = TOOL_WRENCH,
"desc" = "Inner plating is wrenched, and can be welded.",
"forward_message" = "welded internal armor layer",
- "backward_message" = "unfastened internal armor layer"
+ "backward_message" = "unfastened internal armor layer",
)
)
@@ -283,7 +302,7 @@
"back_key" = TOOL_WELDER,
"desc" = "Inner plating is welded, [outer_plating_amount] sheets of [initial(outer_plating.name)] can be used as external armor.",
"forward_message" = "installed external armor layer",
- "backward_message" = "cut off internal armor layer"
+ "backward_message" = "cut off internal armor layer",
)
)
else
@@ -294,7 +313,7 @@
"back_key" = TOOL_WELDER,
"desc" = "Inner plating is welded, [initial(outer_plating.name)] can be used as external armor.",
"forward_message" = "installed external armor layer",
- "backward_message" = "cut off internal armor layer"
+ "backward_message" = "cut off internal armor layer",
)
)
@@ -304,14 +323,14 @@
"back_key" = TOOL_CROWBAR,
"desc" = "External armor is installed, and can be wrenched into place.",
"forward_message" = "secured external armor layer",
- "backward_message" = "pried off external armor layer"
+ "backward_message" = "pried off external armor layer",
),
list(
"key" = TOOL_WELDER,
"back_key" = TOOL_WRENCH,
"desc" = "External armor is wrenched, and can be welded.",
"forward_message" = "welded external armor layer",
- "backward_message" = "unfastened external armor layer"
+ "backward_message" = "unfastened external armor layer",
)
)
@@ -322,8 +341,15 @@
if(diff == FORWARD && steps[index]["forward_message"])
user.balloon_alert_to_viewers(steps[index]["forward_message"])
+ var/list/next_step = index == steps.len ? null : steps[index + 1]
+ if(next_step?["desc"])
+ to_chat(user, span_smallnoticeital(next_step["desc"]))
+
else if(steps[index]["backward_message"])
user.balloon_alert_to_viewers(steps[index]["backward_message"])
+ var/list/last_step = index <= 1 ? null : steps[index - 1]
+ if(last_step?["desc"])
+ to_chat(user, span_smallnoticeital(last_step["desc"]))
return TRUE
@@ -352,22 +378,22 @@
outer_plating_amount = 10
/datum/component/construction/mecha/ripley/get_outer_plating_steps()
+ // we yoink the first step of adding plating and modify the flavor a bit
+ var/list/first_step = ..()[1]
+ first_step["desc"] = "Plating is welded, and 10 rods can be used to install the cockpit."
+ first_step["forward_message"] = "installed cockpit"
+ first_step["backward_message"] = "cut off plating"
+
+ // then we add our own second step for welding the cockpit in place
return list(
- list(
- "key" = /obj/item/stack/rods,
- "amount" = 10,
- "back_key" = TOOL_WELDER,
- "desc" = "Outer plating is welded, and 10 rods can be used to install the cockpit.",
- "forward_message" = "installed cockpit",
- "backward_message" = "cut off outer armor layer"
- ),
+ first_step,
list(
"key" = TOOL_WELDER,
"back_key" = TOOL_WIRECUTTER,
"desc" = "Cockpit wire screen is installed, and can be welded.",
"forward_message" = "welded cockpit",
- "backward_message" = "cut off cockpit"
- ),
+ "backward_message" = "cut off cockpit",
+ )
)
//GYGAX
@@ -584,7 +610,7 @@
var/atom/parent_atom = parent
parent_atom.icon = 'icons/mob/rideables/mech_construct.dmi'
parent_atom.icon_state = "honker_chassis"
- ..()
+ return ..()
/datum/component/construction/mecha/honker/custom_action(obj/item/I, mob/living/user, diff)
if(istype(I, /obj/item/bikehorn))
@@ -646,6 +672,18 @@
outer_plating = /obj/item/mecha_parts/part/phazon_armor
outer_plating_amount = 1
+ var/obj/item/required_core = /obj/item/assembly/signaler/anomaly/ectoplasm
+
+/datum/component/construction/mecha/phazon/custom_action(obj/item/I, mob/living/user, diff)
+ if(!..())
+ return FALSE
+
+ if(istype(I, /obj/item/assembly/signaler/anomaly) && !istype(I, required_core))
+ to_chat(user, span_warning("The anomaly core socket only accepts \a [initial(required_core.name)]!"))
+ return FALSE
+
+ return TRUE
+
/datum/component/construction/mecha/phazon/get_stockpart_steps()
return list(
list(
@@ -654,14 +692,14 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "Weapon control module is secured, and the scanning module can be added.",
"forward_message" = "added scanning module",
- "backward_message" = "unsecured weapon control module"
+ "backward_message" = "unsecured weapon control module",
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
"desc" = "Scanning module is installed, and can be screwed into place.",
"forward_message" = "secured scanning module",
- "backward_message" = "removed scanning module"
+ "backward_message" = "removed scanning module",
),
list(
"key" = /obj/item/stock_parts/capacitor,
@@ -669,14 +707,14 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "Scanning module is secured, and the capacitor can be added.",
"forward_message" = "added capacitor",
- "backward_message" = "unsecured scanning module"
+ "backward_message" = "unsecured scanning module",
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
"desc" = "Capacitor is installed, and can be screwed into place.",
"forward_message" = "secured capacitor",
- "backward_message" = "removed capacitor"
+ "backward_message" = "removed capacitor",
),
list(
"key" = /obj/item/stock_parts/servo,
@@ -684,14 +722,14 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "Capacitor is secured, the servo can be added.",
"forward_message" = "added servo",
- "backward_message" = "unsecured capacitor"
+ "backward_message" = "unsecured capacitor",
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
"desc" = "Servo is installed, and can be screwed into place.",
"forward_message" = "secured servo",
- "backward_message" = "removed servo"
+ "backward_message" = "removed servo",
),
list(
"key" = /obj/item/stack/ore/bluespace_crystal,
@@ -699,7 +737,7 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "Servo is secured, and the bluespace crystal can be added.",
"forward_message" = "added bluespace crystal",
- "backward_message" = "unsecured servo"
+ "backward_message" = "unsecured servo",
),
list(
"key" = /obj/item/stack/cable_coil,
@@ -707,14 +745,16 @@
"back_key" = TOOL_CROWBAR,
"desc" = "The bluespace crystal is installed, and can be wired to the mech systems.",
"forward_message" = "connected bluespace crystal",
- "backward_message" = "removed bluespace crystal"
+ "backward_message" = "removed bluespace crystal",
+ "icon_state" = "phazon19",
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_WIRECUTTER,
"desc" = "The bluespace crystal is connected, and the system can be engaged with a screwdriver.",
"forward_message" = "engaded bluespace crystal",
- "backward_message" = "disconnected bluespace crystal"
+ "backward_message" = "disconnected bluespace crystal",
+ "icon_state" = "phazon20",
),
list(
"key" = /obj/item/stock_parts/power_store/cell,
@@ -722,52 +762,29 @@
"back_key" = TOOL_SCREWDRIVER,
"desc" = "The bluespace crystal is engaged, and the power cell can be added.",
"forward_message" = "added power cell",
- "backward_message" = "disengaged bluespace crystal"
+ "backward_message" = "disengaged bluespace crystal",
+ "icon_state" = "phazon21",
),
list(
"key" = TOOL_SCREWDRIVER,
"back_key" = TOOL_CROWBAR,
- "desc" = "The power cell is installed, and can be screwed into place.",,
+ "desc" = "The power cell is installed, and can be screwed into place.",
"forward_message" = "secured power cell",
"backward_message" = "removed power cell",
- "icon_state" = "phazon19"
- // This is the point where a step icon is skipped, so "icon_state" had to be set manually starting from here.
+ "icon_state" = "phazon21",
)
)
/datum/component/construction/mecha/phazon/get_outer_plating_steps()
- return list(
+ return ..() + list(
list(
- "key" = outer_plating,
- "amount" = 1,
+ "key" = required_core,
"action" = ITEM_DELETE,
"back_key" = TOOL_WELDER,
- "desc" = "Internal armor is welded, [initial(outer_plating.name)] can be used as external armor.",
- "forward_message" = "added external armor layer",
- "backward_message" = "cut off internal armor layer"
- ),
- list(
- "key" = TOOL_WRENCH,
- "back_key" = TOOL_CROWBAR,
- "desc" = "External armor is installed, and can be wrenched into place.",
- "forward_message" = "secured external armor layer",
- "backward_message" = "pried off external armor"
- ),
- list(
- "key" = TOOL_WELDER,
- "back_key" = TOOL_WRENCH,
- "desc" = "External armor is wrenched, and can be welded.",
- "forward_message" = "welded external armor",
- "backward_message" = "unfastened external armor layer"
- ),
- list(
- "key" = /obj/item/assembly/signaler/anomaly/ectoplasm,
- "action" = ITEM_DELETE,
- "back_key" = TOOL_WELDER,
- "desc" = "The external armor is welded, and the ectoplasm anomaly core socket is open.",
- "icon_state" = "phazon26",
- "forward_message" = "inserted ectoplasm anomaly core",
- "backward_message" = "cut off external armor"
+ "desc" = "The external armor is welded, and the [initial(required_core.name)] socket is open.",
+ "forward_message" = "inserted [initial(required_core.name)]",
+ "backward_message" = "cut off external armor",
+ "skip_state" = TRUE,
)
)
diff --git a/code/modules/vehicles/mecha/mecha_mob_interaction.dm b/code/modules/vehicles/mecha/mecha_mob_interaction.dm
index f5bf2fed81c..57250ecb437 100644
--- a/code/modules/vehicles/mecha/mecha_mob_interaction.dm
+++ b/code/modules/vehicles/mecha/mecha_mob_interaction.dm
@@ -111,7 +111,9 @@
brain_mob.log_message("was put into [src] by [key_name(user)]", LOG_GAME, log_globally = FALSE)
return TRUE
+
/obj/vehicle/sealed/mecha/mob_exit(mob/M, silent = FALSE, randomstep = FALSE, forced = FALSE)
+ // FIXME: this code is really bad (shocker). Needs a refactor
var/atom/movable/mob_container
var/turf/newloc = get_turf(src)
if(ishuman(M))
@@ -147,9 +149,7 @@
if(!forced && !silent)
to_chat(AI, span_notice("Returning to core..."))
mecha_flags &= ~SILICON_PILOT
- newloc = get_turf(AI.linked_core)
- qdel(AI.linked_core)
- AI.forceMove(newloc)
+ AI.resolve_core_link()
if(forced)
to_chat(AI, span_danger("ZZUZULU.ERR--ERRR-NEUROLOG-- PERCEP--- DIST-B**@"))
for(var/count in 1 to 5)
diff --git a/code/modules/vehicles/mecha/mecha_parts.dm b/code/modules/vehicles/mecha/mecha_parts.dm
index e0a31c762da..e04d7bc2d14 100644
--- a/code/modules/vehicles/mecha/mecha_parts.dm
+++ b/code/modules/vehicles/mecha/mecha_parts.dm
@@ -255,12 +255,6 @@
name = "\improper Phazon chassis"
construct_type = /datum/component/construction/unordered/mecha_chassis/phazon
-/obj/item/mecha_parts/chassis/phazon/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
- if(istype(tool, /obj/item/assembly/signaler/anomaly) && !istype(tool, /obj/item/assembly/signaler/anomaly/ectoplasm))
- to_chat(user, "The anomaly core socket only accepts ectoplasm anomaly cores!")
- return ITEM_INTERACT_BLOCKING
- return ..()
-
/obj/item/mecha_parts/part/phazon_torso
name="\improper Phazon torso"
desc="A Phazon torso part. The socket for the ectoplasmic core that powers the exosuit's unique phase drives is located in the middle."
diff --git a/code/modules/vehicles/mecha/mecha_wreckage.dm b/code/modules/vehicles/mecha/mecha_wreckage.dm
index dc0414e8606..485c6ee8281 100644
--- a/code/modules/vehicles/mecha/mecha_wreckage.dm
+++ b/code/modules/vehicles/mecha/mecha_wreckage.dm
@@ -42,6 +42,11 @@
if(AI)
QDEL_NULL(AI)
QDEL_LIST(crowbar_salvage)
+ src.visible_message(span_danger("[src]'s superstructure folds in on itself, collapsing into a heap of unsalvageable scrap!"))
+ playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE)
+ for(var/mob/living/witness in range(2, src))
+ shake_camera(witness, 2, 1)
+ new /obj/effect/decal/cleanable/blood/gibs/robot_debris(get_turf(src))
return ..()
/obj/structure/mecha_wreckage/examine(mob/user)
diff --git a/code/modules/vehicles/motorized_wheelchair.dm b/code/modules/vehicles/motorized_wheelchair.dm
index ac8afa1a79a..3e85b2263ca 100644
--- a/code/modules/vehicles/motorized_wheelchair.dm
+++ b/code/modules/vehicles/motorized_wheelchair.dm
@@ -6,6 +6,7 @@
foldabletype = null
max_integrity = 150
ttv_icon = "motor_chair_ttv"
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 14.5, /datum/material/glass = SMALL_MATERIAL_AMOUNT)
///How "fast" the wheelchair goes only affects ramming
var/speed = 2
///Self explanatory, ratio of how much power we use
diff --git a/code/modules/vehicles/scooter.dm b/code/modules/vehicles/scooter.dm
index 9c15c7382dc..d3f1ff5ba10 100644
--- a/code/modules/vehicles/scooter.dm
+++ b/code/modules/vehicles/scooter.dm
@@ -3,6 +3,7 @@
desc = "A fun way to get around."
icon_state = "scooter"
are_legs_exposed = TRUE
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 11)
/obj/vehicle/ridden/scooter/Initialize(mapload)
. = ..()
@@ -39,6 +40,7 @@
desc = "An old, battered skateboard. It's still rideable, but probably unsafe."
icon_state = "skateboard"
density = FALSE
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 10)
///Sparks datum for when we grind on tables
var/datum/effect_system/spark_spread/sparks
///Whether the board is currently grinding
@@ -240,6 +242,7 @@
icon = 'icons/mob/rideables/vehicles.dmi'
icon_state = "scooter_frame"
w_class = WEIGHT_CLASS_NORMAL
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5)
/obj/item/scooter_frame/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
if(!istype(I, /obj/item/stack/sheet/iron))
diff --git a/code/modules/vehicles/vehicle_key.dm b/code/modules/vehicles/vehicle_key.dm
index 69097acc256..4f9085a2cb8 100644
--- a/code/modules/vehicles/vehicle_key.dm
+++ b/code/modules/vehicles/vehicle_key.dm
@@ -93,3 +93,4 @@
attack_verb_simple = list("flog", "whip", "lash", "discipline")
hitsound = 'sound/items/weapons/whip.ogg'
slot_flags = ITEM_SLOT_BELT
+ custom_materials = list(/datum/material/bone = SHEET_MATERIAL_AMOUNT)
diff --git a/code/modules/vehicles/wheelchair.dm b/code/modules/vehicles/wheelchair.dm
index 423baf8627a..3495d95a87a 100644
--- a/code/modules/vehicles/wheelchair.dm
+++ b/code/modules/vehicles/wheelchair.dm
@@ -8,6 +8,7 @@
armor_type = /datum/armor/ridden_wheelchair
density = FALSE
interaction_flags_mouse_drop = ALLOW_RESTING
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5)
/// Run speed delay is multiplied with this for vehicle move delay.
var/delay_multiplier = 6.7
@@ -154,7 +155,7 @@
overlay_icon = "gold_wheelchair_overlay"
max_integrity = 200
armor_type = /datum/armor/wheelchair_gold
- custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT*5)
+ custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT * 5)
foldabletype = /obj/item/wheelchair/gold
/// Handheld wheelchair item
@@ -168,7 +169,7 @@
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
w_class = WEIGHT_CLASS_NORMAL
force = 8 //Force is same as a chair
- custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*5)
+ custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 5)
/// The wheelchair vehicle type we create when we unfold this chair
var/unfolded_type = /obj/vehicle/ridden/wheelchair
@@ -197,7 +198,7 @@
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
force = 10
- custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT*5)
+ custom_materials = list(/datum/material/gold = SHEET_MATERIAL_AMOUNT * 5)
unfolded_type = /obj/vehicle/ridden/wheelchair/gold
/datum/armor/wheelchair_gold
diff --git a/code/modules/vending/custom.dm b/code/modules/vending/custom.dm
index 27b760d887c..78a1f811fe9 100644
--- a/code/modules/vending/custom.dm
+++ b/code/modules/vending/custom.dm
@@ -292,10 +292,10 @@
payee.adjust_money(-dispensed_item.custom_price, , "Vending: [dispensed_item]")
linked_account.adjust_money(dispensed_item.custom_price, "Vending: [dispensed_item] Bought")
linked_account.bank_card_talk("[payee.account_holder] made a [dispensed_item.custom_price] \
- cr purchase at your custom vendor.")
+ [MONEY_SYMBOL] purchase at your custom vendor.")
/// Log the transaction
SSblackbox.record_feedback("amount", "vending_spent", dispensed_item.custom_price)
- log_econ("[dispensed_item.custom_price] credits were spent on [src] buying a \
+ log_econ("[dispensed_item.custom_price] [MONEY_NAME] were spent on [src] buying a \
[dispensed_item] by [payee.account_holder], owned by [linked_account.account_holder].")
/// Make an alert
var/ref = REF(user)
diff --git a/code/modules/vending/sustenance.dm b/code/modules/vending/sustenance.dm
index 05f71b4eb6b..71313d74140 100644
--- a/code/modules/vending/sustenance.dm
+++ b/code/modules/vending/sustenance.dm
@@ -50,7 +50,7 @@
icon_state = "sustenance_labor"
all_products_free = FALSE
displayed_currency_icon = "digging"
- displayed_currency_name = " LP"
+ displayed_currency_name = "LP"
allow_custom = FALSE
/obj/machinery/vending/sustenance/labor_camp/proceed_payment(obj/item/card/id/advanced/prisoner/paying_scum_id, mob/living/mob_paying, datum/data/vending_product/product_to_vend, price_to_use)
diff --git a/code/modules/vending/vendor/_vending.dm b/code/modules/vending/vendor/_vending.dm
index 3df88989705..139a50f62e9 100644
--- a/code/modules/vending/vendor/_vending.dm
+++ b/code/modules/vending/vendor/_vending.dm
@@ -153,7 +153,7 @@
///fontawesome icon name to use in to display the user's balance in the vendor UI
var/displayed_currency_icon = "coins"
///String of the used currency to display in the vendor UI
- var/displayed_currency_name = " cr"
+ var/displayed_currency_name = MONEY_SYMBOL
///Whether our age check is currently functional
var/age_restrictions = TRUE
/// How many credits does this vending machine have? 20% of all sales go to this pool, and are given freely when the machine is restocked, or successfully tilted. Lost on deconstruction.
@@ -330,7 +330,7 @@
return CONTEXTUAL_SCREENTIP_SET
if(panel_open && istype(held_item, refill_canister))
- context[SCREENTIP_CONTEXT_LMB] = "Restock vending machine[credits_contained ? " and collect credits" : null]"
+ context[SCREENTIP_CONTEXT_LMB] = "Restock vending machine[credits_contained ? " and collect [MONEY_NAME]" : null]"
return CONTEXTUAL_SCREENTIP_SET
/**
@@ -370,9 +370,9 @@
else
. += span_notice("\The [src] is fully stocked.")
if(credits_contained < CREDITS_DUMP_THRESHOLD && credits_contained > 0)
- . += span_notice("It should have a handfull of credits stored based on the missing items.")
+ . += span_notice("It should have a handfull of [MONEY_NAME] stored based on the missing items.")
else if (credits_contained > PAYCHECK_CREW)
- . += span_notice("It should have at least a full paycheck worth of credits inside!")
+ . += span_notice("It should have at least a full paycheck worth of [MONEY_NAME] inside!")
/obj/machinery/vending/update_appearance(updates = ALL)
. = ..()
diff --git a/code/modules/vending/vendor/interaction.dm b/code/modules/vending/vendor/interaction.dm
index 38b3be4ea99..3889625f05f 100644
--- a/code/modules/vending/vendor/interaction.dm
+++ b/code/modules/vending/vendor/interaction.dm
@@ -158,7 +158,7 @@
to_chat(user, span_warning("There's nothing to restock!"))
return
- to_chat(user, span_notice("You loaded [restocked] items in [src][credits_contained > 0 ? ", and are rewarded [credits_contained] credits." : "."]"))
+ to_chat(user, span_notice("You loaded [restocked] items in [src][credits_contained > 0 ? ", and are rewarded [credits_contained] [MONEY_NAME]." : "."]"))
var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
cargo_account.adjust_money(round(credits_contained * 0.5), "Vending: Restock")
var/obj/item/holochip/payday = new(src, credits_contained)
@@ -196,7 +196,7 @@
/obj/machinery/vending/proc/freebie(freebies)
PRIVATE_PROC(TRUE)
- visible_message(span_notice("[src] yields [freebies > 1 ? "several free goodies" : "a free goody"][credits_contained > 0 ? " and some credits" : ""]!"))
+ visible_message(span_notice("[src] yields [freebies > 1 ? "several free goodies" : "a free goody"][credits_contained > 0 ? " and some [MONEY_NAME]" : ""]!"))
for(var/i in 1 to freebies)
playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
diff --git a/code/modules/vending/vendor/inventory.dm b/code/modules/vending/vendor/inventory.dm
index ded975a2430..68b028d607b 100644
--- a/code/modules/vending/vendor/inventory.dm
+++ b/code/modules/vending/vendor/inventory.dm
@@ -282,6 +282,6 @@
//transfer money to machine
SSblackbox.record_feedback("amount", "vending_spent", price_to_use)
- log_econ("[price_to_use] credits were inserted into [src] by [account.account_holder] to buy [product_to_vend].")
+ log_econ("[price_to_use] [MONEY_NAME] were inserted into [src] by [account.account_holder] to buy [product_to_vend].")
credits_contained += round(price_to_use * VENDING_CREDITS_COLLECTION_AMOUNT)
return TRUE
diff --git a/code/modules/vending/vendor/ui_data.dm b/code/modules/vending/vendor/ui_data.dm
index 49e90b946de..c375e0a78d6 100644
--- a/code/modules/vending/vendor/ui_data.dm
+++ b/code/modules/vending/vendor/ui_data.dm
@@ -70,7 +70,7 @@
data["jobDiscount"] = DEPARTMENT_DISCOUNT
data["product_records"] = list()
data["displayed_currency_icon"] = displayed_currency_icon
- data["displayed_currency_name"] = displayed_currency_name
+ data["displayed_currency_name"] = " [displayed_currency_name]"
var/list/categories = list()
diff --git a/code/modules/vending/wardrobes.dm b/code/modules/vending/wardrobes.dm
index f0aed8206f6..04130a3d65c 100644
--- a/code/modules/vending/wardrobes.dm
+++ b/code/modules/vending/wardrobes.dm
@@ -509,6 +509,7 @@ GLOBAL_VAR_INIT(roaches_deployed, FALSE)
icon_state = "lawdrobe"
product_ads = "OBJECTION! Get the rule of law for yourself!"
vend_reply = "Thank you for using the LawDrobe!"
+ extra_price = PAYCHECK_COMMAND * 3
products = list(
/obj/item/clothing/accessory/lawyers_badge = 2,
/obj/item/clothing/neck/tie = 3,
@@ -539,6 +540,9 @@ GLOBAL_VAR_INIT(roaches_deployed, FALSE)
/obj/item/storage/box/evidence = 2,
/obj/item/reagent_containers/cup/fish_feed = 1,
)
+ premium = list(
+ /obj/item/mod/control/pre_equipped/portable_suit = 1,
+ )
refill_canister = /obj/item/vending_refill/wardrobe/law_wardrobe
payment_department = ACCOUNT_SRV
diff --git a/code/modules/wiremod/components/id/access_checker.dm b/code/modules/wiremod/components/id/access_checker.dm
index 853df324315..d694834c06a 100644
--- a/code/modules/wiremod/components/id/access_checker.dm
+++ b/code/modules/wiremod/components/id/access_checker.dm
@@ -57,7 +57,7 @@
return check_access_list(subject_accesses.value)
/obj/item/circuit_component/compare/access/ui_perform_action(mob/user, action)
- if(length(required_accesses.connected_ports))
+ if(LAZYLEN(required_accesses.connected_ports))
balloon_alert(user, "disconnect port before manually configuring!")
return
interact(user)
diff --git a/code/modules/wiremod/core/duplicator.dm b/code/modules/wiremod/core/duplicator.dm
index 25e83326804..4f3bc86542e 100644
--- a/code/modules/wiremod/core/duplicator.dm
+++ b/code/modules/wiremod/core/duplicator.dm
@@ -155,7 +155,7 @@ GLOBAL_LIST_INIT(circuit_dupe_whitelisted_types, list(
var/list/input_ports_stored_data = list()
for(var/datum/port/input/input as anything in component.input_ports)
var/list/connection_data = list()
- if(!length(input.connected_ports))
+ if(!LAZYLEN(input.connected_ports))
if(isnull(input.value) || !(input.datatype in GLOB.circuit_dupe_whitelisted_types))
continue
connection_data["stored_data"] = input.value
diff --git a/code/modules/wiremod/core/port.dm b/code/modules/wiremod/core/port.dm
index 0771506076b..4cda3186073 100644
--- a/code/modules/wiremod/core/port.dm
+++ b/code/modules/wiremod/core/port.dm
@@ -141,7 +141,7 @@
/datum/port/input/proc/disconnect(datum/port/output/output)
SIGNAL_HANDLER
- connected_ports -= output
+ LAZYREMOVE(connected_ports, output)
UnregisterSignal(output, COMSIG_PORT_SET_VALUE)
UnregisterSignal(output, COMSIG_PORT_SET_TYPE)
UnregisterSignal(output, COMSIG_PORT_DISCONNECT)
@@ -174,7 +174,6 @@
set_value(default)
if(trigger)
src.trigger = trigger
- src.connected_ports = list()
/**
* Connects an input port to an output port.
@@ -185,7 +184,7 @@
/datum/port/input/proc/connect(datum/port/output/output)
if(output in connected_ports)
return
- connected_ports += output
+ LAZYADD(connected_ports, output)
RegisterSignal(output, COMSIG_PORT_SET_VALUE, PROC_REF(receive_value))
RegisterSignal(output, COMSIG_PORT_SET_TYPE, PROC_REF(check_type))
RegisterSignal(output, COMSIG_PORT_DISCONNECT, PROC_REF(disconnect))
diff --git a/code/modules/wiremod/shell/module.dm b/code/modules/wiremod/shell/module.dm
index a3c1f8572ea..57139cdfc1b 100644
--- a/code/modules/wiremod/shell/module.dm
+++ b/code/modules/wiremod/shell/module.dm
@@ -280,12 +280,16 @@
var/part_name = "Undefined"
if(istype(part, /obj/item/clothing/head/mod))
part_name = "Helmet"
- if(istype(part, /obj/item/clothing/suit/mod))
+ else if(istype(part, /obj/item/clothing/suit/mod))
part_name = "Chestplate"
- if(istype(part, /obj/item/clothing/gloves/mod))
+ else if(istype(part, /obj/item/clothing/gloves/mod))
part_name = "Gloves"
- if(istype(part, /obj/item/clothing/shoes/mod))
+ else if(istype(part, /obj/item/clothing/shoes/mod))
part_name = "Boots"
+ else if(istype(part, /obj/item/clothing/glasses/mod))
+ part_name = "Glasses"
+ else if(istype(part, /obj/item/clothing/neck/mod))
+ part_name = "Tie"
string_list += part_name
deployed_parts.set_output(string_list)
deployed.set_output(is_deployed)
diff --git a/code/modules/wiremod/shell/moneybot.dm b/code/modules/wiremod/shell/moneybot.dm
index 7212da886fd..2a6aa015fae 100644
--- a/code/modules/wiremod/shell/moneybot.dm
+++ b/code/modules/wiremod/shell/moneybot.dm
@@ -161,7 +161,7 @@
return
attached_bot.add_money(amount_to_insert)
- balloon_alert(attacker, "inserted [amount_to_insert] credits.")
+ balloon_alert(attacker, "inserted [amount_to_insert] [MONEY_NAME].")
money_input.set_output(amount_to_insert)
entity.set_output(attacker)
money_trigger.set_output(COMPONENT_SIGNAL)
diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm
index 7e98f95695f..b47e334a65a 100644
--- a/code/modules/zombie/items.dm
+++ b/code/modules/zombie/items.dm
@@ -12,6 +12,8 @@
sharpness = SHARP_EDGED
/obj/item/mutant_hand/zombie/afterattack(atom/target, mob/user, list/modifiers, list/attack_modifiers)
+ if(QDELETED(target))
+ return
if(ishuman(target))
try_to_zombie_infect(target, user, user.zone_selected)
else if(isliving(target))
diff --git a/config/config.txt b/config/config.txt
index 634bd5762b9..39b37028af1 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -132,9 +132,6 @@ ALLOW_VOTE_RESTART
## allow players to initiate a map-change vote
ALLOW_VOTE_MAP
-## allow players to rock the vote, or to vote on the map again if they didn't like the last results
-#ALLOW_ROCK_THE_VOTE
-
## the number of times you wish to allow players to rock the vote in a given shift
MAX_ROCKING_VOTES 1
@@ -237,6 +234,9 @@ IPINTEL_REJECT_BAD
## Ban appeals URL - usually for a forum or wherever people should go to contact your admins.
# BANAPPEALS http://justanotherday.example.com
+## Config URL - Intended for servers that have a repository/public folder/etc. that show off the config that they are running
+# CONFIGURL https://github.com/tgstation-operations/server-config
+
## System command that invokes yt-dlp, used by Play Internet Sound.
## You can install yt-dlp with
## "pip install yt-dlp" if you have pip installed
diff --git a/dependencies.sh b/dependencies.sh
index c79d45cd502..4af9947e3bb 100644
--- a/dependencies.sh
+++ b/dependencies.sh
@@ -20,7 +20,7 @@ export BUN_VERSION=1.2.16
export SPACEMAN_DMM_VERSION=suite-1.11
# Python version for mapmerge and other tools
-export PYTHON_VERSION=3.9.0
+export PYTHON_VERSION=3.11.0
#dreamluau repo
export DREAMLUAU_REPO="tgstation/dreamluau"
diff --git a/html/changelogs/AutoChangeLog-pr-94462.yml b/html/changelogs/AutoChangeLog-pr-94462.yml
new file mode 100644
index 00000000000..25dd63fa3cc
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-94462.yml
@@ -0,0 +1,4 @@
+author: "san7890"
+delete-after: True
+changes:
+ - server: "The ready/not ready states of new players are logged when a shift starts."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-94476.yml b/html/changelogs/AutoChangeLog-pr-94476.yml
new file mode 100644
index 00000000000..3026726ac2f
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-94476.yml
@@ -0,0 +1,4 @@
+author: "SmArtKar"
+delete-after: True
+changes:
+ - bugfix: "Fixed illusions not fleeing their targets and runtiming in place"
\ No newline at end of file
diff --git a/html/changelogs/archive/2025-12.yml b/html/changelogs/archive/2025-12.yml
new file mode 100644
index 00000000000..2e43709711d
--- /dev/null
+++ b/html/changelogs/archive/2025-12.yml
@@ -0,0 +1,507 @@
+2025-12-01:
+ Absolucy:
+ - bugfix: Voltaic combat cyberhearts no longer kill toxlovers (i.e jellypeople),
+ albeit they won't heal toxins for them either.
+ Aliceee2ch:
+ - bugfix: Added missing "require all" type of access for Bitrunning Den.
+ - map: Removes one (1) bush in NebulaStation warehouse.
+ - spellcheck: Plant produce spawner now correctly spawns plum instead of plum seeds.
+ - spellcheck: Removed cellular damage mention from Radioactive Resonance virus description.
+ - bugfix: Fixed missing access to ORM room for miners on CatwalkStation.
+ ArchBTW:
+ - bugfix: Fix crowbar combat mode right clicking an unpowered airlock
+ ArchBTW, WinterDarkraven:
+ - qol: Guns have a directional muzzle flash effect now
+ Ezel:
+ - bugfix: Removes rogue decal from catwalk station
+ FalloutFalcon:
+ - code_imp: standardized defines for currency symbols
+ - balance: Lattice now ignore the slowdowns of turfs
+ - bugfix: Lattice now stop you from dipping into water
+ Fghj240:
+ - bugfix: Durability header no longer appears twice when inspecting armor
+ - bugfix: Exosuit fabricator UI now shows the Savannah-Ivanov's supported equipment
+ - qol: you can swap beakers in a pandemic
+ - qol: pandemics now display overall virus severity
+ - admin: virus logs now include severity along with other stats
+ - qol: weapon rechargers don't wait an extra tick before announcing they've finished
+ recharging
+ - spellcheck: weapon rechargers no longer claim a battle rifle has "finished recharging"
+ when it's finished recalibrating.
+ Melbert:
+ - qol: Stairs now have an indicator that stepping beyond them will take you upwards.
+ You can disable it in accessibility settings if desired.
+ - image: Stairs blend in and together better.
+ SmArtKar:
+ - admin: Added jump and search functions to the planecube debugger
+ - qol: Bot assemblies will only be picked up if you held the medkit/toolbox/helmet/etc
+ in your hands when creating one.
+ - qol: Assembling med/repair/secbots will now put their assemblies in your main
+ hand instead of your offhand
+ - bugfix: Hulk and infusions no longer texture robotic limbs
+ - bugfix: Fixed raptor younglings going invisible when killed
+ SyncIt21:
+ - bugfix: all mounted objects on tables, fences, windows & walls should fall of
+ correctly when the atom it is placed on is destroyed
+ - bugfix: security telescreen now falls off when their mounted wall is destroyed
+ - bugfix: defib wall mount falls off when their mounted wall is destroyed
+ - bugfix: floor lights are mounted to the ground/catwalk/tram floor they are sitting
+ on meaning destroying it will destroy the light
+ - bugfix: wall mounted plaques now fall off when their mounted wall is destroyed
+ Xrmanser:
+ - bugfix: 'graft stat adjustments now apply even when the trait is rejected
+
+ :cl:'
+ ezel:
+ - bugfix: Removes a duplicate fire alarm in metastation botany
+ - bugfix: fixes catwalk atmos pipeline at the courtroom
+ - bugfix: removes random powerline going trough the walls
+ lelandkemble:
+ - spellcheck: abilities that check for consciousness will say you're dead rather
+ than unconscious if you're dead
+2025-12-02:
+ ArchBTW:
+ - bugfix: Click dragging something onto a tile with a decal (pool of oil/blood,
+ etc)
+ - code_imp: Add force_move_pulled element; replace respective code with the element
+ Fghj240:
+ - qol: Revolutionaires gain a positive moodlet instead of a negative one when they
+ see a dead head of staff
+ - balance: Regenerative Coma activation thresholds scales with your max hp
+ - balance: Regenerative Coma can be canceled by gaining nosoftcrit before it activates
+ - balance: Toxolysis food scales with the amount of reagents purged
+ - qol: Quick carry and fast medicine properties are now visible on gloves
+ - bugfix: Getting shoved into a wall while stun immune no longer sends a message
+ saying you were knocked down
+ - bugfix: Nooartrium patients and super hulks can no longer pass out from oxygen
+ damage
+ Melbert:
+ - refactor: Refactored item reskinning (the alt-click way), report any oddities
+ with that
+ - rscdel: Deleted the crusher retool kit, now you can just reskin your crusher with
+ alt-click. The Skull skin is still locked behind having the Ashen Skull trophy
+ applied.
+ - bugfix: Stunswords no longer have an incorrect lore blurb
+ - bugfix: Fixed loadout item reskinning's UI
+ ezel:
+ - rscadd: Compact Defibrillator is now a Spy objective
+ - balance: Compact Defib is CMO exclusive again
+ - balance: you can no longer print Defibrillators in techlathe (purchase them via
+ cargo if needed)
+ - map: catwalk defibs trimmed to 4 instead of 6
+ - map: metastation defib added from 3 to 4
+ - map: wawastation defib added from 3 to 4
+ - map: tramstation defib added from 3 to 4
+ - map: nebulastation defibs trimmed from 6 to 4
+2025-12-03:
+ Aliceee2ch:
+ - map: Added shutters in some of service areas on WawaStation.
+ Cyberboss:
+ - rscadd: Hygiene bots now extinguish when cleaning.
+ Ghommie:
+ - refactor: Follow-up to the crafting/material refactor from months ago. All objects
+ crafted with stacks now inherit their mat composition (not necessarily the effects
+ and color) by default, while previously only a few things like chair, sinks
+ and toilets did. Report any object looking or behaving weirdly as a result.
+ - bugfix: The material composition of ammo boxes is no longer a 1/10 of what it's
+ supposed to be. It was a shitty hack to make it harder to recycle empty ammo
+ boxes. Instead, they lose materials as they're emptied now.
+ Melbert:
+ - bugfix: Fix Flypeople's vomit interaction before anyone realizes it's broken but
+ me
+ - rscadd: Compact defib can be worn and used on the suit storage slot of labcoats,
+ surgical aprons, and medical MODsuits
+ - rscadd: Drones and Dex Holoparasites can (hypothetically) wear and use compact
+ defibs
+ - refactor: Refactored how mech icons update mid-construction, report any disappearing
+ mechs please
+ - bugfix: One of the Phazon's mid construction icons was not visible in the past,
+ now it is again. Yippee.
+ SyncIt21:
+ - qol: added mapping directional helper for warning, radiation & secure area sign
+ allowing them to interact with fire/acid on their mounted turf
+ - qol: adds mapping helper to create diagonally shifted objects
+ - qol: adds mapping directional helper for nanotrasen sign & replaces all var edited
+ instances
+ TealSeer:
+ - code_imp: makes /obj/item/stamp abstract, creates /obj/item/stamp/granted
+ alien3301:
+ - balance: The lethal injection locker is now accessible by the warden.
+ necromanceranne:
+ - balance: High frequency blades (both null rod and admin versions) are able to
+ block melee attacks from mechs.
+ - balance: High frequency blade null rods are now two-handed weapons.
+ - balance: Mech melee attacks can be blocked or avoided by a few rare sources.
+ tontyGH:
+ - refactor: Handling relating to AI cores has been overhauled. Please make an issue
+ report on the Github if unexpected behaviour occurs.
+ uaioy:
+ - bugfix: lavaland crash site ruin should properly be affected by ash storm now
+2025-12-04:
+ Aliceee2ch:
+ - bugfix: Fixes for MetaStation fire alarms.
+ - map: Removed one pair of firelocks in Brig cycling room - now it has fire sanity
+ marker instead. That should prevent trapping anyone without crowbar inside.
+ FalloutFalcon:
+ - bugfix: Cardboard crates are now made of cardboard
+ - bugfix: eyeless overlays with now display properly again
+ - rscadd: Added the medium sized cardboard boxes to random spawners
+ - qol: You can now craft the medium sized cardboard boxes
+ - rscadd: water and saltwater can be scooped from water turfs
+ - rscadd: mineral water (water with minor tox heal), scoop-able from hot-springs
+ - refactor: behavior for scooping reagents from plasma turfs has been made generic
+ and reusable
+ Hatterhat:
+ - bugfix: Non-virtual megafauna should no longer be dropping encrypted caches when
+ mulched with a crusher if their matching virtual domain was deployed.
+ - bugfix: Virtual megafauna no longer count as valid kills for achievement tracking.
+ Jacquerel:
+ - balance: The Captain's MODSuit pathfinder module now flies it directly to their
+ location rather than physically traversing the halls of the station.
+ LT3:
+ - qol: Silent jaws of recovery is now less obvious that it's modified
+ Melbert:
+ - qol: If you are within a disposal bin or chute, and drop an item, the item will
+ go into the bin, rather than onto the floor outside of the bin.
+ TealSeer:
+ - code_imp: ruin loading now logs placement coordinates
+ - bugfix: fixed molotov cocktails not lighting
+ Yobrocharlie:
+ - map: added the "heretic" gateway map (it has nothing related to heretics)
+ lelandkemble:
+ - rscadd: Added a modular SUIT, a deployable, one-piece three-piece suit. Purchasable
+ from the LawDrobe.
+ - balance: The MODsuit equipment techweb node has been split in two, with an offshoot
+ for civilian MODules.
+2025-12-05:
+ Aliceee2ch:
+ - image: Most of mapping spawner icons were updated to match latest version of sprites.
+ ArchBTW:
+ - bugfix: Fix sitting on stools (it's a chair)
+ - bugfix: Fix offset when sitting on a stool
+ FalloutFalcon:
+ - bugfix: You can toss things over cardboard crates
+ Fghj240:
+ - bugfix: Deathmatch felinids now spawn in with pet collars as intended
+ Gboster-0:
+ - balance: wabbajack robot transformations are now weighted like 3 years ago
+ Ghommie:
+ - refactor: Refactored inventory code a little to be hopefully more robust. Report
+ any possible new, quirky nit or outright problematic issue with it.
+ - bugfix: Storing objects with slowdown in a backpack or belt (or any such storage)
+ now properly updates your speed.
+ Melbert:
+ - balance: Tech storage access is no longer a "command level" access, meaning the
+ HoP can give it to crewmembers that have a normal ID card. Note, secure tech
+ storage still requires bridge access, which requires a silver ID card.
+ - bugfix: Tie names are verified
+ - bugfix: Fix mech construction
+ SomeRandomOwl:
+ - bugfix: The forked lizard tongue no longer makes you slur your S's if its in your
+ hands or pockets.
+ SyncIt21:
+ - bugfix: shards can be embedded in posters again
+ TealSeer:
+ - bugfix: fixed cargo manifests missing things in certain crates
+ lelandkemble:
+ - bugfix: Turret control wallframes visibly attach to the wall
+ necromanceranne:
+ - bugfix: Sleeping carp now actually lets you dodge melee attacks without filling
+ your hands with stuff.
+2025-12-07:
+ ArchBTW, Bisar:
+ - balance: Changeling's Biodegrade works against grabs.
+ - balance: Changeling's Biodegrade destroys all restraints instead of one single
+ restraint.
+ - bugfix: Changeling's Biodegrade shows the effect when used inside lockers.
+ - bugfix: Changeling's Biodegrade properly covers restraint types (cuffed prisoner
+ shoes, strait jackets, etc).
+ Fghj240:
+ - bugfix: inhalers no longer have infinite reach
+ - bugfix: using an inhaler no longer results in whacking the target with it afterwards
+ - qol: You can see whether you just locked or unlocked a cyborg
+ LT3:
+ - code_imp: Drag-drop activation can now be added as an element to objects
+ - qol: Portable crew monitor can now be drag-drop activated similar to a PDA
+ Melbert:
+ - rscadd: Readds Sent from my PDA Sent from my PDA
+ - bugfix: Summon animals relic makes you less depressed
+ - bugfix: Fix detached storage (modsuit storage)
+ - rscadd: '[Catwalk] Entirely new Dorms & Holodeck'
+ - rscadd: '[Catwalk] Modified Atmos, Gravity Generator, Engineering and Tech Storage
+ a fair bit'
+ - rscadd: '[Catwalk] Event locations were updated, meaning several random events
+ now trigger in more appropriate places'
+ - rscdel: '[Catwalk] Removed duplicate First Aid stations in a few places'
+ - qol: '[Catwalk] Changed around maint areas (not the actual layouts but the area
+ designations) to be a bit less monolithic'
+ - bugfix: '[Catwalk] Xenobio kill room uses a passive vent'
+ - bugfix: '[Catwalk] Fixes a few erroneous space tiles around sec'
+ - bugfix: Bolas don't slow while in hand
+ - balance: Ammo will no longer show up in the Spy loot pool until a weapon that
+ uses said ammo has been claimed. Some ammo for weapons easily obtainable on
+ station (shotgun shot) are not limited by this restriction. Also, there's a
+ rare chance ammo will show up anyways just to keep people on their toes.
+ - balance: Claiming a weapon will refresh ammo in the loot pool that has already
+ been claimed or cycled out.
+ - balance: Bounty rewards that no Spy claimed have a chance to re-enter the loot
+ pool after a bounty refresh, rather than being lost for the remainder of the
+ round.
+ - balance: When higher tier Spy bounties empty their loot pools, they will attempt
+ to draw from lower tier pools before defaulting to telecrystals.
+ - balance: Spies no longer are rewarded with gun cases for bounties; rather just
+ the gun itself
+ - spellcheck: Modified the Spy antag info text
+ RikuTheKiller:
+ - balance: R&D console boards sourced from printing or tech storage are now locked
+ by default. It's recommended to ask your respective head of department or the
+ science department to complete research for you. Heads of department can remotely
+ access the research network using their PDAs.
+ TealSeer:
+ - bugfix: fixed TRAIT_NO_UNDERWEAR not being respected in human rendering
+ Tostah:
+ - balance: increases mech damage to windows to feel more realistic
+ alien3301:
+ - bugfix: Captain starts with a command megaphone in their locker
+ - bugfix: Executing people with guns no longer makes you hit them with the gun
+ - bugfix: Trying to shoot a gun when it cant shoot doesn't make you melee with it
+ - bugfix: If trying to shoot would make a click sound then executing will also make
+ the sound
+ dungeon_vr, KnigTheThrasher, xPokee:
+ - image: The DNR ghost HUD icon has gotten a new look.
+ lelandkemble:
+ - bugfix: Malf AI "Destroy RCDs" power now properly destroys mech RCDs
+ san7890:
+ - balance: After noticing it was too easy for animals to crawl through maintenance
+ by abusing the Easy-Exit sensors, Nanotrasen has replaced all unrestricted door
+ sensors (like in maintenance) with a pull-down lever that should take around
+ 2-3 seconds to engage. It has been designed to allow the employee to actuate
+ it despite any pressure differentials within an area.
+ - balance: The above change does not apply to select doors in the medical section
+ of the station, which will continue to use the "older" Ready-Exit system.
+ - sound: In order to ensure that staff in high-traffic areas don't go bonkers due
+ to this new change, sound dampeners have been installed to prevent the hydraulics
+ from being heard for super long distances. However, they skimped out on the
+ quality for the ones in maintenance...
+2025-12-08:
+ ArchBTW:
+ - balance: repairbot hp 100 -> 35
+ FalloutFalcon, rye-rice:
+ - rscadd: Adds a small cosmetic recoil to all ballistics (with an accompanying pref
+ option to tweak or disable it)
+ - balance: Changes recoil from an erratic shaking to a kicking of your screen relative
+ to your firing angle
+ - qol: Pref option for darkened screen has some scaling with duration of shake
+ Thunder12345:
+ - rscadd: Added a new set of chaplain armour based on the generic heretic robes
+ unavailable since the path rework.
+2025-12-09:
+ Aliceee2ch:
+ - image: Added 2 new contraband and 1 new official posters.
+ Fghj240:
+ - bugfix: mod pathfinder module no longer gets stuck after an interrupted launch
+ - bugfix: mod pathfinder module gives you a unique balloon alert on your position
+ if you try to recall a suit that's being held by another mob
+ Melbert:
+ - balance: Blood flow and clotting has universally been slashed by about 0.85x.
+ This means blood wounds kill you slightly slower, but heal slightly slower.
+ - balance: Sutures now heal 0.85x less blood flow and gauze's clotting bonus is
+ also 0.85x lower. Medicated sutures are unaffected.
+ - balance: Bloody Mary's blood healing effect now scales with how drunk you are.
+ Worse in low amounts, better in high amounts.
+ - balance: Epoetin Alfa no longer generates Sangurite. It now directly clots bleeding
+ wounds (at a rate ~3x worse than sangurite, but still ~4x better than the natural
+ clotting rate)
+ - balance: Epoetin Alfa no longer heals liver damage.
+ - balance: Epoetin Alfa regenerates 3x less blood. It's still 4x effective than
+ Saline Glucose or Iron.
+ - balance: Epoetin Alfa now causes minor heart damage (0.2 damage per tick).
+ - code_imp: All "coagulating" effects now use a shared function.
+ Rockdtben:
+ - bugfix: Secure Briefcase buttons no longer push column down.
+ lelandkemble:
+ - map: Powered catwalk library office
+ - rscadd: Syndicate Powerfist users and our most athletic employees have discovered
+ that if you punch a meteor with enough force, it can be redirected.
+ necromanceranne:
+ - balance: Boxing combos are now broader and user friendly. Alternating combinations
+ of buttons AND hands produces different results, but all of them good. Experiment!
+ - balance: Evil boxing now has a Sucker Punch. Hit someone from behind to potentially
+ knock them out cold!
+ - bugfix: Punching mitts no longer mention punching rocks.
+ san7890:
+ - admin: Multikeying Notice Messages have taken on a new form factor but should
+ still furnish the same information.
+2025-12-10:
+ ArcaneMusic:
+ - bugfix: Plasma sheets are once again immune to market elasticity, selling for
+ a static 80 credits each yet again.
+ ArchBTW:
+ - bugfix: 'Biodegrade: Fireman carry & knotted shoes works as intended, description
+ update'
+ - balance: Nightmare's light eater will snuff out dragged lights
+ Fghj240:
+ - qol: You can hotkey shockpaddles back into their defibs
+ - bugfix: Obscuring your face prevents people from noticing your facial expression
+ when you're using sign language
+ Melbert:
+ - rscadd: Wardens can alt-click-reskin their Kaza Ruk gloves to match their formal
+ attire
+ - rscadd: You can select a recolorable Santa Hat in the loadout during the festive
+ season (December 1st - 31st)
+ Rockdtben:
+ - bugfix: ScreenTip for Lockers will show "Unlock" or "Lock" instead of always showing
+ "Unlock"
+ SyncIt21:
+ - qol: added mapping directional helper for cargo & vacuum signs allowing them to
+ interact with fire/acid on their mounted turf
+ jlsnow301:
+ - rscdel: TGUI now defaults to fancy mode, there is only fancy mode. Welcome to
+ the future.
+ lelandkemble:
+ - map: Catwalk engi secpoint, prisoner education chamber, & starboard bow solar
+ maintenance repowered
+ - map: Catwalk Engineering Project Room accesses have been made consistent
+ - map: Many of Catwalk's useless pipes have been cut out
+ - map: Catwalk's waste line will no longer constantly vent into space
+ - map: Icebox morgue has been given an additional redundant power line
+ levels0:
+ - bugfix: make evolved lungs actually resistant to co2
+ timothymtorres:
+ - rscadd: Add new emotes "slit", "scratch_h", "thumb_u", "thumb_d", "time", "tap",
+ "halt", "shush", "listen", "think", "beckon", "airquote", "crazy", "squint",
+ "rub".
+ - bugfix: Fix closets, crates, and drawers missing material tags
+2025-12-11:
+ ArchBTW:
+ - bugfix: Fix RMB crowbar consistency for doors
+ - spellcheck: Removed obsolete morph tip of the round
+ - balance: 'Canned ink: Remove uncatchable & paralyze. Lower stagger & blind'
+ Fghj240:
+ - bugfix: Rescue modsuit suit storage can hold anything the medical modsuit suit
+ storage can hold
+ - spellcheck: updated quadruple amputee to mention newer healing items
+ - spellcheck: fixed a missing e in sleeping carp's recall teachings verb
+ Melbert:
+ - balance: Romerol, when applied via touch or vapor, is now guaranteed to infect
+ when using >=1u. (Prior, it had a % chance of infecting equal to the amount
+ of chemicals use - so 1u meant 1% chance to infect.) Other methods of applying
+ Romerol are unaffected (ingesting or injecting any amount is still a guaranteed
+ infection).
+ MrMelbert, Zergspower:
+ - qol: The world is now heavily desaturated while blind, rather than pure monochrome,
+ to give players some visual stimulus
+ - qol: When blind, the brief flicker of the entire screen now only appears for mobs
+ temporarily blinded - ie, mobs blinded from quirk / trauma / genetic mutation
+ no longer experience it
+ Shadow-Quill:
+ - spellcheck: Corrected the spelling of department in the message monitor console.
+ TealSeer:
+ - bugfix: fixed mulebot speed wire max steps being swapped
+ lelandkemble:
+ - spellcheck: Tuberculosis no longer tells naked people to get naked
+ - bugfix: Revenant objectives can only target people that are actually present
+ timothymtorres:
+ - rscadd: Installing camera upgrades now consumes more power. Installing several
+ upgrades onto the same camera has a power multiplier effect that stacks.
+ - bugfix: Fix disabled cameras consuming power
+ xPokee:
+ - spellcheck: The Scrubs, MD abduction spy bounty now has the proper name.
+2025-12-13:
+ ArchBTW:
+ - bugfix: Fix clicking on things underneath directional firelocks
+ Ezel:
+ - map: Engineering deliveries now go to the techlathe room on Deltastation
+ Fghj240:
+ - qol: ear protection is visible on examine
+ - qol: hearing protection modsuit module is now built in
+ Jacquerel:
+ - rscadd: When the game considers it to be Christmas, Cargo can order additional
+ Christmas gifts pre-addressed to random crew members.
+ Rockdtben:
+ - qol: Hold ALT + CLICK and drag a UI window. Provides a work around for stuck uplink
+ UI
+ - qol: 'A new verb under OOC: Reset UI Positions -> Which you can use to fix a stuck
+ uplink UI as well.'
+ TealSeer:
+ - bugfix: fixed disposal unit light not turning on when dumping bag contents inside
+ Thunder12345:
+ - bugfix: Posters printed from the library are no longer poorly coded and ugly.
+ - bugfix: Posters printed from the library will no longer go up in the wrong places.
+ - bugfix: Posters now show their name correctly when rolled up.
+ - bugfix: Random posters will no longer remain random after being put up for the
+ first time.
+ - bugfix: Spacemen now know how to hang posters correctly, and the majority of poster
+ sprites will no longer sit on the floor.
+ alien3301:
+ - rscadd: '"Department Budget" access that lets someone place orders with the NT
+ shopping app'
+ - qol: Warden starts with the shopping app installed
+ - balance: Cargo techs and Warden can place orders with the shopping app by default
+ jlsnow301:
+ - rscdel: Removed the TGUI kitchensink component view
+ - rscadd: Added a new data explorer for TGUI dev mode
+2025-12-14:
+ ArchBTW:
+ - bugfix: Fix not being able to drag click things on tram floor
+ - bugfix: Fix stacking flash assemblies
+ - bugfix: Fix nightmare not properly snuffing dragged lights if other lights were
+ turned on simultaneously
+ - balance: Heavy pirate's armored pirate coat can hold the same items that standard
+ armor can.
+ - bugfix: Gives eligible sentient simplemobs a stamina hud
+ Geoengi:
+ - rscadd: Robot customers and mech wreckage destruction are messier. Bring a mop!
+ Hatterhat:
+ - bugfix: Non-base laser guns now attach their lore properly.
+ - spellcheck: Adjusted the names on the laser guns to have a slash between the numeric
+ type/version and the variant... abbreviation? Letter? One of the two.
+ - spellcheck: Fixed some typos and adjusted the now-visible lore on the laser guns
+ to read a little nicer, theoretically. Whether the grammar tweaks improved anything
+ or not is subjective.
+ LT3:
+ - code_imp: Clausophobia is now only triggered by a properly dressed Santa Claus
+ SmArtKar:
+ - bugfix: Fixed purple raptors' wings action background
+ TealSeer:
+ - rscadd: Added a key memory to the family heirloom quirk
+ krookodilehunter:
+ - qol: Wirecutters are now recolorable when purchasing from a vendor
+ lelandkemble:
+ - rscadd: Added a counterattack for sheathed weapons. When used, an attack from
+ your target within a one-second window will cause you to counterattack with
+ a triple-damage strike.
+2025-12-15:
+ FalloutFalcon:
+ - spellcheck: minor cleanup on some usecases of "credits"
+ Gboster-0:
+ - code_imp: slightly cleaned up command headsets in-code
+ LT3:
+ - admin: Removed ability to enable Rock The Vote because RTV is no longer a thing
+ Melbert:
+ - qol: Stop drop and roll now has a progress bar
+ SmArtKar:
+ - bugfix: Fixed the brimdust status alert sprite
+ Thunder12345:
+ - map: Tomas once more haunts the dormitories of Catwalk Station
+ alien3301:
+ - bugfix: Just like the blood red modsuits, the elite version of them has a pathfinder
+ module
+ - spellcheck: fixed a typo in the moon heretic robe's attack messages
+ jlsnow301:
+ - bugfix: TGUI window drag and resize should be smoother on low end systems.
+ necromanceranne:
+ - bugfix: Fixes laser pistol charging instantly.
+ san7890:
+ - spellcheck: Nanotrasen has updated their Emergency Shuttle Authorization messaging.
+ - bugfix: Paintings can no longer be finalized if you are not adjacent to the painting.
+ - server: Round ID is now output to `log_world()`, so it'll show up stuff like world.log,
+ dd.log, etc.
+ - config: If you publicly host a copy of your server's config, you may now link
+ to the public URL in the 'CONFIGURL' setting in the server config. It will show
+ to players in the Escape Menu's Resources Carousel.
+ timothymtorres:
+ - bugfix: Fix map reader ignoring valid variable names. If a variable had a number
+ inside the name anywhere the map reader would ignore this value when a map was
+ loaded and use the initial value instead. This was due to a faulty regex pattern
+ that did not properly match variable names.
diff --git a/icons/effects/mapping_helpers.dmi b/icons/effects/mapping_helpers.dmi
index cb5ff8f905d..d7ee50ebe8d 100644
Binary files a/icons/effects/mapping_helpers.dmi and b/icons/effects/mapping_helpers.dmi differ
diff --git a/icons/effects/random_spawners.dmi b/icons/effects/random_spawners.dmi
index e75aa4aaa76..525f99a6a7c 100644
Binary files a/icons/effects/random_spawners.dmi and b/icons/effects/random_spawners.dmi differ
diff --git a/icons/hud/escape_menu_icons.dmi b/icons/hud/escape_menu_icons.dmi
index 8f848aa25e4..a6c24f082ab 100644
Binary files a/icons/hud/escape_menu_icons.dmi and b/icons/hud/escape_menu_icons.dmi differ
diff --git a/icons/hud/screen_full.dmi b/icons/hud/screen_full.dmi
index dfe3bd12091..f507bd754fd 100644
Binary files a/icons/hud/screen_full.dmi and b/icons/hud/screen_full.dmi differ
diff --git a/icons/hud/screen_ghost.dmi b/icons/hud/screen_ghost.dmi
index 44c04566ed1..e193198d2f5 100644
Binary files a/icons/hud/screen_ghost.dmi and b/icons/hud/screen_ghost.dmi differ
diff --git a/icons/mob/clothing/back.dmi b/icons/mob/clothing/back.dmi
index af5a6d9bf8c..ff8e847abf7 100644
Binary files a/icons/mob/clothing/back.dmi and b/icons/mob/clothing/back.dmi differ
diff --git a/icons/mob/clothing/head/chaplain.dmi b/icons/mob/clothing/head/chaplain.dmi
index 100b7ee922f..2929fdfe89a 100644
Binary files a/icons/mob/clothing/head/chaplain.dmi and b/icons/mob/clothing/head/chaplain.dmi differ
diff --git a/icons/mob/clothing/head/helmet.dmi b/icons/mob/clothing/head/helmet.dmi
index b2cb68fd2fa..0b5cb437209 100644
Binary files a/icons/mob/clothing/head/helmet.dmi and b/icons/mob/clothing/head/helmet.dmi differ
diff --git a/icons/mob/clothing/modsuit/mod_clothing.dmi b/icons/mob/clothing/modsuit/mod_clothing.dmi
index 9a631a2352e..f7eaab9f9f4 100644
Binary files a/icons/mob/clothing/modsuit/mod_clothing.dmi and b/icons/mob/clothing/modsuit/mod_clothing.dmi differ
diff --git a/icons/mob/clothing/suits/armor.dmi b/icons/mob/clothing/suits/armor.dmi
index be6ab18d646..4a84c7e1281 100644
Binary files a/icons/mob/clothing/suits/armor.dmi and b/icons/mob/clothing/suits/armor.dmi differ
diff --git a/icons/mob/clothing/suits/chaplain.dmi b/icons/mob/clothing/suits/chaplain.dmi
index 8806bf5f679..76c022071a6 100644
Binary files a/icons/mob/clothing/suits/chaplain.dmi and b/icons/mob/clothing/suits/chaplain.dmi differ
diff --git a/icons/mob/human/human_eyes.dmi b/icons/mob/human/human_eyes.dmi
new file mode 100644
index 00000000000..1ed4e37917a
Binary files /dev/null and b/icons/mob/human/human_eyes.dmi differ
diff --git a/icons/mob/human/human_face.dmi b/icons/mob/human/human_face.dmi
index 23e58bcb0c3..6b5d57bcbbb 100644
Binary files a/icons/mob/human/human_face.dmi and b/icons/mob/human/human_face.dmi differ
diff --git a/icons/mob/rideables/mech_construction.dmi b/icons/mob/rideables/mech_construction.dmi
index f26dbe17fd0..bee082ad6f9 100644
Binary files a/icons/mob/rideables/mech_construction.dmi and b/icons/mob/rideables/mech_construction.dmi differ
diff --git a/icons/mob/silicon/ai.dmi b/icons/mob/silicon/ai.dmi
index df9059fb09e..be9060ecd55 100644
Binary files a/icons/mob/silicon/ai.dmi and b/icons/mob/silicon/ai.dmi differ
diff --git a/icons/mob/simple/lavaland/raptor_big.dmi b/icons/mob/simple/lavaland/raptor_big.dmi
index ebc70e20c3c..4e4932f91a4 100644
Binary files a/icons/mob/simple/lavaland/raptor_big.dmi and b/icons/mob/simple/lavaland/raptor_big.dmi differ
diff --git a/icons/obj/clothing/head/chaplain.dmi b/icons/obj/clothing/head/chaplain.dmi
index ed6f6248b31..d653fced198 100644
Binary files a/icons/obj/clothing/head/chaplain.dmi and b/icons/obj/clothing/head/chaplain.dmi differ
diff --git a/icons/obj/clothing/head/helmet.dmi b/icons/obj/clothing/head/helmet.dmi
index 95b0f6deade..804eb6cb52b 100644
Binary files a/icons/obj/clothing/head/helmet.dmi and b/icons/obj/clothing/head/helmet.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_clothing.dmi b/icons/obj/clothing/modsuit/mod_clothing.dmi
index 160606c790a..7caec6da24a 100644
Binary files a/icons/obj/clothing/modsuit/mod_clothing.dmi and b/icons/obj/clothing/modsuit/mod_clothing.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_construction.dmi b/icons/obj/clothing/modsuit/mod_construction.dmi
index 054fafdc12e..c8f1a2f4960 100644
Binary files a/icons/obj/clothing/modsuit/mod_construction.dmi and b/icons/obj/clothing/modsuit/mod_construction.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi
index c0a6103d3fe..0cd210d85da 100644
Binary files a/icons/obj/clothing/modsuit/mod_modules.dmi and b/icons/obj/clothing/modsuit/mod_modules.dmi differ
diff --git a/icons/obj/clothing/suits/armor.dmi b/icons/obj/clothing/suits/armor.dmi
index 806edaafdb6..2a8594c7b20 100644
Binary files a/icons/obj/clothing/suits/armor.dmi and b/icons/obj/clothing/suits/armor.dmi differ
diff --git a/icons/obj/clothing/suits/chaplain.dmi b/icons/obj/clothing/suits/chaplain.dmi
index 730e47cd6fa..a33a458ddce 100644
Binary files a/icons/obj/clothing/suits/chaplain.dmi and b/icons/obj/clothing/suits/chaplain.dmi differ
diff --git a/icons/obj/medical/defib.dmi b/icons/obj/medical/defib.dmi
index 1e707f529ce..74ad889d4e1 100644
Binary files a/icons/obj/medical/defib.dmi and b/icons/obj/medical/defib.dmi differ
diff --git a/icons/obj/poster.dmi b/icons/obj/poster.dmi
index 0ba8b30a047..2aa9eb0f4dd 100644
Binary files a/icons/obj/poster.dmi and b/icons/obj/poster.dmi differ
diff --git a/icons/obj/stairs.dmi b/icons/obj/stairs.dmi
index a59e8f1fc9f..c541967a425 100644
Binary files a/icons/obj/stairs.dmi and b/icons/obj/stairs.dmi differ
diff --git a/icons/obj/tram/heretic_tram.dmi b/icons/obj/tram/heretic_tram.dmi
new file mode 100644
index 00000000000..793d7c6e4f9
Binary files /dev/null and b/icons/obj/tram/heretic_tram.dmi differ
diff --git a/icons/obj/weapons/guns/ammo.dmi b/icons/obj/weapons/guns/ammo.dmi
index 79250688cc4..1eb10778228 100644
Binary files a/icons/obj/weapons/guns/ammo.dmi and b/icons/obj/weapons/guns/ammo.dmi differ
diff --git a/icons/obj/weapons/guns/ninemmsmg/napad_item.dmi b/icons/obj/weapons/guns/ninemmsmg/napad_item.dmi
new file mode 100644
index 00000000000..8dea5d01a8d
Binary files /dev/null and b/icons/obj/weapons/guns/ninemmsmg/napad_item.dmi differ
diff --git a/icons/obj/weapons/guns/ninemmsmg/napad_lefthand.dmi b/icons/obj/weapons/guns/ninemmsmg/napad_lefthand.dmi
new file mode 100644
index 00000000000..a1f2e67f3f4
Binary files /dev/null and b/icons/obj/weapons/guns/ninemmsmg/napad_lefthand.dmi differ
diff --git a/icons/obj/weapons/guns/ninemmsmg/napad_righthand.dmi b/icons/obj/weapons/guns/ninemmsmg/napad_righthand.dmi
new file mode 100644
index 00000000000..c56a7368028
Binary files /dev/null and b/icons/obj/weapons/guns/ninemmsmg/napad_righthand.dmi differ
diff --git a/icons/obj/weapons/guns/ninemmsmg/napad_worn.dmi b/icons/obj/weapons/guns/ninemmsmg/napad_worn.dmi
new file mode 100644
index 00000000000..3ddf2b5f75b
Binary files /dev/null and b/icons/obj/weapons/guns/ninemmsmg/napad_worn.dmi differ
diff --git a/icons/ui/achievements/achievements.dmi b/icons/ui/achievements/achievements.dmi
index 7fa0a6e4c77..80ebc9cd297 100644
Binary files a/icons/ui/achievements/achievements.dmi and b/icons/ui/achievements/achievements.dmi differ
diff --git a/interface/interface.dm b/interface/interface.dm
index 3441e36dcab..cdf929c5efa 100644
--- a/interface/interface.dm
+++ b/interface/interface.dm
@@ -54,6 +54,17 @@
return
DIRECT_OUTPUT(src, link(githuburl))
+/client/verb/config()
+ set name = "config"
+ set desc = "View the server configuration files."
+ set hidden = TRUE
+
+ var/configurl = CONFIG_GET(string/configurl)
+ if(!configurl)
+ to_chat(src, span_danger("The Config URL is not set in the server configuration."))
+ return
+ DIRECT_OUTPUT(src, link(configurl))
+
/client/verb/reportissue()
set name = "report-issue"
set desc = "Report an issue"
diff --git a/modular_skyrat/master_files/code/modules/cargo/packs/general.dm b/modular_skyrat/master_files/code/modules/cargo/packs/general.dm
deleted file mode 100644
index e854c0902ff..00000000000
--- a/modular_skyrat/master_files/code/modules/cargo/packs/general.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-/datum/supply_pack/misc/bicycle
- special = TRUE
diff --git a/modular_skyrat/master_files/code/modules/cargo/packs/security.dm b/modular_skyrat/master_files/code/modules/cargo/packs/security.dm
index 76fc2146756..c8e6fd0634f 100644
--- a/modular_skyrat/master_files/code/modules/cargo/packs/security.dm
+++ b/modular_skyrat/master_files/code/modules/cargo/packs/security.dm
@@ -1,7 +1,6 @@
//OVERRIDES
/datum/supply_pack/security/ammo
- special = TRUE
/datum/supply_pack/security/armory/ballistic
name = "Peacekeeper Combat Shotguns Crates"
diff --git a/modular_skyrat/master_files/code/modules/cargo/packs/service.dm b/modular_skyrat/master_files/code/modules/cargo/packs/service.dm
deleted file mode 100644
index 49ea64b2788..00000000000
--- a/modular_skyrat/master_files/code/modules/cargo/packs/service.dm
+++ /dev/null
@@ -1,2 +0,0 @@
-/datum/supply_pack/service/survivalknives
- special = TRUE
diff --git a/modular_skyrat/modules/bsa_overhaul/code/station_goal.dm b/modular_skyrat/modules/bsa_overhaul/code/station_goal.dm
index 370318f21ec..4c34e4f63d7 100644
--- a/modular_skyrat/modules/bsa_overhaul/code/station_goal.dm
+++ b/modular_skyrat/modules/bsa_overhaul/code/station_goal.dm
@@ -19,7 +19,7 @@
/datum/station_goal/bluespace_cannon/on_report()
//Unlock BSA parts
var/datum/supply_pack/engineering/bsa/parts = SSshuttle.supply_packs[/datum/supply_pack/engineering/bsa]
- parts.special_enabled = TRUE
+ parts.order_flags = ORDER_SPECIAL | ORDER_SPECIAL_ENABLED
/datum/station_goal/bluespace_cannon/check_completion()
if(..())
diff --git a/modular_skyrat/modules/cargo/code/goodies.dm b/modular_skyrat/modules/cargo/code/goodies.dm
index 8747935c798..5b0f68e418e 100644
--- a/modular_skyrat/modules/cargo/code/goodies.dm
+++ b/modular_skyrat/modules/cargo/code/goodies.dm
@@ -76,7 +76,7 @@
desc = "A high-grade sharpening stone made of specialized alloys, meant to sharpen razor-claws. Unfortunately, this particular one has by far seen better days."
cost = CARGO_CRATE_VALUE * 4 //800 credits
contains = list(/obj/item/scratching_stone)
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
/*
* CARPET PACKS
diff --git a/modular_skyrat/modules/cargo/code/packs.dm b/modular_skyrat/modules/cargo/code/packs.dm
index 2692f8b6dce..e3b42defea8 100644
--- a/modular_skyrat/modules/cargo/code/packs.dm
+++ b/modular_skyrat/modules/cargo/code/packs.dm
@@ -660,7 +660,7 @@
name = "Authentic Renaissance Faire Crate"
desc = "Contains two authentic suits of armor, swords, and two bows and cuirass' for the cowards hiding in the back."
cost = CARGO_CRATE_VALUE * 30
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
contains = list(
/obj/item/clothing/suit/armor/riot/knight/larp/red,
/obj/item/clothing/gloves/plate/larp/red,
@@ -740,7 +740,7 @@
/datum/supply_pack/imports/cin_surplus
name = "CIN Military Surplus Crate"
desc = "A collection of surplus equipment sourced from the Coalition of Independent Nations' military stockpiles. Likely to contain old and outdated equipment, as is the nature of surplus."
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
cost = CARGO_CRATE_VALUE * 9
contains = list(
/obj/item/storage/box/colonial_rations = 1,
diff --git a/modular_skyrat/modules/colony_fabricator/code/cargo_packs.dm b/modular_skyrat/modules/colony_fabricator/code/cargo_packs.dm
index b7b8e62233e..7f625f84817 100644
--- a/modular_skyrat/modules/colony_fabricator/code/cargo_packs.dm
+++ b/modular_skyrat/modules/colony_fabricator/code/cargo_packs.dm
@@ -47,4 +47,4 @@
/obj/item/stock_parts/power_store/battery/high,
)
crate_name = "colonization kit crate"
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
diff --git a/modular_skyrat/modules/company_imports/code/armament_component.dm b/modular_skyrat/modules/company_imports/code/armament_component.dm
index b90485effbe..e8d3b3c60f6 100644
--- a/modular_skyrat/modules/company_imports/code/armament_component.dm
+++ b/modular_skyrat/modules/company_imports/code/armament_component.dm
@@ -250,7 +250,7 @@
if(buyer != SSeconomy.get_dep_account(ACCOUNT_CAR))
created_order = new(created_pack, name, rank, ckey, paying_account = buyer, reason = reason, can_be_cancelled = TRUE)
else
- created_pack.goody = FALSE // Cargo ordered stuff should just show up in a box I think
+ // Cargo ordered stuff should just show up in a box I think
created_order = new(created_pack, name, rank, ckey, reason = reason, can_be_cancelled = TRUE)
created_order.selected_entry = armament_entry
created_order.used_component = src
diff --git a/modular_skyrat/modules/company_imports/code/datums/cargo_pack.dm b/modular_skyrat/modules/company_imports/code/datums/cargo_pack.dm
index be62e2e1248..619fff74b76 100644
--- a/modular_skyrat/modules/company_imports/code/datums/cargo_pack.dm
+++ b/modular_skyrat/modules/company_imports/code/datums/cargo_pack.dm
@@ -1,7 +1,6 @@
#define CARGO_CUT 0.05
/datum/supply_pack/armament
- goody = TRUE
crate_type = /obj/structure/closet/crate/large/import
/datum/supply_pack/armament/generate(atom/A, datum/bank_account/paying_account)
diff --git a/modular_skyrat/modules/customization/datums/components/crafting/recipes.dm b/modular_skyrat/modules/customization/datums/components/crafting/recipes.dm
index e4c548c149b..1ea9882d790 100644
--- a/modular_skyrat/modules/customization/datums/components/crafting/recipes.dm
+++ b/modular_skyrat/modules/customization/datums/components/crafting/recipes.dm
@@ -56,7 +56,7 @@
)
result = /obj/item/food/canned/tuna
category = CAT_SEAFOOD
- crafting_flags = parent_type::crafting_flags & ~CRAFT_ENFORCE_MATERIALS_PARITY
+ crafting_flags = parent_type::crafting_flags & CRAFT_SKIP_MATERIALS_PARITY
// Recipes that provide crafting instructions and don't yield any result
diff --git a/modular_skyrat/modules/nanotrasen_rep/code/nanotrasen_consultant.dm b/modular_skyrat/modules/nanotrasen_rep/code/nanotrasen_consultant.dm
index e1785463659..50fa3fdee2a 100644
--- a/modular_skyrat/modules/nanotrasen_rep/code/nanotrasen_consultant.dm
+++ b/modular_skyrat/modules/nanotrasen_rep/code/nanotrasen_consultant.dm
@@ -2,7 +2,6 @@
title = JOB_NT_REP
rpg_title = "Guild Adviser"
description = "Represent Nanotrasen on the station, argue with the HoS about why he can't just field execute people for petty theft, get drunk in your office."
- department_head = list(JOB_CENTCOM)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/modular_skyrat/modules/novaya_ert/code/mod_suit.dm b/modular_skyrat/modules/novaya_ert/code/mod_suit.dm
index 40e7341729d..be543617c44 100644
--- a/modular_skyrat/modules/novaya_ert/code/mod_suit.dm
+++ b/modular_skyrat/modules/novaya_ert/code/mod_suit.dm
@@ -12,7 +12,6 @@
armor_type = /datum/armor/mod_theme_frontline
complexity_max = DEFAULT_MAX_COMPLEXITY
charge_drain = DEFAULT_CHARGE_DRAIN * 1.5
- inbuilt_modules = list(/obj/item/mod/module/hearing_protection)
allowed_suit_storage = list(
/obj/item/flashlight,
/obj/item/tank/internals,
@@ -107,7 +106,6 @@
it would still be right at home in the service of gunrunners and private security forces. \
Though, it's internal systems have degraded, and some of the ablative plating has been removed."
armor_type = /datum/armor/mod_theme_frontline/surplus
- inbuilt_modules = list(/obj/item/mod/module/hearing_protection)
/datum/mod_theme/frontline/surplus/set_skin(obj/item/mod/control/mod, skin)
. = ..()
@@ -137,7 +135,7 @@
cost = CARGO_CRATE_VALUE * 22
contraband = TRUE
contains = list(/obj/item/mod/control/pre_equipped/frontline/surplus)
- crate_name = "surplus MODsuit crate"
+ order_flags = ORDER_CONTRABAND
/datum/mod_theme/policing
name = "policing"
diff --git a/modular_skyrat/modules/salon/code/barber.dm b/modular_skyrat/modules/salon/code/barber.dm
index ce2bd71106b..e1b85c0457b 100644
--- a/modular_skyrat/modules/salon/code/barber.dm
+++ b/modular_skyrat/modules/salon/code/barber.dm
@@ -1,7 +1,6 @@
/datum/job/barber
title = JOB_BARBER
description = "Run your salon and meet the crews sanitary needs, such as hair cutting, massaging and more!"
- department_head = list(JOB_HEAD_OF_PERSONNEL)
faction = FACTION_STATION
total_positions = 2
spawn_positions = 2
diff --git a/modular_skyrat/modules/sec_haul/code/corrections_officer/corrections_officer.dm b/modular_skyrat/modules/sec_haul/code/corrections_officer/corrections_officer.dm
index c98776d8bf1..195c2a1f091 100644
--- a/modular_skyrat/modules/sec_haul/code/corrections_officer/corrections_officer.dm
+++ b/modular_skyrat/modules/sec_haul/code/corrections_officer/corrections_officer.dm
@@ -3,7 +3,6 @@
rpg_title = "Beefeater"
description = "Guard the permabrig, stand around looking imposing, get fired for abusing the prisoners"
auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY
- department_head = list("The Warden and Head of Security")
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/modular_skyrat/modules/telecomms_specialist/telecomms_specialist.dm b/modular_skyrat/modules/telecomms_specialist/telecomms_specialist.dm
index e410c1b1b89..ca350971d76 100644
--- a/modular_skyrat/modules/telecomms_specialist/telecomms_specialist.dm
+++ b/modular_skyrat/modules/telecomms_specialist/telecomms_specialist.dm
@@ -2,7 +2,6 @@
title = JOB_TELECOMMS_SPECIALIST
description = "Monitor, configure, and maintain all station communications \
and assist with light engineering work."
- department_head = list(JOB_CHIEF_ENGINEER)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/modular_zubbers/code/modules/cargo/packs/contraband.dm b/modular_zubbers/code/modules/cargo/packs/contraband.dm
index 1eab3dab04b..58d49bfa60e 100644
--- a/modular_zubbers/code/modules/cargo/packs/contraband.dm
+++ b/modular_zubbers/code/modules/cargo/packs/contraband.dm
@@ -14,7 +14,7 @@
/obj/item/clothing/head/costume/griffin,
)
crate_name = "patriotic crate"
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
/datum/supply_pack/imports/lizardgoodscrate
name = "Lizard Goods Crate"
@@ -24,4 +24,4 @@
/obj/effect/spawner/random/lizard_crate = 8
)
crate_name = "lizard goods crate"
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
diff --git a/modular_zubbers/code/modules/cargo/packs/goodies.dm b/modular_zubbers/code/modules/cargo/packs/goodies.dm
index 6cf11b5c447..977839cb62f 100644
--- a/modular_zubbers/code/modules/cargo/packs/goodies.dm
+++ b/modular_zubbers/code/modules/cargo/packs/goodies.dm
@@ -65,38 +65,6 @@
access_view = ACCESS_WEAPONS
cost = PAYCHECK_COMMAND * 1
-/datum/supply_pack/goody/mars_single
- special = FALSE
-
-/datum/supply_pack/goody/dumdum38
- special = FALSE
-
-/datum/supply_pack/goody/match38
- special = FALSE
-
-/datum/supply_pack/goody/rubber
- special = FALSE
-
-/datum/supply_pack/goody/ballistic_single
- special = FALSE
-
-/datum/supply_pack/goody/disabler_single
- special = FALSE
-
-/datum/supply_pack/goody/energy_single
- cost = PAYCHECK_COMMAND * 7
- special = FALSE
-
-/datum/supply_pack/goody/laser_single
- cost = PAYCHECK_COMMAND * 7
- special = FALSE
-
-/datum/supply_pack/goody/hell_single
- cost = PAYCHECK_CREW * 5
- special = FALSE
-
-/datum/supply_pack/goody/thermal_single
- special = FALSE
/datum/supply_pack/goody/medkit_surgery
name = "High Capacity Surgical Medkit"
diff --git a/modular_zubbers/code/modules/cargo/packs/security.dm b/modular_zubbers/code/modules/cargo/packs/security.dm
index 4c81aeb8fd4..e8b7064303c 100644
--- a/modular_zubbers/code/modules/cargo/packs/security.dm
+++ b/modular_zubbers/code/modules/cargo/packs/security.dm
@@ -19,7 +19,6 @@
/obj/item/ammo_box/speedloader/c38/hotshot,
/obj/item/ammo_box/speedloader/c38/iceblox,
)
- special = FALSE
//This makes the Security ammo crate use the cool advanced ammo boxes instead of the old ones
@@ -101,7 +100,7 @@
desc = "Extra supplies we got from some weird old guy in a blue suit. Contains six uniforms, \
vests, boots, gloves and helmets."
cost = 1116
- contraband = TRUE
+ order_flags = ORDER_CONTRABAND
contains = list(/obj/item/clothing/head/helmet/metrocophelmet = 6,
/obj/item/clothing/suit/armor/vest/alt/sec/metrocop = 6,
/obj/item/clothing/under/rank/security/metrocop = 6,
diff --git a/modular_zubbers/code/modules/changeling_zombies/event.dm b/modular_zubbers/code/modules/changeling_zombies/event.dm
index 69e2c3fa2c0..e94a43e287d 100644
--- a/modular_zubbers/code/modules/changeling_zombies/event.dm
+++ b/modular_zubbers/code/modules/changeling_zombies/event.dm
@@ -23,7 +23,7 @@
/datum/supply_pack/misc/changeling_zombie
name = "NT-CZV-1 Vials"
desc = "Contains a NT-CZV vials. Highly classified."
- special = TRUE //Cannot be ordered via cargo
+ order_flags = ORDER_CONTRABAND //Cannot be ordered via cargo
contains = list() //We don't put contents in this to do snowflake content in populate_contents
crate_type = /obj/structure/closet/crate/changeling_zombie
diff --git a/modular_zubbers/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm b/modular_zubbers/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm
index 5ce14f066b9..e80037fea6f 100644
--- a/modular_zubbers/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm
+++ b/modular_zubbers/code/modules/food_and_drinks/recipes/tablecraft/recipes_pizza.dm
@@ -1,3 +1,3 @@
// Having custom_materials set for the 9mm bullet in this recipe breaks unit tests
/datum/crafting_recipe/food/pizza/arnold
- crafting_flags = parent_type::crafting_flags & ~CRAFT_ENFORCE_MATERIALS_PARITY
+ crafting_flags = parent_type::crafting_flags & CRAFT_SKIP_MATERIALS_PARITY
diff --git a/modular_zubbers/code/modules/job_interns/job_interns.dm b/modular_zubbers/code/modules/job_interns/job_interns.dm
index a9ad275a387..80b9c11a7fc 100644
--- a/modular_zubbers/code/modules/job_interns/job_interns.dm
+++ b/modular_zubbers/code/modules/job_interns/job_interns.dm
@@ -69,7 +69,9 @@
return FALSE
if(!player_client?.prefs?.read_preference(/datum/preference/toggle/be_intern)) // If the pref is off, we stop here
return FALSE
- var/required_time
+ else
+ return TRUE // I am too lazy to fix this, let's just respect the prefs so we can RP being new
+/* var/required_time
var/playtime
if(internship_use_self_exp_type)
var/list/play_records = player_client?.prefs?.exp
@@ -78,11 +80,12 @@
return FALSE
playtime = play_records[title] ? text2num(play_records[title]) : 0
required_time = get_intern_time_threshold()
- else if(CONFIG_GET(flag/use_intern_master_job_unlock_threshold) && length(department_head) && SSjob.get_job(department_head[1]))
+/* else if(CONFIG_GET(flag/use_intern_master_job_unlock_threshold) && length(department_head) && SSjob.get_job(department_head[1]))
// Use first department head job as our master job to compare to
var/datum/job/master_job = SSjob.get_job(department_head[1])
playtime = player_client?.calc_exp_type(master_job.get_exp_req_type())
required_time = master_job.get_exp_req_amount()
+ */ // REWRITE NEEDED
else
var/exp_type = get_intern_exp_type()
if(!exp_type)
@@ -96,12 +99,12 @@
else
stack_trace("[src] client [player_client] checking for playtime resulted in null")
return FALSE
- if(!required_time && SSjob.get_job(department_head[1])) //Jobs lacking a department head shouldn't runtime
+ if(!required_time /* && SSjob.get_job(department_head[1]) */) //Jobs lacking a department head shouldn't runtime
stack_trace("[src] job failed to set intern time threshold")
return FALSE
if(playtime >= required_time)
return FALSE
- return TRUE
+ return TRUE */
/obj/item/card/id
var/intern_status = FALSE
diff --git a/modular_zubbers/code/modules/security/secmed/security_medic.dm b/modular_zubbers/code/modules/security/secmed/security_medic.dm
index fecc3ea2cc6..a07850d400e 100644
--- a/modular_zubbers/code/modules/security/secmed/security_medic.dm
+++ b/modular_zubbers/code/modules/security/secmed/security_medic.dm
@@ -2,7 +2,6 @@
title = JOB_SECURITY_MEDIC
description = "Patch up officers and prisoners, realize you don't have the tools to Tend Wounds, barge into Medbay and tell them how to do their jobs"
auto_deadmin_role_flags = DEADMIN_POSITION_SECURITY
- department_head = list(JOB_HEAD_OF_SECURITY)
faction = FACTION_STATION
total_positions = 1
spawn_positions = 1
diff --git a/sound/items/weapons/gun/rifle/attributions.txt b/sound/items/weapons/gun/rifle/attributions.txt
new file mode 100644
index 00000000000..2b2346b7d80
--- /dev/null
+++ b/sound/items/weapons/gun/rifle/attributions.txt
@@ -0,0 +1 @@
+Heavy SMG and the NAPAD smg sprite were both made by Trelus in GH pull request https://github.com/NovaSector/NovaSector/pull/4880
diff --git a/sound/items/weapons/gun/rifle/smg_heavy.ogg b/sound/items/weapons/gun/rifle/smg_heavy.ogg
new file mode 100644
index 00000000000..77a11a524a7
Binary files /dev/null and b/sound/items/weapons/gun/rifle/smg_heavy.ogg differ
diff --git a/sound/machines/airlock/airlock_latch_hiss.ogg b/sound/machines/airlock/airlock_latch_hiss.ogg
new file mode 100644
index 00000000000..ac89921d0e1
Binary files /dev/null and b/sound/machines/airlock/airlock_latch_hiss.ogg differ
diff --git a/sound/machines/airlock/attributions.txt b/sound/machines/airlock/attributions.txt
new file mode 100644
index 00000000000..ab6ad4b9dc2
--- /dev/null
+++ b/sound/machines/airlock/attributions.txt
@@ -0,0 +1 @@
+airlock_latch_hiss.ogg is sourced from Usernameis1337 under a CC Attribution 4.0 license via https://freesound.org/people/Usernameis1337/sounds/632755/
diff --git a/strings/tips.txt b/strings/tips.txt
index c7e77e98cb6..0ae2546f7e8 100644
--- a/strings/tips.txt
+++ b/strings/tips.txt
@@ -86,7 +86,6 @@ As a Medical Doctor, you can surgically implant or extract things from people's
As a Medical Doctor, you must target the correct limb and not be in combat mode when trying to perform surgery on someone. Right clicking your patient will intentionally fail the surgery step.
As a Monkey, you can crawl through air or scrubber vents by alt+left clicking them. You must drop everything you are wearing and holding to do this, however.
As a Monkey, you can still wear a few human items, such as backpacks, gas masks and hats, and still have two free hands.
-As a Morph, you can talk while disguised, but your words have a chance of being slurred, giving you away!
As a Nuclear Operative, communication is key! Use ; to speak to your fellow operatives and coordinate an attack plan.
As a Nuclear Operative, stick together! While your equipment is robust, your fellow operatives are much better at saving your life: they can drag you away from danger while stunned and provide cover fire.
As a Nuclear Operative, you might end up in a situation where the AI has bolted you into a room. Having some spare C4 in your pocket can save your life.
diff --git a/tgstation.dme b/tgstation.dme
index 6ce53d1731d..406204d71a9 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -209,6 +209,7 @@
#include "code\__DEFINES\regex.dm"
#include "code\__DEFINES\religion.dm"
#include "code\__DEFINES\research.dm"
+#include "code\__DEFINES\reskin.dm"
#include "code\__DEFINES\resonator.dm"
#include "code\__DEFINES\revolution.dm"
#include "code\__DEFINES\robots.dm"
@@ -1184,7 +1185,6 @@
#include "code\datums\ai\movement\ai_movement_complete_stop.dm"
#include "code\datums\ai\movement\ai_movement_dumb.dm"
#include "code\datums\ai\movement\ai_movement_jps.dm"
-#include "code\datums\ai\objects\mod.dm"
#include "code\datums\ai\objects\vending_machines\vending_machine_behaviors.dm"
#include "code\datums\ai\objects\vending_machines\vending_machine_controller.dm"
#include "code\datums\ai\robot_customer\robot_customer_behaviors.dm"
@@ -1415,6 +1415,7 @@
#include "code\datums\components\regenerator.dm"
#include "code\datums\components\religious_tool.dm"
#include "code\datums\components\rename.dm"
+#include "code\datums\components\reskinnable_atom.dm"
#include "code\datums\components\revenge_ability.dm"
#include "code\datums\components\rot.dm"
#include "code\datums\components\rotation.dm"
@@ -1694,6 +1695,7 @@
#include "code\datums\elements\digitalcamo.dm"
#include "code\datums\elements\disarm_attack.dm"
#include "code\datums\elements\door_pryer.dm"
+#include "code\datums\elements\drag_activation.dm"
#include "code\datums\elements\drag_pickup.dm"
#include "code\datums\elements\dryable.dm"
#include "code\datums\elements\earhealing.dm"
@@ -1712,6 +1714,7 @@
#include "code\datums\elements\floorloving.dm"
#include "code\datums\elements\footstep.dm"
#include "code\datums\elements\footstep_override.dm"
+#include "code\datums\elements\force_move_pulled.dm"
#include "code\datums\elements\forced_gravity.dm"
#include "code\datums\elements\frozen.dm"
#include "code\datums\elements\gags_recolorable.dm"
@@ -1779,6 +1782,7 @@
#include "code\datums\elements\radioactive.dm"
#include "code\datums\elements\ranged_armour.dm"
#include "code\datums\elements\raptor_food.dm"
+#include "code\datums\elements\reagent_scoopable_atom.dm"
#include "code\datums\elements\reagents_heated_on_fire.dm"
#include "code\datums\elements\reagents_item_heatable.dm"
#include "code\datums\elements\regal_rat_minion.dm"
@@ -2393,6 +2397,8 @@
#include "code\game\machinery\wall_healer.dm"
#include "code\game\machinery\washing_machine.dm"
#include "code\game\machinery\wishgranter.dm"
+#include "code\game\machinery\ai_core\_core.dm"
+#include "code\game\machinery\ai_core\core_construction.dm"
#include "code\game\machinery\camera\camera.dm"
#include "code\game\machinery\camera\camera_construction.dm"
#include "code\game\machinery\camera\motion.dm"
@@ -2516,7 +2522,6 @@
#include "code\game\objects\buckling.dm"
#include "code\game\objects\empulse.dm"
#include "code\game\objects\items.dm"
-#include "code\game\objects\items_reskin.dm"
#include "code\game\objects\obj_defense.dm"
#include "code\game\objects\objs.dm"
#include "code\game\objects\structures.dm"
@@ -2708,7 +2713,7 @@
#include "code\game\objects\items\pinpointer.dm"
#include "code\game\objects\items\pitchfork.dm"
#include "code\game\objects\items\plushes.dm"
-#include "code\game\objects\items\pneumaticCannon.dm"
+#include "code\game\objects\items\pneumatic_cannon.dm"
#include "code\game\objects\items\powerfist.dm"
#include "code\game\objects\items\puzzle_pieces.dm"
#include "code\game\objects\items\reflex_hammer.dm"
@@ -3012,7 +3017,6 @@
#include "code\game\objects\items\tools\painter\airlock_painter.dm"
#include "code\game\objects\items\tools\painter\decal_painter.dm"
#include "code\game\objects\items\tools\painter\paintable_decals.dm"
-#include "code\game\objects\structures\ai_core.dm"
#include "code\game\objects\structures\aliens.dm"
#include "code\game\objects\structures\bedsheet_bin.dm"
#include "code\game\objects\structures\billboard.dm"
@@ -3444,6 +3448,7 @@
#include "code\modules\antagonists\changeling\changeling_power.dm"
#include "code\modules\antagonists\changeling\fallen_changeling.dm"
#include "code\modules\antagonists\changeling\headslug_eggs.dm"
+#include "code\modules\antagonists\changeling\powers\_biodegrade_bioacid.dm"
#include "code\modules\antagonists\changeling\powers\absorb.dm"
#include "code\modules\antagonists\changeling\powers\adrenaline.dm"
#include "code\modules\antagonists\changeling\powers\augmented_eyesight.dm"
@@ -3889,6 +3894,14 @@
#include "code\modules\awaymissions\mission_code\snowdin.dm"
#include "code\modules\awaymissions\mission_code\stationCollision.dm"
#include "code\modules\awaymissions\mission_code\undergroundoutpost45.dm"
+#include "code\modules\awaymissions\mission_code\heretic\cbrn.dm"
+#include "code\modules\awaymissions\mission_code\heretic\fake_items.dm"
+#include "code\modules\awaymissions\mission_code\heretic\heretic.dm"
+#include "code\modules\awaymissions\mission_code\heretic\heretic_gateway_guns.dm"
+#include "code\modules\awaymissions\mission_code\heretic\heretic_gateway_misc.dm"
+#include "code\modules\awaymissions\mission_code\heretic\heretic_gateway_spawners.dm"
+#include "code\modules\awaymissions\mission_code\heretic\heretic_gateway_tram.dm"
+#include "code\modules\awaymissions\mission_code\heretic\zlevel.dm"
#include "code\modules\balloon_alert\balloon_alert.dm"
#include "code\modules\basketball\basketball.dm"
#include "code\modules\basketball\basketball_map_loading.dm"
@@ -7041,9 +7054,7 @@
#include "modular_skyrat\master_files\code\modules\cargo\exports\tools.dm"
#include "modular_skyrat\master_files\code\modules\cargo\exports\traitor.dm"
#include "modular_skyrat\master_files\code\modules\cargo\markets\market_items\weapons.dm"
-#include "modular_skyrat\master_files\code\modules\cargo\packs\general.dm"
#include "modular_skyrat\master_files\code\modules\cargo\packs\security.dm"
-#include "modular_skyrat\master_files\code\modules\cargo\packs\service.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences_savefile.dm"
#include "modular_skyrat\master_files\code\modules\client\preferences\_admin.dm"
diff --git a/tgui/bun.lock b/tgui/bun.lock
index acef62558fd..d5ca5c7549e 100644
--- a/tgui/bun.lock
+++ b/tgui/bun.lock
@@ -1,5 +1,6 @@
{
"lockfileVersion": 1,
+ "configVersion": 0,
"workspaces": {
"": {
"name": "tgui-workspace",
@@ -49,6 +50,7 @@
"marked-smartypants": "^1.1.9",
"react": "^19.1.0",
"react-dom": "^19.1.0",
+ "react-json-tree": "^0.20.0",
"tgui-core": "^5.3.1",
"tgui-dev-server": "workspace:*",
},
@@ -221,6 +223,8 @@
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
+ "@types/lodash": ["@types/lodash@4.17.21", "", {}, "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ=="],
+
"@types/mime": ["@types/mime@1.3.5", "", {}, "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="],
"@types/node": ["@types/node@24.0.12", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-LtOrbvDf5ndC9Xi+4QZjVL0woFymF/xSTKZKPgrrl7H7XoeDvnD+E2IclKVDyaK9UM756W/3BXqSU+JEHopA9g=="],
@@ -353,10 +357,14 @@
"cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="],
+ "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
+
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+ "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
+
"colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
"colorjs.io": ["colorjs.io@0.5.2", "", {}, "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw=="],
@@ -605,6 +613,8 @@
"ipaddr.js": ["ipaddr.js@2.2.0", "", {}, "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA=="],
+ "is-arrayish": ["is-arrayish@0.3.4", "", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="],
+
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
"is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
@@ -655,6 +665,8 @@
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
+ "lodash-es": ["lodash-es@4.17.21", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="],
+
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
"lru-cache": ["lru-cache@11.1.0", "", {}, "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A=="],
@@ -821,8 +833,12 @@
"react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
+ "react-base16-styling": ["react-base16-styling@0.10.0", "", { "dependencies": { "@types/lodash": "^4.17.0", "color": "^4.2.3", "csstype": "^3.1.3", "lodash-es": "^4.17.21" } }, "sha512-H1k2eFB6M45OaiRru3PBXkuCcn2qNmx+gzLb4a9IPMR7tMH8oBRXU5jGbPDYG1Hz+82d88ED0vjR8BmqU3pQdg=="],
+
"react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
+ "react-json-tree": ["react-json-tree@0.20.0", "", { "dependencies": { "@types/lodash": "^4.17.15", "react-base16-styling": "^0.10.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-h+f9fUNAxzBx1rbrgUF7+zSWKGHDtt2VPYLErIuB0JyKGnWgFMM21ksqQyb3EXwXNnoMW2rdE5kuAaubgGOx2Q=="],
+
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
"readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
@@ -919,6 +935,8 @@
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
+ "simple-swizzle": ["simple-swizzle@0.2.4", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="],
+
"sirv": ["sirv@2.0.4", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ=="],
"smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="],
diff --git a/tgui/docs/tgui-for-custom-html-popups.md b/tgui/docs/tgui-for-custom-html-popups.md
index b666f3a1f54..c98c40c7fa8 100644
--- a/tgui/docs/tgui-for-custom-html-popups.md
+++ b/tgui/docs/tgui-for-custom-html-popups.md
@@ -39,17 +39,13 @@ TGUI in /tg/station codebase has `/datum/asset`, that packs scripts and styleshe
```dm
window.initialize(
- fancy = user.client.prefs.read_preference(
- /datum/preference/toggle/tgui_fancy
- ),
assets = list(
get_asset_datum(/datum/asset/simple/tgui),
))
```
-You can see two new arguments:
+You can see new arguments:
-- `fancy` - See [Fancy mode](#fancy-mode)
- `assets` - This is a list of asset datums, and all JS and CSS in the assets will be loaded in the page.
Using asset datums has a big benefit over including `