diff --git a/.github/workflows/auto_changelog.yml b/.github/workflows/auto_changelog.yml
index a2a438ee0098..363b7f1c1801 100644
--- a/.github/workflows/auto_changelog.yml
+++ b/.github/workflows/auto_changelog.yml
@@ -18,7 +18,7 @@ jobs:
- name: Generate App Token
id: app-token-generation
- uses: actions/create-github-app-token@v2
+ uses: actions/create-github-app-token@v3
if: env.APP_PRIVATE_KEY != '' && env.APP_ID != ''
with:
app-id: ${{ secrets.APP_ID }}
diff --git a/.github/workflows/compile_changelogs.yml b/.github/workflows/compile_changelogs.yml
index 4703909d98af..a015da7b62f6 100644
--- a/.github/workflows/compile_changelogs.yml
+++ b/.github/workflows/compile_changelogs.yml
@@ -56,7 +56,7 @@ jobs:
- name: Generate App Token
id: app-token-generation
- uses: actions/create-github-app-token@v2
+ uses: actions/create-github-app-token@v3
if: env.APP_PRIVATE_KEY != '' && env.APP_ID != ''
with:
app-id: ${{ secrets.APP_ID }}
diff --git a/.github/workflows/generate_client_storage.yml b/.github/workflows/generate_client_storage.yml
index f601288b14b3..a337c23e7d2c 100644
--- a/.github/workflows/generate_client_storage.yml
+++ b/.github/workflows/generate_client_storage.yml
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Generate App Token
id: app-token-generation
- uses: actions/create-github-app-token@v2
+ uses: actions/create-github-app-token@v3
if: env.APP_PRIVATE_KEY != '' && env.APP_ID != ''
with:
app-id: ${{ secrets.APP_ID }}
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 70331a3ae0fb..b52d2fb34e2b 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -37,10 +37,18 @@ jobs:
operations-per-run: 300
- name: Filter staled pull requests for announcement
id: filter-prs
- uses: actions/github-script@v7
+ uses: actions/github-script@v8
with:
script: |
- return JSON.parse(context.job.steps.stale.outputs.staled-issues-prs).filter(issue => !!issue.pull_request)
+ const pull_requests = JSON.parse('${{steps.stale.outputs.staled-issues-prs}}')
+ .filter(issue => !!issue.pull_request)
+ .map(pr => ({
+ title: pr.title,
+ number: pr.number,
+ html_url: pr.pull_request.html_url,
+ }));
+
+ return JSON.stringify(pull_requests)
announce:
runs-on: ubuntu-24.04
@@ -64,7 +72,7 @@ jobs:
steps.secrets_set.outputs.SECRETS_ENABLED
with:
webhook_url: ${{ secrets.DISCORD_WEBHOOK }}
- title: ${{ matrix.pull_request.user.login }} - ${{ matrix.pull_request.title }}
+ title: ${{ matrix.pull_request.title }}
message: ${{ format('**Pull Request \#{0} automatically marked as stale.**', matrix.pull_request.number) }}
include_image: false
show_author: false
diff --git a/.github/workflows/update_tgs_dmapi.yml b/.github/workflows/update_tgs_dmapi.yml
index 6335f9047f50..f4254db6a2f2 100644
--- a/.github/workflows/update_tgs_dmapi.yml
+++ b/.github/workflows/update_tgs_dmapi.yml
@@ -40,7 +40,7 @@ jobs:
- name: Generate App Token
id: app-token-generation
- uses: actions/create-github-app-token@v2
+ uses: actions/create-github-app-token@v3
if: env.APP_PRIVATE_KEY != '' && env.APP_ID != ''
with:
app-id: ${{ secrets.APP_ID }}
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index d68b9ee0c4e0..588a60e23cab 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -293,10 +293,11 @@ DEFINE_BITFIELD(ammo_box_multiload, list(
#define BODY_ZONE_PRECISE_L_FOOT "l_foot"
#define BODY_ZONE_PRECISE_R_FOOT "r_foot"
-GLOBAL_LIST_INIT(all_body_zones, list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
-GLOBAL_LIST_INIT(limb_zones, list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
+// These lists are ordered as bodyparts would be ordered
+GLOBAL_LIST_INIT(all_body_zones, list(BODY_ZONE_CHEST, BODY_ZONE_HEAD, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
+GLOBAL_LIST_INIT(limb_zones, list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
GLOBAL_LIST_INIT(arm_zones, list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
-GLOBAL_LIST_INIT(leg_zones, list(BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
+GLOBAL_LIST_INIT(leg_zones, list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
GLOBAL_LIST_INIT(all_precise_body_zones, list(BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_PRECISE_GROIN, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_R_FOOT))
//We will round to this value in damage calculations.
diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_lighting.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_lighting.dm
index 391ed0112ac9..8e713f0260f9 100644
--- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_lighting.dm
+++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_lighting.dm
@@ -39,6 +39,17 @@
#define COMSIG_ATOM_SET_LIGHT_FLAGS "atom_set_light_flags"
///Called right after the atom changes the value of light_flags to a different one, from base of [/atom/proc/set_light_flags]: (old_flags)
#define COMSIG_ATOM_UPDATE_LIGHT_FLAGS "atom_update_light_flags"
+///Called right before the atom changes the value of light_render_source to a different one, from base [atom/proc/set_light_render_source]: (new_render_source)
+#define COMSIG_ATOM_SET_LIGHT_RENDER_SOURCE "atom_set_light_render_source"
+///Called right after the atom changes the value of light_render_source to a different one, from base of [/atom/proc/set_light_render_source]: (old_render_source)
+#define COMSIG_ATOM_UPDATE_LIGHT_RENDER_SOURCE "atom_update_light_render_source"
///Called when an atom has a light template applied to it. Frombase of [/datum/light_template/proc/mirror_onto]: ()
#define COMSIG_ATOM_LIGHT_TEMPLATE_MIRRORED "atom_light_template_mirrored"
+
+///Called when an atom's overlay component applies visuals, from base of [/datum/component/overlay_lighting/proc/show_to_holder]: (atom/movable/light_holder)
+#define COMSIG_ATOM_OVERLAY_LIGHT_APPLIED "atom_overlay_light_applied"
+
+///Called when an atom's overlay component hides its visuals, from base of [/datum/component/overlay_lighting/proc/hide_from_holder]: (atom/movable/light_holder)
+#define COMSIG_ATOM_OVERLAY_LIGHT_REMOVED "atom_overlay_light_removed"
+
diff --git a/code/__DEFINES/dcs/signals/signals_light_intercept.dm b/code/__DEFINES/dcs/signals/signals_light_intercept.dm
new file mode 100644
index 000000000000..8b4694d9d0e0
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_light_intercept.dm
@@ -0,0 +1,2 @@
+/// from base of [/datum/light_middleman/proc/light_modified] : ()
+#define COMSIG_LIGHT_MIDDLEMAN_UPDATED "light_middleman_updated"
diff --git a/code/__DEFINES/dcs/signals/signals_medical.dm b/code/__DEFINES/dcs/signals/signals_medical.dm
index 7429b6347f34..905118340ec2 100644
--- a/code/__DEFINES/dcs/signals/signals_medical.dm
+++ b/code/__DEFINES/dcs/signals/signals_medical.dm
@@ -26,3 +26,6 @@
#define COMSIG_LIVING_OPERATING_ON "living_operating_on"
/// Sent from /mob/living/perform_surgery: (mob/living/surgeon, list/possible_operations)
#define COMSIG_ATOM_BEING_OPERATED_ON "atom_being_operated_on"
+
+/// From /obj/item/ph_meter/interact_with_atom(): (atom/source, mob/user)
+#define COMSIG_ON_REAGENT_SCAN "on_reagent_scan"
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
index 78c4bc82c887..6b0295e2cd68 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm
@@ -386,3 +386,6 @@
/// Sent to a mob when one of their bodypart's surgery state changes, OR sent from the basic_surgery_state holder when its surgery state changes (old_state, new_state, changed_states)
#define COMSIG_LIVING_UPDATING_SURGERY_STATE "carbon_updating_surgery_state"
+
+/// Sent to a mob when its player DNRs
+#define COMSIG_LIVING_DNR "living_dnr"
diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm
index 9125770ea3c3..dd75bf32ba15 100644
--- a/code/__DEFINES/dcs/signals/signals_object.dm
+++ b/code/__DEFINES/dcs/signals/signals_object.dm
@@ -151,6 +151,10 @@
#define COMSIG_ITEM_STORED "item_stored"
///from base of datum/storage/handle_exit(): (datum/storage/storage)
#define COMSIG_ITEM_UNSTORED "item_unstored"
+///from base of obj/item/do_pickup_animation(): ()
+#define COMSIG_ITEM_BEFORE_PICKUP_ANIMATION "item_before_pickup_animation"
+///from base of obj/item/do_drop_animation(): ()
+#define COMSIG_ITEM_BEFORE_DROP_ANIMATION "item_before_drop_animation"
/**
* From base of datum/strippable_item/get_alternate_actions(): (atom/owner, mob/user, list/alt_actions)
@@ -655,5 +659,15 @@
#define COMSIG_ITEM_IN_UNWRAPPED_TRAITOR_MAIL "traitor_mail_opened"
#define COMPONENT_TRAITOR_MAIL_HANDLED (1<<0)
+/// Send to items that have been unwrapped from a gift
+#define COMSIG_ITEM_OPENED_FROM_GIFT "gift_opened"
+
+/// From /obj/machinery/vending/ui_interact(): (mob/user, datum/vending_ui/ui)
+#define COMSIG_VENDING_UI_INTERACT "vending_ui_interact"
+ #define VENDING_DENIED (1<<0)
+
+/// From /obj/machinery/vending/dispense(): (obj/item/vended_item)
+#define COMSIG_VENDING_DISPENSED "vending_dispensed"
+
/// Sent from /datum/component/reflection when the reflection is updated to the mob reflecting: (atom/movable/reflecting_in, obj/effect/abstract/reflection)
#define COMSIG_REFLECTION_UPDATED "reflection_updated"
diff --git a/code/__DEFINES/dcs/signals/signals_plane_master_group.dm b/code/__DEFINES/dcs/signals/signals_plane_master_group.dm
index d27adb5f8c95..61bd33c3e229 100644
--- a/code/__DEFINES/dcs/signals/signals_plane_master_group.dm
+++ b/code/__DEFINES/dcs/signals/signals_plane_master_group.dm
@@ -1,2 +1,2 @@
-/// from /datum/plane_master_group/proc/set_hud(): (datum/hud/new_hud)
+/// from /datum/plane_master_group/proc/set_hud(): (datum/hud/old_hud, datum/hud/new_hud)
#define COMSIG_GROUP_HUD_CHANGED "group_hud_changed"
diff --git a/code/__DEFINES/dcs/signals/signals_spell.dm b/code/__DEFINES/dcs/signals/signals_spell.dm
index ebbdcc4e2bee..1154eba338f1 100644
--- a/code/__DEFINES/dcs/signals/signals_spell.dm
+++ b/code/__DEFINES/dcs/signals/signals_spell.dm
@@ -14,6 +14,10 @@
/// Return from before cast signals to prevent the spell from going on cooldown before aftercast.
#define SPELL_NO_IMMEDIATE_COOLDOWN (1 << 2)
+/// Sent from /datum/action/cooldown/spell/can_cast_check() to the spell: (feedback)
+#define COMSIG_SPELL_CAN_CAST_CHECK "can_cast_spell"
+ // Return SPELL_CANCEL_CAST to prevent the spell from being cast
+
/// Sent to an mob when a [/datum/action/cooldown/spell] calls try_invoke() to the caster: (datum/action/cooldown/spell/spell, feedback)
#define COMSIG_MOB_TRY_INVOKE_SPELL "try_invoke_spell"
/// The spell gets canceled
diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm
index 08e76678fff3..9b843a695198 100644
--- a/code/__DEFINES/lighting.dm
+++ b/code/__DEFINES/lighting.dm
@@ -11,6 +11,11 @@
/// Nonesensical value for light color, used for null checks.
#define NONSENSICAL_VALUE -99999
+// Light systems that use the overlay light component
+#define IS_OVERLAY_LIGHT_SYSTEM(system) (system == OVERLAY_LIGHT || system == OVERLAY_LIGHT_DIRECTIONAL || system == OVERLAY_LIGHT_BEAM)
+// Light systems that use the cone image of the overlay light component
+#define IS_OVERLAY_CONE_LIGHT_SYSTEM(system) (system == OVERLAY_LIGHT_DIRECTIONAL || system == OVERLAY_LIGHT_BEAM)
+
/// Is our overlay light source attached to another movable (its loc), meaning that the lighting component should go one level deeper.
#define LIGHT_ATTACHED (1<<0)
/// Freezes a light in its current state, blocking any attempts at modification
diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm
index c4f95c49eae6..d2fe40a502cb 100644
--- a/code/__DEFINES/maths.dm
+++ b/code/__DEFINES/maths.dm
@@ -53,6 +53,9 @@
/// Increments a value and wraps it if it exceeds some value. Can be used to circularly iterate through a list through `idx = WRAP_UP(idx, length_of_list)`.
#define WRAP_UP(val, max) (((val) % (max)) + 1)
+/// Helper that increments and wraps the passed in number when it hits the integer limit
+#define WRAP_UID(val) WRAP_UP(val, SHORT_REAL_LIMIT - 1)
+
// Real modulus that handles decimals
#define MODULUS(x, y) ( (x) - FLOOR(x, y))
@@ -233,6 +236,9 @@
#define LORENTZ_DISTRIBUTION(x, s) ( s*tan(TODEGREES(PI*(rand()-0.5))) + x )
#define LORENTZ_CUMULATIVE_DISTRIBUTION(x, y, s) ( (1/PI)*TORADIANS(arctan((x-(y))/s)) + 1/2 )
+/// Fucked up like upside down cauchy dist that I've pinned to 1 so I can use it as a multiplier
+/// https://www.desmos.com/calculator/bt4tfavvi7
+#define ANCHORED_INVERSE_CAUCHY(s) (2 - ( 1 / (s * (1 + ((rand() - 0.5) / s) ** 2 ))) * (s + (0.5 ** 2) / s))
#define RULE_OF_THREE(a, b, x) ((a*x)/b)
diff --git a/code/__DEFINES/monkeys.dm b/code/__DEFINES/monkeys.dm
index a4929bb84dcb..8dfccc937f76 100644
--- a/code/__DEFINES/monkeys.dm
+++ b/code/__DEFINES/monkeys.dm
@@ -35,5 +35,10 @@
/// probability of reducing aggro by one when the monkey attacks
#define MONKEY_HATRED_REDUCTION_PROB 20
+/// Monkey was calmed, such as from weed
+#define MONKEY_CALMED_HATRED_AMOUNT -2
+/// Monkey was angered, such as from alcohol
+#define MONKEY_ANGERED_HATRED_AMOUNT 2
+
///Monkey recruit cooldown
#define MONKEY_RECRUIT_COOLDOWN (1 MINUTES)
diff --git a/code/__DEFINES/movement.dm b/code/__DEFINES/movement.dm
index e07fe65552d5..57b7825bcc95 100644
--- a/code/__DEFINES/movement.dm
+++ b/code/__DEFINES/movement.dm
@@ -29,15 +29,18 @@ GLOBAL_VAR_INIT(glide_size_multiplier, 1.0)
//Movement loop flags
///Should the loop act immediately following its addition?
-#define MOVEMENT_LOOP_START_FAST (1<<0)
+#define MOVEMENT_LOOP_START_INSTANT (1<<0)
+///Should the loop act as soon as is reasonable?
+///(always 1 tick after the next visual tick, makes behavior consistent regardless of when the SS fires in the tick)
+#define MOVEMENT_LOOP_START_FAST (1<<1)
///Do we not use the priority system?
-#define MOVEMENT_LOOP_IGNORE_PRIORITY (1<<1)
+#define MOVEMENT_LOOP_IGNORE_PRIORITY (1<<2)
///Should we override the loop's glide?
-#define MOVEMENT_LOOP_IGNORE_GLIDE (1<<2)
+#define MOVEMENT_LOOP_IGNORE_GLIDE (1<<3)
///Should we not update our movables dir on move?
-#define MOVEMENT_LOOP_NO_DIR_UPDATE (1<<3)
+#define MOVEMENT_LOOP_NO_DIR_UPDATE (1<<4)
///Is the loop moving the movable outside its control, like it's an external force? e.g. footsteps won't play if enabled.
-#define MOVEMENT_LOOP_OUTSIDE_CONTROL (1<<4)
+#define MOVEMENT_LOOP_OUTSIDE_CONTROL (1<<5)
// Movement loop status flags
/// Has the loop been paused, soon to be resumed?
diff --git a/code/__DEFINES/paper.dm b/code/__DEFINES/paper.dm
index 9cede4214bd9..4eea9e7c2f65 100644
--- a/code/__DEFINES/paper.dm
+++ b/code/__DEFINES/paper.dm
@@ -14,9 +14,6 @@
/// Should be able to stamp paper.
#define MODE_STAMPING 2
-#define BARCODE_SCANNER_CHECKIN "check_in"
-#define BARCODE_SCANNER_INVENTORY "inventory"
-
#define IS_WRITING_UTENSIL(thing) (thing?.get_writing_implement_details()?["interaction_mode"] == MODE_WRITING)
/**
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index 1ae8403c644f..6421cdee67ac 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -47,6 +47,7 @@
#define PARALLAX_HIGH "High"
#define PARALLAX_MED "Medium"
#define PARALLAX_LOW "Low"
+#define PARALLAX_BOOMER "Old"
#define PARALLAX_DISABLE "Disabled"
#define SCALING_METHOD_NORMAL "normal"
diff --git a/code/__DEFINES/quirks.dm b/code/__DEFINES/quirks.dm
index 8cc1dee678ba..41b2882e37e8 100644
--- a/code/__DEFINES/quirks.dm
+++ b/code/__DEFINES/quirks.dm
@@ -17,3 +17,5 @@
/// Quirk is similar to brain trauma and should be shown in medical guides as one.
/// You don't need to set this on quirks that apply a trauma, that's redundant.
#define QUIRK_TRAUMALIKE (1<<5)
+/// Do not transfer this quirk via transfer_quirk_datums
+#define QUIRK_NO_TRANSFER (1<<6)
diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm
index ab8fee7c9a08..fcf8b853cada 100644
--- a/code/__DEFINES/surgery.dm
+++ b/code/__DEFINES/surgery.dm
@@ -54,6 +54,9 @@
/// Helper to figure out if a limb is a peg limb
#define IS_PEG_LIMB(limb) (limb.bodytype & BODYTYPE_PEG)
+/// Is the bodypart a stump
+#define IS_STUMP(limb) (limb.bodypart_flags & BODYPART_STUMP)
+
// Flags for the bodypart_flags var on /obj/item/bodypart
/// Bodypart cannot be dismembered or amputated
#define BODYPART_UNREMOVABLE (1<<0)
@@ -65,6 +68,8 @@
#define BODYPART_UNHUSKABLE (1<<3)
/// Bodypart has never been added to a mob
#define BODYPART_VIRGIN (1<<4)
+/// Not a full bodypart, but in fact is part of a missing limb
+#define BODYPART_STUMP (1<<5)
// Bodypart change blocking flags
///Bodypart does not get replaced during set_species()
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 7f335afed043..dea44f553155 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -52,6 +52,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
// Hud traits
/// This hud is owned by a client with an open escape menu
#define TRAIT_ESCAPE_MENU_OPEN "escape_menu_open"
+/// This hud has parallax displayed on it
+#define TRAIT_PARALLAX_DISPLAYED "parallax_displayed"
// Mob traits
/// Forces the user to stay unconscious.
@@ -1462,6 +1464,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_BEAST_EMPATHY "beast_empathy" // you're good with animals, such as with taming them
#define TRAIT_STURDY_FRAME "sturdy_frame" // you suffer much lesser effects from equipment that slows you down
+/// Has this mob been tamed?
+#define TRAIT_TAMED "tamed"
+
/// This item cannot be selected for or used by a theft objective (Spies, Traitors, etc.)
#define TRAIT_ITEM_OBJECTIVE_BLOCKED "item_objective_blocked"
/// This trait lets you attach limbs to any player without surgery.
@@ -1592,9 +1597,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Trait that signals to objects on this turf that its open (has UNDERFLOOR_INTERACTIBLE) but still covers them
#define TRAIT_UNCOVERED_TURF "uncovered_turf"
-/// A trait that blocks the metabolism of formaldehyde
-#define TRAIT_BLOCK_FORMALDEHYDE_METABOLISM "block_formaldehyde_metabolism"
-
///Attached to objects currently on tables and such, allowing them to walk on other objects without the climbing delay
#define TRAIT_ON_CLIMBABLE "on_climbable"
diff --git a/code/__DEFINES/visual_helpers.dm b/code/__DEFINES/visual_helpers.dm
index fda7f6648092..d3cd58f672ba 100644
--- a/code/__DEFINES/visual_helpers.dm
+++ b/code/__DEFINES/visual_helpers.dm
@@ -23,3 +23,8 @@
/// Much like [SET_BASE_PIXEL], except it will not effect pixel offsets in mapping programs
#define SET_BASE_PIXEL_NOMAP(x, y) MAP_SWITCH(SET_BASE_PIXEL(x, y), _SET_BASE_PIXEL_NO_OFFSET(x, y))
+
+/// What world.time will we next complete a visual tick?
+/// This is important because we often want to avoid moving an object twice in a tick, since this can lead to teleportation instead of gliding
+/// (such as when you move an atom that just spawned)
+#define NEXT_VISUAL_TICK (GLOB.last_maptick_time + world.tick_lag)
diff --git a/code/__DEFINES/wiremod.dm b/code/__DEFINES/wiremod.dm
index b606f938869a..8922a522c173 100644
--- a/code/__DEFINES/wiremod.dm
+++ b/code/__DEFINES/wiremod.dm
@@ -30,6 +30,8 @@
#define PORT_TYPE_TABLE "table"
/// Options datatype. Derivative of string.
#define PORT_TYPE_OPTION "option"
+/// Boolean datatype. Derivative of number.
+#define PORT_TYPE_BOOLEAN "boolean"
// Composite datatypes
#define PORT_COMPOSITE_TYPE_LIST "list"
diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm
index 4ff1e10a22f2..5e6f71b33c9c 100644
--- a/code/__HELPERS/cmp.dm
+++ b/code/__HELPERS/cmp.dm
@@ -161,7 +161,7 @@
/// Orders bodyparts by their body_part value, ascending.
/proc/cmp_bodypart_by_body_part_asc(obj/item/bodypart/limb_one, obj/item/bodypart/limb_two)
- return limb_one.body_part - limb_two.body_part
+ return limb_one::body_part - limb_two::body_part
/// Orders by integrated circuit weight
/proc/cmp_port_order_asc(datum/port/compare1, datum/port/compare2)
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index cc01b194bd8d..eb8079bd902a 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -137,6 +137,16 @@ DEFINE_BITFIELD(disease_flags, list(
"CURABLE" = CURABLE,
))
+DEFINE_BITFIELD(ss_flags, list(
+ "SS_NO_INIT" = SS_NO_INIT,
+ "SS_NO_FIRE" = SS_NO_FIRE,
+ "SS_BACKGROUND" = SS_BACKGROUND,
+ "SS_TICKER" = SS_TICKER,
+ "SS_KEEP_TIMING" = SS_KEEP_TIMING,
+ "SS_POST_FIRE_TIMING" = SS_POST_FIRE_TIMING,
+ "SS_OK_TO_FAIL_INIT" = SS_OK_TO_FAIL_INIT,
+))
+
DEFINE_BITFIELD(flags_1, list(
"ADMIN_SPAWNED_1" = ADMIN_SPAWNED_1,
"ALLOW_DARK_PAINTS_1" = ALLOW_DARK_PAINTS_1,
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 4ffa2b3abda0..3bda9eee5ba4 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -150,6 +150,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
),
/datum/hud = list(
"TRAIT_ESCAPE_MENU_OPEN" = TRAIT_ESCAPE_MENU_OPEN,
+ "TRAIT_PARALLAX_DISPLAYED" = TRAIT_PARALLAX_DISPLAYED,
),
/datum/wound = list(
"TRAIT_WOUND_SCANNED" = TRAIT_WOUND_SCANNED,
@@ -200,7 +201,6 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_BIRTHDAY_BOY" = TRAIT_BIRTHDAY_BOY,
"TRAIT_BLOB_ALLY" = TRAIT_BLOB_ALLY,
"TRAIT_BLOCKING_PROJECTILES" = TRAIT_BLOCKING_PROJECTILES,
- "TRAIT_BLOCK_FORMALDEHYDE_METABOLISM" = TRAIT_BLOCK_FORMALDEHYDE_METABOLISM,
"TRAIT_BLOCK_SECHUD" = TRAIT_BLOCK_SECHUD,
"TRAIT_BLOCK_SHUTTLE_MOVEMENT" = TRAIT_BLOCK_SHUTTLE_MOVEMENT,
"TRAIT_BLOODSHOT_EYES" = TRAIT_BLOODSHOT_EYES,
@@ -594,6 +594,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_TACKLING_WINGED_ATTACKER" = TRAIT_TACKLING_WINGED_ATTACKER,
"TRAIT_TACTICALLY_CAMOUFLAGED" = TRAIT_TACTICALLY_CAMOUFLAGED,
"TRAIT_TAGGER" = TRAIT_TAGGER,
+ "TRAIT_TAMED" = TRAIT_TAMED,
"TRAIT_TEMPORARY_BODY" = TRAIT_TEMPORARY_BODY,
"TRAIT_TENACIOUS" = TRAIT_TENACIOUS,
"TRAIT_TENTACLE_IMMUNE" = TRAIT_TENTACLE_IMMUNE,
diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm
index d46153ce8df3..f868cbc5c9af 100644
--- a/code/_globalvars/traits/admin_tooling.dm
+++ b/code/_globalvars/traits/admin_tooling.dm
@@ -310,6 +310,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_TACKLING_TAILED_DEFENDER" = TRAIT_TACKLING_TAILED_DEFENDER,
"TRAIT_TACKLING_TAILED_POUNCE" = TRAIT_TACKLING_TAILED_POUNCE,
"TRAIT_TAGGER" = TRAIT_TAGGER,
+ "TRAIT_TAMED" = TRAIT_TAMED,
"TRAIT_TENTACLE_IMMUNE" = TRAIT_TENTACLE_IMMUNE,
"TRAIT_TESLA_SHOCKIMMUNE" = TRAIT_TESLA_SHOCKIMMUNE,
"TRAIT_TETRODOTOXIN_HEALING" = TRAIT_TETRODOTOXIN_HEALING,
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index e01aab067056..2a5afbcba1c2 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -404,7 +404,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
screenmob.reload_fullscreen()
if(screenmob == mymob)
- update_parallax_pref(screenmob)
+ update_parallax_pref()
else
viewmob.hud_used.update_parallax_pref()
diff --git a/code/_onclick/hud/parallax/parallax.dm b/code/_onclick/hud/parallax/parallax.dm
index bc276cfd25aa..8cebb52a3694 100644
--- a/code/_onclick/hud/parallax/parallax.dm
+++ b/code/_onclick/hud/parallax/parallax.dm
@@ -1,120 +1,76 @@
-
-/datum/hud/proc/create_parallax(mob/viewmob)
- var/mob/screenmob = viewmob || mymob
- var/client/C = screenmob.client
-
- if (!apply_parallax_pref(viewmob)) //don't want shit computers to crash when specing someone with insane parallax, so use the viewer's pref
- for(var/atom/movable/screen/plane_master/parallax as anything in get_true_plane_masters(PLANE_SPACE_PARALLAX))
- parallax.hide_plane(screenmob)
- return
-
- for(var/atom/movable/screen/plane_master/parallax as anything in get_true_plane_masters(PLANE_SPACE_PARALLAX))
- parallax.unhide_plane(screenmob)
-
- if(isnull(C.parallax_rock))
- C.parallax_rock = new(null, src)
- C.screen |= C.parallax_rock
-
- if(!length(C.parallax_layers_cached))
- C.parallax_layers_cached = list()
- C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_1(null, src)
- C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_2(null, src)
- C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/umbra(null, src) // DARKPACK EDIT ADD - UMBRA
- /* // DARKPACK EDIT REMOVAL - We dont use these layers
- C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/planet(null, src)
- if(SSparallax.random_layer)
- C.parallax_layers_cached += new SSparallax.random_layer.type(null, src, FALSE, SSparallax.random_layer)
- C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_3(null, src)
- */
-
- C.parallax_layers = C.parallax_layers_cached.Copy()
-
- if (length(C.parallax_layers) > C.parallax_layers_max)
- C.parallax_layers.len = C.parallax_layers_max
-
- C.parallax_rock.vis_contents = C.parallax_layers
- // We could do not do parallax for anything except the main plane group
- // This could be changed, but it would require refactoring this whole thing
- // And adding non client particular hooks for all the inputs, and I do not have the time I'm sorry :(
- for(var/atom/movable/screen/plane_master/plane_master as anything in screenmob.hud_used.get_true_plane_masters(PLANE_SPACE))
- if(screenmob != mymob)
- C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen
- C.screen += plane_master
- // this color makes parallax not black
- plane_master.color = list(
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 1, 1, 1, 1,
- 0, 0, 0, 0
- )
-
-/datum/hud/proc/remove_parallax(mob/viewmob)
- var/mob/screenmob = viewmob || mymob
- var/client/C = screenmob.client
- C.screen -= (C.parallax_rock)
- for(var/atom/movable/screen/plane_master/plane_master as anything in screenmob.hud_used.get_true_plane_masters(PLANE_SPACE))
- if(screenmob != mymob)
- C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen
- C.screen += plane_master
- plane_master.color = initial(plane_master.color)
- C.parallax_layers = null
-
-/datum/hud/proc/apply_parallax_pref(mob/viewmob)
- var/mob/screenmob = viewmob || mymob
- var/turf/screen_location = get_turf(screenmob)
+/// Decides if parallax should be rendered or not, and sets things up accordingly
+/datum/hud/proc/check_parallax()
+ var/client/displaying_client = mymob.client
+ if(isnull(displaying_client.parallax_rock))
+ displaying_client.parallax_rock = new(null, null, displaying_client)
+
+ /// Applies our preferences to our existing display
+ apply_parallax_pref()
+ var/atom/movable/screen/parallax_home/rock = displaying_client?.parallax_rock
+
+ // Because other parts of the code can just REMOVE US FROM THE SCREEN for no reason as a joke
+ if (rock.displaying_layers)
+ ADD_TRAIT(src, TRAIT_PARALLAX_DISPLAYED, TRAIT_GENERIC)
+ displaying_client.screen |= rock
+ else
+ REMOVE_TRAIT(src, TRAIT_PARALLAX_DISPLAYED, TRAIT_GENERIC)
+ displaying_client.screen -= rock
+
+/datum/hud/proc/apply_parallax_pref()
+ var/turf/screen_location = get_turf(mymob)
+ var/client/displaying_client = mymob.client
+ var/atom/movable/screen/parallax_home/rock = displaying_client.parallax_rock
if(SSmapping.level_trait(screen_location?.z, ZTRAIT_NOPARALLAX))
- return FALSE
+ rock.set_layer_settings(layers_to_draw = 0, draw_old_space = FALSE, animate_parallax = FALSE)
+ return
- if (SSlag_switch.measures[DISABLE_PARALLAX] && !HAS_TRAIT(viewmob, TRAIT_BYPASS_MEASURES))
- return FALSE
+ if (SSlag_switch.measures[DISABLE_PARALLAX] && !HAS_TRAIT(mymob, TRAIT_BYPASS_MEASURES))
+ rock.set_layer_settings(layers_to_draw = 0, draw_old_space = FALSE, animate_parallax = FALSE)
+ return
- var/client/C = screenmob.client
// Default to HIGH
- var/parallax_selection = C?.prefs.read_preference(/datum/preference/choiced/parallax) || PARALLAX_HIGH
+ var/parallax_selection = displaying_client?.prefs.read_preference(/datum/preference/choiced/parallax) || PARALLAX_HIGH
switch(parallax_selection)
if (PARALLAX_INSANE)
- C.parallax_layers_max = 5
- C.do_parallax_animations = TRUE
- return TRUE
+ rock.set_layer_settings(layers_to_draw = 5, draw_old_space = FALSE, animate_parallax = TRUE)
+ return
if(PARALLAX_HIGH)
- C.parallax_layers_max = 4
- C.do_parallax_animations = TRUE
- return TRUE
+ rock.set_layer_settings(layers_to_draw = 4, draw_old_space = FALSE, animate_parallax = TRUE)
+ return
if (PARALLAX_MED)
- C.parallax_layers_max = 3
- C.do_parallax_animations = TRUE
- return TRUE
+ rock.set_layer_settings(layers_to_draw = 3, draw_old_space = FALSE, animate_parallax = TRUE)
+ return
if (PARALLAX_LOW)
- C.parallax_layers_max = 1
- C.do_parallax_animations = FALSE
- return TRUE
+ rock.set_layer_settings(layers_to_draw = 1, draw_old_space = FALSE, animate_parallax = FALSE)
+ return
+
+ if (PARALLAX_BOOMER)
+ rock.set_layer_settings(layers_to_draw = 0, draw_old_space = TRUE, animate_parallax = TRUE)
+ return
if (PARALLAX_DISABLE)
- return FALSE
+ rock.set_layer_settings(layers_to_draw = 0, draw_old_space = FALSE, animate_parallax = FALSE)
+ return
-/datum/hud/proc/update_parallax_pref(mob/viewmob)
- var/mob/screen_mob = viewmob || mymob
- if(!screen_mob.client)
+/datum/hud/proc/update_parallax_pref()
+ if(!mymob.client)
return
- remove_parallax(screen_mob)
- create_parallax(screen_mob)
- update_parallax(screen_mob)
+ check_parallax()
+ update_parallax()
// This sets which way the current shuttle is moving (returns true if the shuttle has stopped moving so the caller can append their animation)
-/datum/hud/proc/set_parallax_movedir(new_parallax_movedir = NONE, skip_windups, mob/viewmob)
+/datum/hud/proc/set_parallax_movedir(new_parallax_movedir = NONE, skip_windups)
. = FALSE
- var/mob/screenmob = viewmob || mymob
- var/client/C = screenmob.client
- if(new_parallax_movedir == C.parallax_movedir)
+ var/client/displaying_client = mymob.client
+ if(new_parallax_movedir == displaying_client.parallax_movedir)
return
- var/animation_dir = new_parallax_movedir || C.parallax_movedir
+ var/animation_dir = new_parallax_movedir || displaying_client.parallax_movedir
var/matrix/new_transform
switch(animation_dir)
if(NORTH)
@@ -127,17 +83,17 @@
new_transform = matrix(1, 0,-480, 0, 1, 0)
var/longest_timer = 0
- for(var/key in C.parallax_animate_timers)
- deltimer(C.parallax_animate_timers[key])
- C.parallax_animate_timers = list()
- for(var/atom/movable/screen/parallax_layer/layer as anything in C.parallax_layers)
+ for(var/key in displaying_client.parallax_animate_timers)
+ deltimer(displaying_client.parallax_animate_timers[key])
+ displaying_client.parallax_animate_timers = list()
+ for(var/atom/movable/screen/parallax_layer/layer as anything in displaying_client.parallax_rock.parallax_layers)
var/scaled_time = PARALLAX_LOOP_TIME / layer.speed
if(new_parallax_movedir == NONE) // If we're stopping, we need to stop on the same dime, yeah?
scaled_time = PARALLAX_LOOP_TIME
longest_timer = max(longest_timer, scaled_time)
if(skip_windups)
- update_parallax_motionblur(C, layer, new_parallax_movedir, new_transform)
+ update_parallax_motionblur(displaying_client, layer, new_parallax_movedir, new_transform)
continue
layer.transform = new_transform
@@ -147,15 +103,15 @@
//queue up another animate so lag doesn't create a shutter
animate(transform = new_transform, time = 0)
animate(transform = matrix(), time = scaled_time / 2)
- C.parallax_animate_timers[layer] = addtimer(CALLBACK(src, PROC_REF(update_parallax_motionblur), C, layer, new_parallax_movedir, new_transform), scaled_time, TIMER_CLIENT_TIME|TIMER_STOPPABLE)
+ displaying_client.parallax_animate_timers[layer] = addtimer(CALLBACK(src, PROC_REF(update_parallax_motionblur), displaying_client, layer, new_parallax_movedir, new_transform), scaled_time, TIMER_CLIENT_TIME|TIMER_STOPPABLE)
- C.dont_animate_parallax = world.time + min(longest_timer, PARALLAX_LOOP_TIME)
- C.parallax_movedir = new_parallax_movedir
+ displaying_client.dont_animate_parallax = world.time + min(longest_timer, PARALLAX_LOOP_TIME)
+ displaying_client.parallax_movedir = new_parallax_movedir
-/datum/hud/proc/update_parallax_motionblur(client/C, atom/movable/screen/parallax_layer/layer, new_parallax_movedir, matrix/new_transform)
- if(!C)
+/datum/hud/proc/update_parallax_motionblur(client/displaying_client, atom/movable/screen/parallax_layer/layer, new_parallax_movedir, matrix/new_transform)
+ if(!displaying_client)
return
- C.parallax_animate_timers -= layer
+ displaying_client.parallax_animate_timers -= layer
// If we are moving in a direction, we used the QUAD_EASING function with EASE_IN
// This means our position function is x^2. This is always LESS then the linear we're using here
@@ -167,33 +123,34 @@
animate(layer, transform = new_transform, time = 0, loop = -1, flags = ANIMATION_END_NOW)
animate(transform = matrix(), time = scaled_time)
-/datum/hud/proc/update_parallax(mob/viewmob)
- var/mob/screenmob = viewmob || mymob
- var/client/C = screenmob.client
- var/turf/posobj = get_turf(C.eye)
+/datum/hud/proc/update_parallax()
+ var/client/displaying_client = mymob.client
+ var/turf/posobj = get_turf(displaying_client.eye)
if(!posobj)
return
var/area/areaobj = posobj.loc
// Update the movement direction of the parallax if necessary (for shuttles)
- set_parallax_movedir(areaobj.parallax_movedir, FALSE, screenmob)
+ set_parallax_movedir(areaobj.parallax_movedir, FALSE, mymob)
- if(!C.previous_turf || (C.previous_turf.z != posobj.z))
- C.previous_turf = posobj
+ if(!displaying_client.previous_turf || (displaying_client.previous_turf.z != posobj.z))
+ displaying_client.previous_turf = posobj
//Doing it this way prevents parallax layers from "jumping" when you change Z-Levels.
- var/offset_x = posobj.x - C.previous_turf.x
- var/offset_y = posobj.y - C.previous_turf.y
+ var/offset_x = posobj.x - displaying_client.previous_turf.x
+ var/offset_y = posobj.y - displaying_client.previous_turf.y
- var/glide_rate = round(ICON_SIZE_ALL / screenmob.glide_size * world.tick_lag, world.tick_lag)
- C.previous_turf = posobj
+ var/glide_rate = round(ICON_SIZE_ALL / mymob.glide_size * world.tick_lag, world.tick_lag)
+ displaying_client.previous_turf = posobj
var/largest_change = max(abs(offset_x), abs(offset_y))
var/max_allowed_dist = (glide_rate / world.tick_lag) + 1
+ var/atom/movable/screen/parallax_home/rock = displaying_client.parallax_rock
+
// If we aren't already moving/don't allow parallax, have made some movement, and that movement was smaller then our "glide" size, animate
- var/run_parralax = (C.do_parallax_animations && glide_rate && !areaobj.parallax_movedir && C.dont_animate_parallax <= world.time && largest_change <= max_allowed_dist)
+ var/run_parralax = (rock.animate_parallax && glide_rate && !areaobj.parallax_movedir && displaying_client.dont_animate_parallax <= world.time && largest_change <= max_allowed_dist)
- for(var/atom/movable/screen/parallax_layer/parallax_layer as anything in C.parallax_layers)
+ for(var/atom/movable/screen/parallax_layer/parallax_layer as anything in rock.parallax_layers)
var/our_speed = parallax_layer.speed
var/change_x
var/change_y
@@ -235,15 +192,15 @@
/atom/movable/proc/update_parallax_contents()
for(var/mob/client_mob as anything in client_mobs_in_contents)
- if(length(client_mob?.client?.parallax_layers) && client_mob.hud_used)
+ if(client_mob?.client?.parallax_rock?.displaying_layers && client_mob.hud_used)
client_mob.hud_used.update_parallax()
/mob/proc/update_parallax_teleport() //used for arrivals shuttle
- if(client?.eye && hud_used && length(client.parallax_layers))
+ if(client?.eye && hud_used && client?.parallax_rock?.displaying_layers)
var/area/areaobj = get_area(client.eye)
hud_used.set_parallax_movedir(areaobj.parallax_movedir, TRUE)
-// Root object for parallax, all parallax layers are drawn onto this
+// Root object for parallax, all parallax layers are drawn onto this and it manages them
INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_home)
/atom/movable/screen/parallax_home
icon = null
@@ -251,6 +208,87 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_home)
plane = PLANE_SPACE_PARALLAX
screen_loc = "CENTER-7,CENTER-7"
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ /// Layers we are currently displaying
+ var/list/atom/movable/screen/parallax_layer/parallax_layers = list()
+ /// Pallet of layers we CAN display if we choose to, depending on our client's prefs
+ /// ensures quick removal/reinsertion doesn't cause cycling qdels
+ var/list/atom/movable/screen/parallax_layer/parallax_layers_cached = list()
+ /// How many normal space layers we want to draw, in increasing order of "depth"
+ var/layers_to_draw = 0
+ /// If we want to draw the old space layer
+ var/draw_old_space = FALSE
+ /// Are we currently displaying any layers?
+ var/displaying_layers = FALSE
+ /// Are we animating parallax?
+ var/animate_parallax = FALSE
+ /// The client that owns us
+ var/client/owner
+
+/atom/movable/screen/parallax_home/Initialize(mapload, datum/hud/hud_owner, client/owner)
+ . = ..()
+ src.owner = owner
+
+/atom/movable/screen/parallax_home/Destroy()
+ clear_layers()
+ owner = null
+ return ..()
+
+/atom/movable/screen/parallax_home/proc/display_layers()
+ if(displaying_layers || length(parallax_layers_cached) == 0)
+ return
+ parallax_layers = parallax_layers_cached
+ vis_contents = parallax_layers_cached
+ displaying_layers = TRUE
+
+/atom/movable/screen/parallax_home/proc/hide_layers()
+ if(!displaying_layers)
+ return
+ parallax_layers = list()
+ vis_contents = list()
+ displaying_layers = FALSE
+
+/atom/movable/screen/parallax_home/proc/set_layer_settings(layers_to_draw, draw_old_space, animate_parallax)
+ src.animate_parallax = animate_parallax
+ if(src.layers_to_draw == layers_to_draw && src.draw_old_space == draw_old_space)
+ return
+ src.layers_to_draw = layers_to_draw
+ src.draw_old_space = draw_old_space
+ regenerate_layers()
+
+/atom/movable/screen/parallax_home/proc/generate_space_layer(index)
+ switch(index)
+ if(1)
+ return new /atom/movable/screen/parallax_layer/layer_1(null, null, owner)
+ if(2)
+ return new /atom/movable/screen/parallax_layer/layer_2(null, null, owner)
+ if(3)
+ return new /atom/movable/screen/parallax_layer/planet(null, null, owner)
+ if(4)
+ if(SSparallax.random_layer)
+ return new SSparallax.random_layer.type(null, null, owner, FALSE, SSparallax.random_layer)
+ else
+ return new /atom/movable/screen/parallax_layer/layer_3(null, null, owner)
+ if(5)
+ if(SSparallax.random_layer)
+ return new /atom/movable/screen/parallax_layer/layer_3(null, null, owner)
+
+/atom/movable/screen/parallax_home/proc/regenerate_layers()
+ clear_layers()
+ if(layers_to_draw == 0 && !draw_old_space)
+ return
+
+ parallax_layers_cached = list()
+ for(var/space_layer in 1 to layers_to_draw)
+ parallax_layers_cached += generate_space_layer(space_layer)
+
+ if(draw_old_space)
+ parallax_layers_cached += new /atom/movable/screen/parallax_layer/old(null, null, owner)
+
+ display_layers()
+
+/atom/movable/screen/parallax_home/proc/clear_layers()
+ hide_layers()
+ QDEL_LIST(parallax_layers_cached)
// We need parallax to always pass its args down into initialize, so we immediate init it
INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
@@ -264,8 +302,10 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
blend_mode = BLEND_ADD
plane = PLANE_SPACE_PARALLAX
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ /// View size we're being rendered with
+ var/working_view = ""
-/atom/movable/screen/parallax_layer/Initialize(mapload, datum/hud/hud_owner, template = FALSE)
+/atom/movable/screen/parallax_layer/Initialize(mapload, datum/hud/hud_owner, client/owner, template = FALSE)
. = ..()
// Parallax layers are independent of hud, they care about client
// Not doing this will just create a bunch of hard deletes
@@ -274,42 +314,48 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
if(template)
return
- var/client/boss = hud_owner?.mymob?.canon_client
-
- if(!boss) // If this typepath all starts to harddel your culprit is likely this
+ if(!owner) // If this typepath all starts to harddel your culprit is likely this
return INITIALIZE_HINT_QDEL
// I do not want to know bestie
- var/view = boss.view || world.view
+ var/view = owner.view || world.view
update_o(view)
- RegisterSignal(boss, COMSIG_VIEW_SET, PROC_REF(on_view_change))
+ RegisterSignal(owner, COMSIG_VIEW_SET, PROC_REF(on_view_change))
/atom/movable/screen/parallax_layer/proc/on_view_change(datum/source, new_size)
SIGNAL_HANDLER
update_o(new_size)
-/atom/movable/screen/parallax_layer/proc/update_o(view)
- if (!view)
- view = world.view
- var/static/pixel_grid_size = ICON_SIZE_ALL * 15
- var/static/parallax_scaler = ICON_SIZE_ALL / pixel_grid_size
+/atom/movable/screen/parallax_layer/proc/update_o(new_view)
+ if(working_view == new_view)
+ return
+ working_view = new_view
+ update_appearance()
+
+/atom/movable/screen/parallax_layer/update_overlays()
+ . = ..()
+ var/overlay_view = working_view
+ if (!overlay_view)
+ overlay_view = world.view
+ var/pixel_grid_size = ICON_SIZE_ALL * 15
+ var/parallax_scaler = ICON_SIZE_ALL / pixel_grid_size
// Turn the view size into a grid of correctly scaled overlays
- var/list/viewscales = getviewsize(view)
+ var/list/viewscales = getviewsize(overlay_view)
// This could be half the size but we need to provide space for parallax movement on mob movement, and movement on scroll from shuttles, so like this instead
var/countx = (CEILING((viewscales[1] / 2) * parallax_scaler, 1) + 1)
var/county = (CEILING((viewscales[2] / 2) * parallax_scaler, 1) + 1)
- var/list/new_overlays = new
for(var/x in -countx to countx)
for(var/y in -county to county)
if(x == 0 && y == 0)
continue
- var/mutable_appearance/texture_overlay = mutable_appearance(icon, icon_state)
+ var/mutable_appearance/texture_overlay = tileable_appearance()
texture_overlay.pixel_w += pixel_grid_size * x
texture_overlay.pixel_z += pixel_grid_size * y
- new_overlays += texture_overlay
- cut_overlays()
- add_overlay(new_overlays)
+ . += texture_overlay
+
+/atom/movable/screen/parallax_layer/proc/tileable_appearance()
+ return mutable_appearance(icon, icon_state)
/atom/movable/screen/parallax_layer/layer_1
icon_state = "layer1"
@@ -326,6 +372,34 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
speed = 1.4
layer = 3
+/atom/movable/screen/parallax_layer/old
+ icon = null
+ icon_state = null // dog there's gonna be so many overlays...
+ speed = 0.6
+ layer = 1 // Draws on its own
+
+/atom/movable/screen/parallax_layer/old/tileable_appearance()
+ var/mutable_appearance/copy = mutable_appearance(null, "")
+ // We have to use render targets to draw one of these flat and reuse it for this because FOR SOME REASON
+ // 16 (tile count) * (14 (animated state count) * 4 (frame count) + 1 (1 is not animated)) 480x480 states
+ // is TOO MUCH for the client. Whatever, see if I care.
+ copy.render_source = "*old_space_parallax"
+ return copy
+
+/atom/movable/screen/parallax_layer/old/update_overlays()
+ . = ..()
+ var/mutable_appearance/relayed_overlay = mutable_appearance('icons/effects/old_parallax.dmi', "1", appearance_flags = RESET_TRANSFORM|PIXEL_SCALE|KEEP_TOGETHER|KEEP_APART)
+ var/list/old_states = list("19", "21", "23", "24", "26", "29", "30", "31", "34", "35", "36", "37", "43", "46")
+ var/list/holder_overlays = list()
+ for(var/state in old_states)
+ holder_overlays += mutable_appearance('icons/effects/old_parallax.dmi', state)
+ relayed_overlay.overlays = holder_overlays
+ relayed_overlay.render_target = "*old_space_parallax"
+ // Renders the like, "input" appearance we draw to everything else
+ . += relayed_overlay
+ // The 0,0 appearance, can't reuse relayed_overlay for this because otherwise transforms would stack
+ . += tileable_appearance()
+
/atom/movable/screen/parallax_layer/planet
icon_state = "planet"
blend_mode = BLEND_OVERLAY
@@ -333,17 +407,16 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
speed = 3
layer = 30
-/atom/movable/screen/parallax_layer/planet/Initialize(mapload, datum/hud/hud_owner)
+/atom/movable/screen/parallax_layer/planet/Initialize(mapload, datum/hud/hud_owner, client/owner)
. = ..()
- var/client/boss = hud_owner?.mymob?.canon_client
- if(!boss)
+ if(!owner)
return
var/static/list/connections = list(
COMSIG_MOVABLE_Z_CHANGED = PROC_REF(on_z_change),
COMSIG_MOB_LOGOUT = PROC_REF(on_mob_logout),
)
- AddComponent(/datum/component/connect_mob_behalf, boss, connections)
- on_z_change(hud_owner?.mymob)
+ AddComponent(/datum/component/connect_mob_behalf, owner, connections)
+ on_z_change(owner.mob)
/atom/movable/screen/parallax_layer/planet/proc/on_mob_logout(mob/source)
SIGNAL_HANDLER
diff --git a/code/_onclick/hud/parallax/random_layer.dm b/code/_onclick/hud/parallax/random_layer.dm
index 379c0cbe2d9b..51cf69a92c00 100644
--- a/code/_onclick/hud/parallax/random_layer.dm
+++ b/code/_onclick/hud/parallax/random_layer.dm
@@ -4,7 +4,7 @@
speed = 2
layer = 3
-/atom/movable/screen/parallax_layer/random/Initialize(mapload, datum/hud/hud_owner, template, atom/movable/screen/parallax_layer/random/twin)
+/atom/movable/screen/parallax_layer/random/Initialize(mapload, datum/hud/hud_owner, client/owner, template, atom/movable/screen/parallax_layer/random/twin)
. = ..()
if(twin)
diff --git a/code/_onclick/hud/rendering/plane_master_group.dm b/code/_onclick/hud/rendering/plane_master_group.dm
index 27c57ced0eea..5b84a5c05a2e 100644
--- a/code/_onclick/hud/rendering/plane_master_group.dm
+++ b/code/_onclick/hud/rendering/plane_master_group.dm
@@ -34,12 +34,13 @@
if(our_hud)
our_hud.master_groups -= key
hide_hud()
+ var/datum/hud/old_hud = our_hud
our_hud = new_hud
if(new_hud)
our_hud.master_groups[key] = src
show_hud()
build_planes_offset(our_hud, active_offset)
- SEND_SIGNAL(src, COMSIG_GROUP_HUD_CHANGED, our_hud)
+ SEND_SIGNAL(src, COMSIG_GROUP_HUD_CHANGED, old_hud, our_hud)
/// Display a plane master group to some viewer, so show all our planes to it
/datum/plane_master_group/proc/attach_to(datum/hud/viewing_hud)
diff --git a/code/_onclick/hud/rendering/plane_masters/_plane_master.dm b/code/_onclick/hud/rendering/plane_masters/_plane_master.dm
index a29a76fa9cee..02d1f0ca3b4d 100644
--- a/code/_onclick/hud/rendering/plane_masters/_plane_master.dm
+++ b/code/_onclick/hud/rendering/plane_masters/_plane_master.dm
@@ -64,15 +64,18 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/plane_master)
/// If this plane master is outside of our visual bounds right now
var/is_outside_bounds = FALSE
+ /// Has this plane master had its offset made concrete? Avoids modifications/uses that are going to immediately break
+ var/offset_already_updated = FALSE
+
/atom/movable/screen/plane_master/Initialize(mapload, datum/hud/hud_owner, datum/plane_master_group/home, offset = 0)
. = ..()
src.offset = offset
true_alpha = alpha
real_plane = plane
+ update_offset()
if(!set_home(home))
return INITIALIZE_HINT_QDEL
- update_offset()
if(!documentation && !(istype(src, /atom/movable/screen/plane_master) || istype(src, /atom/movable/screen/plane_master/rendering_plate)))
stack_trace("Plane master created without a description. Document how your thing works so people will know in future, and we can display it in the debug menu")
if(start_hidden)
@@ -109,6 +112,7 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/plane_master)
render_relay_planes[i] = GET_NEW_PLANE(render_relay_planes[i], offset)
if(initial(render_target))
render_target = OFFSET_RENDER_TARGET(initial(render_target), offset)
+ offset_already_updated = TRUE
/atom/movable/screen/plane_master/proc/set_alpha(new_alpha)
true_alpha = new_alpha
diff --git a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
index 0599f56df347..36fd9f37d2d6 100644
--- a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
+++ b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
@@ -67,6 +67,36 @@
. = ..()
add_relay_to(GET_NEW_PLANE(RENDER_PLANE_EMISSIVE, offset), relay_layer = EMISSIVE_SPACE_LAYER)
+/atom/movable/screen/plane_master/parallax_white/set_home(datum/plane_master_group/home)
+ . = ..()
+ if(home)
+ RegisterSignal(home, COMSIG_GROUP_HUD_CHANGED, PROC_REF(hud_changed))
+ hud_changed(null, null, home.our_hud)
+
+/atom/movable/screen/plane_master/parallax_white/proc/hud_changed(datum/source, datum/hud/old_hud, datum/hud/new_hud)
+ SIGNAL_HANDLER
+ if(old_hud)
+ UnregisterSignal(old_hud, list(SIGNAL_ADDTRAIT(TRAIT_PARALLAX_DISPLAYED), SIGNAL_REMOVETRAIT(TRAIT_PARALLAX_DISPLAYED)), PROC_REF(parallax_updated))
+ if(new_hud)
+ RegisterSignals(new_hud, list(SIGNAL_ADDTRAIT(TRAIT_PARALLAX_DISPLAYED), SIGNAL_REMOVETRAIT(TRAIT_PARALLAX_DISPLAYED)), PROC_REF(parallax_updated))
+ parallax_updated(new_hud)
+
+/atom/movable/screen/plane_master/parallax_white/proc/parallax_updated(datum/source)
+ SIGNAL_HANDLER
+ if(isnull(home.our_hud?.mymob))
+ return
+ if(HAS_TRAIT(home.our_hud, TRAIT_PARALLAX_DISPLAYED))
+ // Gives parallax a fullwhite backdrop to multiply against
+ color = list(
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 1, 1, 1, 1,
+ 0, 0, 0, 0
+ )
+ else
+ color = initial(color)
+
///Contains space parallax
/atom/movable/screen/plane_master/parallax
name = "Parallax"
@@ -92,6 +122,29 @@
narsie_start_midway(GLOB.narsie_effect_last_modified) // We assume we're on the start, so we can use this number
offset_increase(0, SSmapping.max_plane_offset)
+/atom/movable/screen/plane_master/parallax/set_home(datum/plane_master_group/home)
+ . = ..()
+ if(home)
+ RegisterSignal(home, COMSIG_GROUP_HUD_CHANGED, PROC_REF(hud_changed))
+ hud_changed(null, null, home.our_hud)
+
+/atom/movable/screen/plane_master/parallax/proc/hud_changed(datum/source, datum/hud/old_hud, datum/hud/new_hud)
+ SIGNAL_HANDLER
+ if(old_hud)
+ UnregisterSignal(old_hud, list(SIGNAL_ADDTRAIT(TRAIT_PARALLAX_DISPLAYED), SIGNAL_REMOVETRAIT(TRAIT_PARALLAX_DISPLAYED)), PROC_REF(parallax_updated))
+ if(new_hud)
+ RegisterSignals(new_hud, list(SIGNAL_ADDTRAIT(TRAIT_PARALLAX_DISPLAYED), SIGNAL_REMOVETRAIT(TRAIT_PARALLAX_DISPLAYED)), PROC_REF(parallax_updated))
+ parallax_updated(new_hud)
+
+/atom/movable/screen/plane_master/parallax/proc/parallax_updated(datum/source)
+ SIGNAL_HANDLER
+ if(isnull(home.our_hud?.mymob))
+ return
+ if(HAS_TRAIT(home.our_hud, TRAIT_PARALLAX_DISPLAYED))
+ show_to(home.our_hud.mymob)
+ else
+ hide_from(home.our_hud.mymob)
+
/atom/movable/screen/plane_master/parallax/proc/on_offset_increase(datum/source, old_offset, new_offset)
SIGNAL_HANDLER
offset_increase(old_offset, new_offset)
@@ -401,19 +454,19 @@
plane = CAMERA_STATIC_PLANE
render_relay_planes = list(RENDER_PLANE_GAME)
-/atom/movable/screen/plane_master/camera_static/show_to(mob/mymob)
+/atom/movable/screen/plane_master/camera_static/set_home(datum/plane_master_group/home)
. = ..()
- if(!.)
- return
- var/datum/hud/our_hud = home.our_hud
- if(isnull(our_hud))
- return
+ if(home)
+ RegisterSignal(home, COMSIG_GROUP_HUD_CHANGED, PROC_REF(hud_changed))
+ hud_changed(null, null, home.our_hud)
- // We'll hide the slate if we're not seeing through a camera eye
- // This can call on a cycle cause we don't clear in hide_from
- // Yes this is the best way of hooking into the hud, I hate myself too
- RegisterSignal(our_hud, COMSIG_HUD_EYE_CHANGED, PROC_REF(eye_changed), override = TRUE)
- eye_changed(our_hud, null, our_hud.mymob?.canon_client?.eye)
+/atom/movable/screen/plane_master/camera_static/proc/hud_changed(datum/source, datum/hud/old_hud, datum/hud/new_hud)
+ SIGNAL_HANDLER
+ if(old_hud)
+ UnregisterSignal(old_hud, COMSIG_HUD_EYE_CHANGED, PROC_REF(eye_changed))
+ if(new_hud)
+ RegisterSignal(new_hud, COMSIG_HUD_EYE_CHANGED, PROC_REF(eye_changed))
+ eye_changed(new_hud, null, new_hud.mymob?.canon_client?.eye)
/atom/movable/screen/plane_master/camera_static/proc/eye_changed(datum/hud/source, atom/old_eye, atom/new_eye)
SIGNAL_HANDLER
diff --git a/code/_onclick/hud/rendering/render_plate.dm b/code/_onclick/hud/rendering/render_plate.dm
index d7c2382b790b..ad48e91f6f8b 100644
--- a/code/_onclick/hud/rendering/render_plate.dm
+++ b/code/_onclick/hud/rendering/render_plate.dm
@@ -203,6 +203,12 @@
add_filter("emissives", 1, alpha_mask_filter(render_source = OFFSET_RENDER_TARGET(EMISSIVE_RENDER_TARGET, offset), flags = MASK_INVERSE))
set_light_cutoff(10)
+/atom/movable/screen/plane_master/rendering_plate/lighting/set_home(datum/plane_master_group/home)
+ . = ..()
+ if(home)
+ RegisterSignal(home, COMSIG_GROUP_HUD_CHANGED, PROC_REF(hud_changed))
+ hud_changed(null, null, home.our_hud)
+
/atom/movable/screen/plane_master/rendering_plate/lighting/show_to(mob/mymob)
. = ..()
if(!.)
@@ -218,23 +224,20 @@
backdrop = mymob.overlay_fullscreen("lighting_backdrop_unlit_[home.key]#[offset]", /atom/movable/screen/fullscreen/lighting_backdrop/unlit)
SET_PLANE_EXPLICIT(backdrop, PLANE_TO_TRUE(backdrop.plane), src)
- // Sorry, this is a bit annoying
- // Basically, we only want the lighting plane we can actually see to attempt to render
- // If we don't our lower plane gets totally overriden by the black void of the upper plane
- var/datum/hud/hud = home.our_hud
- // show_to can be called twice successfully with no hide_from call. Ensure no runtimes off the registers from this
- if(hud)
- RegisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change), override = TRUE)
- offset_change(hud?.current_plane_offset || 0)
set_light_cutoff(mymob.lighting_cutoff, mymob.lighting_color_cutoffs)
/atom/movable/screen/plane_master/rendering_plate/lighting/hide_from(mob/oldmob)
. = ..()
oldmob.clear_fullscreen("lighting_backdrop_lit_[home.key]#[offset]")
oldmob.clear_fullscreen("lighting_backdrop_unlit_[home.key]#[offset]")
- var/datum/hud/hud = home.our_hud
- if(hud)
- UnregisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change))
+
+/atom/movable/screen/plane_master/rendering_plate/lighting/proc/hud_changed(datum/source, datum/hud/old_hud, datum/hud/new_hud)
+ SIGNAL_HANDLER
+ if(old_hud)
+ UnregisterSignal(old_hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change))
+ if(new_hud)
+ RegisterSignal(new_hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change))
+ offset_change(new_hud?.current_plane_offset || 0)
/atom/movable/screen/plane_master/rendering_plate/lighting/proc/on_offset_change(datum/source, old_offset, new_offset)
SIGNAL_HANDLER
@@ -443,26 +446,22 @@
plane = RENDER_PLANE_MASTER
render_relay_planes = list()
-/atom/movable/screen/plane_master/rendering_plate/master/show_to(mob/mymob)
+/atom/movable/screen/plane_master/rendering_plate/master/set_home(datum/plane_master_group/home)
. = ..()
- if(!.)
- return
- if(offset == 0)
- return
// Non 0 offset render plates will relay up to the transparent plane above them, assuming they're not on the same z level as their target of course
- var/datum/hud/hud = home.our_hud
- // show_to can be called twice successfully with no hide_from call. Ensure no runtimes off the registers from this
- if(hud)
- RegisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change), override = TRUE)
- offset_change(hud?.current_plane_offset || 0)
-
-/atom/movable/screen/plane_master/rendering_plate/master/hide_from(mob/oldmob)
- . = ..()
if(offset == 0)
return
- var/datum/hud/hud = home.our_hud
- if(hud)
- UnregisterSignal(hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change))
+ if(home)
+ RegisterSignal(home, COMSIG_GROUP_HUD_CHANGED, PROC_REF(hud_changed))
+ hud_changed(null, null, home.our_hud)
+
+/atom/movable/screen/plane_master/rendering_plate/master/proc/hud_changed(datum/source, datum/hud/old_hud, datum/hud/new_hud)
+ SIGNAL_HANDLER
+ if(old_hud)
+ UnregisterSignal(old_hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change))
+ if(new_hud)
+ RegisterSignal(new_hud, COMSIG_HUD_OFFSET_CHANGED, PROC_REF(on_offset_change))
+ offset_change(new_hud?.current_plane_offset || 0)
/atom/movable/screen/plane_master/rendering_plate/master/proc/on_offset_change(datum/source, old_offset, new_offset)
SIGNAL_HANDLER
@@ -507,6 +506,8 @@
/atom/movable/screen/plane_master/proc/add_relay_to(target_plane, blend_override, relay_layer, relay_color)
if(get_relay_to(target_plane))
return
+ if(!offset_already_updated)
+ CRASH("Attempted to draw a render relay before our offset has been applied, this WILL break")
render_relay_planes += target_plane
var/client/display_lad = home?.our_hud?.mymob?.canon_client
var/atom/movable/render_plane_relay/relay = generate_relay_to(target_plane, show_to = display_lad, blend_override = blend_override, relay_layer = relay_layer)
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 77c30b8869c4..a449f99dfd36 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -799,8 +799,9 @@
update_appearance()
/atom/movable/screen/healthdoll/human/update_body_zones()
- limbs = list()
vis_contents.Cut()
+ QDEL_LIST_ASSOC_VAL(limbs)
+ limbs ||= list()
var/mob/living/carbon/human/owner = hud.mymob
for(var/body_zone in owner.get_all_limbs())
var/atom/movable/screen/healthdoll_limb/limb = new(src, null)
@@ -827,12 +828,13 @@
var/list/current_animated = LAZYLISTDUPLICATE(animated_zones)
- for(var/obj/item/bodypart/body_part as anything in owner.bodyparts)
+ for(var/part_zone, body_part_untyped in owner.get_bodyparts_by_zones())
var/icon_key = 0
- var/part_zone = body_part.body_zone
-
+ var/obj/item/bodypart/body_part = body_part_untyped
var/list/overridable_key = list(icon_key)
- if(body_part.bodypart_disabled)
+ if(isnull(body_part) || IS_STUMP(body_part))
+ icon_key = 6
+ else if(body_part.bodypart_disabled)
icon_key = 7
else if(owner.stat == DEAD)
icon_key = "DEAD"
@@ -843,15 +845,11 @@
// calculate what icon state (1-5, or 0 if undamaged) to use based on damage
icon_key = clamp(ceil(damage * 5), 0, 5)
- if(length(body_part.wounds))
+ if(length(body_part?.wounds))
LAZYSET(animated_zones, part_zone, TRUE)
else
LAZYREMOVE(animated_zones, part_zone)
limbs[part_zone].icon_state = "[part_zone][icon_key]"
- // handle leftovers
- for(var/missing_zone in owner.get_missing_limbs())
- limbs[missing_zone].icon_state = "[missing_zone]6"
- LAZYREMOVE(animated_zones, missing_zone)
// time to re-sync animations, something changed
if(animated_zones ~! current_animated)
for(var/animated_zone in animated_zones)
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 3759c6b0f6af..d180cb9979e4 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -414,7 +414,7 @@
return TRUE
/mob/living/carbon/attack_effects(damage_done, hit_zone, armor_block, obj/item/attacking_item, mob/living/attacker)
- var/obj/item/bodypart/hit_bodypart = get_bodypart(hit_zone) || bodyparts[1]
+ var/obj/item/bodypart/hit_bodypart = get_bodypart(hit_zone) || get_bodypart()
if(!hit_bodypart.can_bleed())
return FALSE
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index 80a0c486fae1..3303eed9c5c1 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -191,7 +191,7 @@ ADMIN_VERB(cmd_controller_view_ui, R_SERVER|R_DEBUG, "Controller Overview", "Vie
"last_fire" = subsystem.last_fire,
"next_fire" = subsystem.next_fire,
"can_fire" = subsystem.can_fire,
- "doesnt_fire" = !!(subsystem.flags & SS_NO_FIRE),
+ "doesnt_fire" = !!(subsystem.ss_flags & SS_NO_FIRE),
"cost_ms" = subsystem.cost,
"tick_usage" = subsystem.tick_usage,
"usage_per_tick" = average,
@@ -305,7 +305,7 @@ ADMIN_VERB(cmd_controller_view_ui, R_SERVER|R_DEBUG, "Controller Overview", "Vie
FireHim = TRUE
if(3)
msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be put offline."
- BadBoy.flags |= SS_NO_FIRE
+ BadBoy.ss_flags |= SS_NO_FIRE
if(msg)
to_chat(GLOB.admins, span_boldannounce("[msg]"))
log_world(msg)
@@ -494,7 +494,7 @@ ADMIN_VERB(cmd_controller_view_ui, R_SERVER|R_DEBUG, "Controller Overview", "Vie
SS_INIT_NO_MESSAGE,
)
- if ((subsystem.flags & SS_NO_INIT) || subsystem.initialized) //Don't init SSs with the corresponding flag or if they already are initialized
+ if ((subsystem.ss_flags & SS_NO_INIT) || subsystem.initialized) //Don't init SSs with the corresponding flag or if they already are initialized
subsystem.initialized = TRUE // set initialized to TRUE, because the value of initialized may still be checked on SS_NO_INIT subsystems as an "is this ready" check
return
@@ -599,7 +599,7 @@ ADMIN_VERB(cmd_controller_view_ui, R_SERVER|R_DEBUG, "Controller Overview", "Vie
var/timer = world.time
for (var/thing in subsystems)
var/datum/controller/subsystem/SS = thing
- if (SS.flags & SS_NO_FIRE)
+ if (SS.ss_flags & SS_NO_FIRE)
continue
if (SS.init_stage > init_stage)
continue
@@ -607,7 +607,7 @@ ADMIN_VERB(cmd_controller_view_ui, R_SERVER|R_DEBUG, "Controller Overview", "Vie
SS.queue_next = null
SS.queue_prev = null
SS.state = SS_IDLE
- if ((SS.flags & (SS_TICKER|SS_BACKGROUND)) == SS_TICKER)
+ if ((SS.ss_flags & (SS_TICKER|SS_BACKGROUND)) == SS_TICKER)
tickersubsystems += SS
// Timer subsystems aren't allowed to bunch up, so we offset them a bit
timer += TICKS2DS(rand(0, 1))
@@ -618,7 +618,7 @@ ADMIN_VERB(cmd_controller_view_ui, R_SERVER|R_DEBUG, "Controller Overview", "Vie
if(SS.init_stage == init_stage - 1 && (SS.runlevels & current_runlevel))
// Give em a random offset so things don't clump up too bad
var/delay = SS.wait
- if(SS.flags & SS_TICKER)
+ if(SS.ss_flags & SS_TICKER)
delay = TICKS2DS(delay)
// Gotta convert to ticks cause rand needs integers
SS.next_fire = world.time + TICKS2DS(rand(0, DS2TICKS(min(delay, 2 SECONDS))))
@@ -720,7 +720,7 @@ ADMIN_VERB(cmd_controller_view_ui, R_SERVER|R_DEBUG, "Controller Overview", "Vie
continue
// If they're new, give em a random offset so things don't clump up too bad
var/delay = SS.wait
- if(SS.flags & SS_TICKER)
+ if(SS.ss_flags & SS_TICKER)
delay = TICKS2DS(delay)
SS.next_fire = world.time + TICKS2DS(rand(0, DS2TICKS(min(delay, 2 SECONDS))))
@@ -813,7 +813,7 @@ ADMIN_VERB(cmd_controller_view_ui, R_SERVER|R_DEBUG, "Controller Overview", "Vie
continue
if (SS.next_fire > world.time)
continue
- SS_flags = SS.flags
+ SS_flags = SS.ss_flags
if (SS_flags & SS_NO_FIRE)
subsystemstocheck -= SS
continue
@@ -855,7 +855,7 @@ ADMIN_VERB(cmd_controller_view_ui, R_SERVER|R_DEBUG, "Controller Overview", "Vie
while (queue_node)
if (ran && TICK_USAGE > TICK_LIMIT_RUNNING)
break
- queue_node_flags = queue_node.flags
+ queue_node_flags = queue_node.ss_flags
queue_node_priority = queue_node.queued_priority
if (!(queue_node_flags & SS_TICKER) && skip_ticks)
diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm
index 200c65a4c75c..92a3e70d02f0 100644
--- a/code/controllers/subsystem.dm
+++ b/code/controllers/subsystem.dm
@@ -34,8 +34,8 @@
/// Priority Weight: When multiple subsystems need to run in the same tick, higher priority subsystems will be given a higher share of the tick before MC_TICK_CHECK triggers a sleep, higher priority subsystems also run before lower priority subsystems
var/priority = FIRE_PRIORITY_DEFAULT
- /// [Subsystem Flags][SS_NO_INIT] to control binary behavior. Flags must be set at compile time or before preinit finishes to take full effect. (You can also restart the mc to force them to process again)
- var/flags = NONE
+ /// [Subsystem flags][SS_NO_INIT] to control binary behavior. ss_flags must be set at compile time or before preinit finishes to take full effect. (You can also restart the mc to force them to process again)
+ var/ss_flags = NONE
/// Which stage does this subsystem init at. Earlier stages can fire while later stages init.
var/init_stage = INITSTAGE_MAIN
@@ -161,13 +161,13 @@
///fire() seems more suitable. This is the procedure that gets called every 'wait' deciseconds.
///Sleeping in here prevents future fires until returned.
/datum/controller/subsystem/proc/fire(resumed = FALSE)
- flags |= SS_NO_FIRE
+ ss_flags |= SS_NO_FIRE
CRASH("Subsystem [src]([type]) does not fire() but did not set the SS_NO_FIRE flag. Please add the SS_NO_FIRE flag to any subsystem that doesn't fire so it doesn't get added to the processing list and waste cpu.")
/datum/controller/subsystem/Destroy()
dequeue()
can_fire = 0
- flags |= SS_NO_FIRE
+ ss_flags |= SS_NO_FIRE
if (Master)
Master.subsystems -= src
return ..()
@@ -177,7 +177,7 @@
* reset_time (bool) - Ignore things that would normally alter the next fire, like tick_overrun, and last_fire. (also resets postpone)
*/
/datum/controller/subsystem/proc/update_nextfire(reset_time = FALSE)
- var/queue_node_flags = flags
+ var/queue_node_flags = ss_flags
if (reset_time)
postponed_fires = 0
@@ -202,14 +202,14 @@
/// (this lets us sort our run order correctly without having to re-sort the entire already sorted list)
/datum/controller/subsystem/proc/enqueue()
var/SS_priority = priority
- var/SS_flags = flags
+ var/SS_flags = ss_flags
var/datum/controller/subsystem/queue_node
var/queue_node_priority
var/queue_node_flags
for (queue_node = Master.queue_head; queue_node; queue_node = queue_node.queue_next)
queue_node_priority = queue_node.queued_priority
- queue_node_flags = queue_node.flags
+ queue_node_flags = queue_node.ss_flags
if (queue_node_flags & (SS_TICKER|SS_BACKGROUND) == SS_TICKER)
if ((SS_flags & (SS_TICKER|SS_BACKGROUND)) != SS_TICKER)
@@ -290,7 +290,7 @@
return SS_INIT_NONE
/datum/controller/subsystem/stat_entry(msg)
- if(can_fire && !(SS_NO_FIRE & flags) && init_stage <= Master.init_stage_completed)
+ if(can_fire && !(SS_NO_FIRE & ss_flags) && init_stage <= Master.init_stage_completed)
msg = "[round(cost,1)]ms|[round(tick_usage,1)]%([round(tick_overrun,1)]%)|[round(ticks,0.1)] [msg]"
else
msg = "OFFLINE\t[msg]"
diff --git a/code/controllers/subsystem/achievements.dm b/code/controllers/subsystem/achievements.dm
index 56a85284cd4c..9548de2ce604 100644
--- a/code/controllers/subsystem/achievements.dm
+++ b/code/controllers/subsystem/achievements.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(achievements)
name = "Achievements"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
var/achievements_enabled = FALSE
///List of achievements
diff --git a/code/controllers/subsystem/admin_verbs.dm b/code/controllers/subsystem/admin_verbs.dm
index 732508f0f8a0..9891f13a4cf2 100644
--- a/code/controllers/subsystem/admin_verbs.dm
+++ b/code/controllers/subsystem/admin_verbs.dm
@@ -2,7 +2,7 @@ GENERAL_PROTECT_DATUM(/datum/controller/subsystem/admin_verbs)
SUBSYSTEM_DEF(admin_verbs)
name = "Admin Verbs"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
init_stage = INITSTAGE_EARLY
/// A list of all admin verbs indexed by their type.
var/list/datum/admin_verb/admin_verbs_by_type = list()
diff --git a/code/controllers/subsystem/ai_controllers.dm b/code/controllers/subsystem/ai_controllers.dm
index 95abc7e8da35..4102806be67f 100644
--- a/code/controllers/subsystem/ai_controllers.dm
+++ b/code/controllers/subsystem/ai_controllers.dm
@@ -1,7 +1,7 @@
/// The subsystem used to tick [/datum/ai_controllers] instances. Handling the re-checking of plans.
SUBSYSTEM_DEF(ai_controllers)
name = "AI Controller Ticker"
- flags = SS_POST_FIRE_TIMING|SS_BACKGROUND
+ ss_flags = SS_POST_FIRE_TIMING|SS_BACKGROUND
priority = FIRE_PRIORITY_NPC
dependencies = list(
/datum/controller/subsystem/movement/ai_movement,
diff --git a/code/controllers/subsystem/ai_idle_controllers.dm b/code/controllers/subsystem/ai_idle_controllers.dm
index 19fcad9c50e4..94b43207f82f 100644
--- a/code/controllers/subsystem/ai_idle_controllers.dm
+++ b/code/controllers/subsystem/ai_idle_controllers.dm
@@ -1,6 +1,6 @@
AI_CONTROLLER_SUBSYSTEM_DEF(ai_idle_controllers)
name = "AI Idle Controllers"
- flags = SS_POST_FIRE_TIMING | SS_BACKGROUND
+ ss_flags = SS_POST_FIRE_TIMING | SS_BACKGROUND
priority = FIRE_PRIORITY_IDLE_NPC
dependencies = list(
/datum/controller/subsystem/ai_controllers,
diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm
index b991b73918da..b75d9e813e3d 100644
--- a/code/controllers/subsystem/air.dm
+++ b/code/controllers/subsystem/air.dm
@@ -6,7 +6,7 @@ SUBSYSTEM_DEF(air)
)
priority = FIRE_PRIORITY_AIR
wait = 0.5 SECONDS
- flags = SS_BACKGROUND
+ ss_flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/cached_cost = 0
diff --git a/code/controllers/subsystem/ambience.dm b/code/controllers/subsystem/ambience.dm
index 88db3ffcc332..79f5e7dbd1a0 100644
--- a/code/controllers/subsystem/ambience.dm
+++ b/code/controllers/subsystem/ambience.dm
@@ -1,7 +1,7 @@
/// The subsystem used to play ambience to users every now and then, makes them real excited.
SUBSYSTEM_DEF(ambience)
name = "Ambience"
- flags = SS_BACKGROUND|SS_NO_INIT
+ ss_flags = SS_BACKGROUND|SS_NO_INIT
priority = FIRE_PRIORITY_AMBIENCE
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 1 SECONDS
diff --git a/code/controllers/subsystem/area_contents.dm b/code/controllers/subsystem/area_contents.dm
index 830c6857561e..ae33933a4b9b 100644
--- a/code/controllers/subsystem/area_contents.dm
+++ b/code/controllers/subsystem/area_contents.dm
@@ -8,7 +8,7 @@
*/
SUBSYSTEM_DEF(area_contents)
name = "Area Contents"
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
runlevels = RUNLEVEL_LOBBY|RUNLEVELS_DEFAULT
var/list/currentrun
var/list/area/marked_for_clearing = list()
diff --git a/code/controllers/subsystem/asset_loading.dm b/code/controllers/subsystem/asset_loading.dm
index 8bafe88ffbc4..d591475c8806 100644
--- a/code/controllers/subsystem/asset_loading.dm
+++ b/code/controllers/subsystem/asset_loading.dm
@@ -4,7 +4,7 @@
SUBSYSTEM_DEF(asset_loading)
name = "Asset Loading"
priority = FIRE_PRIORITY_ASSETS
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
runlevels = RUNLEVEL_LOBBY|RUNLEVELS_DEFAULT
var/list/datum/asset/generate_queue = list()
var/assets_generating = 0
diff --git a/code/controllers/subsystem/assets.dm b/code/controllers/subsystem/assets.dm
index e3b03c01222a..dad696c002a3 100644
--- a/code/controllers/subsystem/assets.dm
+++ b/code/controllers/subsystem/assets.dm
@@ -5,7 +5,7 @@ SUBSYSTEM_DEF(assets)
/datum/controller/subsystem/persistent_paintings,
/datum/controller/subsystem/greyscale_previews,
)
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
var/list/datum/asset_cache_item/cache = list()
var/list/preload = list()
var/datum/asset_transport/transport = new()
diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm
index ad09a0caeb28..80e649d75046 100644
--- a/code/controllers/subsystem/atoms.dm
+++ b/code/controllers/subsystem/atoms.dm
@@ -6,7 +6,7 @@ SUBSYSTEM_DEF(atoms)
/datum/controller/subsystem/mapping,
/datum/controller/subsystem/job,
)
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
/// A stack of list(source, desired initialized state)
/// We read the source of init changes from the last entry, and assert that all changes will come with a reset
@@ -47,7 +47,7 @@ SUBSYSTEM_DEF(atoms)
// Generate a unique mapload source for this run of InitializeAtoms
var/static/uid = 0
- uid = (uid + 1) % (SHORT_REAL_LIMIT - 1)
+ uid = WRAP_UID(uid + 1)
var/source = "subsystem init [uid]"
set_tracked_initalized(INITIALIZATION_INNEW_MAPLOAD, source)
diff --git a/code/controllers/subsystem/augury.dm b/code/controllers/subsystem/augury.dm
index e686e5cdb62f..681ac744c15e 100644
--- a/code/controllers/subsystem/augury.dm
+++ b/code/controllers/subsystem/augury.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(augury)
name = "Augury"
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/watchers = list()
diff --git a/code/controllers/subsystem/ban_cache.dm b/code/controllers/subsystem/ban_cache.dm
index 21e1a92a7ed4..1e4c710031bc 100644
--- a/code/controllers/subsystem/ban_cache.dm
+++ b/code/controllers/subsystem/ban_cache.dm
@@ -5,7 +5,7 @@ SUBSYSTEM_DEF(ban_cache)
/datum/controller/subsystem/ban_cache
name = "Ban Cache"
init_stage = INITSTAGE_LAST
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
var/query_started = FALSE
/datum/controller/subsystem/ban_cache/Initialize()
diff --git a/code/controllers/subsystem/blood_drying.dm b/code/controllers/subsystem/blood_drying.dm
index 79202cb8d8c4..ec8d9d174e0b 100644
--- a/code/controllers/subsystem/blood_drying.dm
+++ b/code/controllers/subsystem/blood_drying.dm
@@ -5,7 +5,7 @@
*/
PROCESSING_SUBSYSTEM_DEF(blood_drying)
name = "Blood Drying"
- flags = SS_NO_INIT | SS_BACKGROUND
+ ss_flags = SS_NO_INIT | SS_BACKGROUND
priority = FIRE_PRIORITY_BLOOD_DRYING
runlevels = RUNLEVEL_GAME
wait = 4 SECONDS
diff --git a/code/controllers/subsystem/cameras.dm b/code/controllers/subsystem/cameras.dm
index 62e8af1c973b..d6f7866b8084 100644
--- a/code/controllers/subsystem/cameras.dm
+++ b/code/controllers/subsystem/cameras.dm
@@ -1,7 +1,7 @@
/// Manages the security cameras and camera chunks
SUBSYSTEM_DEF(cameras)
name = "Cameras"
- flags = SS_BACKGROUND
+ ss_flags = SS_BACKGROUND
priority = FIRE_PRIORITY_CAMERAS
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 2 MINUTES
diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm
index d2303074077a..4864e9aec8a5 100644
--- a/code/controllers/subsystem/chat.dm
+++ b/code/controllers/subsystem/chat.dm
@@ -5,7 +5,7 @@
SUBSYSTEM_DEF(chat)
name = "Chat"
- flags = SS_TICKER|SS_NO_INIT
+ ss_flags = SS_TICKER|SS_NO_INIT
wait = 1
priority = FIRE_PRIORITY_CHAT
init_stage = INITSTAGE_LAST
diff --git a/code/controllers/subsystem/dbcore.dm b/code/controllers/subsystem/dbcore.dm
index 17b2bf737ddd..b68454a5acce 100644
--- a/code/controllers/subsystem/dbcore.dm
+++ b/code/controllers/subsystem/dbcore.dm
@@ -1,7 +1,7 @@
#define SHUTDOWN_QUERY_TIMELIMIT (1 MINUTES)
SUBSYSTEM_DEF(dbcore)
name = "Database"
- flags = SS_TICKER
+ ss_flags = SS_TICKER
init_stage = INITSTAGE_FIRST
wait = 10 // Not seconds because we're running on SS_TICKER
runlevels = RUNLEVEL_LOBBY|RUNLEVELS_DEFAULT
diff --git a/code/controllers/subsystem/dcs.dm b/code/controllers/subsystem/dcs.dm
index a3dcc26af54f..e65c820e9445 100644
--- a/code/controllers/subsystem/dcs.dm
+++ b/code/controllers/subsystem/dcs.dm
@@ -1,6 +1,6 @@
PROCESSING_SUBSYSTEM_DEF(dcs)
name = "Datum Component System"
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
wait = 1 SECONDS
var/list/elements_by_type = list()
diff --git a/code/controllers/subsystem/disease.dm b/code/controllers/subsystem/disease.dm
index 357cbcd6ec73..7573ad8694e0 100644
--- a/code/controllers/subsystem/disease.dm
+++ b/code/controllers/subsystem/disease.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(disease)
name = "Disease"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
var/list/active_diseases = list() //List of Active disease in all mobs; purely for quick referencing.
var/list/diseases
diff --git a/code/controllers/subsystem/dynamic/dynamic.dm b/code/controllers/subsystem/dynamic/dynamic.dm
index 51bda1835205..0e4fec400dae 100644
--- a/code/controllers/subsystem/dynamic/dynamic.dm
+++ b/code/controllers/subsystem/dynamic/dynamic.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(dynamic)
name = "Dynamic"
- // flags = SS_NO_INIT // DARKPACK EDIT REMOVAL
+ // ss_flags = SS_NO_INIT // DARKPACK EDIT REMOVAL
wait = 5 MINUTES
// These vars just exist for admins interfacing with dynamic
diff --git a/code/controllers/subsystem/early_assets.dm b/code/controllers/subsystem/early_assets.dm
index df155a4db10b..a480fbc49a98 100644
--- a/code/controllers/subsystem/early_assets.dm
+++ b/code/controllers/subsystem/early_assets.dm
@@ -14,7 +14,7 @@ SUBSYSTEM_DEF(early_assets)
/datum/controller/subsystem/atoms,
)
init_stage = INITSTAGE_EARLY
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
/datum/controller/subsystem/early_assets/Initialize()
var/init_source = "early assets"
diff --git a/code/controllers/subsystem/economy.dm b/code/controllers/subsystem/economy.dm
index 9a765423eaa7..85cc898ad6d1 100644
--- a/code/controllers/subsystem/economy.dm
+++ b/code/controllers/subsystem/economy.dm
@@ -218,7 +218,7 @@ SUBSYSTEM_DEF(economy)
audit_log += list(list(
"account" = "[account.account_holder]",
"cost" = price_to_use,
- "vendor" = "[vendor]",
+ "vendor" = "[astype(vendor, /atom)?.name || vendor]",
"stationtime" = station_time_timestamp("hh:mm"),
))
diff --git a/code/controllers/subsystem/explosions.dm b/code/controllers/subsystem/explosions.dm
index c01fe4f718fd..f5c733c0fd6a 100644
--- a/code/controllers/subsystem/explosions.dm
+++ b/code/controllers/subsystem/explosions.dm
@@ -9,7 +9,7 @@ SUBSYSTEM_DEF(explosions)
name = "Explosions"
priority = FIRE_PRIORITY_EXPLOSIONS
wait = 1
- flags = SS_TICKER|SS_NO_INIT
+ ss_flags = SS_TICKER|SS_NO_INIT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/cost_lowturf = 0
diff --git a/code/controllers/subsystem/fluids.dm b/code/controllers/subsystem/fluids.dm
index 6b68ae717222..9ceec065086c 100644
--- a/code/controllers/subsystem/fluids.dm
+++ b/code/controllers/subsystem/fluids.dm
@@ -20,7 +20,7 @@
SUBSYSTEM_DEF(fluids)
name = "Fluid"
wait = 0 // Will be autoset to whatever makes the most sense given the spread and effect waits.
- flags = SS_KEEP_TIMING
+ ss_flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME
priority = FIRE_PRIORITY_FLUIDS
diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm
index bb1475a4f50e..ae01679b0943 100644
--- a/code/controllers/subsystem/garbage.dm
+++ b/code/controllers/subsystem/garbage.dm
@@ -25,7 +25,7 @@ SUBSYSTEM_DEF(garbage)
name = "Garbage"
priority = FIRE_PRIORITY_GARBAGE
wait = 2 SECONDS
- flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
+ ss_flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
init_stage = INITSTAGE_FIRST
diff --git a/code/controllers/subsystem/greyscale_previews.dm b/code/controllers/subsystem/greyscale_previews.dm
index 9a63cb6b2de5..79bc1fa1fe9b 100644
--- a/code/controllers/subsystem/greyscale_previews.dm
+++ b/code/controllers/subsystem/greyscale_previews.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(greyscale_previews)
name = "Greyscale Previews"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
init_stage = INITSTAGE_EARLY
dependencies = list(
/datum/controller/subsystem/processing/greyscale,
diff --git a/code/controllers/subsystem/icon_smooth.dm b/code/controllers/subsystem/icon_smooth.dm
index 77a2c1670ae3..7fb7f5c61997 100644
--- a/code/controllers/subsystem/icon_smooth.dm
+++ b/code/controllers/subsystem/icon_smooth.dm
@@ -5,7 +5,7 @@ SUBSYSTEM_DEF(icon_smooth)
)
wait = 1
priority = FIRE_PRIORITY_SMOOTHING
- flags = SS_TICKER
+ ss_flags = SS_TICKER
///Blueprints assemble an image of what pipes/manifolds/wires look like on initialization, and thus should be taken after everything's been smoothed
var/list/blueprint_queue = list()
diff --git a/code/controllers/subsystem/init_profiler.dm b/code/controllers/subsystem/init_profiler.dm
index dcb163e596a4..a27c1a5bf39b 100644
--- a/code/controllers/subsystem/init_profiler.dm
+++ b/code/controllers/subsystem/init_profiler.dm
@@ -5,7 +5,7 @@
SUBSYSTEM_DEF(init_profiler)
name = "Init Profiler"
init_stage = INITSTAGE_LAST
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
/datum/controller/subsystem/init_profiler/Initialize()
if(CONFIG_GET(flag/auto_profile))
diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm
index 11b75be8af75..dff1edad0548 100644
--- a/code/controllers/subsystem/input.dm
+++ b/code/controllers/subsystem/input.dm
@@ -1,7 +1,7 @@
VERB_MANAGER_SUBSYSTEM_DEF(input)
name = "Input"
init_stage = INITSTAGE_EARLY
- flags = SS_TICKER
+ ss_flags = SS_TICKER
priority = FIRE_PRIORITY_INPUT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
diff --git a/code/controllers/subsystem/ipintel.dm b/code/controllers/subsystem/ipintel.dm
index 6ba87c02780c..e3e33d1d79e5 100644
--- a/code/controllers/subsystem/ipintel.dm
+++ b/code/controllers/subsystem/ipintel.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(ipintel)
name = "XKeyScore"
- flags = SS_NO_INIT|SS_NO_FIRE
+ ss_flags = SS_NO_INIT|SS_NO_FIRE
/// The threshold for probability to be considered a VPN and/or bad IP
var/probability_threshold
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 47fa47704827..096a56e42364 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -3,7 +3,7 @@ SUBSYSTEM_DEF(job)
dependencies = list(
/datum/controller/subsystem/processing/station,
)
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
/// List of all jobs.
var/list/datum/job/all_occupations = list()
diff --git a/code/controllers/subsystem/lag_switch.dm b/code/controllers/subsystem/lag_switch.dm
index 291e80fe18f1..70979b40816f 100644
--- a/code/controllers/subsystem/lag_switch.dm
+++ b/code/controllers/subsystem/lag_switch.dm
@@ -1,7 +1,7 @@
/// The subsystem for controlling drastic performance enhancements aimed at reducing server load for a smoother albeit slightly duller gaming experience
SUBSYSTEM_DEF(lag_switch)
name = "Lag Switch"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
/// If the lag switch measures should attempt to trigger automatically, TRUE if a config value exists
var/auto_switch = FALSE
diff --git a/code/controllers/subsystem/library.dm b/code/controllers/subsystem/library.dm
index 87b64f1af870..a368a3903041 100644
--- a/code/controllers/subsystem/library.dm
+++ b/code/controllers/subsystem/library.dm
@@ -5,7 +5,7 @@ SUBSYSTEM_DEF(library)
/datum/controller/subsystem/atoms,
/datum/controller/subsystem/mapping,
)
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
/// List of bookselves to prefill with books
var/list/shelves_to_load = list()
diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm
index 2bff25b00a59..4717e7dcf8cd 100644
--- a/code/controllers/subsystem/lighting.dm
+++ b/code/controllers/subsystem/lighting.dm
@@ -5,7 +5,7 @@ SUBSYSTEM_DEF(lighting)
/datum/controller/subsystem/mapping,
)
wait = 2
- flags = SS_TICKER
+ ss_flags = SS_TICKER
var/static/list/sources_queue = list() // List of lighting sources queued for update.
var/static/list/corners_queue = list() // List of lighting corners queued for update.
var/static/list/objects_queue = list() // List of lighting objects queued for update.
diff --git a/code/controllers/subsystem/machines.dm b/code/controllers/subsystem/machines.dm
index e3e0b0357a0d..8e19623ad295 100644
--- a/code/controllers/subsystem/machines.dm
+++ b/code/controllers/subsystem/machines.dm
@@ -3,7 +3,7 @@ SUBSYSTEM_DEF(machines)
dependencies = list(
/datum/controller/subsystem/atoms,
)
- flags = SS_KEEP_TIMING
+ ss_flags = SS_KEEP_TIMING
wait = 2 SECONDS
/// Assosciative list of all machines that exist.
diff --git a/code/controllers/subsystem/map_vote.dm b/code/controllers/subsystem/map_vote.dm
index 6b0495613eab..4229ee82737b 100644
--- a/code/controllers/subsystem/map_vote.dm
+++ b/code/controllers/subsystem/map_vote.dm
@@ -2,7 +2,7 @@
SUBSYSTEM_DEF(map_vote)
name = "Map Vote"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
/// Has an admin specifically set a map.
var/admin_override = FALSE
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index 25a6c290cb27..690ddbda3b55 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -351,7 +351,7 @@ Used by the AI doomsday and the self-destruct nuke.
/datum/controller/subsystem/mapping/Recover()
- flags |= SS_NO_INIT
+ ss_flags |= SS_NO_INIT
initialized = SSmapping.initialized
map_templates = SSmapping.map_templates
ruins_templates = SSmapping.ruins_templates
diff --git a/code/controllers/subsystem/market.dm b/code/controllers/subsystem/market.dm
index 8d165bee25db..62631201ba4d 100644
--- a/code/controllers/subsystem/market.dm
+++ b/code/controllers/subsystem/market.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(market)
name = "Market"
- flags = SS_BACKGROUND
+ ss_flags = SS_BACKGROUND
dependencies = list(
/datum/controller/subsystem/atoms,
)
diff --git a/code/controllers/subsystem/materials.dm b/code/controllers/subsystem/materials.dm
index 76e0d407a642..791a10ccf46c 100644
--- a/code/controllers/subsystem/materials.dm
+++ b/code/controllers/subsystem/materials.dm
@@ -6,7 +6,7 @@ These materials call on_applied() on whatever item they are applied to, common e
*/
SUBSYSTEM_DEF(materials)
name = "Materials"
- flags = SS_NO_FIRE | SS_NO_INIT
+ ss_flags = SS_NO_FIRE | SS_NO_INIT
/// Dictionary of material.id || material ref
var/list/materials
/// Flat list of materials
diff --git a/code/controllers/subsystem/minor_mapping.dm b/code/controllers/subsystem/minor_mapping.dm
index aca5845a14dd..5f59bf8d5972 100644
--- a/code/controllers/subsystem/minor_mapping.dm
+++ b/code/controllers/subsystem/minor_mapping.dm
@@ -6,7 +6,7 @@ SUBSYSTEM_DEF(minor_mapping)
/datum/controller/subsystem/mapping,
/datum/controller/subsystem/atoms,
)
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
///a list of vermin we pick from to spawn.
var/list/vermin_chances = list(
/mob/living/basic/mouse = 72,
diff --git a/code/controllers/subsystem/mobs.dm b/code/controllers/subsystem/mobs.dm
index 7f500e08289c..d0078891c224 100644
--- a/code/controllers/subsystem/mobs.dm
+++ b/code/controllers/subsystem/mobs.dm
@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(mobs)
name = "Mobs"
priority = FIRE_PRIORITY_MOBS
- flags = SS_KEEP_TIMING | SS_NO_INIT
+ ss_flags = SS_KEEP_TIMING | SS_NO_INIT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 2 SECONDS
diff --git a/code/controllers/subsystem/moods.dm b/code/controllers/subsystem/moods.dm
index 20111747bdb2..5f2c3151ad28 100644
--- a/code/controllers/subsystem/moods.dm
+++ b/code/controllers/subsystem/moods.dm
@@ -1,5 +1,5 @@
PROCESSING_SUBSYSTEM_DEF(mood)
name = "Mood"
- flags = SS_NO_INIT | SS_BACKGROUND
+ ss_flags = SS_NO_INIT | SS_BACKGROUND
priority = 20
wait = 1 SECONDS
diff --git a/code/controllers/subsystem/mouse_entered.dm b/code/controllers/subsystem/mouse_entered.dm
index 6dc8d995a522..baf60e330247 100644
--- a/code/controllers/subsystem/mouse_entered.dm
+++ b/code/controllers/subsystem/mouse_entered.dm
@@ -2,7 +2,7 @@
SUBSYSTEM_DEF(mouse_entered)
name = "MouseEntered"
wait = 1
- flags = SS_NO_INIT | SS_TICKER
+ ss_flags = SS_NO_INIT | SS_TICKER
priority = FIRE_PRIORITY_MOUSE_ENTERED
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
diff --git a/code/controllers/subsystem/movement/ai_movement.dm b/code/controllers/subsystem/movement/ai_movement.dm
index 8703fbfdfc6e..9c96ea6a22bd 100644
--- a/code/controllers/subsystem/movement/ai_movement.dm
+++ b/code/controllers/subsystem/movement/ai_movement.dm
@@ -1,7 +1,7 @@
/// The subsystem used to tick [/datum/ai_movement] instances. Handling the movement of individual AI instances
MOVEMENT_SUBSYSTEM_DEF(ai_movement)
name = "AI movement"
- flags = SS_BACKGROUND|SS_TICKER
+ ss_flags = SS_BACKGROUND|SS_TICKER
priority = FIRE_PRIORITY_NPC_MOVEMENT
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
diff --git a/code/controllers/subsystem/movement/cliff_falling.dm b/code/controllers/subsystem/movement/cliff_falling.dm
index b3d7f114dfba..d9e56eb2a02d 100644
--- a/code/controllers/subsystem/movement/cliff_falling.dm
+++ b/code/controllers/subsystem/movement/cliff_falling.dm
@@ -2,7 +2,7 @@
MOVEMENT_SUBSYSTEM_DEF(cliff_falling)
name = "Cliff Falling"
priority = FIRE_PRIORITY_CLIFF_FALLING
- flags = SS_NO_INIT|SS_TICKER
+ ss_flags = SS_NO_INIT|SS_TICKER
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
/// Who are currently falling and with which movemanager?
diff --git a/code/controllers/subsystem/movement/hyperspace_drift.dm b/code/controllers/subsystem/movement/hyperspace_drift.dm
index bd1ac67a6f0a..66710bd02f32 100644
--- a/code/controllers/subsystem/movement/hyperspace_drift.dm
+++ b/code/controllers/subsystem/movement/hyperspace_drift.dm
@@ -2,5 +2,5 @@
MOVEMENT_SUBSYSTEM_DEF(hyperspace_drift)
name = "Hyperspace Drift"
priority = FIRE_PRIORITY_HYPERSPACE_DRIFT
- flags = SS_NO_INIT|SS_TICKER
+ ss_flags = SS_NO_INIT|SS_TICKER
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
diff --git a/code/controllers/subsystem/movement/movement.dm b/code/controllers/subsystem/movement/movement.dm
index 2b0463db7905..052b82991e32 100644
--- a/code/controllers/subsystem/movement/movement.dm
+++ b/code/controllers/subsystem/movement/movement.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(movement)
name = "Movement Loops"
- flags = SS_NO_INIT|SS_TICKER
+ ss_flags = SS_NO_INIT|SS_TICKER
wait = 1 //Fire each tick
/*
A breif aside about the bucketing system here
diff --git a/code/controllers/subsystem/movement/movement_types.dm b/code/controllers/subsystem/movement/movement_types.dm
index 6acce747bbe1..1aa16a42fa07 100644
--- a/code/controllers/subsystem/movement/movement_types.dm
+++ b/code/controllers/subsystem/movement/movement_types.dm
@@ -59,11 +59,16 @@
SEND_SIGNAL(src, COMSIG_MOVELOOP_START)
status |= MOVELOOP_STATUS_RUNNING
//If this is our first time starting to move with this loop
- //And we're meant to start instantly
+ //And we want to start consistently fast
if(!timer && flags & MOVEMENT_LOOP_START_FAST)
- timer = world.time
+ // + tick_lag because we want to avoid weird jumping in atoms that were just created (and avoid inconsistencies around subsystem timing)
+ timer = NEXT_VISUAL_TICK + world.tick_lag
+ return
+ //And we're meant to start instantly
+ if(!timer && flags & MOVEMENT_LOOP_START_INSTANT)
+ timer = NEXT_VISUAL_TICK
return
- timer = world.time + delay
+ timer = NEXT_VISUAL_TICK + delay
///Called when a loop is stopped, doesn't stop the loop itself
/datum/move_loop/proc/loop_stopped()
diff --git a/code/controllers/subsystem/movement/newtonian_movement.dm b/code/controllers/subsystem/movement/newtonian_movement.dm
index e4143669678b..d88a7d72f576 100644
--- a/code/controllers/subsystem/movement/newtonian_movement.dm
+++ b/code/controllers/subsystem/movement/newtonian_movement.dm
@@ -1,7 +1,7 @@
/// The subsystem is intended to tick things related to space/newtonian movement, such as constant sources of inertia
MOVEMENT_SUBSYSTEM_DEF(newtonian_movement)
name = "Newtonian Movement"
- flags = SS_NO_INIT|SS_TICKER
+ ss_flags = SS_NO_INIT|SS_TICKER
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/stat_tag = "P" //Used for logging
diff --git a/code/controllers/subsystem/networks/bitrunning.dm b/code/controllers/subsystem/networks/bitrunning.dm
index 6f6d68a0b05c..1bb8eb49e4d7 100644
--- a/code/controllers/subsystem/networks/bitrunning.dm
+++ b/code/controllers/subsystem/networks/bitrunning.dm
@@ -2,7 +2,7 @@
SUBSYSTEM_DEF(bitrunning)
name = "Bitrunning"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
var/list/all_domains = list()
diff --git a/code/controllers/subsystem/networks/circuit_component.dm b/code/controllers/subsystem/networks/circuit_component.dm
index bae302ed9d0a..54ece2029032 100644
--- a/code/controllers/subsystem/networks/circuit_component.dm
+++ b/code/controllers/subsystem/networks/circuit_component.dm
@@ -2,7 +2,7 @@ SUBSYSTEM_DEF(circuit_component)
name = "Circuit Components"
wait = 0.1 SECONDS
priority = FIRE_PRIORITY_DEFAULT
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
var/list/callbacks_to_invoke = list()
var/list/currentrun = list()
diff --git a/code/controllers/subsystem/networks/id_access.dm b/code/controllers/subsystem/networks/id_access.dm
index 889c59fbf5bb..482bd5e119f8 100644
--- a/code/controllers/subsystem/networks/id_access.dm
+++ b/code/controllers/subsystem/networks/id_access.dm
@@ -3,7 +3,7 @@
*/
SUBSYSTEM_DEF(id_access)
name = "IDs and Access"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
/// Dictionary of access flags. Keys are accesses. Values are their associated bitflags.
var/list/flags_by_access = list()
diff --git a/code/controllers/subsystem/networks/radio.dm b/code/controllers/subsystem/networks/radio.dm
index 6d880cedeb30..8ddc9ae955ca 100644
--- a/code/controllers/subsystem/networks/radio.dm
+++ b/code/controllers/subsystem/networks/radio.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(radio)
name = "Radio"
- flags = SS_NO_FIRE|SS_NO_INIT
+ ss_flags = SS_NO_FIRE|SS_NO_INIT
var/list/datum/radio_frequency/frequencies = list()
var/list/saymodes = list()
diff --git a/code/controllers/subsystem/networks/wiremod_composite.dm b/code/controllers/subsystem/networks/wiremod_composite.dm
index 15b21beefa1b..e099a2bfa902 100644
--- a/code/controllers/subsystem/networks/wiremod_composite.dm
+++ b/code/controllers/subsystem/networks/wiremod_composite.dm
@@ -7,7 +7,7 @@
**/
SUBSYSTEM_DEF(wiremod_composite)
name = "Wiremod Composite Templates"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
/// The templates created and stored
var/list/templates = list()
diff --git a/code/controllers/subsystem/npcpool.dm b/code/controllers/subsystem/npcpool.dm
index 6fbfdadf6828..a03e7c9872bc 100644
--- a/code/controllers/subsystem/npcpool.dm
+++ b/code/controllers/subsystem/npcpool.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(npcpool)
name = "NPC Pool"
- flags = SS_POST_FIRE_TIMING|SS_NO_INIT|SS_BACKGROUND
+ ss_flags = SS_POST_FIRE_TIMING|SS_NO_INIT|SS_BACKGROUND
priority = FIRE_PRIORITY_NPC
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm
index 96041edd4237..dda905504d87 100644
--- a/code/controllers/subsystem/overlays.dm
+++ b/code/controllers/subsystem/overlays.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(overlays)
name = "Overlay"
- flags = SS_NO_FIRE|SS_NO_INIT
+ ss_flags = SS_NO_FIRE|SS_NO_INIT
var/list/stats
/datum/controller/subsystem/overlays/PreInit()
diff --git a/code/controllers/subsystem/pai.dm b/code/controllers/subsystem/pai.dm
index 4abc752ea81a..b2db11c4a51f 100644
--- a/code/controllers/subsystem/pai.dm
+++ b/code/controllers/subsystem/pai.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(pai)
name = "pAI"
- flags = SS_NO_INIT|SS_NO_FIRE
+ ss_flags = SS_NO_INIT|SS_NO_FIRE
/// List of pAI candidates, including those not submitted.
var/list/candidates = list()
diff --git a/code/controllers/subsystem/parallax.dm b/code/controllers/subsystem/parallax.dm
index 28ebd80560f8..1b509f8d7578 100644
--- a/code/controllers/subsystem/parallax.dm
+++ b/code/controllers/subsystem/parallax.dm
@@ -4,7 +4,7 @@
SUBSYSTEM_DEF(parallax)
name = "Parallax"
wait = 2
- flags = SS_POST_FIRE_TIMING | SS_BACKGROUND | SS_NO_INIT
+ ss_flags = SS_POST_FIRE_TIMING | SS_BACKGROUND | SS_NO_INIT
priority = FIRE_PRIORITY_PARALLAX
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
var/list/currentrun
@@ -71,7 +71,7 @@ SUBSYSTEM_DEF(parallax)
if(picked_parallax == PARALLAX_NONE)
return
- random_layer = new picked_parallax(null, /* hud_owner = */ null, /* template = */ TRUE)
+ random_layer = new picked_parallax(null, /* hud_owner = */ null, /* owner = */ null, /* template = */ TRUE)
RegisterSignal(random_layer, COMSIG_QDELETING, PROC_REF(clear_references))
random_layer.get_random_look()
@@ -85,8 +85,9 @@ SUBSYSTEM_DEF(parallax)
//Parallax is one of the first things to be set (during client join), so rarely is anything fast enough to swap it out
//That's why we need to swap the layers out for fast joining clients :/
for(var/client/client as anything in GLOB.clients)
- client.parallax_layers_cached?.Cut()
- client.mob?.hud_used?.update_parallax_pref(client.mob)
+ // gotta clear things out
+ client?.parallax_rock?.set_layer_settings(0, FALSE, FALSE)
+ client.mob?.hud_used?.update_parallax_pref()
/datum/controller/subsystem/parallax/proc/clear_references()
SIGNAL_HANDLER
diff --git a/code/controllers/subsystem/persistence/_persistence.dm b/code/controllers/subsystem/persistence/_persistence.dm
index 59006d75ce08..ea34cb24dd1d 100644
--- a/code/controllers/subsystem/persistence/_persistence.dm
+++ b/code/controllers/subsystem/persistence/_persistence.dm
@@ -7,7 +7,7 @@ SUBSYSTEM_DEF(persistence)
/datum/controller/subsystem/mapping,
/datum/controller/subsystem/atoms,
)
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
///instantiated wall engraving components
var/list/wall_engravings = list()
diff --git a/code/controllers/subsystem/persistent_paintings.dm b/code/controllers/subsystem/persistent_paintings.dm
index e9a057432fe9..6d9aabf99893 100644
--- a/code/controllers/subsystem/persistent_paintings.dm
+++ b/code/controllers/subsystem/persistent_paintings.dm
@@ -120,7 +120,7 @@
SUBSYSTEM_DEF(persistent_paintings)
name = "Persistent Paintings"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
dependencies = list(
/datum/controller/subsystem/persistence,
)
diff --git a/code/controllers/subsystem/ping.dm b/code/controllers/subsystem/ping.dm
index 22a64c2802a5..0607f89162c4 100644
--- a/code/controllers/subsystem/ping.dm
+++ b/code/controllers/subsystem/ping.dm
@@ -8,7 +8,7 @@ SUBSYSTEM_DEF(ping)
priority = FIRE_PRIORITY_PING
init_stage = INITSTAGE_EARLY
wait = 4 SECONDS
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
var/list/currentrun = list()
diff --git a/code/controllers/subsystem/points_of_interest.dm b/code/controllers/subsystem/points_of_interest.dm
index 7bec303d66a6..6844c84ecdcf 100644
--- a/code/controllers/subsystem/points_of_interest.dm
+++ b/code/controllers/subsystem/points_of_interest.dm
@@ -2,7 +2,7 @@
SUBSYSTEM_DEF(points_of_interest)
name = "Points of Interest"
- flags = SS_NO_FIRE | SS_NO_INIT
+ ss_flags = SS_NO_FIRE | SS_NO_INIT
/// List of mob POIs. This list is automatically sorted.
var/list/datum/point_of_interest/mob_poi/mob_points_of_interest = list()
diff --git a/code/controllers/subsystem/polling.dm b/code/controllers/subsystem/polling.dm
index 1477fa7e7c36..b830aec53ef5 100644
--- a/code/controllers/subsystem/polling.dm
+++ b/code/controllers/subsystem/polling.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(polling)
name = "Polling"
- flags = SS_BACKGROUND | SS_NO_INIT
+ ss_flags = SS_BACKGROUND | SS_NO_INIT
wait = 1 SECONDS
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
/// List of polls currently ongoing, to be checked on next fire()
diff --git a/code/controllers/subsystem/processing/acid.dm b/code/controllers/subsystem/processing/acid.dm
index a92880f51e3d..2e16efdda891 100644
--- a/code/controllers/subsystem/processing/acid.dm
+++ b/code/controllers/subsystem/processing/acid.dm
@@ -2,5 +2,5 @@
PROCESSING_SUBSYSTEM_DEF(acid)
name = "Acid"
priority = FIRE_PRIORITY_ACID
- flags = SS_NO_INIT|SS_BACKGROUND
+ ss_flags = SS_NO_INIT|SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
diff --git a/code/controllers/subsystem/processing/ai_basic_avoidance.dm b/code/controllers/subsystem/processing/ai_basic_avoidance.dm
index 2a3fe992f447..9fdd5d2d4534 100644
--- a/code/controllers/subsystem/processing/ai_basic_avoidance.dm
+++ b/code/controllers/subsystem/processing/ai_basic_avoidance.dm
@@ -1,4 +1,4 @@
PROCESSING_SUBSYSTEM_DEF(basic_avoidance)
name = "Basic Avoidance"
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
wait = 2 SECONDS
diff --git a/code/controllers/subsystem/processing/ai_behaviors.dm b/code/controllers/subsystem/processing/ai_behaviors.dm
index 56904fb3c2f6..7304c5ed3628 100644
--- a/code/controllers/subsystem/processing/ai_behaviors.dm
+++ b/code/controllers/subsystem/processing/ai_behaviors.dm
@@ -1,7 +1,7 @@
/// The subsystem used to tick [/datum/ai_behavior] instances. Handling the individual actions an AI can take like punching someone in the fucking NUTS
PROCESSING_SUBSYSTEM_DEF(ai_behaviors)
name = "AI Behavior Ticker"
- flags = SS_POST_FIRE_TIMING|SS_BACKGROUND
+ ss_flags = SS_POST_FIRE_TIMING|SS_BACKGROUND
priority = FIRE_PRIORITY_NPC_ACTIONS
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
dependencies = list(
diff --git a/code/controllers/subsystem/processing/ai_idle_behaviors.dm b/code/controllers/subsystem/processing/ai_idle_behaviors.dm
index c776451ab203..2b631da96f3b 100644
--- a/code/controllers/subsystem/processing/ai_idle_behaviors.dm
+++ b/code/controllers/subsystem/processing/ai_idle_behaviors.dm
@@ -1,6 +1,6 @@
PROCESSING_SUBSYSTEM_DEF(idle_ai_behaviors)
name = "AI Idle Behaviors"
- flags = SS_BACKGROUND
+ ss_flags = SS_BACKGROUND
wait = 1.5 SECONDS
priority = FIRE_PRIORITY_IDLE_NPC
dependencies = list(
diff --git a/code/controllers/subsystem/processing/antag_hud.dm b/code/controllers/subsystem/processing/antag_hud.dm
index aaf10a5e2ecb..98e439039e84 100644
--- a/code/controllers/subsystem/processing/antag_hud.dm
+++ b/code/controllers/subsystem/processing/antag_hud.dm
@@ -1,4 +1,4 @@
PROCESSING_SUBSYSTEM_DEF(antag_hud)
name = "Antag HUDs"
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
wait = 2 SECONDS
diff --git a/code/controllers/subsystem/processing/aura.dm b/code/controllers/subsystem/processing/aura.dm
index 12a7d511fb79..992a64680a36 100644
--- a/code/controllers/subsystem/processing/aura.dm
+++ b/code/controllers/subsystem/processing/aura.dm
@@ -1,5 +1,5 @@
/// The subsystem used to tick auras ([/datum/component/aura_healing] and [/datum/component/damage_aura]).
PROCESSING_SUBSYSTEM_DEF(aura)
name = "Aura"
- flags = SS_NO_INIT | SS_BACKGROUND | SS_KEEP_TIMING
+ ss_flags = SS_NO_INIT | SS_BACKGROUND | SS_KEEP_TIMING
wait = 0.3 SECONDS
diff --git a/code/controllers/subsystem/processing/clock_component.dm b/code/controllers/subsystem/processing/clock_component.dm
index d07acaa6d793..13d165e90ff9 100644
--- a/code/controllers/subsystem/processing/clock_component.dm
+++ b/code/controllers/subsystem/processing/clock_component.dm
@@ -1,5 +1,5 @@
/// The subsystem used to tick [/datum/component/acid] instances.
PROCESSING_SUBSYSTEM_DEF(clock_component)
name = "Clock Component"
- flags = SS_NO_INIT|SS_BACKGROUND|SS_KEEP_TIMING
+ ss_flags = SS_NO_INIT|SS_BACKGROUND|SS_KEEP_TIMING
wait = COMP_CLOCK_DELAY
diff --git a/code/controllers/subsystem/processing/digital_clock.dm b/code/controllers/subsystem/processing/digital_clock.dm
index 6981e785d1c3..4146a2c857fe 100644
--- a/code/controllers/subsystem/processing/digital_clock.dm
+++ b/code/controllers/subsystem/processing/digital_clock.dm
@@ -1,5 +1,5 @@
/// The subsystem used to tick digital clocks
PROCESSING_SUBSYSTEM_DEF(digital_clock)
name = "Digital Clocks"
- flags = SS_NO_INIT|SS_BACKGROUND|SS_KEEP_TIMING
+ ss_flags = SS_NO_INIT|SS_BACKGROUND|SS_KEEP_TIMING
wait = 1 SECONDS
diff --git a/code/controllers/subsystem/processing/fire_burning.dm b/code/controllers/subsystem/processing/fire_burning.dm
index 6cde5e3c34f6..35b794b5f548 100644
--- a/code/controllers/subsystem/processing/fire_burning.dm
+++ b/code/controllers/subsystem/processing/fire_burning.dm
@@ -2,5 +2,5 @@
PROCESSING_SUBSYSTEM_DEF(burning)
name = "Burning"
priority = FIRE_PRIORITY_BURNING
- flags = SS_NO_INIT|SS_BACKGROUND
+ ss_flags = SS_NO_INIT|SS_BACKGROUND
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
diff --git a/code/controllers/subsystem/processing/fishing.dm b/code/controllers/subsystem/processing/fishing.dm
index a607995157ca..01745de438c0 100644
--- a/code/controllers/subsystem/processing/fishing.dm
+++ b/code/controllers/subsystem/processing/fishing.dm
@@ -4,7 +4,7 @@ PROCESSING_SUBSYSTEM_DEF(fishing)
dependencies = list(
/datum/controller/subsystem/atoms,
)
- flags = SS_BACKGROUND
+ ss_flags = SS_BACKGROUND
wait = 0.05 SECONDS // If you raise it to 0.1 SECONDS, you better also modify [datum/fish_movement/move_fish()]
///A list of cached fish icons
var/list/cached_fish_icons
diff --git a/code/controllers/subsystem/processing/greyscale.dm b/code/controllers/subsystem/processing/greyscale.dm
index 03b19ac2effe..551f7a12e49a 100644
--- a/code/controllers/subsystem/processing/greyscale.dm
+++ b/code/controllers/subsystem/processing/greyscale.dm
@@ -1,6 +1,6 @@
PROCESSING_SUBSYSTEM_DEF(greyscale)
name = "Greyscale"
- flags = SS_BACKGROUND
+ ss_flags = SS_BACKGROUND
wait = 3 SECONDS
init_stage = INITSTAGE_EARLY
var/list/datum/greyscale_config/configurations = list()
diff --git a/code/controllers/subsystem/processing/instruments.dm b/code/controllers/subsystem/processing/instruments.dm
index 9cd8706e0c4f..197c16047b7f 100644
--- a/code/controllers/subsystem/processing/instruments.dm
+++ b/code/controllers/subsystem/processing/instruments.dm
@@ -1,7 +1,7 @@
PROCESSING_SUBSYSTEM_DEF(instruments)
name = "Instruments"
wait = 0.5
- flags = SS_KEEP_TIMING
+ ss_flags = SS_KEEP_TIMING
priority = FIRE_PRIORITY_INSTRUMENTS
/// List of all instrument data, associative id = datum
var/static/list/datum/instrument/instrument_data = list()
diff --git a/code/controllers/subsystem/processing/newplayer.dm b/code/controllers/subsystem/processing/newplayer.dm
index e7537da5ead4..8b12cb5bf31b 100644
--- a/code/controllers/subsystem/processing/newplayer.dm
+++ b/code/controllers/subsystem/processing/newplayer.dm
@@ -1,5 +1,5 @@
PROCESSING_SUBSYSTEM_DEF(newplayer_info)
name = "New Player Info"
- flags = SS_NO_INIT | SS_BACKGROUND
+ ss_flags = SS_NO_INIT | SS_BACKGROUND
wait = 1 SECONDS
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
diff --git a/code/controllers/subsystem/processing/obj.dm b/code/controllers/subsystem/processing/obj.dm
index 3566e8a4dc22..c5eb6e3374cb 100644
--- a/code/controllers/subsystem/processing/obj.dm
+++ b/code/controllers/subsystem/processing/obj.dm
@@ -1,5 +1,5 @@
PROCESSING_SUBSYSTEM_DEF(obj)
name = "Objects"
priority = FIRE_PRIORITY_OBJ
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
wait = 2 SECONDS
diff --git a/code/controllers/subsystem/processing/personality.dm b/code/controllers/subsystem/processing/personality.dm
index 0f9c1021befd..8b642edd84a5 100644
--- a/code/controllers/subsystem/processing/personality.dm
+++ b/code/controllers/subsystem/processing/personality.dm
@@ -1,7 +1,7 @@
PROCESSING_SUBSYSTEM_DEF(personalities)
name = "Personalities"
runlevels = RUNLEVEL_GAME
- flags = SS_BACKGROUND|SS_POST_FIRE_TIMING
+ ss_flags = SS_BACKGROUND|SS_POST_FIRE_TIMING
wait = 3 SECONDS
/// All personality singletons indexed by their type
diff --git a/code/controllers/subsystem/processing/plumbing.dm b/code/controllers/subsystem/processing/plumbing.dm
index 34d6d8f882e9..5a9204edf647 100644
--- a/code/controllers/subsystem/processing/plumbing.dm
+++ b/code/controllers/subsystem/processing/plumbing.dm
@@ -2,4 +2,4 @@ PROCESSING_SUBSYSTEM_DEF(plumbing)
name = "Plumbing"
wait = 10
stat_tag = "FD" //its actually Fluid Ducts
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
diff --git a/code/controllers/subsystem/processing/priority_effects.dm b/code/controllers/subsystem/processing/priority_effects.dm
index f3be081ec50c..51274cbfefd0 100644
--- a/code/controllers/subsystem/processing/priority_effects.dm
+++ b/code/controllers/subsystem/processing/priority_effects.dm
@@ -1,6 +1,6 @@
PROCESSING_SUBSYSTEM_DEF(priority_effects)
name = "Priority Status Effects"
- flags = SS_KEEP_TIMING | SS_NO_INIT
+ ss_flags = SS_KEEP_TIMING | SS_NO_INIT
wait = 0.2 SECONDS // Same as SSfastprocess, but can be anything, assuming you refactor all high-priority status effect intervals and durations to be a multiple of it.
priority = FIRE_PRIORITY_PRIORITY_EFFECTS
stat_tag = "PEFF"
diff --git a/code/controllers/subsystem/processing/processing.dm b/code/controllers/subsystem/processing/processing.dm
index d2b255211686..4338ae420d9c 100644
--- a/code/controllers/subsystem/processing/processing.dm
+++ b/code/controllers/subsystem/processing/processing.dm
@@ -3,7 +3,7 @@
SUBSYSTEM_DEF(processing)
name = "Processing"
priority = FIRE_PRIORITY_PROCESS
- flags = SS_BACKGROUND|SS_POST_FIRE_TIMING|SS_NO_INIT
+ ss_flags = SS_BACKGROUND|SS_POST_FIRE_TIMING|SS_NO_INIT
wait = 1 SECONDS
var/stat_tag = "P" //Used for logging
diff --git a/code/controllers/subsystem/processing/projectiles.dm b/code/controllers/subsystem/processing/projectiles.dm
index 0124296a3a23..75c0e20ccf8e 100644
--- a/code/controllers/subsystem/processing/projectiles.dm
+++ b/code/controllers/subsystem/processing/projectiles.dm
@@ -2,7 +2,7 @@ PROCESSING_SUBSYSTEM_DEF(projectiles)
name = "Projectiles"
wait = 1
stat_tag = "PP"
- flags = SS_NO_INIT|SS_TICKER
+ ss_flags = SS_NO_INIT|SS_TICKER
/*
* Maximum amount of pixels a projectile can pass per tick *unless* its a hitscan projectile.
* This prevents projectiles from turning into essentially hitscans if SSprojectiles starts chugging
diff --git a/code/controllers/subsystem/processing/quirks.dm b/code/controllers/subsystem/processing/quirks.dm
index 63effdc09f57..e57bd1f15dd3 100644
--- a/code/controllers/subsystem/processing/quirks.dm
+++ b/code/controllers/subsystem/processing/quirks.dm
@@ -44,7 +44,7 @@ GLOBAL_LIST_INIT(quirk_string_blacklist, generate_quirk_string_blacklist())
// - Quirk datums are stored and hold different effects, as well as being a vector for applying trait string
PROCESSING_SUBSYSTEM_DEF(quirks)
name = "Quirks"
- flags = SS_BACKGROUND
+ ss_flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME
wait = 1 SECONDS
diff --git a/code/controllers/subsystem/processing/reagents.dm b/code/controllers/subsystem/processing/reagents.dm
index b116d859709b..2891a4cdd67c 100644
--- a/code/controllers/subsystem/processing/reagents.dm
+++ b/code/controllers/subsystem/processing/reagents.dm
@@ -4,7 +4,7 @@ PROCESSING_SUBSYSTEM_DEF(reagents)
name = "Reagents"
priority = FIRE_PRIORITY_REAGENTS
wait = 0.25 SECONDS //You might think that rate_up_lim has to be set to half, but since everything is normalised around seconds_per_tick, it automatically adjusts it to be per second. Magic!
- flags = SS_KEEP_TIMING
+ ss_flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
init_stage = INITSTAGE_EARLY
///What time was it when we last ticked
diff --git a/code/controllers/subsystem/processing/station.dm b/code/controllers/subsystem/processing/station.dm
index 3f363934e4d4..0d99ea353004 100644
--- a/code/controllers/subsystem/processing/station.dm
+++ b/code/controllers/subsystem/processing/station.dm
@@ -1,6 +1,6 @@
PROCESSING_SUBSYSTEM_DEF(station)
name = "Station"
- flags = SS_BACKGROUND
+ ss_flags = SS_BACKGROUND
runlevels = RUNLEVEL_GAME
wait = 5 SECONDS
diff --git a/code/controllers/subsystem/processing/turrets.dm b/code/controllers/subsystem/processing/turrets.dm
index 34f128b5c22a..f7818a8f2a28 100644
--- a/code/controllers/subsystem/processing/turrets.dm
+++ b/code/controllers/subsystem/processing/turrets.dm
@@ -1,6 +1,6 @@
/// The subsystem used for portable turrets, as they're relatively more intensive compared to most other machines, so we don't want them hogging tick usage from everything else.
PROCESSING_SUBSYSTEM_DEF(turrets)
name = "Turret Processing"
- flags = SS_NO_INIT | SS_KEEP_TIMING
+ ss_flags = SS_NO_INIT | SS_KEEP_TIMING
wait = 2 SECONDS
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
diff --git a/code/controllers/subsystem/queuelinks.dm b/code/controllers/subsystem/queuelinks.dm
index 20b556229318..781d863f6517 100644
--- a/code/controllers/subsystem/queuelinks.dm
+++ b/code/controllers/subsystem/queuelinks.dm
@@ -2,7 +2,7 @@
SUBSYSTEM_DEF(queuelinks)
name = "Queue Links"
- flags = SS_NO_FIRE | SS_NO_INIT
+ ss_flags = SS_NO_FIRE | SS_NO_INIT
///assoc list of pending queues, id = /datum/queue_link
var/list/queues = list()
diff --git a/code/controllers/subsystem/radiation.dm b/code/controllers/subsystem/radiation.dm
index a129c2a34641..31e06b1afe1d 100644
--- a/code/controllers/subsystem/radiation.dm
+++ b/code/controllers/subsystem/radiation.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(radiation)
name = "Radiation"
- flags = SS_BACKGROUND | SS_NO_INIT
+ ss_flags = SS_BACKGROUND | SS_NO_INIT
wait = 0.5 SECONDS
@@ -132,7 +132,7 @@ SUBSYSTEM_DEF(radiation)
/// Returns whether or not the human is covered head to toe in rad-protected clothing.
/datum/controller/subsystem/radiation/proc/wearing_rad_protected_clothing(mob/living/carbon/human/human)
- for (var/obj/item/bodypart/limb as anything in human.bodyparts)
+ for (var/obj/item/bodypart/limb as anything in human.get_bodyparts())
var/protected = FALSE
for (var/obj/item/clothing as anything in human.get_clothing_on_part(limb))
diff --git a/code/controllers/subsystem/radioactive_nebula.dm b/code/controllers/subsystem/radioactive_nebula.dm
index 0778725accbe..44a908bbb3f7 100644
--- a/code/controllers/subsystem/radioactive_nebula.dm
+++ b/code/controllers/subsystem/radioactive_nebula.dm
@@ -7,7 +7,7 @@ SUBSYSTEM_DEF(radioactive_nebula)
dependencies = list(
/datum/controller/subsystem/processing/station,
)
- flags = SS_BACKGROUND
+ ss_flags = SS_BACKGROUND
wait = 30 SECONDS
VAR_PRIVATE
diff --git a/code/controllers/subsystem/restaurant.dm b/code/controllers/subsystem/restaurant.dm
index 034a7610cb6f..c22c9163fbb3 100644
--- a/code/controllers/subsystem/restaurant.dm
+++ b/code/controllers/subsystem/restaurant.dm
@@ -5,7 +5,7 @@ This subsystem exists to serve as a holder for important info for the restaurant
SUBSYSTEM_DEF(restaurant)
name = "Restaurant"
wait = 20 SECONDS //Roll for new guests but don't do it too fast.
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
///All venues that exist, assoc list of type - reference
var/list/all_venues = list()
///All customer data datums that exist, assoc list of type - reference
diff --git a/code/controllers/subsystem/server_maint.dm b/code/controllers/subsystem/server_maint.dm
index c4428f297aee..44a13093a7f6 100644
--- a/code/controllers/subsystem/server_maint.dm
+++ b/code/controllers/subsystem/server_maint.dm
@@ -3,7 +3,7 @@
SUBSYSTEM_DEF(server_maint)
name = "Server Tasks"
wait = 6
- flags = SS_POST_FIRE_TIMING
+ ss_flags = SS_POST_FIRE_TIMING
priority = FIRE_PRIORITY_SERVER_MAINT
init_stage = INITSTAGE_EARLY
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index 7b1ba551a8e4..0553cc6d5654 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -14,7 +14,7 @@ SUBSYSTEM_DEF(shuttle)
/datum/controller/subsystem/atoms,
/datum/controller/subsystem/air,
)
- flags = SS_KEEP_TIMING
+ ss_flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_SETUP | RUNLEVEL_GAME
/// A list of all the mobile docking ports.
diff --git a/code/controllers/subsystem/skills.dm b/code/controllers/subsystem/skills.dm
index 0ece2ac2aaa9..0a7ac5383ed2 100644
--- a/code/controllers/subsystem/skills.dm
+++ b/code/controllers/subsystem/skills.dm
@@ -4,7 +4,7 @@ This subsystem mostly exists to populate and manage the skill singletons.
SUBSYSTEM_DEF(skills)
name = "Skills"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
///Dictionary of skill.type || skill ref
var/list/all_skills = list()
///List of level names with index corresponding to skill level
diff --git a/code/controllers/subsystem/sounds.dm b/code/controllers/subsystem/sounds.dm
index 36597f53789e..4832f576f7c5 100644
--- a/code/controllers/subsystem/sounds.dm
+++ b/code/controllers/subsystem/sounds.dm
@@ -2,7 +2,7 @@
SUBSYSTEM_DEF(sounds)
name = "Sounds"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
init_stage = INITSTAGE_EARLY
var/static/using_channels_max = CHANNEL_HIGHEST_AVAILABLE //BYOND max channels
/// Amount of channels to reserve for random usage rather than reservations being allowed to reserve all channels. Also a nice safeguard for when someone screws up.
diff --git a/code/controllers/subsystem/sprite_accessories.dm b/code/controllers/subsystem/sprite_accessories.dm
index 2ceed1d3fbb9..b053136cb6f8 100644
--- a/code/controllers/subsystem/sprite_accessories.dm
+++ b/code/controllers/subsystem/sprite_accessories.dm
@@ -14,7 +14,7 @@
/// A sprite accessory is something that we add to a human sprite to make them look different. This is hair, facial hair, underwear, mutant bits, etc.
SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity
name = "Sprite Accessories"
- flags = SS_NO_FIRE | SS_NO_INIT
+ ss_flags = SS_NO_FIRE | SS_NO_INIT
// HOLY SHIT COMPACT THIS INTO ASSOCIATED LISTS SO WE STOP ADDING VARIABLES
//Hairstyles
@@ -29,17 +29,17 @@ SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity
var/list/hair_masks_list //! stores /datum/hair_mask indexed by type
//Underwear
- var/list/underwear_list //! stores /datum/sprite_accessory/underwear indexed by name
+ var/list/underwear_list //! stores /datum/sprite_accessory/clothing/underwear indexed by name
var/list/underwear_m //! stores only underwear name
var/list/underwear_f //! stores only underwear name
//Undershirts
- var/list/undershirt_list //! stores /datum/sprite_accessory/undershirt indexed by name
+ var/list/undershirt_list //! stores /datum/sprite_accessory/clothing/undershirt indexed by name
var/list/undershirt_m //! stores only undershirt name
var/list/undershirt_f //! stores only undershirt name
//Socks
- var/list/socks_list //! stores /datum/sprite_accessory/socks indexed by name
+ var/list/socks_list //! stores /datum/sprite_accessory/clothing/socks indexed by name
//All features, indexed by feature key, then name of the sprite accessory to the datum iteslf
var/list/list/feature_list
@@ -64,17 +64,17 @@ SUBSYSTEM_DEF(accessories) // just 'accessories' for brevity
facial_hairstyles_male_list = facial_hair_lists[MALE_SPRITE_LIST]
facial_hairstyles_female_list = facial_hair_lists[FEMALE_SPRITE_LIST]
- var/underwear_lists = init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear)
+ var/underwear_lists = init_sprite_accessory_subtypes(/datum/sprite_accessory/clothing/underwear)
underwear_list = underwear_lists[DEFAULT_SPRITE_LIST]
underwear_m = underwear_lists[MALE_SPRITE_LIST]
underwear_f = underwear_lists[FEMALE_SPRITE_LIST]
- var/undershirt_lists = init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt)
+ var/undershirt_lists = init_sprite_accessory_subtypes(/datum/sprite_accessory/clothing/undershirt)
undershirt_list = undershirt_lists[DEFAULT_SPRITE_LIST]
undershirt_m = undershirt_lists[MALE_SPRITE_LIST]
undershirt_f = undershirt_lists[FEMALE_SPRITE_LIST]
- socks_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/socks)[DEFAULT_SPRITE_LIST]
+ socks_list = init_sprite_accessory_subtypes(/datum/sprite_accessory/clothing/socks)[DEFAULT_SPRITE_LIST]
feature_list = list()
// felinids
diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm
index 7de2877e519f..6bee629ade65 100644
--- a/code/controllers/subsystem/statpanel.dm
+++ b/code/controllers/subsystem/statpanel.dm
@@ -3,7 +3,7 @@ SUBSYSTEM_DEF(statpanels)
wait = 4
priority = FIRE_PRIORITY_STATPANEL
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
var/list/currentrun = list()
var/list/global_data
var/list/mc_data
diff --git a/code/controllers/subsystem/stickyban.dm b/code/controllers/subsystem/stickyban.dm
index 8a955b2fb0df..e19c574a9fa8 100644
--- a/code/controllers/subsystem/stickyban.dm
+++ b/code/controllers/subsystem/stickyban.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(stickyban)
name = "PRISM"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
var/list/cache = list()
var/list/dbcache = list()
diff --git a/code/controllers/subsystem/tcgsetup.dm b/code/controllers/subsystem/tcgsetup.dm
index 5904c821e79e..0a4914ec97c2 100644
--- a/code/controllers/subsystem/tcgsetup.dm
+++ b/code/controllers/subsystem/tcgsetup.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(trading_card_game)
name = "Trading Card Game"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
/// Base directory for all related string files
var/card_directory = "strings/tcg"
/// List of card files to load
diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm
index ea8b02e81296..9f5d518b6c2a 100644
--- a/code/controllers/subsystem/tgui.dm
+++ b/code/controllers/subsystem/tgui.dm
@@ -13,7 +13,7 @@
SUBSYSTEM_DEF(tgui)
name = "tgui"
wait = 9
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
priority = FIRE_PRIORITY_TGUI
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
index 95c6923269f6..db7512a62712 100644
--- a/code/controllers/subsystem/throwing.dm
+++ b/code/controllers/subsystem/throwing.dm
@@ -5,7 +5,7 @@ SUBSYSTEM_DEF(throwing)
name = "Throwing"
priority = FIRE_PRIORITY_THROWING
wait = 1
- flags = SS_NO_INIT|SS_KEEP_TIMING|SS_TICKER
+ ss_flags = SS_NO_INIT|SS_KEEP_TIMING|SS_TICKER
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/currentrun
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index f53ac1891627..c355a47f1e2e 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -4,7 +4,7 @@
SUBSYSTEM_DEF(ticker)
name = "Ticker"
priority = FIRE_PRIORITY_TICKER
- flags = SS_KEEP_TIMING
+ ss_flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME
/// state of current round (used by process()) Use the defines GAME_STATE_* !
diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm
index ef6826396c2d..a9f3d9dcd34b 100644
--- a/code/controllers/subsystem/timer.dm
+++ b/code/controllers/subsystem/timer.dm
@@ -18,7 +18,7 @@ SUBSYSTEM_DEF(timer)
name = "Timer"
wait = 1 // SS_TICKER subsystem, so wait is in ticks
priority = FIRE_PRIORITY_TIMER
- flags = SS_TICKER|SS_NO_INIT
+ ss_flags = SS_TICKER|SS_NO_INIT
/// Queue used for storing timers that do not fit into the current buckets
var/list/datum/timedevent/second_queue = list()
diff --git a/code/controllers/subsystem/title.dm b/code/controllers/subsystem/title.dm
index ab32a0e044fd..51205001b459 100644
--- a/code/controllers/subsystem/title.dm
+++ b/code/controllers/subsystem/title.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(title)
name = "Title Screen"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
init_stage = INITSTAGE_EARLY
var/file_path
var/icon/icon
diff --git a/code/controllers/subsystem/traitor.dm b/code/controllers/subsystem/traitor.dm
index ad765dd916b5..edb0f237f5e3 100644
--- a/code/controllers/subsystem/traitor.dm
+++ b/code/controllers/subsystem/traitor.dm
@@ -4,7 +4,7 @@ SUBSYSTEM_DEF(traitor)
/datum/controller/subsystem/mapping,
/datum/controller/subsystem/atoms,
)
- flags = SS_KEEP_TIMING
+ ss_flags = SS_KEEP_TIMING
wait = 10 SECONDS
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
diff --git a/code/controllers/subsystem/tts.dm b/code/controllers/subsystem/tts.dm
index 15c57b15e5da..12d904af246d 100644
--- a/code/controllers/subsystem/tts.dm
+++ b/code/controllers/subsystem/tts.dm
@@ -156,7 +156,7 @@ SUBSYSTEM_DEF(tts)
/datum/controller/subsystem/tts/fire(resumed)
if(!tts_enabled)
- flags |= SS_NO_FIRE
+ ss_flags |= SS_NO_FIRE
return
if(!resumed)
diff --git a/code/controllers/subsystem/tutorials.dm b/code/controllers/subsystem/tutorials.dm
index ceb1fa787867..b03af66028df 100644
--- a/code/controllers/subsystem/tutorials.dm
+++ b/code/controllers/subsystem/tutorials.dm
@@ -1,7 +1,7 @@
/// Namespace for housing code relating to giving contextual tutorials to users.
SUBSYSTEM_DEF(tutorials)
name = "Tutorials"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
/// A mapping of /datum/tutorial type to their manager singleton.
/// You probably shouldn't be indexing this directly.
diff --git a/code/controllers/subsystem/unplanned_controllers.dm b/code/controllers/subsystem/unplanned_controllers.dm
index 49fbcb2e83b5..e2e78e0951ee 100644
--- a/code/controllers/subsystem/unplanned_controllers.dm
+++ b/code/controllers/subsystem/unplanned_controllers.dm
@@ -2,7 +2,7 @@ GLOBAL_LIST_EMPTY(unplanned_controller_subsystems)
/// Handles making mobs perform lightweight "idle" behaviors such as wandering around when they have nothing planned
SUBSYSTEM_DEF(unplanned_controllers)
name = "Unplanned AI Controllers"
- flags = SS_POST_FIRE_TIMING|SS_BACKGROUND
+ ss_flags = SS_POST_FIRE_TIMING|SS_BACKGROUND
priority = FIRE_PRIORITY_UNPLANNED_NPC
dependencies = list(
/datum/controller/subsystem/movement/ai_movement,
diff --git a/code/controllers/subsystem/verb_manager.dm b/code/controllers/subsystem/verb_manager.dm
index f09c05096415..0949033ab170 100644
--- a/code/controllers/subsystem/verb_manager.dm
+++ b/code/controllers/subsystem/verb_manager.dm
@@ -22,7 +22,7 @@
SUBSYSTEM_DEF(verb_manager)
name = "Verb Manager"
wait = 1
- flags = SS_TICKER | SS_NO_INIT
+ ss_flags = SS_TICKER | SS_NO_INIT
priority = FIRE_PRIORITY_DELAYED_VERBS
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
@@ -120,7 +120,7 @@ SUBSYSTEM_DEF(verb_manager)
return TRUE
if((usr.client?.holder && !can_queue_admin_verbs) \
- || (!initialized && !(flags & SS_NO_INIT)) \
+ || (!initialized && !(ss_flags & SS_NO_INIT)) \
|| FOR_ADMINS_IF_VERBS_FUCKED_immediately_execute_all_verbs \
|| !(runlevels & Master.current_runlevel))
return FALSE
diff --git a/code/controllers/subsystem/vis_overlays.dm b/code/controllers/subsystem/vis_overlays.dm
index 717455a77bc8..9a4d0c6f08e3 100644
--- a/code/controllers/subsystem/vis_overlays.dm
+++ b/code/controllers/subsystem/vis_overlays.dm
@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(vis_overlays)
name = "Vis contents overlays"
wait = 1 MINUTES
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
priority = FIRE_PRIORITY_VIS
var/list/vis_overlay_cache = list()
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index 776c787f0a4a..0d5678a4abe4 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -4,7 +4,7 @@
SUBSYSTEM_DEF(vote)
name = "Vote"
wait = 1 SECONDS
- flags = SS_KEEP_TIMING
+ ss_flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
/// A list of all generated action buttons
diff --git a/code/controllers/subsystem/wardrobe.dm b/code/controllers/subsystem/wardrobe.dm
index 4f25ff47d1c8..f394c9b30d2b 100644
--- a/code/controllers/subsystem/wardrobe.dm
+++ b/code/controllers/subsystem/wardrobe.dm
@@ -8,7 +8,7 @@
SUBSYSTEM_DEF(wardrobe)
name = "Wardrobe"
wait = 10 // This is more like a queue then anything else
- flags = SS_BACKGROUND
+ ss_flags = SS_BACKGROUND
dependencies = list(
/datum/controller/subsystem/atoms,
/datum/controller/subsystem/mapping,
diff --git a/code/controllers/subsystem/weather.dm b/code/controllers/subsystem/weather.dm
index c330455fb15f..7a89236e08a2 100644
--- a/code/controllers/subsystem/weather.dm
+++ b/code/controllers/subsystem/weather.dm
@@ -1,7 +1,7 @@
/// Used for all kinds of weather, ex. lavaland ash storms.
SUBSYSTEM_DEF(weather)
name = "Weather"
- flags = SS_BACKGROUND
+ ss_flags = SS_BACKGROUND
dependencies = list(
/datum/controller/subsystem/mapping,
)
diff --git a/code/datums/ai/movement/ai_movement_basic_avoidance.dm b/code/datums/ai/movement/ai_movement_basic_avoidance.dm
index 6659da244f3d..169301f70470 100644
--- a/code/datums/ai/movement/ai_movement_basic_avoidance.dm
+++ b/code/datums/ai/movement/ai_movement_basic_avoidance.dm
@@ -21,4 +21,4 @@
/// Move immediately and don't update our facing
/datum/ai_movement/basic_avoidance/backstep
- move_flags = MOVEMENT_LOOP_START_FAST | MOVEMENT_LOOP_NO_DIR_UPDATE
+ move_flags = MOVEMENT_LOOP_START_INSTANT | MOVEMENT_LOOP_NO_DIR_UPDATE
diff --git a/code/datums/ai/objects/vending_machines/vending_machine_behaviors.dm b/code/datums/ai/objects/vending_machines/vending_machine_behaviors.dm
index 1067d94339eb..1aa936e124d4 100644
--- a/code/datums/ai/objects/vending_machines/vending_machine_behaviors.dm
+++ b/code/datums/ai/objects/vending_machines/vending_machine_behaviors.dm
@@ -26,6 +26,8 @@
vendor_pawn.say(pick("Supersize this!", "Eat my shiny metal ass!", "Want to consume some of my products?", "SMASH!", "Don't you love these smashing prices!"))
controller.set_blackboard_key(BB_VENDING_LAST_HIT_SUCCESSFUL, TRUE)
else
+ if(vendor_pawn.icon_deny)
+ flick(vendor_pawn.icon_deny, vendor_pawn)
vendor_pawn.say(pick("Get back here!", "Don't you want my well priced love?"))
controller.set_blackboard_key(BB_VENDING_LAST_HIT_SUCCESSFUL, FALSE)
finish_action(controller, TRUE)
diff --git a/code/datums/ai/objects/vending_machines/vending_machine_controller.dm b/code/datums/ai/objects/vending_machines/vending_machine_controller.dm
index 3af2bcbba609..ab888040fb51 100644
--- a/code/datums/ai/objects/vending_machines/vending_machine_controller.dm
+++ b/code/datums/ai/objects/vending_machines/vending_machine_controller.dm
@@ -1,12 +1,18 @@
///AI controller for vending machine gone rogue, Don't try using this on anything else, it wont work.
/datum/ai_controller/vending_machine
movement_delay = 0.4 SECONDS
- blackboard = list(BB_VENDING_CURRENT_TARGET = null,
- BB_VENDING_TILT_COOLDOWN = 0,
- BB_VENDING_UNTILT_COOLDOWN = 0,
- BB_VENDING_BUSY_TILTING = FALSE,
- BB_VENDING_LAST_HIT_SUCCESSFUL = FALSE)
+ blackboard = list(
+ BB_VENDING_CURRENT_TARGET = null,
+ BB_VENDING_TILT_COOLDOWN = 0,
+ BB_VENDING_UNTILT_COOLDOWN = 0,
+ BB_VENDING_BUSY_TILTING = FALSE,
+ BB_VENDING_LAST_HIT_SUCCESSFUL = FALSE,
+ )
+ /// If TRUE stops mobs from buying things from active machines
+ var/block_usage = FALSE
+ /// Range to search for mobs to crunch
var/vision_range = 7
+ /// Seconds between attempts to find a new mob to crunch
var/search_for_enemy_cooldown = 2 SECONDS
/datum/ai_controller/vending_machine/TryPossessPawn(atom/new_pawn)
@@ -17,6 +23,7 @@
vendor_pawn.AddElementTrait(TRAIT_WADDLING, REF(src), /datum/element/waddling)
vendor_pawn.AddElement(/datum/element/footstep, FOOTSTEP_OBJ_MACHINE, 1, -6, sound_vary = TRUE)
vendor_pawn.squish_damage = 15
+ RegisterSignal(vendor_pawn, COMSIG_VENDING_UI_INTERACT, PROC_REF(deny_vending_interact))
return ..() //Run parent at end
/datum/ai_controller/vending_machine/UnpossessPawn(destroy)
@@ -25,6 +32,7 @@
REMOVE_TRAIT(vendor_pawn, TRAIT_WADDLING, REF(src))
vendor_pawn.squish_damage = initial(vendor_pawn.squish_damage)
RemoveElement(/datum/element/footstep, FOOTSTEP_OBJ_MACHINE, 1, -6, sound_vary = TRUE)
+ UnregisterSignal(vendor_pawn, COMSIG_VENDING_UI_INTERACT)
return ..() //Run parent at end
/datum/ai_controller/vending_machine/SelectBehaviors(seconds_per_tick)
@@ -46,3 +54,18 @@
queue_behavior(/datum/ai_behavior/vendor_crush, BB_VENDING_CURRENT_TARGET)
return
set_blackboard_key(BB_VENDING_TILT_COOLDOWN, world.time + search_for_enemy_cooldown)
+
+/datum/ai_controller/vending_machine/proc/deny_vending_interact(obj/machinery/vending/vending_machine, mob/user, datum/tgui/ui)
+ SIGNAL_HANDLER
+ if(!block_usage)
+ return NONE
+ vending_machine.speak(pick(
+ "Once in a life time offer, and you [pick("blew it", "missed it", "screwed it up")]!",
+ "The deals are off!",
+ "We don't accept card, only accept flesh and blood!",
+ "You had your chance!",
+ ))
+ return VENDING_DENIED
+
+/datum/ai_controller/vending_machine/eventspawn
+ block_usage = TRUE
diff --git a/code/datums/bodypart_overlays/texture_bodypart_overlay.dm b/code/datums/bodypart_overlays/texture_bodypart_overlay.dm
index 70086d45f9cf..920c2973ac8d 100644
--- a/code/datums/bodypart_overlays/texture_bodypart_overlay.dm
+++ b/code/datums/bodypart_overlays/texture_bodypart_overlay.dm
@@ -17,7 +17,8 @@
appearance.add_filter("bodypart_texture_[texture_icon_state]", 1, layering_filter(icon = cached_texture_icon, blend_mode = BLEND_INSET_OVERLAY))
/datum/bodypart_overlay/texture/generate_icon_cache()
- return "[type]"
+ . = ..()
+ . += "[type]"
/datum/bodypart_overlay/texture/can_draw_on_bodypart(obj/item/bodypart/bodypart_owner, mob/living/carbon/owner, is_husked = FALSE)
if (!..())
diff --git a/code/datums/components/amputating_limbs.dm b/code/datums/components/amputating_limbs.dm
index bfaf52ca90ca..28a69ee5a19f 100644
--- a/code/datums/components/amputating_limbs.dm
+++ b/code/datums/components/amputating_limbs.dm
@@ -58,7 +58,7 @@
return
var/list/valid_targets = list()
- for (var/obj/item/bodypart/possible_target as anything in limbed_victim.bodyparts)
+ for (var/obj/item/bodypart/possible_target as anything in limbed_victim.get_bodyparts())
if (possible_target.bodypart_flags & BODYPART_UNREMOVABLE)
continue
if (!(possible_target.body_zone in target_zones))
diff --git a/code/datums/components/atom_mounted.dm b/code/datums/components/atom_mounted.dm
index 2d2cea36abff..429f68a4126e 100644
--- a/code/datums/components/atom_mounted.dm
+++ b/code/datums/components/atom_mounted.dm
@@ -4,12 +4,13 @@
var/atom/hanging_support_atom
/datum/component/atom_mounted/Initialize(target_structure)
- . = ..()
if(!isobj(parent) || !isatom(target_structure))
return COMPONENT_INCOMPATIBLE
+ . = ..()
+
hanging_support_atom = target_structure
RegisterSignal(hanging_support_atom, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
- if(isclosedturf(hanging_support_atom))
+ if(isturf(hanging_support_atom))
RegisterSignal(hanging_support_atom, COMSIG_TURF_CHANGE, PROC_REF(on_turf_changing))
else
RegisterSignal(hanging_support_atom, COMSIG_QDELETING, PROC_REF(on_structure_delete))
@@ -28,6 +29,7 @@
UnregisterSignal(parent, signals)
/datum/component/atom_mounted/Destroy(force)
+ UnregisterSignal(hanging_support_atom, COMSIG_ATOM_EXAMINE)
hanging_support_atom = null
return ..()
@@ -37,25 +39,44 @@
/datum/component/atom_mounted/proc/on_examine(datum/source, mob/user, list/examine_list)
SIGNAL_HANDLER
- if (parent in view(user.client?.view || world.view, user))
+ if(parent in view(user.client?.view || world.view, user))
examine_list += span_notice("\The [hanging_support_atom] is currently supporting [span_bold("\the [parent]")]. Deconstruction or excessive damage would cause it to [span_bold("fall to the ground")].")
/// When the type of turf changes, if it is changing into a floor we should drop our contents
-/datum/component/atom_mounted/proc/on_turf_changing(datum/source, path, new_baseturfs, flags, post_change_callbacks)
+/datum/component/atom_mounted/proc/on_turf_changing(turf/source, path, new_baseturfs, flags, post_change_callbacks)
SIGNAL_HANDLER
- if(ispath(path, /turf/open))
- drop_wallmount()
+ //if we transforming from open to open turf we can skip deconstruction under some conditions
+ if(isopenturf(source) && ispath(path, /turf/open))
+ var/reload = FALSE
+
+ //we are transforming from plating into anything that isn't space
+ if(isplatingturf(source) && !ispath(path, /turf/open/space))
+ reload = TRUE
+ //we are transforming into plating turf
+ else if(ispath(LAZYACCESS(source.baseturfs, length(source.baseturfs)), /turf/open/floor/plating))
+ reload = TRUE
+
+ if(reload)
+ var/obj/target = parent
+ qdel(src)
+ post_change_callbacks += CALLBACK(target, TYPE_PROC_REF(/obj, remount))
+ return
+
+ drop_wallmount()
///When the atom the object is mounted on is destroyed deconstruct
/datum/component/atom_mounted/proc/on_structure_delete(datum/source, force)
SIGNAL_HANDLER
+ PRIVATE_PROC(TRUE)
drop_wallmount()
/// If we get dragged from our wall (by a singulo for instance) we should deconstruct
/datum/component/atom_mounted/proc/on_move(datum/source, atom/old_loc, dir, forced, list/old_locs)
SIGNAL_HANDLER
+ PRIVATE_PROC(TRUE)
+
// If we're having our lighting messed with we're likely to get dragged about
// That shouldn't lead to a decon
if(HAS_TRAIT(parent, TRAIT_LIGHTING_DEBUGGED))
@@ -65,6 +86,7 @@
///Called when the object is about to be shuttle rotated so we have to delete ourself and mount again later
/datum/component/atom_mounted/proc/detach(datum/source, newT, rotation, move_mode, moving_dock)
SIGNAL_HANDLER
+ PRIVATE_PROC(TRUE)
qdel(src)
@@ -175,8 +197,8 @@
obj_flags |= MOUNT_ON_LATE_INITIALIZE
return FALSE
-///Used to remount an object after shuttle move
-/obj/proc/remount(datum/source, oldT)
+///Used to remount an object in special cases
+/obj/proc/remount()
SIGNAL_HANDLER
PRIVATE_PROC(TRUE)
diff --git a/code/datums/components/bloodysoles.dm b/code/datums/components/bloodysoles.dm
index 4dc7a7feeb66..09c945c2f8d3 100644
--- a/code/datums/components/bloodysoles.dm
+++ b/code/datums/components/bloodysoles.dm
@@ -326,7 +326,7 @@
return
// Find any leg of our human and add that to the footprint, instead of the default which is to just add the human type
- for(var/obj/item/bodypart/leg/affecting in wielder.bodyparts)
+ for(var/obj/item/bodypart/leg/affecting in wielder.get_bodyparts())
if(!affecting.bodypart_disabled)
LAZYSET(footprint.species_types, affecting.limb_id, TRUE)
diff --git a/code/datums/components/butchering.dm b/code/datums/components/butchering.dm
index b1f7eebab1fb..bdc41329a744 100644
--- a/code/datums/components/butchering.dm
+++ b/code/datums/components/butchering.dm
@@ -167,7 +167,7 @@
if (target.body_zone == BODY_ZONE_CHEST && target.owner)
// Cannot butcher the chest until we hack off all the other limbs
- for (var/obj/item/bodypart/limb as anything in target.owner.bodyparts)
+ for (var/obj/item/bodypart/limb as anything in target.owner.get_bodyparts())
if (limb != target && limb.butcher_drops && limb.butcher_replacement)
to_chat(user, span_warning("You need to butcher all other limbs first!"))
return
@@ -249,7 +249,7 @@
for(var/obj/item/result as anything in results)
if (reagents_in_produced)
if (target.owner.reagents)
- target.owner.reagents.trans_to(result, target.owner.reagents.total_volume / reagents_in_produced / length(target.owner.bodyparts), remove_blacklisted = TRUE)
+ target.owner.reagents.trans_to(result, target.owner.reagents.total_volume / reagents_in_produced / length(target.owner.get_bodyparts()), remove_blacklisted = TRUE)
result.reagents?.add_reagent(/datum/reagent/consumable/nutriment/fat, target.owner.nutrition / /datum/reagent/consumable/nutriment/fat::nutriment_factor / reagents_in_produced)
if(LAZYLEN(diseases))
diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm
index 69f1ea69cf7a..28ae7a18d5be 100644
--- a/code/datums/components/chasm.dm
+++ b/code/datums/components/chasm.dm
@@ -145,6 +145,7 @@
return // We're already handling this
if(SEND_SIGNAL(dropped_thing, COMSIG_MOVABLE_CHASM_DROPPED, parent) & COMPONENT_NO_CHASM_DROP)
+ LAZYREMOVE(falling_atoms, falling_ref)
return
// Free (if possible) and drop all buckled mobs separately, so drivers can escape their doomed vehicle if they're not glued to it
@@ -184,6 +185,7 @@
if (get_turf(falling_mob) != get_turf(parent))
REMOVE_TRAIT(falling_mob, TRAIT_NO_TRANSFORM, REF(src))
falling_mob.Paralyze(17 SECONDS, ignore_canstun = TRUE) // Wow nice job
+ LAZYREMOVE(falling_atoms, falling_ref)
return
dropped_thing.visible_message(span_boldwarning("[dropped_thing] falls into [parent]!"), span_userdanger("[oblivion_message]"))
@@ -213,6 +215,7 @@
storage = (locate() in parent) || new(parent)
if(storage.contains(dropped_thing))
+ LAZYREMOVE(falling_atoms, falling_ref)
return
dropped_thing.alpha = oldalpha
diff --git a/code/datums/components/free_operation.dm b/code/datums/components/free_operation.dm
index f1bddc05c657..1c557cda5ea5 100644
--- a/code/datums/components/free_operation.dm
+++ b/code/datums/components/free_operation.dm
@@ -3,21 +3,29 @@
dupe_mode = COMPONENT_DUPE_SOURCES
/datum/component/free_operation/Initialize(check)
- if (!iscarbon(parent))
+ if (!isliving(parent))
return COMPONENT_INCOMPATIBLE
+ if (!iscarbon(parent) && !isbasicmob(parent))
+ return COMPONENT_REDUNDANT
ADD_TRAIT(parent, TRAIT_READY_TO_OPERATE, REF(src))
var/mob/living/carbon/owner = parent
- for (var/obj/item/bodypart/limb as anything in owner.bodyparts)
+ if (!istype(owner))
+ return
+ for (var/obj/item/bodypart/limb as anything in owner.get_bodyparts(include_stumps = TRUE))
ADD_TRAIT(limb, TRAIT_READY_TO_OPERATE, REF(src))
/datum/component/free_operation/Destroy(force)
REMOVE_TRAIT(parent, TRAIT_READY_TO_OPERATE, REF(src))
var/mob/living/carbon/owner = parent
- for (var/obj/item/bodypart/limb as anything in owner.bodyparts)
+ if (!istype(owner))
+ return ..()
+ for (var/obj/item/bodypart/limb as anything in owner.get_bodyparts(include_stumps = TRUE))
REMOVE_TRAIT(limb, TRAIT_READY_TO_OPERATE, REF(src))
return ..()
/datum/component/free_operation/RegisterWithParent()
+ if (isbasicmob(parent))
+ return
RegisterSignal(parent, COMSIG_CARBON_ATTACH_LIMB, PROC_REF(flag_limb))
RegisterSignal(parent, COMSIG_CARBON_REMOVE_LIMB, PROC_REF(unflag_limb))
diff --git a/code/datums/components/ghostrole_on_revive.dm b/code/datums/components/ghostrole_on_revive.dm
index a8930831c887..52ea1d0bd073 100644
--- a/code/datums/components/ghostrole_on_revive.dm
+++ b/code/datums/components/ghostrole_on_revive.dm
@@ -6,14 +6,31 @@
var/datum/callback/on_successful_revive
/// The chance to twitch when orbiting the spawn
var/twitch_chance = 30
-
-/datum/component/ghostrole_on_revive/Initialize(refuse_revival_if_failed, on_successful_revive)
- . = ..()
-
+ /// The title shown to ghosts when polled to enter the body
+ var/revive_title
+
+ // Text displayed in the spawners menu
+ var/spawn_text
+ var/you_are_text
+ var/flavor_text
+ var/important_text
+
+/datum/component/ghostrole_on_revive/Initialize(
+ refuse_revival_if_failed = FALSE,
+ datum/callback/on_successful_revive,
+ revive_title = "a recovered crewmember",
+ spawn_text = "Recovered Crew",
+ you_are_text = "You are a long dead crewmember, but are soon to be revived to rejoin the crew!",
+ flavor_text = "Get a job and get back to work!",
+ important_text = "Do your best to help the station. You still roll for midround antagonists."
+)
src.refuse_revival_if_failed = refuse_revival_if_failed
src.on_successful_revive = on_successful_revive
-
- ADD_TRAIT(parent, TRAIT_GHOSTROLE_ON_REVIVE, REF(src)) //for adding an alternate examination
+ src.revive_title = revive_title
+ src.spawn_text = spawn_text
+ src.you_are_text = you_are_text
+ src.flavor_text = flavor_text
+ src.important_text = important_text
if(ismob(parent))
prepare_mob(parent)
@@ -22,138 +39,152 @@
if(!istype(parent, /obj/item/organ/brain))
return COMPONENT_INCOMPATIBLE
- var/obj/item/organ/brain/brein = parent
- if(brein.owner)
- prepare_mob(brein.owner)
+ var/obj/item/organ/brain/brain_parent = parent
+ if(brain_parent.owner)
+ prepare_mob(brain_parent.owner)
else
- prepare_brain(brein)
+ prepare_brain(brain_parent)
/// Give the appropriate signals, and watch for organ removal
-/datum/component/ghostrole_on_revive/proc/prepare_mob(mob/living/liver)
- RegisterSignal(liver, COMSIG_LIVING_REVIVE, PROC_REF(on_revive))
- ADD_TRAIT(liver, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
-
- add_orbit_twitching(liver)
-
- liver.med_hud_set_status()
-
- if(iscarbon(liver))
- var/mob/living/carbon/carbon = liver
- var/obj/item/organ/brain = carbon.get_organ_by_type(/obj/item/organ/brain)
- if(brain)
- RegisterSignal(brain, COMSIG_ORGAN_REMOVED, PROC_REF(on_remove))
-
-/datum/component/ghostrole_on_revive/proc/on_remove(obj/item/organ/brain, mob/living/old_owner)
+/datum/component/ghostrole_on_revive/proc/prepare_mob(mob/living/to_prepare)
+ RegisterSignal(to_prepare, COMSIG_LIVING_REVIVE, PROC_REF(on_revive))
+ RegisterSignal(to_prepare, COMSIG_MOB_REAGENT_TICK, PROC_REF(block_formaldehyde_metabolism))
+ if(istype(parent, /obj/item/organ/brain))
+ RegisterSignal(to_prepare, COMSIG_CARBON_LOSE_ORGAN, PROC_REF(on_remove))
+ ADD_TRAIT(to_prepare, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
+
+ add_orbit_twitching(to_prepare)
+ to_prepare.med_hud_set_status()
+
+/datum/component/ghostrole_on_revive/proc/unprepare_mob(mob/living/to_unprepare)
+ REMOVE_TRAIT(to_unprepare, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
+ UnregisterSignal(to_unprepare, list(
+ COMSIG_LIVING_REVIVE,
+ COMSIG_MOB_REAGENT_TICK,
+ COMSIG_CARBON_LOSE_ORGAN,
+ ))
+
+ to_unprepare.med_hud_set_status()
+ remove_orbit_twitching(to_unprepare)
+
+/datum/component/ghostrole_on_revive/proc/on_remove(mob/living/carbon/source, obj/item/organ/removed_brain)
SIGNAL_HANDLER
- REMOVE_TRAIT(old_owner, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
- remove_orbit_twitching(old_owner)
+ if(!istype(removed_brain, /obj/item/organ/brain))
+ return
+
+ unprepare_mob(source)
// we might have some lingering blinking eyes
- var/obj/item/bodypart/head/head = old_owner?.get_bodypart(BODY_ZONE_HEAD)
+ var/obj/item/bodypart/head/head = source.get_bodypart(BODY_ZONE_HEAD)
if(head)
var/soul_eyes = locate(/datum/bodypart_overlay/simple/soul_pending_eyes) in head.bodypart_overlays
if(soul_eyes)
head.remove_bodypart_overlay(soul_eyes)
- prepare_brain(brain)
+ prepare_brain(removed_brain)
-/datum/component/ghostrole_on_revive/proc/prepare_brain(obj/item/organ/brein)
- SIGNAL_HANDLER
+/datum/component/ghostrole_on_revive/proc/prepare_brain(obj/item/organ/source)
+ ADD_TRAIT(source, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
+ RegisterSignal(source, COMSIG_ORGAN_IMPLANTED, PROC_REF(prepare_mob_from_brain))
+ UnregisterSignal(source, COMSIG_ORGAN_REMOVED)
- RegisterSignal(brein, COMSIG_ORGAN_IMPLANTED, PROC_REF(prepare_mob_from_brain))
- UnregisterSignal(brein, COMSIG_ORGAN_REMOVED)
+/datum/component/ghostrole_on_revive/proc/unprepare_brain(obj/item/organ/source)
+ REMOVE_TRAIT(source, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
+ UnregisterSignal(source, COMSIG_ORGAN_IMPLANTED)
+ RegisterSignal(source, COMSIG_ORGAN_REMOVED, PROC_REF(prepare_brain))
+ source.owner?.med_hud_set_status()
-/datum/component/ghostrole_on_revive/proc/prepare_mob_from_brain(obj/item/organ/brain/brein, mob/living/owner)
+/datum/component/ghostrole_on_revive/proc/prepare_mob_from_brain(obj/item/organ/brain/source, mob/living/owner)
SIGNAL_HANDLER
- UnregisterSignal(brein, COMSIG_ORGAN_IMPLANTED)
+ REMOVE_TRAIT(source, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
+ UnregisterSignal(source, COMSIG_ORGAN_IMPLANTED)
prepare_mob(owner)
-/datum/component/ghostrole_on_revive/proc/on_revive(mob/living/aliver)
+/datum/component/ghostrole_on_revive/proc/on_revive(mob/living/source)
SIGNAL_HANDLER
- INVOKE_ASYNC(src, PROC_REF(poll_ghosts), aliver)
+ INVOKE_ASYNC(src, PROC_REF(poll_ghosts), source)
-/datum/component/ghostrole_on_revive/proc/poll_ghosts(mob/living/aliver)
- var/soul_eyes
- var/obj/item/bodypart/head
+/datum/component/ghostrole_on_revive/proc/poll_ghosts(mob/living/reviving)
+ var/datum/bodypart_overlay/simple/soul_pending_eyes/soul_eyes
// adds soulful SOUL PENDING eyes to indicate what's happening to observers
-
- var/mob/living/carbon/human/hewmon
- if(ishuman(aliver))
- hewmon = aliver
- head = hewmon.get_bodypart(BODY_ZONE_HEAD)
- if(head)
- soul_eyes = new /datum/bodypart_overlay/simple/soul_pending_eyes()
- head.add_bodypart_overlay(soul_eyes)
-
- // So during the potentially short period of time we're revived, we don't metabolize formaldehyde
- // Really just a QOL for coroners, so we dont suddenly start decaying
- ADD_TRAIT(aliver, TRAIT_BLOCK_FORMALDEHYDE_METABOLISM, type)
+ var/obj/item/bodypart/head/head = reviving.get_bodypart(BODY_ZONE_HEAD)
+ if(head)
+ soul_eyes = new()
+ head.add_bodypart_overlay(soul_eyes)
+
+ var/list/jobbans = list(ROLE_RECOVERED_CREW)
+ if(reviving.mind)
+ jobbans |= reviving.mind.assigned_role.title
+ for(var/datum/antagonist/antag as anything in reviving.mind.antag_datums)
+ if(antag.jobban_flag)
+ jobbans |= antag.jobban_flag
+ if(antag.pref_flag)
+ jobbans |= antag.pref_flag
+ if(!(antag.antag_flags & ANTAG_FAKE))
+ jobbans |= ROLE_SYNDICATE
var/mob/dead/observer/chosen_one = SSpolling.poll_ghosts_for_target(
- question = "Would you like to play as a recovered crewmember?",
- role = null,
- check_jobban = ROLE_RECOVERED_CREW,
+ question = "Would you like to play as [revive_title]?",
+ check_jobban = jobbans,
poll_time = 15 SECONDS,
- checked_target = aliver,
+ checked_target = reviving,
ignore_category = POLL_IGNORE_RECOVERED_CREW,
- alert_pic = aliver,
- role_name_text = "recovered crew",
+ alert_pic = reviving,
+ role_name_text = revive_title,
)
- REMOVE_TRAIT(aliver, TRAIT_BLOCK_FORMALDEHYDE_METABOLISM, type)
-
if(head)
head.remove_bodypart_overlay(soul_eyes)
+ if(soul_eyes)
+ qdel(soul_eyes)
- if(!isobserver(chosen_one))
- if(refuse_revival_if_failed)
- aliver.death()
- aliver.visible_message(span_deadsay("[aliver.name]'s soul is struggling to return!"))
- else
- aliver.PossessByPlayer(chosen_one.ckey)
- on_successful_revive?.Invoke(aliver)
+ if(isobserver(chosen_one))
+ reviving.PossessByPlayer(chosen_one.ckey)
+ on_successful_revive?.Invoke(reviving)
qdel(src)
-/datum/component/ghostrole_on_revive/proc/add_orbit_twitching(mob/living/liver)
- liver.AddElement(/datum/element/orbit_twitcher, twitch_chance)
+ else if(refuse_revival_if_failed)
+ reviving.death()
+ reviving.visible_message(span_deadsay("[reviving]'s soul is struggling to return!"))
+
+/datum/component/ghostrole_on_revive/proc/add_orbit_twitching(mob/living/parent_mob)
+ parent_mob.AddElement(/datum/element/orbit_twitcher, twitch_chance)
// Add it to the ghostrole spawner menu. Note that we can't directly spawn from it, but we can make it twitch to alert bystanders to defib it
- LAZYADD(GLOB.joinable_mobs[format_text("Recovered Crew")], liver)
- RegisterSignal(liver, COMSIG_LIVING_GHOSTROLE_INFO, PROC_REF(set_spawner_info))
+ LAZYADDASSOCLIST(GLOB.joinable_mobs, spawn_text, parent_mob)
+ RegisterSignal(parent_mob, COMSIG_LIVING_GHOSTROLE_INFO, PROC_REF(set_spawner_info))
-/datum/component/ghostrole_on_revive/proc/set_spawner_info(datum/spawners_menu/menu, string_info)
+/datum/component/ghostrole_on_revive/proc/set_spawner_info(datum/spawners_menu/menu, list/string_info)
SIGNAL_HANDLER
- string_info["you_are_text"] = "You are a long dead crewmember, but are soon to be revived to rejoin the crew!"
- string_info["flavor_text"] = "Get a job and get back to work!"
- string_info["important_text"] = "Do your best to help the station. You still roll for midround antagonists."
-
-/datum/component/ghostrole_on_revive/proc/remove_orbit_twitching(mob/living/living)
- living.RemoveElement(/datum/element/orbit_twitcher, twitch_chance)
+ string_info["you_are_text"] = src.you_are_text
+ string_info["flavor_text"] = src.flavor_text
+ string_info["important_text"] = src.important_text
- // Remove from the ghostrole spawning menu
- var/list/spawners = GLOB.joinable_mobs[format_text("Recovered Crew")]
- LAZYREMOVE(spawners, living)
+/datum/component/ghostrole_on_revive/proc/remove_orbit_twitching(mob/living/parent_mob)
+ parent_mob.RemoveElement(/datum/element/orbit_twitcher, twitch_chance)
+ LAZYREMOVEASSOC(GLOB.joinable_mobs, spawn_text, parent_mob)
+ UnregisterSignal(parent_mob, COMSIG_LIVING_GHOSTROLE_INFO)
- if(!LAZYLEN(spawners))
- GLOB.joinable_mobs -= format_text("Recovered Crew")
+// Block formaldehyde from being metabolized, Coroner QoL
+/datum/component/ghostrole_on_revive/proc/block_formaldehyde_metabolism(mob/living/source, datum/reagent/chem)
+ SIGNAL_HANDLER
- UnregisterSignal(living, COMSIG_LIVING_GHOSTROLE_INFO)
+ if(istype(chem, /datum/reagent/toxin/formaldehyde))
+ return COMSIG_MOB_STOP_REAGENT_TICK
/datum/component/ghostrole_on_revive/Destroy(force)
- REMOVE_TRAIT(parent, TRAIT_GHOSTROLE_ON_REVIVE, REF(src))
-
- var/mob/living/living
if(isliving(parent))
- living = parent
+ unprepare_mob(parent)
+
else if(istype(parent, /obj/item/organ/brain))
var/obj/item/organ/brain/brain = parent
- living = brain.owner
- living?.med_hud_set_status()
- if(living)
- remove_orbit_twitching(living)
+ if(brain.owner)
+ unprepare_mob(brain.owner)
+ else
+ unprepare_brain(brain)
return ..()
diff --git a/code/datums/components/healing_touch.dm b/code/datums/components/healing_touch.dm
index b8580e2e458f..f7a9515c9c06 100644
--- a/code/datums/components/healing_touch.dm
+++ b/code/datums/components/healing_touch.dm
@@ -174,7 +174,7 @@
if (!iscarbon(target))
return (target.get_brute_loss() > 0 && heal_brute) || (target.get_fire_loss() > 0 && heal_burn)
var/mob/living/carbon/carbon_target = target
- for (var/obj/item/bodypart/part in carbon_target.bodyparts)
+ for (var/obj/item/bodypart/part in carbon_target.get_bodyparts())
if (!(part.brute_dam && heal_brute) && !(part.burn_dam && heal_burn))
continue
if (!isnull(required_bodytype) && !(part.bodytype & required_bodytype))
diff --git a/code/datums/components/hide_weather_planes.dm b/code/datums/components/hide_weather_planes.dm
index 3ef26d53b1ab..5b7addb199e7 100644
--- a/code/datums/components/hide_weather_planes.dm
+++ b/code/datums/components/hide_weather_planes.dm
@@ -55,7 +55,7 @@
else
care_about.hide_plane(our_lad)
-/datum/component/hide_weather_planes/proc/new_hud_attached(datum/source, datum/hud/new_hud)
+/datum/component/hide_weather_planes/proc/new_hud_attached(datum/source, datum/hud/old_hud, datum/hud/new_hud)
SIGNAL_HANDLER
attach_hud(new_hud)
diff --git a/code/datums/components/houlihan_teleport.dm b/code/datums/components/houlihan_teleport.dm
index b819742e495a..4927f77ca7d1 100644
--- a/code/datums/components/houlihan_teleport.dm
+++ b/code/datums/components/houlihan_teleport.dm
@@ -47,12 +47,12 @@
var/turf/user_turf = get_turf(user)
var/atom/movable/dragged = user.pulling
user.forceMove(destination_turf)
- user_turf.balloon_alert_to_viewers("(pop)")
+ user_turf.balloon_alert_to_hearers("*pop*")
if(dragged)
var/turf/dragged_turf = get_turf(dragged)
dragged.forceMove(destination_turf)
user.start_pulling(dragged, force = TRUE)
- dragged_turf.balloon_alert_to_viewers("(pop)")
+ dragged_turf.balloon_alert_to_hearers("*pop*")
to_chat(list(user, dragged), span_notice("You blink and find yourself in [get_area_name(destination_turf)]."))
user.emote("blink")
diff --git a/code/datums/components/interaction_booby_trap.dm b/code/datums/components/interaction_booby_trap.dm
index ef8d3c78cfcb..3c1c55100ccc 100644
--- a/code/datums/components/interaction_booby_trap.dm
+++ b/code/datums/components/interaction_booby_trap.dm
@@ -75,7 +75,7 @@
if (explode_timer)
return
explode_timer = addtimer(CALLBACK(src, PROC_REF(explode), source), 0.5 SECONDS)
- source.balloon_alert_to_viewers("beep")
+ source.balloon_alert_to_hearers("*beep*")
playsound(parent, triggered_sound, 50, FALSE)
return
diff --git a/code/datums/components/orbiter.dm b/code/datums/components/orbiter.dm
index 7bfead87625b..ffe9dfae4f80 100644
--- a/code/datums/components/orbiter.dm
+++ b/code/datums/components/orbiter.dm
@@ -126,7 +126,7 @@
UnregisterSignal(source, COMSIG_ATOM_AFTER_SHUTTLE_MOVE)
begin_orbit(arglist(list(source) + orbiter_params[source]))
-/datum/component/orbiter/proc/end_orbit(atom/movable/orbiter, refreshing=FALSE)
+/datum/component/orbiter/proc/end_orbit(atom/movable/orbiter, refreshing = FALSE)
if(!orbiter_list[orbiter])
return
UnregisterSignal(orbiter, list(COMSIG_MOVABLE_MOVED, COMSIG_ATOM_BEFORE_SHUTTLE_MOVE, COMSIG_ATOM_AFTER_SHUTTLE_MOVE))
@@ -137,7 +137,7 @@
orbiter_list -= orbiter
if(!refreshing)
orbiter_params -= orbiter
- orbiter.stop_orbit(src)
+ orbiter.stop_orbit(src, refreshing)
orbiter.orbiting = null
if(ismob(orbiter))
@@ -200,9 +200,11 @@
orbit_target = A
return A.AddComponent(/datum/component/orbiter, src, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
-/atom/movable/proc/stop_orbit(datum/component/orbiter/orbits)
+/atom/movable/proc/stop_orbit(datum/component/orbiter/orbits, refreshing = FALSE)
+ if(refreshing)
+ return //Only null the target if we're actually stopping the orbit for real, not if we're merely shuttle moving(or orbiting the same thing again). We will never get it back unless the orbit is fully deleted and reinstated.
orbit_target = null
- return // We're just a simple hook
+
/atom/proc/transfer_observers_to(atom/target)
if(!orbiters || !istype(target) || !get_turf(target) || target == src)
diff --git a/code/datums/components/overlay_lighting.dm b/code/datums/components/overlay_lighting.dm
index ca8d4e7b23dd..8f07b57d9124 100644
--- a/code/datums/components/overlay_lighting.dm
+++ b/code/datums/components/overlay_lighting.dm
@@ -64,6 +64,8 @@
var/beam = FALSE
///A cone overlay for directional light, its alpha and color are dependent on the light
var/image/cone
+ /// Are we currently displaying light on our holder?
+ var/currently_displaying = FALSE
///Current tracked direction for the directional cast behaviour
var/current_direction
///Tracks current directional x offset so we don't update unnecessarily
@@ -78,7 +80,7 @@
return COMPONENT_INCOMPATIBLE
var/atom/movable/movable_parent = parent
- if(!force && movable_parent.light_system != OVERLAY_LIGHT && movable_parent.light_system != OVERLAY_LIGHT_DIRECTIONAL && movable_parent.light_system != OVERLAY_LIGHT_BEAM)
+ if(!force && !IS_OVERLAY_LIGHT_SYSTEM(movable_parent.light_system))
stack_trace("[type] added to [parent], with [movable_parent.light_system] value for the light_system var. Use [OVERLAY_LIGHT], [OVERLAY_LIGHT_DIRECTIONAL] or [OVERLAY_LIGHT_BEAM] instead.")
return COMPONENT_INCOMPATIBLE
@@ -111,7 +113,7 @@
set_color(parent, movable_parent.light_color)
if(!isnull(starts_on))
movable_parent.set_light_on(starts_on)
-
+ set_light_render_source(parent, "")
/datum/component/overlay_lighting/RegisterWithParent()
. = ..()
@@ -122,10 +124,12 @@
RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_COLOR, PROC_REF(set_color))
RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_ON, PROC_REF(on_toggle))
RegisterSignal(parent, COMSIG_ATOM_UPDATE_LIGHT_FLAGS, PROC_REF(on_light_flags_change))
+ RegisterSignal(parent, COMSIG_ATOM_SET_LIGHT_RENDER_SOURCE, PROC_REF(set_light_render_source))
RegisterSignal(parent, COMSIG_ATOM_USED_IN_CRAFT, PROC_REF(on_parent_crafted))
RegisterSignal(parent, COMSIG_LIGHT_EATER_QUEUE, PROC_REF(on_light_eater))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_parent_moved))
RegisterSignal(parent, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_z_move))
+ RegisterSignal(parent, COMSIG_ITEM_BEFORE_PICKUP_ANIMATION, PROC_REF(on_pickup_anim))
var/atom/movable/movable_parent = parent
if(movable_parent.light_flags & LIGHT_ATTACHED)
overlay_lighting_flags |= LIGHTING_ATTACHED
@@ -200,18 +204,44 @@
///Adds the luminosity and source for the affected movable atoms to keep track of their visibility.
/datum/component/overlay_lighting/proc/add_dynamic_lumi()
LAZYSET(current_holder.affected_dynamic_lights, src, lumcount_range + 1)
- current_holder.underlays += visible_mask
+ show_to_holder()
current_holder.update_dynamic_luminosity()
- if(directional)
- current_holder.underlays += cone
///Removes the luminosity and source for the affected movable atoms to keep track of their visibility.
/datum/component/overlay_lighting/proc/remove_dynamic_lumi()
LAZYREMOVE(current_holder.affected_dynamic_lights, src)
- current_holder.underlays -= visible_mask
+ hide_from_holder()
current_holder.update_dynamic_luminosity()
+
+/// Adds our overlays to our holder, assuming everything's setup proper
+/datum/component/overlay_lighting/proc/show_to_holder()
+ if(currently_displaying)
+ return
+ if(isnull(current_holder) || !(overlay_lighting_flags & LIGHTING_ON))
+ currently_displaying = FALSE
+ return
+ current_holder.underlays += visible_mask
+ if(directional)
+ current_holder.underlays += cone
+ currently_displaying = TRUE
+ // These are very intentionally copies so recipients cannot
+ // Accidentially brick lighting overlays by mutating them
+ var/mutable_appearance/mask_clone = new (visible_mask)
+ var/mutable_appearance/cone_clone = directional ? new /mutable_appearance(cone) : null
+ SEND_SIGNAL(parent, COMSIG_ATOM_OVERLAY_LIGHT_APPLIED, mask_clone, cone_clone, current_holder)
+
+/// Removes our overlay from our holder, assuming everything's setup proper
+/// MUST be called before modifying cone or visible_mask, or you will cause stuck lighting
+/datum/component/overlay_lighting/proc/hide_from_holder()
+ if(!currently_displaying)
+ return
+ if(isnull(current_holder) || !(overlay_lighting_flags & LIGHTING_ON))
+ return
+ current_holder.underlays -= visible_mask
if(directional)
current_holder.underlays -= cone
+ currently_displaying = FALSE
+ SEND_SIGNAL(parent, COMSIG_ATOM_OVERLAY_LIGHT_REMOVED, current_holder)
///Called to change the value of parent_attached_to.
/datum/component/overlay_lighting/proc/set_parent_attached_to(atom/movable/new_parent_attached_to)
@@ -321,15 +351,16 @@
/datum/component/overlay_lighting/proc/on_z_move(atom/source)
SIGNAL_HANDLER
- if(current_holder && overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays -= visible_mask
- current_holder.underlays -= cone
+ hide_from_holder()
SET_PLANE_EXPLICIT(visible_mask, O_LIGHTING_VISUAL_PLANE, source)
if(cone)
SET_PLANE_EXPLICIT(cone, O_LIGHTING_VISUAL_PLANE, source)
- if(current_holder && overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays += visible_mask
- current_holder.underlays += cone
+ show_to_holder()
+
+// Avoids duplicate overlays (one from our NEXT holder, selected after the animation, one from the pickup animation)
+/datum/component/overlay_lighting/proc/on_pickup_anim(atom/source)
+ SIGNAL_HANDLER
+ hide_from_holder()
///Called when the current_holder is qdeleted, to remove the light effect.
/datum/component/overlay_lighting/proc/on_parent_attached_to_qdel(atom/movable/source, force)
@@ -364,20 +395,18 @@
range = clamp(CEILING(new_range, 0.5), 1, 6)
var/pixel_bounds = ((range - 1) * 64) + 32
lumcount_range = CEILING(range, 1)
- if(current_holder && overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays -= visible_mask
+ hide_from_holder()
+
visible_mask.icon = light_overlays["[pixel_bounds]"]
if(pixel_bounds == 32)
- if(!directional) // it's important that we make it to the end of this function if we are a directional light
- visible_mask.transform = null
- return
+ visible_mask.transform = null
else
var/offset = (pixel_bounds - 32) * 0.5
var/matrix/transform = new
transform.Translate(-offset, -offset)
visible_mask.transform = transform
- if(current_holder && overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays += visible_mask
+
+ show_to_holder()
if(directional)
if(beam)
cast_range = max(round(new_range * 0.5), 1)
@@ -393,39 +422,24 @@
var/new_power = source.light_power
set_lum_power(new_power >= 0 ? 0.5 : -0.5)
set_alpha = min(230, (abs(new_power) * 120) + 30)
- if(current_holder && overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays -= visible_mask
+ hide_from_holder()
visible_mask.alpha = set_alpha
visible_mask.blend_mode = new_power > 0 ? BLEND_ADD : BLEND_SUBTRACT
- if(current_holder && overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays += visible_mask
- if(!directional)
- return
- if(current_holder && overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays -= cone
- cone.alpha = min(120, (abs(new_power) * 60) + 15)
- cone.blend_mode = new_power > 0 ? BLEND_ADD : BLEND_SUBTRACT
- if(current_holder && overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays += cone
+ if(directional)
+ cone.alpha = min(120, (abs(new_power) * 60) + 15)
+ cone.blend_mode = new_power > 0 ? BLEND_ADD : BLEND_SUBTRACT
+ show_to_holder()
///Changes the light's color, pretty straightforward.
/datum/component/overlay_lighting/proc/set_color(atom/source, old_color)
SIGNAL_HANDLER
var/new_color = source.light_color
- if(current_holder && overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays -= visible_mask
+ hide_from_holder()
visible_mask.color = new_color
- if(current_holder && overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays += visible_mask
- if(!directional)
- return
- if(current_holder && overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays -= cone
- cone.color = new_color
- if(current_holder && overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays += cone
-
+ if(directional)
+ cone.color = new_color
+ show_to_holder()
///Toggles the light on and off.
/datum/component/overlay_lighting/proc/on_toggle(atom/source, old_value)
@@ -436,7 +450,6 @@
return
turn_off() //Falsey value, turn off.
-
///Triggered right after the parent light flags change.
/datum/component/overlay_lighting/proc/on_light_flags_change(atom/source, old_flags)
SIGNAL_HANDLER
@@ -453,6 +466,18 @@
overlay_lighting_flags &= ~LIGHTING_ATTACHED
set_parent_attached_to(null)
+///Changes the light's color, pretty straightforward.
+/datum/component/overlay_lighting/proc/set_light_render_source(atom/source, old_render_source)
+ SIGNAL_HANDLER
+ var/new_source = source.light_render_source
+ hide_from_holder()
+ visible_mask.render_source = new_source
+ if(directional)
+ var/new_cone_source = ""
+ if(new_source)
+ new_cone_source = "[new_source]_cone"
+ cone.render_source = new_cone_source
+ show_to_holder()
///Toggles the light on.
/datum/component/overlay_lighting/proc/turn_on()
@@ -504,7 +529,7 @@
break
scanning = next_turf
- current_holder.underlays -= visible_mask
+ hide_from_holder()
var/translate_x = -((range - 1) * 32)
var/translate_y = translate_x
@@ -536,8 +561,8 @@
transform.Scale(scale_x, scale_y)
transform.Translate(translate_x, translate_y)
visible_mask.transform = transform
- if(overlay_lighting_flags & LIGHTING_ON)
- current_holder.underlays += visible_mask
+
+ show_to_holder()
///Called when current_holder changes loc.
/datum/component/overlay_lighting/proc/on_holder_dir_change(atom/movable/source, olddir, newdir)
diff --git a/code/datums/components/riding/riding_mob.dm b/code/datums/components/riding/riding_mob.dm
index c515df2cdd02..ba6153aa77f6 100644
--- a/code/datums/components/riding/riding_mob.dm
+++ b/code/datums/components/riding/riding_mob.dm
@@ -739,3 +739,23 @@
TEXT_EAST = list(0, 5),
TEXT_WEST = list(0, 5),
)
+
+/datum/component/riding/creature/spider
+ rider_traits = list(TRAIT_WEB_SURFER, TRAIT_FENCE_CLIMBER)
+ ride_check_flags = RIDER_NEEDS_ARM | UNBUCKLE_DISABLED_RIDER
+
+/datum/component/riding/creature/spider/get_rider_offsets_and_layers(pass_index, mob/offsetter)
+ return list(
+ TEXT_NORTH = list( 0, 10),
+ TEXT_SOUTH = list( 0, 10),
+ TEXT_EAST = list(-5, 10),
+ TEXT_WEST = list( 5, 10),
+ )
+
+/datum/component/riding/creature/spider/get_parent_offsets_and_layers()
+ return list(
+ TEXT_NORTH = list(0, 0, MOB_BELOW_PIGGYBACK_LAYER),
+ TEXT_SOUTH = list(0, 0, MOB_ABOVE_PIGGYBACK_LAYER),
+ TEXT_EAST = list(0, 0, MOB_BELOW_PIGGYBACK_LAYER),
+ TEXT_WEST = list(0, 0, MOB_BELOW_PIGGYBACK_LAYER),
+ )
diff --git a/code/datums/components/usb_port.dm b/code/datums/components/usb_port.dm
index 832d9fe67a9f..f5c8b5195b9c 100644
--- a/code/datums/components/usb_port.dm
+++ b/code/datums/components/usb_port.dm
@@ -259,7 +259,7 @@
var/atom/atom_parent = parent
usb_cable.forceMove(atom_parent.drop_location())
- usb_cable.balloon_alert_to_viewers("snap")
+ usb_cable.balloon_alert_to_hearers("*snap*")
physical_object = null
attached_circuit = null
diff --git a/code/datums/diseases/advance/symptoms/flesh_eating.dm b/code/datums/diseases/advance/symptoms/flesh_eating.dm
index d12e0812335b..d767f33fae33 100644
--- a/code/datums/diseases/advance/symptoms/flesh_eating.dm
+++ b/code/datums/diseases/advance/symptoms/flesh_eating.dm
@@ -60,7 +60,7 @@ Bonus
if(bleed)
if(ishuman(M))
var/mob/living/carbon/human/H = M
- var/obj/item/bodypart/random_part = pick(H.bodyparts)
+ var/obj/item/bodypart/random_part = pick(H.get_bodyparts())
random_part.adjustBleedStacks(5 * power)
return 1
diff --git a/code/datums/drift_handler.dm b/code/datums/drift_handler.dm
index 53133025657f..0508123c1388 100644
--- a/code/datums/drift_handler.dm
+++ b/code/datums/drift_handler.dm
@@ -23,7 +23,7 @@
parent.drift_handler = src
var/flags = MOVEMENT_LOOP_OUTSIDE_CONTROL
if(instant)
- flags |= MOVEMENT_LOOP_START_FAST
+ flags |= MOVEMENT_LOOP_START_INSTANT
src.drift_force = drift_force
drifting_loop = GLOB.move_manager.smooth_move(moving = parent, angle = inertia_angle, delay = get_loop_delay(parent), subsystem = SSnewtonian_movement, priority = MOVEMENT_SPACE_PRIORITY, flags = flags)
diff --git a/code/datums/elements/blood_limb_overlay.dm b/code/datums/elements/blood_limb_overlay.dm
index 3028d3f4754e..bab7b6cb0ec0 100644
--- a/code/datums/elements/blood_limb_overlay.dm
+++ b/code/datums/elements/blood_limb_overlay.dm
@@ -12,10 +12,10 @@
. = ..()
UnregisterSignal(source, list(COMSIG_BODYPART_GET_LIMB_ICON, COMSIG_BODYPART_GENERATE_ICON_KEY))
-/datum/element/blood_limb_overlay/proc/on_limb_icon(obj/item/bodypart/source, list/limb_icons, dropped, mob/living/carbon/update_on)
+/datum/element/blood_limb_overlay/proc/on_limb_icon(obj/item/bodypart/source, list/limb_icons, dropped)
SIGNAL_HANDLER
- var/list/blood_dna_info = source.blood_dna_info || update_on?.get_blood_dna_list()
+ var/list/blood_dna_info = source.blood_dna_info
if (!LAZYLEN(blood_dna_info) || source.is_invisible)
return
@@ -38,4 +38,4 @@
var/list/blood_dna_info = source.blood_dna_info || source.owner?.get_blood_dna_list()
if (LAZYLEN(blood_dna_info) && !source.is_invisible)
- icon_keys += "-blood-[get_color_from_blood_list(blood_dna_info)]"
+ icon_keys += "blood:[get_color_from_blood_list(blood_dna_info)]"
diff --git a/code/datums/elements/food/foodlike_drink.dm b/code/datums/elements/food/foodlike_drink.dm
deleted file mode 100644
index e8f6cf49f9e6..000000000000
--- a/code/datums/elements/food/foodlike_drink.dm
+++ /dev/null
@@ -1,50 +0,0 @@
-#define DOAFTER_SOURCE_FOODLIKE_DRINK "doafter_foodlike_drink"
-
-/**
- * This element can be attached to a reagent container to make it loop after drinking like a food item
- */
-/datum/element/foodlike_drink
-
-/datum/element/foodlike_drink/Attach(datum/target)
- . = ..()
- if(!is_reagent_container(target))
- return ELEMENT_INCOMPATIBLE
-
- RegisterSignal(target, COMSIG_GLASS_DRANK, PROC_REF(on_drink))
-
-/datum/element/foodlike_drink/Detach(datum/source, ...)
- . = ..()
- UnregisterSignal(source, COMSIG_GLASS_DRANK)
-
-/datum/element/foodlike_drink/proc/on_drink(obj/item/reagent_containers/source, mob/living/drinker, mob/living/user)
- SIGNAL_HANDLER
-
- if(drinker != user)
- return
-
- if(DOING_INTERACTION(user, DOAFTER_SOURCE_FOODLIKE_DRINK))
- return
-
- INVOKE_ASYNC(src, PROC_REF(continue_drinking), source, user)
-
-/datum/element/foodlike_drink/proc/continue_drinking(obj/item/reagent_containers/source, mob/living/user)
- if(!do_after(
- user = user,
- delay = 1.25 SECONDS,
- timed_action_flags = IGNORE_USER_LOC_CHANGE,
- extra_checks = CALLBACK(src, PROC_REF(can_keep_drinking), source, user),
- interaction_key = DOAFTER_SOURCE_FOODLIKE_DRINK,
- ))
- return
-
- source.attack(user, user)
- user.hud_used?.hunger?.update_hunger_bar()
-
-/datum/element/foodlike_drink/proc/can_keep_drinking(obj/item/reagent_containers/source, mob/living/user)
- if(QDELETED(source) || user.get_active_held_item() != source)
- return FALSE
- if(source.reagents.total_volume <= 0)
- return FALSE
- return TRUE
-
-#undef DOAFTER_SOURCE_FOODLIKE_DRINK
diff --git a/code/datums/elements/organ_set_bonus.dm b/code/datums/elements/organ_set_bonus.dm
index 07d7e2474d22..49cd58ddf935 100644
--- a/code/datums/elements/organ_set_bonus.dm
+++ b/code/datums/elements/organ_set_bonus.dm
@@ -105,7 +105,7 @@
RegisterSignal(carbon_owner, COMSIG_CARBON_ATTACH_LIMB, PROC_REF(texture_limb))
RegisterSignal(carbon_owner, COMSIG_CARBON_REMOVE_LIMB, PROC_REF(untexture_limb))
- for(var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in carbon_owner.get_bodyparts())
if (!(limb.bodytype & BODYTYPE_ORGANIC))
continue
limb.add_bodypart_overlay(new limb_overlay(), update = FALSE)
@@ -139,7 +139,7 @@
if(QDELETED(carbon_owner))
return
- for(var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in carbon_owner.get_bodyparts())
var/overlay = locate(limb_overlay) in limb.bodypart_overlays
if(!overlay)
continue
diff --git a/code/datums/elements/spooky.dm b/code/datums/elements/spooky.dm
index 85daaaeda78d..4ae70bb50953 100644
--- a/code/datums/elements/spooky.dm
+++ b/code/datums/elements/spooky.dm
@@ -52,7 +52,7 @@
if(isskeleton(human))
return FALSE //undeads are unaffected by the spook-pocalypse.
var/bone_amount = 0
- for(var/obj/item/bodypart/part as anything in human.bodyparts)
+ for(var/obj/item/bodypart/part as anything in human.get_bodyparts())
if((part.biological_state & BIO_FLESH_BONE) == BIO_FLESH_BONE)
bone_amount++
if(bone_amount)
diff --git a/code/datums/embedding.dm b/code/datums/embedding.dm
index 77fec4f87469..32d838eedf4f 100644
--- a/code/datums/embedding.dm
+++ b/code/datums/embedding.dm
@@ -133,7 +133,7 @@
failed_embed(victim, hit_zone, random = TRUE)
return
- var/obj/item/bodypart/limb = victim.get_bodypart(hit_zone) || victim.bodyparts[1]
+ var/obj/item/bodypart/limb = victim.get_bodypart(hit_zone) || victim.get_bodypart()
embed_into(victim, limb)
return MOVABLE_IMPACT_ZONE_OVERRIDE
@@ -153,7 +153,7 @@
failed_embed(victim, hit_zone, random = TRUE)
return
- var/obj/item/bodypart/limb = victim.get_bodypart(hit_zone) || victim.bodyparts[1]
+ var/obj/item/bodypart/limb = victim.get_bodypart(hit_zone) || victim.get_bodypart()
embed_into(victim, limb)
SEND_SIGNAL(source, COMSIG_PROJECTILE_ON_EMBEDDED, payload, hit)
diff --git a/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm b/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm
index 3cfad1fef7f2..f8ab474c3598 100644
--- a/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm
+++ b/code/datums/greyscale/config_types/greyscale_configs/greyscale_clothes.dm
@@ -693,9 +693,13 @@
/datum/greyscale_config/digitigrade
name = "Digitigrade Clothes"
- icon_file = 'icons/mob/clothing/digi_template.dmi'
+ icon_file = 'icons/mob/clothing/digi_template_equpiment.dmi'
json_config = 'code/datums/greyscale/json_configs/digitigrade.json'
+/datum/greyscale_config/digitigrade_underwear
+ name = "Digitigrade Underwear"
+ icon_file = 'icons/mob/clothing/digi_template_underwear.dmi'
+ json_config = 'code/datums/greyscale/json_configs/digitigrade_underwear.json'
//
// SUIT + HEAD
diff --git a/code/datums/greyscale/json_configs/digitigrade_underwear.json b/code/datums/greyscale/json_configs/digitigrade_underwear.json
new file mode 100644
index 000000000000..3d95852230ff
--- /dev/null
+++ b/code/datums/greyscale/json_configs/digitigrade_underwear.json
@@ -0,0 +1,70 @@
+{
+ "short_short": [
+ {
+ "type": "icon_state",
+ "icon_state": "short_short_digi",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ }
+ ],
+ "short": [
+ {
+ "type": "icon_state",
+ "icon_state": "short_digi",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ }
+ ],
+ "boxers": [
+ {
+ "type": "icon_state",
+ "icon_state": "male_boxers_digi",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ }
+ ],
+ "midway": [
+ {
+ "type": "icon_state",
+ "icon_state": "male_midway_digi",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ }
+ ],
+ "longjohns": [
+ {
+ "type": "icon_state",
+ "icon_state": "male_longjohns_digi",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ }
+ ],
+ "boxers_stripe": [
+ {
+ "type": "icon_state",
+ "icon_state": "male_boxers_stripe_digi",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ }
+ ],
+ "boxers_stripe_threecolor": [
+ {
+ "type": "icon_state",
+ "icon_state": "male_boxers_stripe_twocolor_digi_a",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "male_boxers_stripe_twocolor_digi_b",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "male_boxers_stripe_twocolor_digi_c",
+ "blend_mode": "overlay",
+ "color_ids": [ 3 ]
+ }
+ ]
+}
diff --git a/code/datums/mutations/autotomy.dm b/code/datums/mutations/autotomy.dm
index 03575a8f0396..da34bfd46606 100644
--- a/code/datums/mutations/autotomy.dm
+++ b/code/datums/mutations/autotomy.dm
@@ -27,7 +27,7 @@
return
var/list/parts = list()
- for(var/obj/item/bodypart/to_remove as anything in cast_on.bodyparts)
+ for(var/obj/item/bodypart/to_remove as anything in cast_on.get_bodyparts())
if(to_remove.body_zone == BODY_ZONE_HEAD || to_remove.body_zone == BODY_ZONE_CHEST)
continue
if(to_remove.bodypart_flags & BODYPART_UNREMOVABLE)
diff --git a/code/datums/mutations/hulk.dm b/code/datums/mutations/hulk.dm
index 9de066eeacbc..6fd350263093 100644
--- a/code/datums/mutations/hulk.dm
+++ b/code/datums/mutations/hulk.dm
@@ -33,7 +33,7 @@
. = ..()
if(!.)
return
- for(var/obj/item/bodypart/part as anything in owner.bodyparts)
+ for(var/obj/item/bodypart/part as anything in owner.get_bodyparts())
if (part.bodytype & BODYTYPE_ORGANIC)
part.add_color_override(bodypart_color, LIMB_COLOR_HULK)
owner.update_body_parts()
@@ -74,7 +74,7 @@
/datum/mutation/hulk/on_losing(mob/living/carbon/human/owner)
if(..())
return
- for(var/obj/item/bodypart/part as anything in owner.bodyparts)
+ for(var/obj/item/bodypart/part as anything in owner.get_bodyparts())
part.remove_color_override(LIMB_COLOR_HULK)
owner.update_body_parts()
owner.clear_mood_event("hulk")
diff --git a/code/datums/mutations/touch.dm b/code/datums/mutations/touch.dm
index ba24f4f30bc4..7a91cc390e8f 100644
--- a/code/datums/mutations/touch.dm
+++ b/code/datums/mutations/touch.dm
@@ -226,7 +226,7 @@
// Get at least organic limb to transfer the damage to
var/list/mendicant_organic_limbs = list()
- for(var/obj/item/bodypart/possible_limb in mendicant.bodyparts)
+ for(var/obj/item/bodypart/possible_limb in mendicant.get_bodyparts())
if(IS_ORGANIC_LIMB(possible_limb))
mendicant_organic_limbs += possible_limb
// None? Gtfo
@@ -258,7 +258,7 @@
// Get the hurtguy's limbs and the mendicant's limbs to attempt a 1-1 transfer.
var/list/hurt_limbs = hurtguy.get_damaged_bodyparts(1, 1, BODYTYPE_ORGANIC) + hurtguy.get_wounded_bodyparts(BODYTYPE_ORGANIC)
var/list/mendicant_organic_limbs = list()
- for(var/obj/item/bodypart/possible_limb in mendicant.bodyparts)
+ for(var/obj/item/bodypart/possible_limb in mendicant.get_bodyparts())
if(IS_ORGANIC_LIMB(possible_limb))
mendicant_organic_limbs += possible_limb
diff --git a/code/datums/outfit.dm b/code/datums/outfit.dm
index 182df168bfd3..44d47bb11cdc 100644
--- a/code/datums/outfit.dm
+++ b/code/datums/outfit.dm
@@ -125,9 +125,9 @@
var/preload = FALSE
/// Any undershirt. While on humans it is a string, here we use paths to stay consistent with the rest of the equips.
- var/datum/sprite_accessory/undershirt = null
- var/datum/sprite_accessory/underwear = null
- var/datum/sprite_accessory/socks = null
+ var/datum/sprite_accessory/clothing/undershirt = null
+ var/datum/sprite_accessory/clothing/underwear = null
+ var/datum/sprite_accessory/clothing/socks = null
/**
* Called at the start of the equip proc
diff --git a/code/datums/quirks/_quirk.dm b/code/datums/quirks/_quirk.dm
index 69bf447a04e1..ac0b01684dd0 100644
--- a/code/datums/quirks/_quirk.dm
+++ b/code/datums/quirks/_quirk.dm
@@ -296,5 +296,7 @@
var/datum/preferences/to_pass = client || to_mob.client
for(var/datum/quirk/quirk as anything in quirks)
+ if(quirk.quirk_flags & QUIRK_NO_TRANSFER)
+ continue
quirk.remove_from_current_holder(quirk_transfer = TRUE)
quirk.add_to_holder(to_mob, quirk_transfer = TRUE, client_source = to_pass)
diff --git a/code/datums/quirks/negative_quirks/body_purist.dm b/code/datums/quirks/negative_quirks/body_purist.dm
index 76221df7a5c6..063887b87464 100644
--- a/code/datums/quirks/negative_quirks/body_purist.dm
+++ b/code/datums/quirks/negative_quirks/body_purist.dm
@@ -31,7 +31,7 @@
var/mob/living/carbon/owner = quirk_holder
if(!istype(owner))
return
- for(var/obj/item/bodypart/limb as anything in owner.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in owner.get_bodyparts())
if(IS_ROBOTIC_LIMB(limb))
cybernetics_level++
for(var/obj/item/organ/organ as anything in owner.organs)
diff --git a/code/datums/quirks/neutral_quirks/thanatorenasia.dm b/code/datums/quirks/neutral_quirks/thanatorenasia.dm
new file mode 100644
index 000000000000..6b2d1303fb8a
--- /dev/null
+++ b/code/datums/quirks/neutral_quirks/thanatorenasia.dm
@@ -0,0 +1,55 @@
+/datum/quirk/death_dnr_poll
+ name = "Thanatorenasia"
+ desc = "Whenever you die and elect to \"Do Not Resuscitate\", your body may be taken over by another ghost upon revival - \
+ giving it an entirely new personality and fresh set of memories."
+ icon = FA_ICON_ZAP
+ value = 0
+ medical_record_text = "Patient has Thanatorenasia - in the event of their death and resuscitation, \
+ they may experience memory loss or a change in personality."
+ medical_symptom_text = "In the event of the patient's death and resuscitation, \
+ they may experience memory loss or a change in personality."
+ quirk_flags = QUIRK_NO_TRANSFER
+
+ // Used the the spawners menu to describe the quirk
+ var/you_are_text = "You are a deceased crewmember, afflicted with Thanatorenasia - \
+ a condition which alters personality and causes memory loss upon death and revival."
+ var/flavor_text = "Something feels... different. \
+ You're not entirely sure who you are or what happened - All you remember is your name and that you work here. \
+ Oh well, better get back to work - the last thing you want is to be both unemployed AND an amnesiac."
+ var/important_text = "Resume your assigned duty. \
+ If you choose to \"Do Not Resuscitate\" upon death, another ghost will be allowed to take over the body. \
+ You still roll for midround antagonists."
+
+/datum/quirk/death_dnr_poll/add_unique(client/client_source)
+ . = ..()
+ RegisterSignal(quirk_holder, COMSIG_LIVING_DNR, PROC_REF(mob_died))
+
+/datum/quirk/death_dnr_poll/remove()
+ . = ..()
+ UnregisterSignal(quirk_holder, COMSIG_LIVING_DNR)
+
+/datum/quirk/death_dnr_poll/proc/mob_died(mob/living/source, mob/dead/observer/dnring)
+ SIGNAL_HANDLER
+
+ var/whomst = source.real_name
+ if(source.mind && !is_unassigned_job(source.mind.assigned_role))
+ whomst += "Job: [span_notice(source.mind.assigned_role.title)]."
+ if(length(source.mind?.get_special_roles()))
+ whomst += "Status: [span_boldnotice(english_list(source.mind?.get_special_roles()))]."
+
+ source.AddComponent(/datum/component/ghostrole_on_revive, \
+ refuse_revival_if_failed = TRUE, \
+ on_successful_revive = CALLBACK(src, PROC_REF(on_successful_revive)), \
+ revive_title = whomst, \
+ spawn_text = "Deceased Crew", \
+ you_are_text = src.you_are_text, \
+ flavor_text = src.flavor_text, \
+ important_text = src.important_text, \
+ )
+ source.log_message("was made ghostrole pollable by [name] quirk.", LOG_GAME, color = COLOR_PURPLE)
+
+/datum/quirk/death_dnr_poll/proc/on_successful_revive()
+ quirk_holder.log_message("has had their body taken over by a ghost due to the [name] quirk.", LOG_GAME, color = COLOR_PURPLE)
+ var/welcome_msg = boxed_message(span_notice("[quirk_holder.real_name] has [name] - you are [quirk_holder.p_their()] new owner.
\
+ If you choose to \"Do Not Resuscitate\" upon death, another ghost will take over the body once again."))
+ addtimer(CALLBACK(src, GLOBAL_PROC_REF(to_chat), quirk_holder, welcome_msg), 2 SECONDS)
diff --git a/code/datums/quirks/neutral_quirks/transhumanist.dm b/code/datums/quirks/neutral_quirks/transhumanist.dm
index 82570e6cf21c..6dfa2d16f6c2 100644
--- a/code/datums/quirks/neutral_quirks/transhumanist.dm
+++ b/code/datums/quirks/neutral_quirks/transhumanist.dm
@@ -76,7 +76,7 @@
var/organic_bodytypes = 0
var/silicon_bodytypes = 0
var/other_bodytypes = FALSE
- for(var/obj/item/bodypart/part as anything in target.bodyparts)
+ for(var/obj/item/bodypart/part as anything in target.get_bodyparts())
if(part.bodytype & BODYTYPE_ROBOTIC)
silicon_bodytypes += 1
else if(part.bodytype & BODYTYPE_ORGANIC)
diff --git a/code/datums/spawners_menu.dm b/code/datums/spawners_menu.dm
index b6adf6782e64..1c91fbd48da3 100644
--- a/code/datums/spawners_menu.dm
+++ b/code/datums/spawners_menu.dm
@@ -50,9 +50,10 @@
this["name"] = mob_type
this["amount_left"] = 0
for(var/mob/joinable_mob as anything in GLOB.joinable_mobs[mob_type])
+ SEND_SIGNAL(joinable_mob, COMSIG_LIVING_GHOSTROLE_INFO, this)
this["amount_left"] += 1
- if(!SEND_SIGNAL(joinable_mob, COMSIG_LIVING_GHOSTROLE_INFO, this))
- this["desc"] = initial(joinable_mob.desc)
+ this["desc"] ||= initial(joinable_mob.desc)
+
if(this["amount_left"] > 0)
data["spawners"] += list(this)
return data
diff --git a/code/datums/sprite_accessories.dm b/code/datums/sprite_accessories.dm
index 88f1a4043e66..02ef3786598a 100644
--- a/code/datums/sprite_accessories.dm
+++ b/code/datums/sprite_accessories.dm
@@ -1244,179 +1244,243 @@ GLOBAL_LIST_EMPTY(blended_hair_icons_cache)
icon_state = null
gender = NEUTER
+/datum/sprite_accessory/clothing
+ /// Allows you to specify a greyscale config
+ var/greyscale_config
+ /// Icon state in the digitigrade template file to use if the wearer is digitigrade.
+ /// If null, no special digitigrade handling is done.
+ var/digi_icon_state
+ /// Color pallete for static colored underwear, like hearts.
+ /// Used so greyscale copies can have the same palette.
+ var/greyscale_colors = "#FFFFFF#FFFFFF#FFFFFF"
+
+/**
+ * Generate an appearance from this clothing datum
+ *
+ * * color - if this is NOT a statically colored clothing article and NOT gags, uses this color.
+ * * physique - physique of the wearer (male or female)
+ * * bodyshape - bodyshape of the wearer (humanoid, digitigrade, etc)
+ */
+/datum/sprite_accessory/clothing/proc/make_appearance(color = COLOR_WHITE, physique = MALE, bodyshape = BODYSHAPE_HUMANOID)
+ var/static/list/cached_icons = list()
+ var/use_female = physique == FEMALE
+ var/use_digi = digi_icon_state && (bodyshape & BODYSHAPE_DIGITIGRADE)
+
+ var/key = "[icon_state]-[greyscale_config || "ng"]-[use_female]-[use_digi]-[greyscale_colors]"
+ var/mutable_appearance/result
+ if(cached_icons[key]) // it's already cached
+ result = mutable_appearance(icon(cached_icons[key]))
+
+ else if(greyscale_config || use_female || use_digi) // icon ops ahead
+ var/icon/created = icon(greyscale_config ? SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors) : icon, icon_state)
+ if(use_female)
+ created = wear_female_version(icon_state, icon, FEMALE_UNIFORM_FULL)
+ if(use_digi)
+ var/icon/replacement = icon(SSgreyscale.GetColoredIconByType(/datum/greyscale_config/digitigrade_underwear, greyscale_colors), digi_icon_state)
+ created = replace_icon_legs(created, replacement)
+
+ cached_icons[key] = fcopy_rsc(created)
+ result = mutable_appearance(created)
+
+ else // no caching necessary
+ result = mutable_appearance(icon, icon_state)
+
+ result.layer = -BODY_LAYER
+ result.color = use_static ? null : color
+
+ return result
+
+
///////////////////////////
// Underwear Definitions //
///////////////////////////
-/datum/sprite_accessory/underwear
+/datum/sprite_accessory/clothing/underwear
icon = 'icons/mob/clothing/underwear.dmi'
use_static = FALSE
em_block = TRUE
-
//MALE UNDERWEAR
-/datum/sprite_accessory/underwear/nude
+/datum/sprite_accessory/clothing/underwear/nude
name = "Nude"
icon_state = null
gender = NEUTER
-/datum/sprite_accessory/underwear/male_briefs
+/datum/sprite_accessory/clothing/underwear/nude/make_appearance(mob/living/carbon/human/for_who)
+ return
+
+/datum/sprite_accessory/clothing/underwear/male_briefs
name = "Briefs"
icon_state = "male_briefs"
gender = MALE
-/datum/sprite_accessory/underwear/male_boxers
+/datum/sprite_accessory/clothing/underwear/male_boxers
name = "Boxers"
icon_state = "male_boxers"
gender = MALE
+ digi_icon_state = "boxers"
-/datum/sprite_accessory/underwear/male_stripe
+/datum/sprite_accessory/clothing/underwear/male_stripe
name = "Striped Boxers"
icon_state = "male_stripe"
gender = MALE
+ digi_icon_state = "boxers_stripe"
-/datum/sprite_accessory/underwear/male_midway
+/datum/sprite_accessory/clothing/underwear/male_midway
name = "Midway Boxers"
icon_state = "male_midway"
gender = MALE
+ digi_icon_state = "midway"
-/datum/sprite_accessory/underwear/male_longjohns
+/datum/sprite_accessory/clothing/underwear/male_longjohns
name = "Long Johns"
icon_state = "male_longjohns"
gender = MALE
+ digi_icon_state = "longjohns"
-/datum/sprite_accessory/underwear/male_kinky
+/datum/sprite_accessory/clothing/underwear/male_kinky
name = "Jockstrap"
icon_state = "male_kinky"
gender = MALE
-/datum/sprite_accessory/underwear/male_mankini
+/datum/sprite_accessory/clothing/underwear/male_mankini
name = "Mankini"
icon_state = "male_mankini"
gender = MALE
-/datum/sprite_accessory/underwear/male_hearts
+/datum/sprite_accessory/clothing/underwear/male_hearts
name = "Hearts Boxers"
icon_state = "male_hearts"
gender = MALE
use_static = TRUE
+ digi_icon_state = "boxers_stripe_threecolor"
+ greyscale_colors = "#D62626#EEEEEE#D62626#"
-/datum/sprite_accessory/underwear/male_commie
+/datum/sprite_accessory/clothing/underwear/male_commie
name = "Commie Boxers"
icon_state = "male_commie"
gender = MALE
use_static = TRUE
+ digi_icon_state = "boxers_stripe_twocolor"
+ greyscale_colors = "#D62626#D1B62C#D62626"
-/datum/sprite_accessory/underwear/male_usastripe
+/datum/sprite_accessory/clothing/underwear/male_usastripe
name = "Freedom Boxers"
icon_state = "male_assblastusa"
gender = MALE
use_static = TRUE
+ digi_icon_state = "boxers_stripe_threecolor"
+ greyscale_colors = "#D62626#EEEEEE#2E26D6"
-/datum/sprite_accessory/underwear/male_uk
+/datum/sprite_accessory/clothing/underwear/male_uk
name = "UK Boxers"
icon_state = "male_uk"
gender = MALE
use_static = TRUE
-
+ digi_icon_state = "boxers_stripe_threecolor"
+ greyscale_colors = "#D62626#EEEEEE#2E26D6"
//FEMALE UNDERWEAR
-/datum/sprite_accessory/underwear/female_bikini
+/datum/sprite_accessory/clothing/underwear/female_bikini
name = "Bikini"
icon_state = "female_bikini"
gender = FEMALE
-/datum/sprite_accessory/underwear/female_lace
+/datum/sprite_accessory/clothing/underwear/female_lace
name = "Lace Bikini"
icon_state = "female_lace"
gender = FEMALE
-/datum/sprite_accessory/underwear/female_bralette
+/datum/sprite_accessory/clothing/underwear/female_bralette
name = "Bralette w/ Boyshorts"
icon_state = "female_bralette"
gender = FEMALE
+ digi_icon_state = "short_short"
-/datum/sprite_accessory/underwear/female_sport
+/datum/sprite_accessory/clothing/underwear/female_sport
name = "Sports Bra w/ Boyshorts"
icon_state = "female_sport"
gender = FEMALE
+ digi_icon_state = "short"
-/datum/sprite_accessory/underwear/female_thong
+/datum/sprite_accessory/clothing/underwear/female_thong
name = "Thong"
icon_state = "female_thong"
gender = FEMALE
-/datum/sprite_accessory/underwear/female_strapless
+/datum/sprite_accessory/clothing/underwear/female_strapless
name = "Strapless Bikini"
icon_state = "female_strapless"
gender = FEMALE
-/datum/sprite_accessory/underwear/female_babydoll
+/datum/sprite_accessory/clothing/underwear/female_babydoll
name = "Babydoll"
icon_state = "female_babydoll"
gender = FEMALE
-/datum/sprite_accessory/underwear/swimsuit_onepiece
+/datum/sprite_accessory/clothing/underwear/swimsuit_onepiece
name = "One-Piece Swimsuit"
icon_state = "swim_onepiece"
gender = FEMALE
-/datum/sprite_accessory/underwear/swimsuit_strapless_onepiece
+/datum/sprite_accessory/clothing/underwear/swimsuit_strapless_onepiece
name = "Strapless One-Piece Swimsuit"
icon_state = "swim_strapless_onepiece"
gender = FEMALE
-/datum/sprite_accessory/underwear/swimsuit_twopiece
+/datum/sprite_accessory/clothing/underwear/swimsuit_twopiece
name = "Two-Piece Swimsuit"
icon_state = "swim_twopiece"
gender = FEMALE
+ digi_icon_state = "short_short"
-/datum/sprite_accessory/underwear/swimsuit_strapless_twopiece
+/datum/sprite_accessory/clothing/underwear/swimsuit_strapless_twopiece
name = "Strapless Two-Piece Swimsuit"
icon_state = "swim_strapless_twopiece"
gender = FEMALE
+ digi_icon_state = "short_short"
-/datum/sprite_accessory/underwear/swimsuit_stripe
+/datum/sprite_accessory/clothing/underwear/swimsuit_stripe
name = "Strapless Striped Swimsuit"
icon_state = "swim_stripe"
gender = FEMALE
-/datum/sprite_accessory/underwear/swimsuit_halter
+/datum/sprite_accessory/clothing/underwear/swimsuit_halter
name = "Halter Swimsuit"
icon_state = "swim_halter"
gender = FEMALE
-/datum/sprite_accessory/underwear/female_white_neko
+/datum/sprite_accessory/clothing/underwear/female_white_neko
name = "Neko Bikini (White)"
icon_state = "female_neko_white"
gender = FEMALE
use_static = TRUE
-/datum/sprite_accessory/underwear/female_black_neko
+/datum/sprite_accessory/clothing/underwear/female_black_neko
name = "Neko Bikini (Black)"
icon_state = "female_neko_black"
gender = FEMALE
use_static = TRUE
-/datum/sprite_accessory/underwear/female_commie
+/datum/sprite_accessory/clothing/underwear/female_commie
name = "Commie Bikini"
icon_state = "female_commie"
gender = FEMALE
use_static = TRUE
-/datum/sprite_accessory/underwear/female_usastripe
+/datum/sprite_accessory/clothing/underwear/female_usastripe
name = "Freedom Bikini"
icon_state = "female_assblastusa"
gender = FEMALE
use_static = TRUE
-/datum/sprite_accessory/underwear/female_uk
+/datum/sprite_accessory/clothing/underwear/female_uk
name = "UK Bikini"
icon_state = "female_uk"
gender = FEMALE
use_static = TRUE
-/datum/sprite_accessory/underwear/female_kinky
+/datum/sprite_accessory/clothing/underwear/female_kinky
name = "Lingerie"
icon_state = "female_kinky"
gender = FEMALE
@@ -1426,283 +1490,286 @@ GLOBAL_LIST_EMPTY(blended_hair_icons_cache)
// Undershirt Definitions //
////////////////////////////
-/datum/sprite_accessory/undershirt
+/datum/sprite_accessory/clothing/undershirt
icon = 'icons/mob/clothing/underwear.dmi'
em_block = TRUE
-/datum/sprite_accessory/undershirt/nude
+/datum/sprite_accessory/clothing/undershirt/nude
name = "Nude"
icon_state = null
gender = NEUTER
+/datum/sprite_accessory/clothing/undershirt/nude/make_appearance(mob/living/carbon/human/for_who)
+ return
+
// please make sure they're sorted alphabetically and categorized
-/datum/sprite_accessory/undershirt/bluejersey
+/datum/sprite_accessory/clothing/undershirt/bluejersey
name = "Jersey (Blue)"
icon_state = "shirt_bluejersey"
gender = NEUTER
-/datum/sprite_accessory/undershirt/redjersey
+/datum/sprite_accessory/clothing/undershirt/redjersey
name = "Jersey (Red)"
icon_state = "shirt_redjersey"
gender = NEUTER
-/datum/sprite_accessory/undershirt/bluepolo
+/datum/sprite_accessory/clothing/undershirt/bluepolo
name = "Polo Shirt (Blue)"
icon_state = "bluepolo"
gender = NEUTER
-/datum/sprite_accessory/undershirt/grayyellowpolo
+/datum/sprite_accessory/clothing/undershirt/grayyellowpolo
name = "Polo Shirt (Gray-Yellow)"
icon_state = "grayyellowpolo"
gender = NEUTER
-/datum/sprite_accessory/undershirt/redpolo
+/datum/sprite_accessory/clothing/undershirt/redpolo
name = "Polo Shirt (Red)"
icon_state = "redpolo"
gender = NEUTER
-/datum/sprite_accessory/undershirt/whitepolo
+/datum/sprite_accessory/clothing/undershirt/whitepolo
name = "Polo Shirt (White)"
icon_state = "whitepolo"
gender = NEUTER
-/datum/sprite_accessory/undershirt/alienshirt
+/datum/sprite_accessory/clothing/undershirt/alienshirt
name = "Shirt (Alien)"
icon_state = "shirt_alien"
gender = NEUTER
-/datum/sprite_accessory/undershirt/mondmondjaja
+/datum/sprite_accessory/clothing/undershirt/mondmondjaja
name = "Shirt (Band)"
icon_state = "band"
gender = NEUTER
-/datum/sprite_accessory/undershirt/shirt_black
+/datum/sprite_accessory/clothing/undershirt/shirt_black
name = "Shirt (Black)"
icon_state = "shirt_black"
gender = NEUTER
-/datum/sprite_accessory/undershirt/blueshirt
+/datum/sprite_accessory/clothing/undershirt/blueshirt
name = "Shirt (Blue)"
icon_state = "shirt_blue"
gender = NEUTER
-/datum/sprite_accessory/undershirt/clownshirt
+/datum/sprite_accessory/clothing/undershirt/clownshirt
name = "Shirt (Clown)"
icon_state = "shirt_clown"
gender = NEUTER
-/datum/sprite_accessory/undershirt/commie
+/datum/sprite_accessory/clothing/undershirt/commie
name = "Shirt (Commie)"
icon_state = "shirt_commie"
gender = NEUTER
-/datum/sprite_accessory/undershirt/greenshirt
+/datum/sprite_accessory/clothing/undershirt/greenshirt
name = "Shirt (Green)"
icon_state = "shirt_green"
gender = NEUTER
-/datum/sprite_accessory/undershirt/shirt_grey
+/datum/sprite_accessory/clothing/undershirt/shirt_grey
name = "Shirt (Grey)"
icon_state = "shirt_grey"
gender = NEUTER
-/datum/sprite_accessory/undershirt/ian
+/datum/sprite_accessory/clothing/undershirt/ian
name = "Shirt (Ian)"
icon_state = "ian"
gender = NEUTER
-/datum/sprite_accessory/undershirt/ilovent
+/datum/sprite_accessory/clothing/undershirt/ilovent
name = "Shirt (I Love NT)"
icon_state = "ilovent"
gender = NEUTER
-/datum/sprite_accessory/undershirt/lover
+/datum/sprite_accessory/clothing/undershirt/lover
name = "Shirt (Lover)"
icon_state = "lover"
gender = NEUTER
-/datum/sprite_accessory/undershirt/matroska
+/datum/sprite_accessory/clothing/undershirt/matroska
name = "Shirt (Matroska)"
icon_state = "matroska"
gender = NEUTER
-/datum/sprite_accessory/undershirt/meat
+/datum/sprite_accessory/clothing/undershirt/meat
name = "Shirt (Meat)"
icon_state = "shirt_meat"
gender = NEUTER
-/datum/sprite_accessory/undershirt/nano
+/datum/sprite_accessory/clothing/undershirt/nano
name = "Shirt (Nanotrasen)"
icon_state = "shirt_nano"
gender = NEUTER
-/datum/sprite_accessory/undershirt/peace
+/datum/sprite_accessory/clothing/undershirt/peace
name = "Shirt (Peace)"
icon_state = "peace"
gender = NEUTER
-/datum/sprite_accessory/undershirt/pacman
+/datum/sprite_accessory/clothing/undershirt/pacman
name = "Shirt (Pogoman)"
icon_state = "pogoman"
gender = NEUTER
-/datum/sprite_accessory/undershirt/question
+/datum/sprite_accessory/clothing/undershirt/question
name = "Shirt (Question)"
icon_state = "shirt_question"
gender = NEUTER
-/datum/sprite_accessory/undershirt/redshirt
+/datum/sprite_accessory/clothing/undershirt/redshirt
name = "Shirt (Red)"
icon_state = "shirt_red"
gender = NEUTER
-/datum/sprite_accessory/undershirt/skull
+/datum/sprite_accessory/clothing/undershirt/skull
name = "Shirt (Skull)"
icon_state = "shirt_skull"
gender = NEUTER
-/datum/sprite_accessory/undershirt/ss13
+/datum/sprite_accessory/clothing/undershirt/ss13
name = "Shirt (SS13)"
icon_state = "shirt_ss13"
gender = NEUTER
-/datum/sprite_accessory/undershirt/stripe
+/datum/sprite_accessory/clothing/undershirt/stripe
name = "Shirt (Striped)"
icon_state = "shirt_stripes"
gender = NEUTER
-/datum/sprite_accessory/undershirt/tiedye
+/datum/sprite_accessory/clothing/undershirt/tiedye
name = "Shirt (Tie-dye)"
icon_state = "shirt_tiedye"
gender = NEUTER
-/datum/sprite_accessory/undershirt/uk
+/datum/sprite_accessory/clothing/undershirt/uk
name = "Shirt (UK)"
icon_state = "uk"
gender = NEUTER
-/datum/sprite_accessory/undershirt/usa
+/datum/sprite_accessory/clothing/undershirt/usa
name = "Shirt (USA)"
icon_state = "shirt_assblastusa"
gender = NEUTER
-/datum/sprite_accessory/undershirt/shirt_white
+/datum/sprite_accessory/clothing/undershirt/shirt_white
name = "Shirt (White)"
icon_state = "shirt_white"
gender = NEUTER
-/datum/sprite_accessory/undershirt/blackshortsleeve
+/datum/sprite_accessory/clothing/undershirt/blackshortsleeve
name = "Short-sleeved Shirt (Black)"
icon_state = "blackshortsleeve"
gender = NEUTER
-/datum/sprite_accessory/undershirt/blueshortsleeve
+/datum/sprite_accessory/clothing/undershirt/blueshortsleeve
name = "Short-sleeved Shirt (Blue)"
icon_state = "blueshortsleeve"
gender = NEUTER
-/datum/sprite_accessory/undershirt/greenshortsleeve
+/datum/sprite_accessory/clothing/undershirt/greenshortsleeve
name = "Short-sleeved Shirt (Green)"
icon_state = "greenshortsleeve"
gender = NEUTER
-/datum/sprite_accessory/undershirt/purpleshortsleeve
+/datum/sprite_accessory/clothing/undershirt/purpleshortsleeve
name = "Short-sleeved Shirt (Purple)"
icon_state = "purpleshortsleeve"
gender = NEUTER
-/datum/sprite_accessory/undershirt/whiteshortsleeve
+/datum/sprite_accessory/clothing/undershirt/whiteshortsleeve
name = "Short-sleeved Shirt (White)"
icon_state = "whiteshortsleeve"
gender = NEUTER
-/datum/sprite_accessory/undershirt/sports_bra
+/datum/sprite_accessory/clothing/undershirt/sports_bra
name = "Sports Bra"
icon_state = "sports_bra"
gender = NEUTER
-/datum/sprite_accessory/undershirt/sports_bra2
+/datum/sprite_accessory/clothing/undershirt/sports_bra2
name = "Sports Bra (Alt)"
icon_state = "sports_bra_alt"
gender = NEUTER
-/datum/sprite_accessory/undershirt/blueshirtsport
+/datum/sprite_accessory/clothing/undershirt/blueshirtsport
name = "Sports Shirt (Blue)"
icon_state = "blueshirtsport"
gender = NEUTER
-/datum/sprite_accessory/undershirt/greenshirtsport
+/datum/sprite_accessory/clothing/undershirt/greenshirtsport
name = "Sports Shirt (Green)"
icon_state = "greenshirtsport"
gender = NEUTER
-/datum/sprite_accessory/undershirt/redshirtsport
+/datum/sprite_accessory/clothing/undershirt/redshirtsport
name = "Sports Shirt (Red)"
icon_state = "redshirtsport"
gender = NEUTER
-/datum/sprite_accessory/undershirt/tank_black
+/datum/sprite_accessory/clothing/undershirt/tank_black
name = "Tank Top (Black)"
icon_state = "tank_black"
gender = NEUTER
-/datum/sprite_accessory/undershirt/tankfire
+/datum/sprite_accessory/clothing/undershirt/tankfire
name = "Tank Top (Fire)"
icon_state = "tank_fire"
gender = NEUTER
-/datum/sprite_accessory/undershirt/tank_grey
+/datum/sprite_accessory/clothing/undershirt/tank_grey
name = "Tank Top (Grey)"
icon_state = "tank_grey"
gender = NEUTER
-/datum/sprite_accessory/undershirt/female_midriff
+/datum/sprite_accessory/clothing/undershirt/female_midriff
name = "Tank Top (Midriff)"
icon_state = "tank_midriff"
gender = FEMALE
-/datum/sprite_accessory/undershirt/tank_red
+/datum/sprite_accessory/clothing/undershirt/tank_red
name = "Tank Top (Red)"
icon_state = "tank_red"
gender = NEUTER
-/datum/sprite_accessory/undershirt/tankstripe
+/datum/sprite_accessory/clothing/undershirt/tankstripe
name = "Tank Top (Striped)"
icon_state = "tank_stripes"
gender = NEUTER
-/datum/sprite_accessory/undershirt/tank_white
+/datum/sprite_accessory/clothing/undershirt/tank_white
name = "Tank Top (White)"
icon_state = "tank_white"
gender = NEUTER
-/datum/sprite_accessory/undershirt/redtop
+/datum/sprite_accessory/clothing/undershirt/redtop
name = "Top (Red)"
icon_state = "redtop"
gender = FEMALE
-/datum/sprite_accessory/undershirt/whitetop
+/datum/sprite_accessory/clothing/undershirt/whitetop
name = "Top (White)"
icon_state = "whitetop"
gender = FEMALE
-/datum/sprite_accessory/undershirt/tshirt_blue
+/datum/sprite_accessory/clothing/undershirt/tshirt_blue
name = "T-Shirt (Blue)"
icon_state = "blueshirt"
gender = NEUTER
-/datum/sprite_accessory/undershirt/tshirt_green
+/datum/sprite_accessory/clothing/undershirt/tshirt_green
name = "T-Shirt (Green)"
icon_state = "greenshirt"
gender = NEUTER
-/datum/sprite_accessory/undershirt/tshirt_red
+/datum/sprite_accessory/clothing/undershirt/tshirt_red
name = "T-Shirt (Red)"
icon_state = "redshirt"
gender = NEUTER
-/datum/sprite_accessory/undershirt/yellowshirt
+/datum/sprite_accessory/clothing/undershirt/yellowshirt
name = "T-Shirt (Yellow)"
icon_state = "yellowshirt"
gender = NEUTER
@@ -1711,169 +1778,172 @@ GLOBAL_LIST_EMPTY(blended_hair_icons_cache)
// Socks Definitions //
///////////////////////
-/datum/sprite_accessory/socks
+/datum/sprite_accessory/clothing/socks
icon = 'icons/mob/clothing/underwear.dmi'
em_block = TRUE
-/datum/sprite_accessory/socks/nude
+/datum/sprite_accessory/clothing/socks/nude
name = "Nude"
icon_state = null
+/datum/sprite_accessory/clothing/socks/nude/make_appearance(mob/living/carbon/human/for_who)
+ return
+
// please make sure they're sorted alphabetically and categorized
-/datum/sprite_accessory/socks/ace_knee
+/datum/sprite_accessory/clothing/socks/ace_knee
name = "Knee-high (Ace)"
icon_state = "ace_knee"
-/datum/sprite_accessory/socks/bee_knee
+/datum/sprite_accessory/clothing/socks/bee_knee
name = "Knee-high (Bee)"
icon_state = "bee_knee"
-/datum/sprite_accessory/socks/black_knee
+/datum/sprite_accessory/clothing/socks/black_knee
name = "Knee-high (Black)"
icon_state = "black_knee"
-/datum/sprite_accessory/socks/commie_knee
+/datum/sprite_accessory/clothing/socks/commie_knee
name = "Knee-High (Commie)"
icon_state = "commie_knee"
-/datum/sprite_accessory/socks/usa_knee
+/datum/sprite_accessory/clothing/socks/usa_knee
name = "Knee-High (Freedom)"
icon_state = "assblastusa_knee"
-/datum/sprite_accessory/socks/rainbow_knee
+/datum/sprite_accessory/clothing/socks/rainbow_knee
name = "Knee-high (Rainbow)"
icon_state = "rainbow_knee"
-/datum/sprite_accessory/socks/striped_knee
+/datum/sprite_accessory/clothing/socks/striped_knee
name = "Knee-high (Striped)"
icon_state = "striped_knee"
-/datum/sprite_accessory/socks/thin_knee
+/datum/sprite_accessory/clothing/socks/thin_knee
name = "Knee-high (Thin)"
icon_state = "thin_knee"
-/datum/sprite_accessory/socks/trans_knee
+/datum/sprite_accessory/clothing/socks/trans_knee
name = "Knee-high (Trans)"
icon_state = "trans_knee"
-/datum/sprite_accessory/socks/uk_knee
+/datum/sprite_accessory/clothing/socks/uk_knee
name = "Knee-High (UK)"
icon_state = "uk_knee"
-/datum/sprite_accessory/socks/white_knee
+/datum/sprite_accessory/clothing/socks/white_knee
name = "Knee-high (White)"
icon_state = "white_knee"
-/datum/sprite_accessory/socks/fishnet_knee
+/datum/sprite_accessory/clothing/socks/fishnet_knee
name = "Knee-high (Fishnet)"
icon_state = "fishnet_knee"
-/datum/sprite_accessory/socks/black_norm
+/datum/sprite_accessory/clothing/socks/black_norm
name = "Normal (Black)"
icon_state = "black_norm"
-/datum/sprite_accessory/socks/white_norm
+/datum/sprite_accessory/clothing/socks/white_norm
name = "Normal (White)"
icon_state = "white_norm"
-/datum/sprite_accessory/socks/pantyhose
+/datum/sprite_accessory/clothing/socks/pantyhose
name = "Pantyhose"
icon_state = "pantyhose"
-/datum/sprite_accessory/socks/black_short
+/datum/sprite_accessory/clothing/socks/black_short
name = "Short (Black)"
icon_state = "black_short"
-/datum/sprite_accessory/socks/white_short
+/datum/sprite_accessory/clothing/socks/white_short
name = "Short (White)"
icon_state = "white_short"
-/datum/sprite_accessory/socks/stockings_blue
+/datum/sprite_accessory/clothing/socks/stockings_blue
name = "Stockings (Blue)"
icon_state = "stockings_blue"
-/datum/sprite_accessory/socks/stockings_cyan
+/datum/sprite_accessory/clothing/socks/stockings_cyan
name = "Stockings (Cyan)"
icon_state = "stockings_cyan"
-/datum/sprite_accessory/socks/stockings_dpink
+/datum/sprite_accessory/clothing/socks/stockings_dpink
name = "Stockings (Dark Pink)"
icon_state = "stockings_dpink"
-/datum/sprite_accessory/socks/stockings_green
+/datum/sprite_accessory/clothing/socks/stockings_green
name = "Stockings (Green)"
icon_state = "stockings_green"
-/datum/sprite_accessory/socks/stockings_orange
+/datum/sprite_accessory/clothing/socks/stockings_orange
name = "Stockings (Orange)"
icon_state = "stockings_orange"
-/datum/sprite_accessory/socks/stockings_programmer
+/datum/sprite_accessory/clothing/socks/stockings_programmer
name = "Stockings (Programmer)"
icon_state = "stockings_lpink"
-/datum/sprite_accessory/socks/stockings_purple
+/datum/sprite_accessory/clothing/socks/stockings_purple
name = "Stockings (Purple)"
icon_state = "stockings_purple"
-/datum/sprite_accessory/socks/stockings_yellow
+/datum/sprite_accessory/clothing/socks/stockings_yellow
name = "Stockings (Yellow)"
icon_state = "stockings_yellow"
-/datum/sprite_accessory/socks/stockings_fishnet
+/datum/sprite_accessory/clothing/socks/stockings_fishnet
name = "Stockings (Fishnet)"
icon_state = "fishnet_full"
-/datum/sprite_accessory/socks/ace_thigh
+/datum/sprite_accessory/clothing/socks/ace_thigh
name = "Thigh-high (Ace)"
icon_state = "ace_thigh"
-/datum/sprite_accessory/socks/bee_thigh
+/datum/sprite_accessory/clothing/socks/bee_thigh
name = "Thigh-high (Bee)"
icon_state = "bee_thigh"
-/datum/sprite_accessory/socks/black_thigh
+/datum/sprite_accessory/clothing/socks/black_thigh
name = "Thigh-high (Black)"
icon_state = "black_thigh"
-/datum/sprite_accessory/socks/commie_thigh
+/datum/sprite_accessory/clothing/socks/commie_thigh
name = "Thigh-high (Commie)"
icon_state = "commie_thigh"
-/datum/sprite_accessory/socks/usa_thigh
+/datum/sprite_accessory/clothing/socks/usa_thigh
name = "Thigh-high (Freedom)"
icon_state = "assblastusa_thigh"
-/datum/sprite_accessory/socks/rainbow_thigh
+/datum/sprite_accessory/clothing/socks/rainbow_thigh
name = "Thigh-high (Rainbow)"
icon_state = "rainbow_thigh"
-/datum/sprite_accessory/socks/striped_thigh
+/datum/sprite_accessory/clothing/socks/striped_thigh
name = "Thigh-high (Striped)"
icon_state = "striped_thigh"
-/datum/sprite_accessory/socks/thin_thigh
+/datum/sprite_accessory/clothing/socks/thin_thigh
name = "Thigh-high (Thin)"
icon_state = "thin_thigh"
-/datum/sprite_accessory/socks/trans_thigh
+/datum/sprite_accessory/clothing/socks/trans_thigh
name = "Thigh-high (Trans)"
icon_state = "trans_thigh"
-/datum/sprite_accessory/socks/uk_thigh
+/datum/sprite_accessory/clothing/socks/uk_thigh
name = "Thigh-high (UK)"
icon_state = "uk_thigh"
-/datum/sprite_accessory/socks/white_thigh
+/datum/sprite_accessory/clothing/socks/white_thigh
name = "Thigh-high (White)"
icon_state = "white_thigh"
-/datum/sprite_accessory/socks/fishnet_thigh
+/datum/sprite_accessory/clothing/socks/fishnet_thigh
name = "Thigh-high (Fishnet)"
icon_state = "fishnet_thigh"
-/datum/sprite_accessory/socks/thocks
+/datum/sprite_accessory/clothing/socks/thocks
name = "Thocks"
icon_state = "thocks"
diff --git a/code/datums/status_effects/debuffs/rust_corruption.dm b/code/datums/status_effects/debuffs/rust_corruption.dm
index 6692ef111a08..66374898ab78 100644
--- a/code/datums/status_effects/debuffs/rust_corruption.dm
+++ b/code/datums/status_effects/debuffs/rust_corruption.dm
@@ -13,6 +13,6 @@
if(!iscarbon(owner))
return
var/mob/living/carbon/carbon_owner = owner
- for(var/obj/item/bodypart/robotic_limb as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/robotic_limb as anything in carbon_owner.get_bodyparts())
if(IS_ROBOTIC_LIMB(robotic_limb))
robotic_limb.receive_damage(10)
diff --git a/code/datums/voice_of_god_command.dm b/code/datums/voice_of_god_command.dm
index a49c7cb6c340..22a3993fa780 100644
--- a/code/datums/voice_of_god_command.dm
+++ b/code/datums/voice_of_god_command.dm
@@ -205,7 +205,7 @@ GLOBAL_LIST_INIT(voice_of_god_commands, init_voice_of_god_commands())
/datum/voice_of_god_command/bleed/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
for(var/mob/living/carbon/human/target in listeners)
- var/obj/item/bodypart/chosen_part = pick(target.bodyparts)
+ var/obj/item/bodypart/chosen_part = pick(target.get_bodyparts())
chosen_part.adjustBleedStacks(5)
/// This command sets the listeners ablaze.
diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm
index cdaa2ea434c9..76c5744c0f98 100644
--- a/code/game/atom/_atom.dm
+++ b/code/game/atom/_atom.dm
@@ -73,17 +73,23 @@
var/light_power = 1
///Hexadecimal RGB string representing the colour of the light. White by default.
var/light_color = COLOR_WHITE
+ ///Boolean variable for toggleable lights. Has no effect without the proper light_system, light_range and light_power values.
+ var/light_on = TRUE
+ ///Bitflags to determine lighting-related atom properties.
+ var/light_flags = NONE
+
+ // OVERLAY_LIGHT only values
+ /// An optional render_source to apply to this atom's light overlay
+ var/light_render_source = ""
+
+ // COMPLEX_LIGHT only values
/// Angle of light to show in light_dir
/// 360 is a circle, 90 is a cone, etc.
var/light_angle = 360
/// What angle to project light in
var/light_dir = NORTH
- ///Boolean variable for toggleable lights. Has no effect without the proper light_system, light_range and light_power values.
- var/light_on = TRUE
/// How many tiles "up" this light is. 1 is typical, should only really change this if it's a floor light
var/light_height = LIGHTING_HEIGHT
- ///Bitflags to determine lighting-related atom properties.
- var/light_flags = NONE
///Our light source. Don't fuck with this directly unless you have a good reason!
var/tmp/datum/light_source/light
///Any light sources that are "inside" of us, for example, if src here was a mob that's carrying a flashlight, that flashlight's light source would be part of this list.
@@ -799,6 +805,8 @@
///Called after the atom is 'tamed' for type-specific operations, Usually called by the tameable component but also other things.
/atom/proc/tamed(mob/living/tamer, obj/item/food)
+ SHOULD_CALL_PARENT(TRUE)
+ ADD_TRAIT(src, TRAIT_TAMED, INNATE_TRAIT)
return
/**
diff --git a/code/game/atom/atom_vv.dm b/code/game/atom/atom_vv.dm
index 14e20d23b9d7..d9600d4bae94 100644
--- a/code/game/atom/atom_vv.dm
+++ b/code/game/atom/atom_vv.dm
@@ -263,6 +263,27 @@
// I'm sorry
old_light_flags = var_value
. = TRUE
+ if(NAMEOF(src, light_render_source))
+ set_light_render_source(var_value)
+ . = TRUE
+ if(NAMEOF(src, light_angle))
+ if(light_system == COMPLEX_LIGHT)
+ set_light(l_angle = var_value)
+ else
+ set_light_angle(var_value)
+ . = TRUE
+ if(NAMEOF(src, light_dir))
+ if(light_system == COMPLEX_LIGHT)
+ set_light(l_dir = var_value)
+ else
+ set_light_dir(var_value)
+ . = TRUE
+ if(NAMEOF(src, light_height))
+ if(light_system == COMPLEX_LIGHT)
+ set_light(l_height = var_value)
+ else
+ set_light_height(var_value)
+ . = TRUE
if(NAMEOF(src, smoothing_junction))
set_smoothed_icon_state(var_value)
. = TRUE
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 6dd1f6a4b129..476b1d06caa9 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -498,6 +498,9 @@
use_power = new_use_power
+ if(use_power)
+ power_change()
+
return TRUE
///updates the power channel this machine uses. removes the static power usage from the old channel and readds it to the new channel
diff --git a/code/game/machinery/computer/operating_computer.dm b/code/game/machinery/computer/operating_computer.dm
index 688bcca26713..d2522c298777 100644
--- a/code/game/machinery/computer/operating_computer.dm
+++ b/code/game/machinery/computer/operating_computer.dm
@@ -171,7 +171,7 @@
return data
data["patient"] = list()
- var/mob/living/carbon/patient = table.patient
+ var/mob/living/patient = table.patient
switch(patient.stat)
if(CONSCIOUS)
@@ -240,33 +240,14 @@
"mechanic" = operation.operation_flags & OPERATION_MECHANIC,
))
- if(!any_recommended && table?.patient)
- var/obj/item/part = table.patient.get_bodypart(deprecise_zone(target_zone))
- var/just_drapes = FALSE
- if(table.patient.has_limbs)
- if(isnull(part))
- data["surgeries"] += list(list(
- "name" = "Prepare for [/datum/surgery_operation/prosthetic_replacement::name]",
- "desc" = "Prepare the patient's chest for prosthetic limb attachment.",
- "tool_rec" = "operate on chest",
- "show_as_next" = TRUE,
- "show_in_list" = FALSE,
- ))
-
- else if(!HAS_TRAIT(part, TRAIT_READY_TO_OPERATE))
- just_drapes = TRUE
-
- else if(!HAS_TRAIT(table.patient, TRAIT_READY_TO_OPERATE))
- just_drapes = TRUE
-
- if(just_drapes)
- data["surgeries"] += list(list(
- "name" = "Prepare for surgery",
- "desc" = "Begin surgery by applying surgical drapes to the patient.",
- "tool_rec" = /obj/item/surgical_drapes::name,
- "show_as_next" = TRUE,
- "show_in_list" = FALSE,
- ))
+ if(!any_recommended && table?.patient && !HAS_TRAIT(table.patient, TRAIT_READY_TO_OPERATE))
+ data["surgeries"] += list(list(
+ "name" = "Prepare for surgery",
+ "desc" = "Begin surgery by applying surgical drapes to the patient or by buckling the patient to the surgical table.",
+ "tool_rec" = /obj/item/surgical_drapes::name,
+ "show_as_next" = TRUE,
+ "show_in_list" = FALSE,
+ ))
return data
diff --git a/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm b/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm
index 2f760fe712e8..83e67734f258 100644
--- a/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/fish_organs.dm
@@ -93,7 +93,7 @@
if (new_value >= FISH_INFUSION_ALL_ORGANS && tail_color)
if (!color_active)
- for(var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in carbon_owner.get_bodyparts())
limb.add_color_override(tail_color, LIMB_COLOR_FISH_INFUSION)
color_active = TRUE
return
@@ -101,7 +101,7 @@
if (!color_active)
return
- for(var/obj/item/bodypart/limb as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in carbon_owner.get_bodyparts())
limb.remove_color_override(LIMB_COLOR_FISH_INFUSION)
color_active = FALSE
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index ff4ab5fc1623..883084e6c9a8 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -486,6 +486,8 @@
/obj/machinery/door/airlock/shock(mob/living/shocking, chance, shock_source, siemens_coeff)
if(!hasPower()) // unpowered, no shock
return FALSE
+ if(!isliving(shocking))
+ return FALSE
if(HAS_TRAIT(shocking, TRAIT_AIRLOCK_SHOCKIMMUNE)) // Be a bit more clever man come on
return FALSE
if(!COOLDOWN_FINISHED(src, shockCooldown))
diff --git a/code/game/machinery/experimental_cloner/experimental_cloning_record.dm b/code/game/machinery/experimental_cloner/experimental_cloning_record.dm
index 96c1b9bc164d..caea82e36775 100644
--- a/code/game/machinery/experimental_cloner/experimental_cloning_record.dm
+++ b/code/game/machinery/experimental_cloner/experimental_cloning_record.dm
@@ -75,7 +75,7 @@
for (var/trauma_type in brain_traumas)
subject.gain_trauma(trauma_type)
- for (var/obj/item/bodypart/limb as anything in subject.bodyparts)
+ for (var/obj/item/bodypart/limb as anything in subject.get_bodyparts())
limb.update_limb(is_creating = TRUE)
subject.updateappearance(mutcolor_update = TRUE)
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index 74600bab3841..5aa9520df7f3 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -670,7 +670,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/firealarm, 26)
alarm_trigger = add_input_port("Set", PORT_TYPE_SIGNAL)
reset_trigger = add_input_port("Reset", PORT_TYPE_SIGNAL)
- is_on = add_output_port("Is On", PORT_TYPE_NUMBER)
+ is_on = add_output_port("Is On", PORT_TYPE_BOOLEAN)
triggered = add_output_port("Triggered", PORT_TYPE_SIGNAL)
reset = add_output_port("Reset", PORT_TYPE_SIGNAL)
diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm
index 70849a1198f1..0af6c9933a4b 100644
--- a/code/game/machinery/harvester.dm
+++ b/code/game/machinery/harvester.dm
@@ -101,7 +101,7 @@
header = "Gruesome!",
)
- operation_order = reverseList(carbon_occupant.bodyparts) //Chest and head are first in bodyparts, so we invert it to make them suffer more
+ operation_order = reverse_range(carbon_occupant.get_bodyparts()) //Chest and head are first in bodyparts, so we invert it to make them suffer more
warming_up = TRUE
harvesting = TRUE
visible_message(span_notice("\The [src] begins warming up!"))
diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm
index 58e5bdf209e3..7d553e47b2d6 100644
--- a/code/game/machinery/lightswitch.dm
+++ b/code/game/machinery/lightswitch.dm
@@ -135,8 +135,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/light_switch, 26)
var/obj/machinery/light_switch/attached_switch
/obj/item/circuit_component/light_switch/populate_ports()
- on_setting = add_input_port("On", PORT_TYPE_NUMBER)
- is_on = add_output_port("Is On", PORT_TYPE_NUMBER)
+ on_setting = add_input_port("On", PORT_TYPE_BOOLEAN)
+ is_on = add_output_port("Is On", PORT_TYPE_BOOLEAN)
/obj/item/circuit_component/light_switch/register_usb_parent(atom/movable/parent)
. = ..()
diff --git a/code/game/machinery/machine_frame.dm b/code/game/machinery/machine_frame.dm
index e7be3814a18d..0f88210348c7 100644
--- a/code/game/machinery/machine_frame.dm
+++ b/code/game/machinery/machine_frame.dm
@@ -445,6 +445,9 @@
user.balloon_alert(user, "missing components!")
return FALSE
+ if(!circuit.completion_requirements(src))
+ return FALSE
+
tool.play_tool_sound(src)
// Prevent us from dropping stuff thanks to /Exited
var/obj/item/circuitboard/machine/leaving_circuit = circuit
diff --git a/code/game/objects/effects/decals/cleanable/blood.dm b/code/game/objects/effects/decals/cleanable/blood.dm
index c6b342cb7bc5..7dcef673bd9d 100644
--- a/code/game/objects/effects/decals/cleanable/blood.dm
+++ b/code/game/objects/effects/decals/cleanable/blood.dm
@@ -797,6 +797,8 @@
base_suffix = "splatter"
can_dry = FALSE // No point
+ mergeable_decal = FALSE
+
/// The turf we just came from, so we can back up when we hit a wall
var/turf/prev_loc
/// Skip making the final blood splatter when we're done, like if we're not in a turf
diff --git a/code/game/objects/effects/decals/turfdecal/weakpoint.dm b/code/game/objects/effects/decals/turfdecal/weakpoint.dm
index 47749187b0f7..915d948e6e24 100644
--- a/code/game/objects/effects/decals/turfdecal/weakpoint.dm
+++ b/code/game/objects/effects/decals/turfdecal/weakpoint.dm
@@ -34,7 +34,7 @@
/turf/open/misc/snow,
))
if(severity < required_strength)
- balloon_alert_to_viewers("crack!")
+ balloon_alert_to_hearers("*crack*")
playsound(source = src, soundin = SFX_HULL_CREAKING, vol = 50, vary = TRUE, pressure_affected = FALSE, ignore_walls = TRUE)
return //return ominous sounds when we're under the threshold.
diff --git a/code/game/objects/effects/effect_system/effect_system.dm b/code/game/objects/effects/effect_system/effect_system.dm
index c91b2168454c..5bd2f3267ca0 100644
--- a/code/game/objects/effects/effect_system/effect_system.dm
+++ b/code/game/objects/effects/effect_system/effect_system.dm
@@ -57,6 +57,16 @@
var/total_effects = 0
/// Should the system delete itself after finishing?
var/autocleanup = FALSE
+ /// Should the system delete effects that stop moving?
+ var/delete_on_stop = FALSE
+ /// How frequently (in deciseconds) should we move our particles?
+ var/step_delay = 0.5 SECONDS
+
+ // Internal use
+ /// The length of the previous assigned moveloop in deciseconds
+ var/last_loop_length = 0
+ /// List of dirs avalible to pick, used to avoid accidential duplicates
+ var/list/pickable_dirs = list()
/datum/effect_system/basic/New(turf/location, amount = null, cardinals_only = null)
. = ..()
@@ -73,25 +83,48 @@
return
generate_effect()
+/// Returns how many steps to attempt to move a generated effect
+/datum/effect_system/basic/proc/get_step_count()
+ return rand(1, 3)
+
+/// Generates a effect for our system to control, returns the generated effect
/datum/effect_system/basic/proc/generate_effect()
if(holder)
location = get_turf(holder)
var/obj/effect/effect = new effect_type(location)
total_effects++
- var/direction
- if(cardinals_only)
- direction = pick(GLOB.cardinals)
- else
- direction = pick(GLOB.alldirs)
-
- var/step_amt = rand(1, 3)
- var/step_delay = 5
- var/datum/move_loop/loop = GLOB.move_manager.move(effect, direction, step_delay, timeout = step_delay * step_amt, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
- RegisterSignal(loop, COMSIG_QDELETING, PROC_REF(decrement_total_effect))
-
-/datum/effect_system/basic/proc/decrement_total_effect(datum/source)
+
+ if(!length(pickable_dirs))
+ if(cardinals_only)
+ pickable_dirs = GLOB.cardinals.Copy()
+ else
+ pickable_dirs = GLOB.alldirs.Copy()
+ // Try not to reuse dirs if possible to avoid weird stacking
+ var/direction = pick_n_take(pickable_dirs)
+
+ var/step_count = get_step_count()
+ var/datum/move_loop/loop = GLOB.move_manager.move(effect, direction, step_delay, timeout = step_delay * step_count, priority = MOVEMENT_ABOVE_SPACE_PRIORITY, flags = MOVEMENT_LOOP_START_FAST)
+ RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_move))
+ RegisterSignal(loop, COMSIG_QDELETING, PROC_REF(loop_end))
+ last_loop_length = loop.lifetime
+ return effect
+
+/datum/effect_system/basic/proc/post_move(datum/move_loop/source, result, visual_delay)
+ SIGNAL_HANDLER
+ if(result == MOVELOOP_FAILURE)
+ move_failed(source, source.moving)
+
+/// Allows us to hook into being unable to automatically move
+/datum/effect_system/basic/proc/move_failed(datum/move_loop/loop, obj/effect/failed)
+ if(QDELETED(failed) || !delete_on_stop)
+ return
+ qdel(failed)
+
+/datum/effect_system/basic/proc/loop_end(datum/move_loop/source)
SIGNAL_HANDLER
total_effects--
+ if(delete_on_stop && !QDELETED(source.moving))
+ qdel(source.moving)
if(autocleanup && total_effects == 0)
QDEL_IN(src, 2 SECONDS)
diff --git a/code/game/objects/effects/effect_system/effects_explosion.dm b/code/game/objects/effects/effect_system/effects_explosion.dm
index 9f4db05d967b..5e9ff4e1ade6 100644
--- a/code/game/objects/effects/effect_system/effects_explosion.dm
+++ b/code/game/objects/effects/effect_system/effects_explosion.dm
@@ -4,14 +4,6 @@
opacity = TRUE
anchored = TRUE
-/obj/effect/particle_effect/expl_particles/Initialize(mapload)
- ..()
- return INITIALIZE_HINT_LATELOAD
-
-/obj/effect/particle_effect/expl_particles/LateInitialize()
- var/step_amt = pick(25;1, 50;2, 100;3, 200;4)
- var/datum/move_loop/loop = GLOB.move_manager.move(src, pick(GLOB.alldirs), 1, timeout = step_amt, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
- RegisterSignal(loop, COMSIG_QDELETING, PROC_REF(end_particle))
/obj/effect/particle_effect/expl_particles/proc/end_particle(datum/source)
SIGNAL_HANDLER
@@ -19,10 +11,20 @@
qdel(src)
/datum/effect_system/basic/expl_particles
+ effect_type = /obj/effect/particle_effect/expl_particles
amount = 10
+ step_delay = 0.1 SECONDS
+ delete_on_stop = TRUE
-/datum/effect_system/basic/expl_particles/generate_effect()
- new /obj/effect/particle_effect/expl_particles(location)
+/datum/effect_system/basic/expl_particles/get_step_count()
+ return pick(25;1, 50;2, 100;3, 200;4)
+
+/datum/effect_system/basic/expl_particles/loop_end(datum/move_loop/source)
+ . = ..()
+ var/obj/effect/explosion_particle = source.moving
+ if(QDELETED(explosion_particle))
+ return
+ qdel(explosion_particle)
/obj/effect/explosion
name = "fire"
diff --git a/code/game/objects/effects/effect_system/effects_sparks.dm b/code/game/objects/effects/effect_system/effects_sparks.dm
index 8a40659a003a..aaf1efa3fbab 100644
--- a/code/game/objects/effects/effect_system/effects_sparks.dm
+++ b/code/game/objects/effects/effect_system/effects_sparks.dm
@@ -18,11 +18,20 @@
anchored = TRUE
light_system = OVERLAY_LIGHT
light_range = 1.5
- light_power = 0.8
+ light_power = 2
light_color = LIGHT_COLOR_FIRE
+ /// Should this spark's effect be animated
+ var/animated = TRUE
+ /// Timer id for the timer that will wipe us out
+ var/delete_timer_id = TIMER_ID_NULL
+ /// Middleman object we're using to animate our light
+ var/datum/light_middleman/middleman
/obj/effect/particle_effect/sparks/Initialize(mapload)
..()
+ if(animated)
+ middleman = new(src, "sparks")
+ middleman.being_overriding_light()
return INITIALIZE_HINT_LATELOAD
/obj/effect/particle_effect/sparks/LateInitialize()
@@ -32,7 +41,29 @@
var/turf/location = loc
if(isturf(location))
affect_location(location, just_initialized = TRUE)
- QDEL_IN(src, 2 SECONDS)
+ decay_in(2 SECONDS)
+
+/obj/effect/particle_effect/sparks/Destroy()
+ if(!isnull(middleman))
+ QDEL_NULL(middleman)
+ return ..()
+
+/// Sets up our death effects given the passed in duration
+/obj/effect/particle_effect/sparks/proc/decay_in(decay_time)
+ if(delete_timer_id != TIMER_ID_NULL)
+ deltimer(delete_timer_id)
+ delete_timer_id = QDEL_IN_STOPPABLE(src, decay_time + world.tick_lag)
+ if(!animated)
+ return
+ var/obj/effect/abstract/main_light = middleman.primary_intercept
+ // We're going to fade our light out so it's less jarring when we fully disappear
+ // Note, a refresh of the overlay light would break this, we're basically just sorta assuming that won't happen
+ // Would need to track time and sort of "replay" where we should be otherwise
+ if(decay_time >= 0.7 SECONDS) // duration of all animated spark's actual icon state animation
+ animate(main_light, alpha = 220, time = 0.4 SECONDS)
+ animate(alpha = 0, time = decay_time - 0.4 SECONDS, easing = CIRCULAR_EASING | EASE_IN)
+ else
+ animate(main_light, alpha = 0, time = decay_time)
/obj/effect/particle_effect/sparks/Destroy()
var/turf/location = loc
@@ -91,6 +122,20 @@
/datum/effect_system/basic/spark_spread
effect_type = /obj/effect/particle_effect/sparks
+ step_delay = 0.35 SECONDS // chosen so we will always take at least the duration of our animation to finish
+
+/datum/effect_system/basic/spark_spread/generate_effect()
+ var/obj/effect/particle_effect/sparks/spark = ..()
+ spark.decay_in(last_loop_length)
+
+/datum/effect_system/basic/spark_spread/get_step_count()
+ return rand(2, 3) // never 1 cause 1 looks dumb
+
+/datum/effect_system/basic/spark_spread/move_failed(datum/move_loop/loop, obj/effect/failed)
+ if(QDELETED(failed))
+ return
+ var/obj/effect/particle_effect/sparks/spark = failed
+ spark.decay_in(0.1 SECONDS)
/datum/effect_system/basic/spark_spread/quantum
effect_type = /obj/effect/particle_effect/sparks/quantum
@@ -100,6 +145,8 @@
/obj/effect/particle_effect/sparks/electricity
name = "lightning"
icon_state = "electricity"
+ animated = FALSE
/datum/effect_system/basic/lightning_spread
+ delete_on_stop = TRUE
effect_type = /obj/effect/particle_effect/sparks/electricity
diff --git a/code/game/objects/effects/effect_system/effects_water.dm b/code/game/objects/effects/effect_system/effects_water.dm
index f8f62c13b1b8..cbe4472614fb 100644
--- a/code/game/objects/effects/effect_system/effects_water.dm
+++ b/code/game/objects/effects/effect_system/effects_water.dm
@@ -39,7 +39,7 @@
/// Starts the effect moving at a target with a delay in deciseconds, and a lifetime in moves
/// Returns the created loop
/obj/effect/particle_effect/water/extinguisher/proc/move_at(atom/target, delay, lifetime)
- var/datum/move_loop/loop = GLOB.move_manager.move_towards_legacy(src, target, delay, timeout = delay * lifetime, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
+ var/datum/move_loop/loop = GLOB.move_manager.move_towards_legacy(src, target, delay, timeout = delay * lifetime, priority = MOVEMENT_ABOVE_SPACE_PRIORITY, flags = MOVEMENT_LOOP_START_FAST)
RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_forcemove))
RegisterSignal(loop, COMSIG_QDELETING, PROC_REF(movement_stopped))
return loop
@@ -88,4 +88,5 @@
QDEL_IN(src, 2 SECONDS)
/datum/effect_system/basic/steam_spread
+ delete_on_stop = TRUE
effect_type = /obj/effect/particle_effect/steam
diff --git a/code/game/objects/effects/spiderwebs.dm b/code/game/objects/effects/spiderwebs.dm
index d47c00590003..b8317dd62591 100644
--- a/code/game/objects/effects/spiderwebs.dm
+++ b/code/game/objects/effects/spiderwebs.dm
@@ -41,8 +41,6 @@
smoothing_flags = SMOOTH_BITMASK
smoothing_groups = SMOOTH_GROUP_SPIDER_WEB
canSmoothWith = SMOOTH_GROUP_SPIDER_WEB + SMOOTH_GROUP_WALLS
- ///Whether or not the web is from the genetics power
- var/genetic = FALSE
///Whether or not the web is a sealed web
var/sealed = FALSE
///Do we need to offset this based on a sprite frill?
@@ -57,6 +55,11 @@
if (has_frill)
pixel_x = -9
pixel_y = -9
+
+ var/static/list/loc_connections = list(
+ COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
+ )
+ AddElement(/datum/element/connect_loc, loc_connections)
return ..()
/obj/structure/spider/stickyweb/attack_hand(mob/user, list/modifiers)
@@ -75,31 +78,42 @@
/obj/structure/spider/stickyweb/CanAllowThrough(atom/movable/mover, border_dir)
. = ..()
- if(genetic)
- return
if(sealed)
return FALSE
- if(isliving(mover))
- if(HAS_TRAIT(mover, TRAIT_WEB_SURFER))
- return TRUE
- if(mover.pulledby && HAS_TRAIT(mover.pulledby, TRAIT_WEB_SURFER))
- return TRUE
- if(prob(stuck_chance))
- stuck_react(mover)
- return FALSE
- return .
if(isprojectile(mover))
return prob(projectile_stuck_chance)
return .
-/// Show some feedback when you can't pass through something
-/obj/structure/spider/stickyweb/proc/stuck_react(atom/movable/stuck_guy)
- loc.balloon_alert(stuck_guy, "stuck in web!")
- stuck_guy.Shake(duration = 0.1 SECONDS)
+/obj/structure/spider/stickyweb/proc/is_whitelisted(mob/candidate)
+ return HAS_TRAIT(candidate, TRAIT_WEB_SURFER)
+
+/obj/structure/spider/stickyweb/proc/on_entered(datum/source, atom/movable/victim, old_loc)
+ SIGNAL_HANDLER
+
+ if(!isliving(victim))
+ return
+ if(is_whitelisted(victim) || victim.pulledby && is_whitelisted(victim.pulledby))
+ return
+ if(prob(stuck_chance))
+ stuck_react(victim)
+
+/// Drains stamina and shows feedback when you get stuck moving thru a web
+/obj/structure/spider/stickyweb/proc/stuck_react(mob/living/victim)
+ if(victim.get_stamina_loss() > 90)
+ if(victim.body_position != LYING_DOWN)
+ to_chat(victim, span_warning("You trip over \the [src] due to exhaustion!"))
+
+ victim.SetKnockdown(3 SECONDS)
+ return
+
+ if(prob(25))
+ loc.balloon_alert(victim, "stuck in web!")
+ victim.Shake(duration = 0.2 SECONDS)
+
+ victim.adjust_stamina_loss(rand(10, 15))
/// Web made by geneticists, needs special handling to allow them to pass through their own webs
/obj/structure/spider/stickyweb/genetic
- genetic = TRUE
desc = "It's stringy, sticky, and came out of your coworker."
/// Mob with special permission to cross this web
var/mob/living/allowed_mob
@@ -113,19 +127,8 @@
allowed_mob = allowedmob
return ..()
-/obj/structure/spider/stickyweb/genetic/CanAllowThrough(atom/movable/mover, border_dir)
- . = ..()
- if(mover == allowed_mob)
- return TRUE
- else if(isliving(mover)) //we change the spider to not be able to go through here
- if(mover.pulledby == allowed_mob)
- return TRUE
- if(prob(50))
- stuck_react(mover)
- return FALSE
- else if(isprojectile(mover))
- return prob(30)
- return .
+/obj/structure/spider/stickyweb/genetic/is_whitelisted(mob/candidate)
+ return candidate == allowed_mob
/// Web with a 100% chance to intercept movement
/obj/structure/spider/stickyweb/very_sticky
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index a28ee6c97f67..0296b4f896e5 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -1538,6 +1538,7 @@
if(!istype(loc, /turf))
return
source = loc
+ SEND_SIGNAL(src, COMSIG_ITEM_BEFORE_PICKUP_ANIMATION)
var/image/pickup_animation = image(icon = src)
SET_PLANE(pickup_animation, GAME_PLANE, source)
pickup_animation.transform.Scale(0.75)
@@ -1574,6 +1575,7 @@
if(!istype(moving_from))
return
+ SEND_SIGNAL(src, COMSIG_ITEM_BEFORE_DROP_ANIMATION)
var/turf/current_turf = get_turf(src)
var/direction = get_dir(moving_from, current_turf)
var/from_x = moving_from.base_pixel_x
@@ -2240,7 +2242,7 @@
return FALSE
if (!istype(target_limb))
- target_limb = victim.get_bodypart(target_limb) || victim.bodyparts[1]
+ target_limb = victim.get_bodypart(target_limb) || victim.get_bodypart()
return get_embed()?.embed_into(victim, target_limb)
diff --git a/code/game/objects/items/circuitboards/circuitboard.dm b/code/game/objects/items/circuitboards/circuitboard.dm
index df4d12233213..40e85008cd40 100644
--- a/code/game/objects/items/circuitboards/circuitboard.dm
+++ b/code/game/objects/items/circuitboards/circuitboard.dm
@@ -64,6 +64,14 @@
/obj/item/circuitboard/proc/configure_machine(obj/machinery/machine)
return
+/**
+ * This proc is called during /obj/structure/frame/machine/finalize_construction in case there's anything else that needs to be met before completion.
+ * Arguments:
+ * * install_frame - The frame the circuit has been installed into for reference.
+ */
+/obj/item/circuitboard/proc/completion_requirements(obj/structure/frame/install_frame)
+ return TRUE
+
// Circuitboard/machine
/*Common Parts: Parts List: Ignitor, Timer, Infra-red laser, Infra-red sensor, t_scanner, Capacitor, Valve, sensor unit,
micro-manipulator, console screen, beaker, Microlaser, matter bin, power cells.
diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
index 06a363a1b77f..9fb7633daf4a 100644
--- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
@@ -36,6 +36,18 @@
/datum/stock_part/servo/tier3 = 5,
/obj/item/stack/cable_coil = 2)
+/obj/item/circuitboard/machine/dna_vault/completion_requirements(obj/structure/frame/install_frame)
+ var/turf/center = get_turf(install_frame)
+ var/blocked = FALSE
+ for(var/turf/potential_turf as anything in CORNER_BLOCK_OFFSET(center, 3, 3, -1, -2))
+ if(potential_turf.density)
+ new /obj/effect/temp_visual/point(potential_turf)
+ blocked = TRUE
+ if(blocked)
+ balloon_alert_to_viewers("no room! (3x3)")
+ return FALSE
+ return TRUE
+
//Engineering
/obj/item/circuitboard/machine/announcement_system
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index 9737e7ba1f54..9655aed9cf65 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -978,7 +978,7 @@
return ITEM_INTERACT_BLOCKING
var/obj/machinery/atmospherics/target_pipe = target
target_pipe.paint(paint_color)
- balloon_alert(user, "painted in [GLOB.pipe_color_name[paint_color]] color")
+ balloon_alert(user, "painted in [GLOB.pipe_color_name[paint_color]] color")
else if (is_type_in_typecache(target, direct_color_types))
target.add_atom_colour(paint_color, WASHABLE_COLOUR_PRIORITY)
else
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 8bc39d0f1443..9b208798f60b 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -468,9 +468,15 @@
/datum/material/plasma = SMALL_MATERIAL_AMOUNT * 0.5,
/datum/material/plastic = SMALL_MATERIAL_AMOUNT * 0.5,
)
+ /// Lighting middleman, lets us do a flicker effect
+ var/datum/light_middleman/middleman
/obj/item/flashlight/flare/Initialize(mapload)
. = ..()
+ if(IS_OVERLAY_LIGHT_SYSTEM(light_system))
+ middleman = new(src, "flashlight")
+ RegisterSignal(middleman, COMSIG_LIGHT_MIDDLEMAN_UPDATED, PROC_REF(light_updated))
+ middleman.being_overriding_light()
if(randomize_fuel)
fuel = rand(10 MINUTES, 15 MINUTES)
if(light_on)
@@ -489,6 +495,8 @@
/obj/item/flashlight/flare/Destroy()
STOP_PROCESSING(SSobj, src)
+ if(middleman)
+ QDEL_NULL(middleman)
return ..()
/obj/item/flashlight/flare/afterattack(atom/target, mob/user, click_parameters)
@@ -522,6 +530,10 @@
damtype = initial(damtype)
update_brightness()
+/obj/item/flashlight/flare/proc/light_updated(datum/source)
+ SIGNAL_HANDLER
+ fire_flicker_middleman(middleman)
+
/obj/item/flashlight/flare/extinguish()
. = ..()
if((fuel != INFINITY) && can_be_extinguished)
diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm
index e4487f621928..01dfb14d8910 100644
--- a/code/game/objects/items/devices/scanners/health_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/health_analyzer.dm
@@ -219,7 +219,7 @@
if(iscarbon(target))
var/mob/living/carbon/carbontarget = target
var/any_damage = brute_loss > 0 || fire_loss > 0 || oxy_loss > 0 || tox_loss > 0 || fire_loss > 0
- var/any_missing = length(carbontarget.bodyparts) < (carbontarget.dna?.species?.max_bodypart_count || 6)
+ var/any_missing = length(carbontarget.get_missing_limbs())
var/any_wounded = length(carbontarget.all_wounds)
var/any_embeds = carbontarget.has_embedded_objects()
if(any_damage || (mode == SCANNER_VERBOSE && (any_missing || any_wounded || any_embeds)))
diff --git a/code/game/objects/items/flatpacks.dm b/code/game/objects/items/flatpacks.dm
index b2a49a517875..090c09db4f90 100644
--- a/code/game/objects/items/flatpacks.dm
+++ b/code/game/objects/items/flatpacks.dm
@@ -172,6 +172,11 @@
board = /obj/item/circuitboard/machine/flatpacker
custom_premium_price = PAYCHECK_COMMAND
+/obj/item/flatpack/shuttle_engine
+ name = "shuttle propulsion engine"
+ board = /obj/item/circuitboard/machine/engine/propulsion
+ custom_premium_price = PAYCHECK_CREW * 2
+
// Cargo flatpacks
/obj/item/flatpack/mailsorter // to have a roundstart mail sorter at cargo
diff --git a/code/game/objects/items/food/salad.dm b/code/game/objects/items/food/salad.dm
index c95a284e8f55..c350c0bd4a59 100644
--- a/code/game/objects/items/food/salad.dm
+++ b/code/game/objects/items/food/salad.dm
@@ -192,10 +192,11 @@
volume = SOUP_SERVING_SIZE + 5
gulp_size = 3
+ loop_drink = TRUE
+
/obj/item/reagent_containers/cup/bowl/Initialize(mapload)
. = ..()
RegisterSignal(src, COMSIG_ATOM_REAGENT_EXAMINE, PROC_REF(reagent_special_examine))
- AddElement(/datum/element/foodlike_drink)
AddComponent(/datum/component/ingredients_holder, /obj/item/food/salad/empty, CUSTOM_INGREDIENT_ICON_FILL, max_ingredients = 6)
AddComponent( \
/datum/component/takes_reagent_appearance, \
diff --git a/code/game/objects/items/gift.dm b/code/game/objects/items/gift.dm
index 03fefc39ee18..414e813b49cd 100644
--- a/code/game/objects/items/gift.dm
+++ b/code/game/objects/items/gift.dm
@@ -49,6 +49,7 @@
user.investigate_log("has unwrapped a present containing [thing.type].", INVESTIGATE_PRESENTS)
user.put_in_hands(thing)
thing.add_fingerprint(user)
+ SEND_SIGNAL(thing, COMSIG_ITEM_OPENED_FROM_GIFT, user)
qdel(src)
diff --git a/code/game/objects/items/lighter.dm b/code/game/objects/items/lighter.dm
index cd85b2d6d0fb..222a4a42fe75 100644
--- a/code/game/objects/items/lighter.dm
+++ b/code/game/objects/items/lighter.dm
@@ -38,9 +38,15 @@
)
/// Whether the lighter starts with fuel
var/spawns_with_reagent = TRUE
+ /// Lighting middleman, lets us do a flicker effect
+ var/datum/light_middleman/middleman
/obj/item/lighter/Initialize(mapload)
. = ..()
+ if(IS_OVERLAY_LIGHT_SYSTEM(light_system))
+ middleman = new(src, "flashlight")
+ RegisterSignal(middleman, COMSIG_LIGHT_MIDDLEMAN_UPDATED, PROC_REF(light_updated))
+ middleman.being_overriding_light()
create_reagents(maximum_fuel, REFILLABLE | DRAINABLE)
if(spawns_with_reagent)
reagents.add_reagent(/datum/reagent/fuel, maximum_fuel)
@@ -54,6 +60,11 @@
)
update_appearance()
+/obj/item/lighter/Destroy(force)
+ if(!isnull(middleman))
+ QDEL_NULL(middleman)
+ return ..()
+
/obj/item/lighter/grind_results()
return list(/datum/reagent/iron = 1, /datum/reagent/fuel = 5, /datum/reagent/fuel/oil = 5)
@@ -64,6 +75,10 @@
else
. += span_notice("It contains [get_fuel()] units of fuel out of [maximum_fuel].")
+/obj/item/lighter/proc/light_updated(datum/source)
+ SIGNAL_HANDLER
+ fire_flicker_middleman(middleman)
+
/// Destroy the lighter when it's shot by a bullet
/obj/item/lighter/proc/on_intercepted_bullet(mob/living/victim, obj/projectile/bullet)
victim.visible_message(span_warning("\The [bullet] shatters on [victim]'s lighter!"))
diff --git a/code/game/objects/items/religion.dm b/code/game/objects/items/religion.dm
index 7ccf96f158c5..0e96116ed3d1 100644
--- a/code/game/objects/items/religion.dm
+++ b/code/game/objects/items/religion.dm
@@ -409,6 +409,7 @@
icon = 'icons/obj/weapons/sword.dmi'
icon_state = "claymore_old"
worn_icon = 'icons/mob/clothing/back.dmi'
+ worn_icon_state = "claymore"
force = 30
armour_penetration = 15
diff --git a/code/game/objects/items/stacks/golem_food/golem_status_effects.dm b/code/game/objects/items/stacks/golem_food/golem_status_effects.dm
index ab8e3796f22c..fe44c1eef416 100644
--- a/code/game/objects/items/stacks/golem_food/golem_status_effects.dm
+++ b/code/game/objects/items/stacks/golem_food/golem_status_effects.dm
@@ -78,7 +78,7 @@
if(isgolem(owner))
var/mob/living/carbon/golem_owner = owner
- for (var/obj/item/bodypart/part in golem_owner.bodyparts)
+ for (var/obj/item/bodypart/part in golem_owner.get_bodyparts())
// these overlays won't look good on anything but golem limbs
if (part.limb_id != SPECIES_GOLEM)
continue
@@ -307,7 +307,7 @@
owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/light_speed)
var/mob/living/carbon/carbon_owner = owner
- for (var/obj/item/bodypart/arm/arm in carbon_owner.bodyparts)
+ for (var/obj/item/bodypart/arm/arm in carbon_owner.get_bodyparts())
set_arm_fluff(arm)
return TRUE
@@ -388,7 +388,7 @@
var/mob/living/carbon/human/human_owner = owner
RegisterSignal(human_owner, COMSIG_LIVING_UNARMED_ATTACK, PROC_REF(on_punched))
human_owner.physiology.brute_mod *= brute_modifier
- for (var/obj/item/bodypart/arm/arm in human_owner.bodyparts)
+ for (var/obj/item/bodypart/arm/arm in human_owner.get_bodyparts())
buff_arm(arm)
/// Give mining mobs an extra slap
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index 5f1fcdb4f185..6ec4d1b37ce9 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -230,7 +230,7 @@
PRIVATE_PROC(TRUE)
var/list/other_affected_limbs = list()
- for(var/obj/item/bodypart/limb as anything in patient.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in patient.get_bodyparts())
if(!try_heal_checks(patient, user, limb.body_zone, silent = TRUE))
continue
other_affected_limbs += limb.body_zone
@@ -783,7 +783,7 @@
return BRUTELOSS
patient.emote("scream")
- for(var/obj/item/bodypart/bone as anything in patient.bodyparts)
+ for(var/obj/item/bodypart/bone as anything in patient.get_bodyparts())
// fine to just, use these raw, its a meme anyway
var/datum/wound/blunt/bone/severe/oof_ouch = new
oof_ouch.apply_wound(bone, wound_source = "bone gel")
diff --git a/code/game/objects/items/stacks/wrap.dm b/code/game/objects/items/stacks/wrap.dm
index 432e040940b5..47f58f11030d 100644
--- a/code/game/objects/items/stacks/wrap.dm
+++ b/code/game/objects/items/stacks/wrap.dm
@@ -219,6 +219,9 @@
amount = 5
merge_type = /obj/item/stack/package_wrap/small
+/obj/item/stack/package_wrap/one
+ amount = 1
+
/obj/item/c_tube
name = "cardboard tube"
desc = "A tube... of cardboard."
diff --git a/code/game/objects/items/tools/engineering/weldingtool.dm b/code/game/objects/items/tools/engineering/weldingtool.dm
index 0d9dded1c2f8..0c33f8da7e1a 100644
--- a/code/game/objects/items/tools/engineering/weldingtool.dm
+++ b/code/game/objects/items/tools/engineering/weldingtool.dm
@@ -48,6 +48,8 @@
var/activation_sound = 'sound/items/tools/welderactivate.ogg'
var/deactivation_sound = 'sound/items/tools/welderdeactivate.ogg'
+ /// Lighting middleman, lets us do a flicker effect
+ var/datum/light_middleman/middleman
custom_price = 20 // DARKPACK EDIT ADD - ECONOMY
/datum/armor/item_weldingtool
@@ -56,6 +58,10 @@
/obj/item/weldingtool/Initialize(mapload)
. = ..()
+ if(IS_OVERLAY_LIGHT_SYSTEM(light_system))
+ middleman = new(src, "flashlight")
+ RegisterSignal(middleman, COMSIG_LIGHT_MIDDLEMAN_UPDATED, PROC_REF(light_updated))
+ middleman.being_overriding_light()
AddElement(/datum/element/update_icon_updates_onmob)
AddElement(/datum/element/tool_flash, light_range)
AddElement(/datum/element/falling_hazard, damage = force, wound_bonus = wound_bonus, hardhat_safety = TRUE, crushes = FALSE, impact_sound = hitsound)
@@ -65,6 +71,10 @@
reagents.add_reagent(/datum/reagent/fuel, max_fuel)
update_appearance()
+/obj/item/weldingtool/Destroy(force)
+ QDEL_NULL(middleman)
+ return ..()
+
/obj/item/weldingtool/update_icon_state()
if(welding)
inhand_icon_state = "[initial(inhand_icon_state)]1"
@@ -146,6 +156,10 @@
return try_heal_loop(interacting_with, user)
+/obj/item/weldingtool/proc/light_updated(datum/source)
+ SIGNAL_HANDLER
+ fire_flicker_middleman(middleman)
+
/obj/item/weldingtool/proc/try_heal_loop(atom/interacting_with, mob/living/user, repeating = FALSE)
var/mob/living/carbon/human/attacked_humanoid = interacting_with
var/obj/item/bodypart/affecting = attacked_humanoid.get_bodypart(check_zone(user.zone_selected))
diff --git a/code/game/objects/items/tools/extinguisher.dm b/code/game/objects/items/tools/extinguisher.dm
index ff9a9453d124..14bff716ea97 100644
--- a/code/game/objects/items/tools/extinguisher.dm
+++ b/code/game/objects/items/tools/extinguisher.dm
@@ -343,7 +343,7 @@
//Chair movement loop
/obj/item/extinguisher/proc/move_chair(obj/buckled_object, movementdirection)
- var/datum/move_loop/loop = GLOB.move_manager.move(buckled_object, movementdirection, 1, timeout = 9, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
+ var/datum/move_loop/loop = GLOB.move_manager.move(buckled_object, movementdirection, 1, timeout = 9, flags = MOVEMENT_LOOP_START_INSTANT, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
//This means the chair slowing down is dependant on the extinguisher existing, which is weird
//Couldn't figure out a better way though
RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(manage_chair_speed))
diff --git a/code/game/objects/items/weaponry/melee/sabre.dm b/code/game/objects/items/weaponry/melee/sabre.dm
index 770f5b53a9c2..0cc900208005 100644
--- a/code/game/objects/items/weaponry/melee/sabre.dm
+++ b/code/game/objects/items/weaponry/melee/sabre.dm
@@ -81,7 +81,7 @@
var/list/legs = list()
var/obj/item/bodypart/bodypart
- for(bodypart in Cuser.bodyparts)
+ for(bodypart in Cuser.get_bodyparts())
if(bodypart == holding_bodypart)
continue
if(bodypart.body_part & ARMS)
diff --git a/code/game/objects/items/wiki_manuals.dm b/code/game/objects/items/wiki_manuals.dm
index 5c0c08024646..56efbd0669a4 100644
--- a/code/game/objects/items/wiki_manuals.dm
+++ b/code/game/objects/items/wiki_manuals.dm
@@ -213,7 +213,7 @@
var/obj/item/bodypart/head = H.get_bodypart(BODY_ZONE_HEAD)
if(head)
ADD_TRAIT(head, TRAIT_DISFIGURED, TRAIT_GENERIC)
- for(var/obj/item/bodypart/part as anything in H.bodyparts)
+ for(var/obj/item/bodypart/part as anything in H.get_bodyparts())
part.adjustBleedStacks(5)
H.gib_animation()
sleep(0.3 SECONDS)
diff --git a/code/game/objects/structures/life_candle.dm b/code/game/objects/structures/life_candle.dm
index d9eb81c783c3..b21d14619dbc 100644
--- a/code/game/objects/structures/life_candle.dm
+++ b/code/game/objects/structures/life_candle.dm
@@ -3,7 +3,11 @@
desc = "You are dead. Insert quarter to continue."
icon = 'icons/obj/candle.dmi'
icon_state = "candle1"
+ light_system = OVERLAY_LIGHT
light_color = LIGHT_COLOR_FIRE
+ light_power = 1.5
+ light_range = 2
+ light_on = FALSE
var/icon_state_active = "candle1_lit"
var/icon_state_inactive = "candle1"
@@ -23,11 +27,21 @@
// How long until we respawn them after their death.
var/respawn_time = 50
var/respawn_sound = 'sound/effects/magic/staff_animation.ogg'
+ /// Lighting middleman, lets us do a flicker effect
+ var/datum/light_middleman/middleman
/obj/structure/life_candle/Initialize(mapload)
. = ..()
+ if(IS_OVERLAY_LIGHT_SYSTEM(light_system))
+ middleman = new(src, "flashlight")
+ RegisterSignal(middleman, COMSIG_LIGHT_MIDDLEMAN_UPDATED, PROC_REF(light_updated))
+ middleman.being_overriding_light()
AddElement(/datum/element/movetype_handler)
+/obj/structure/life_candle/Destroy(force)
+ QDEL_NULL(middleman)
+ return ..()
+
/obj/structure/life_candle/attack_hand(mob/user, list/modifiers)
. = ..()
if(.)
@@ -48,10 +62,10 @@
update_appearance()
if(linked_minds.len)
START_PROCESSING(SSobj, src)
- set_light(lit_luminosity)
+ set_light_on(TRUE)
else
STOP_PROCESSING(SSobj, src)
- set_light(0)
+ set_light_on(FALSE)
/obj/structure/life_candle/update_icon_state()
icon_state = linked_minds.len ? icon_state_active : icon_state_inactive
@@ -74,6 +88,10 @@
if(!mind.current || (mind.current && mind.current.stat == DEAD))
addtimer(CALLBACK(src, PROC_REF(respawn), mind), respawn_time, TIMER_UNIQUE)
+/obj/structure/life_candle/proc/light_updated(datum/source)
+ SIGNAL_HANDLER
+ fire_flicker_middleman(middleman)
+
/obj/structure/life_candle/proc/respawn(datum/mind/mind)
var/turf/T = get_turf(src)
var/mob/living/body
diff --git a/code/game/objects/structures/mannequin.dm b/code/game/objects/structures/mannequin.dm
index 8fedeebb1143..92973340e0e7 100644
--- a/code/game/objects/structures/mannequin.dm
+++ b/code/game/objects/structures/mannequin.dm
@@ -95,21 +95,18 @@
var/mutable_appearance/pedestal = mutable_appearance(icon, "pedestal_[material]")
pedestal.pixel_z = -3
. += pedestal
- var/datum/sprite_accessory/underwear/underwear = SSaccessories.underwear_list[underwear_name]
- if(underwear)
- if(body_type == FEMALE && underwear.gender == MALE)
- . += mutable_appearance(wear_female_version(underwear.icon_state, underwear.icon, FEMALE_UNIFORM_FULL), layer = -BODY_LAYER)
- else
- . += mutable_appearance(underwear.icon, underwear.icon_state, layer = -BODY_LAYER)
- var/datum/sprite_accessory/undershirt/undershirt = SSaccessories.undershirt_list[undershirt_name]
- if(undershirt)
- if(body_type == FEMALE)
- . += mutable_appearance(wear_female_version(undershirt.icon_state, undershirt.icon), layer = -BODY_LAYER)
- else
- . += mutable_appearance(undershirt.icon, undershirt.icon_state, layer = -BODY_LAYER)
- var/datum/sprite_accessory/socks/socks = SSaccessories.socks_list[socks_name]
- if(socks)
- . += mutable_appearance(socks.icon, socks.icon_state, -BODY_LAYER)
+ var/datum/sprite_accessory/clothing/underwear/underwear = SSaccessories.underwear_list[underwear_name]
+ var/mutable_appearance/underwear_overlay = underwear?.make_appearance(COLOR_WHITE, body_type, BODYSHAPE_HUMANOID)
+ if(underwear_overlay)
+ . += underwear_overlay
+ var/datum/sprite_accessory/clothing/undershirt/undershirt = SSaccessories.undershirt_list[undershirt_name]
+ var/mutable_appearance/undershirt_overlay = undershirt?.make_appearance(COLOR_WHITE, body_type, BODYSHAPE_HUMANOID)
+ if(undershirt_overlay)
+ . += undershirt_overlay
+ var/datum/sprite_accessory/clothing/socks/socks = SSaccessories.socks_list[socks_name]
+ var/mutable_appearance/socks_overlay = socks?.make_appearance(COLOR_WHITE, body_type, BODYSHAPE_HUMANOID)
+ if(socks_overlay)
+ . += socks_overlay
for(var/slot_flag in worn_items)
var/obj/item/worn_item = worn_items[slot_flag]
if(!worn_item)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index e8907126282d..bd9bf786e183 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -913,7 +913,7 @@
can_flip = FALSE
slam_gently = TRUE
/// Mob currently lying on the table
- var/mob/living/carbon/patient = null
+ var/mob/living/patient = null
/// Operating computer we're linked to, to sync operations from
var/obj/machinery/computer/operating/computer = null
/// Tank attached under the table
@@ -1092,7 +1092,8 @@
set_patient(found_replacement)
-/obj/structure/table/optable/proc/set_patient(mob/living/carbon/new_patient)
+/// Updates [var/patient] with out new patient, re-registers surgery related signals, updates UI data for operation computers and vital monitors if those connected.
+/obj/structure/table/optable/proc/set_patient(mob/living/new_patient)
if (patient)
UnregisterSignal(patient, list(
SIGNAL_ADDTRAIT(TRAIT_READY_TO_OPERATE),
@@ -1101,8 +1102,10 @@
COMSIG_ATOM_SURGERY_FINISHED,
COMSIG_LIVING_UPDATING_SURGERY_STATE,
))
- if (patient.external && patient.external == air_tank)
- patient.close_externals()
+
+ var/mob/living/carbon/breather_patient = patient
+ if (iscarbon(breather_patient) && breather_patient.external && breather_patient.external == air_tank)
+ breather_patient.close_externals()
SEND_SIGNAL(src, COMSIG_OPERATING_TABLE_SET_PATIENT, new_patient)
patient = new_patient
@@ -1198,8 +1201,10 @@
balloon_alert(user, "tank detached")
if (air_tank.IsReachableBy(user))
user.put_in_hands(air_tank)
- if (patient?.external && patient.external == air_tank)
- patient.close_externals()
+
+ var/mob/living/carbon/carbon_patient = patient
+ if (iscarbon(carbon_patient) && carbon_patient?.external && carbon_patient.external == air_tank)
+ carbon_patient.close_externals()
air_tank = null
update_appearance()
return ITEM_INTERACT_SUCCESS
@@ -1244,14 +1249,20 @@
return TRUE
/obj/structure/table/optable/mouse_drop_dragged(atom/over, mob/living/user, src_location, over_location, params)
+
if (over != patient || !istype(user) || !IsReachableBy(user) || !user.can_interact_with(src))
return
+ if(!iscarbon(patient))
+ balloon_alert(user, "no internals connector!")
+ return
+
if (!air_tank)
balloon_alert(user, "no tank attached!")
return
- var/internals = patient.can_breathe_internals()
+ var/mob/living/carbon/carbon_patient = patient
+ var/internals = carbon_patient.can_breathe_internals()
if (!internals)
balloon_alert(user, "no internals connector!")
return
@@ -1262,10 +1273,10 @@
if (!do_after(user, 4 SECONDS, patient))
return
- if (!air_tank || patient != over || !patient.can_breathe_internals())
+ if (!air_tank || patient != over || !carbon_patient.can_breathe_internals())
return
- patient.open_internals(air_tank, is_external = TRUE)
+ carbon_patient.open_internals(air_tank, is_external = TRUE)
to_chat(user, span_notice("You connect [src]'s [air_tank] to [patient]'s [internals]."))
to_chat(patient, span_userdanger("[user] connects [src]'s [air_tank] to your [internals]!"))
diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm
index fb8c30c79c21..a931748a577c 100644
--- a/code/game/turfs/open/lava.dm
+++ b/code/game/turfs/open/lava.dm
@@ -353,7 +353,7 @@
*/
/turf/open/lava/proc/drop_contents_into_lava()
SIGNAL_HANDLER
- balloon_alert_to_viewers("[pick("splash","pshhhh","hiss","blorble")]!")
+ balloon_alert_to_hearers("[pick("splash","pshhhh","hiss","blorble")]!")
playsound(src, 'sound/items/match_strike.ogg', 15, TRUE)
for(var/atom/movable/each_content as anything in contents)
on_atom_inited(src, each_content)
@@ -437,7 +437,7 @@
var/list/immune_parts = list() // Parts we can't transform because they're not organic or can't be dismembered
var/list/transform_parts = list() // Parts we want to transform
- for(var/obj/item/bodypart/burn_limb as anything in burn_human.bodyparts)
+ for(var/obj/item/bodypart/burn_limb as anything in burn_human.get_bodyparts())
if(!IS_ORGANIC_LIMB(burn_limb) || !burn_limb.can_dismember())
immune_parts += burn_limb
continue
diff --git a/code/game/world.dm b/code/game/world.dm
index e2370fedb1fa..a243dd09e383 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -276,6 +276,12 @@ GLOBAL_VAR(restart_counter)
world.log = file("[GLOB.log_directory]/dd.log") //not all runtimes trigger world/Error, so this is the only way to ensure we can see all of them.
#endif
+/// The world.time we last ran maptick, used for stupid reasons
+GLOBAL_VAR_INIT(last_maptick_time, 0)
+/world/Tick()
+ // We need a hook for if maptick has happen yet
+ GLOB.last_maptick_time = world.time
+
/world/Topic(T, addr, master, key)
TGS_TOPIC //redirect to server tools if necessary
diff --git a/code/modules/admin/smites/berforate.dm b/code/modules/admin/smites/berforate.dm
index 6b5fffa38e62..8bd027eb8bc1 100644
--- a/code/modules/admin/smites/berforate.dm
+++ b/code/modules/admin/smites/berforate.dm
@@ -41,7 +41,7 @@
dude.Immobilize(5 SECONDS)
for (var/wound_bonus_rep in 1 to repetitions)
- for (var/_limb in dude.bodyparts)
+ for (var/_limb in dude.get_bodyparts())
var/obj/item/bodypart/limb = _limb
var/shots_this_limb = 0
for (var/_iter_turf in shuffle(open_adj_turfs))
diff --git a/code/modules/admin/smites/bloodless.dm b/code/modules/admin/smites/bloodless.dm
index c970e920f225..df43ed2048af 100644
--- a/code/modules/admin/smites/bloodless.dm
+++ b/code/modules/admin/smites/bloodless.dm
@@ -8,7 +8,7 @@
to_chat(user, span_warning("This must be used on a carbon mob."), confidential = TRUE)
return
var/mob/living/carbon/carbon_target = target
- for(var/_limb in carbon_target.bodyparts)
+ for(var/_limb in carbon_target.get_bodyparts())
var/obj/item/bodypart/limb = _limb // fine to use this raw, its a meme smite
var/type_wound = pick(list(/datum/wound/slash/flesh/severe, /datum/wound/slash/flesh/moderate))
limb.force_wound_upwards(type_wound, smited = TRUE)
diff --git a/code/modules/admin/smites/boneless.dm b/code/modules/admin/smites/boneless.dm
index 3fd1a1121f4b..5dd2afd6ef2b 100644
--- a/code/modules/admin/smites/boneless.dm
+++ b/code/modules/admin/smites/boneless.dm
@@ -10,7 +10,7 @@
return
var/mob/living/carbon/carbon_target = target
- for(var/obj/item/bodypart/limb as anything in carbon_target.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in carbon_target.get_bodyparts())
var/severity = pick_weight(alist(
WOUND_SEVERITY_MODERATE = 1,
WOUND_SEVERITY_SEVERE = 2,
diff --git a/code/modules/admin/smites/dock_pay.dm b/code/modules/admin/smites/dock_pay.dm
index 6eaa7b53792f..fc8c03a28a02 100644
--- a/code/modules/admin/smites/dock_pay.dm
+++ b/code/modules/admin/smites/dock_pay.dm
@@ -18,14 +18,14 @@
if (card.registered_account.account_balance == 0)
to_chat(user, span_warning("ID Card lacks any funds. No pay to dock."))
return
- var/new_cost = input("How much pay are we docking? Current balance: [card.registered_account.account_balance] [MONEY_NAME].", "BUDGET CUTS") as num|null
+ var/new_cost = input("How much pay are we docking? Negative = giving money. Current balance: [card.registered_account.account_balance] [MONEY_NAME].", "BUDGET CUTS") as num|null
if (!new_cost)
return
- if (!(card.registered_account.has_money(new_cost)))
- to_chat(user, span_warning("ID Card lacked funds. Emptying account."))
- card.registered_account.bank_card_talk("[new_cost] [MONEY_NAME] deducted from your account based on performance review.")
- card.registered_account.account_balance = 0
+ if(new_cost < 0)
+ card.registered_account.adjust_money(new_cost, "Central Command: Pay Bonus")
+ card.registered_account.bank_card_talk("[new_cost] [MONEY_NAME] added to your account based on performance review by Central Command.", force = TRUE)
else
- card.registered_account.account_balance = card.registered_account.account_balance - new_cost
- card.registered_account.bank_card_talk("[new_cost] [MONEY_NAME] deducted from your account based on performance review.")
+ SSeconomy.add_audit_entry(card.registered_account, new_cost, "Central Command")
+ card.registered_account.adjust_money(-new_cost, "Central Command: Pay Cut")
+ card.registered_account.bank_card_talk("[new_cost] [MONEY_NAME] deducted from your account based on performance review by Central Command.", force = TRUE)
SEND_SOUND(target, 'sound/machines/buzz/buzz-sigh.ogg')
diff --git a/code/modules/admin/smites/nugget.dm b/code/modules/admin/smites/nugget.dm
index 18e5254e615f..9efde93dfeeb 100644
--- a/code/modules/admin/smites/nugget.dm
+++ b/code/modules/admin/smites/nugget.dm
@@ -11,7 +11,7 @@
var/mob/living/carbon/carbon_target = target
var/timer = 2 SECONDS
- for (var/_limb in carbon_target.bodyparts)
+ for (var/_limb in carbon_target.get_bodyparts())
var/obj/item/bodypart/limb = _limb
if (limb.body_part == HEAD || limb.body_part == CHEST)
continue
diff --git a/code/modules/antagonists/abductor/abductor.dm b/code/modules/antagonists/abductor/abductor.dm
index 380eea90b96a..fdbfc81b23cd 100644
--- a/code/modules/antagonists/abductor/abductor.dm
+++ b/code/modules/antagonists/abductor/abductor.dm
@@ -99,7 +99,7 @@
// If we have a team skincolor, apply it here. Applied by admins or 2% chance of natural occurance
if(!isnull(team.team_skincolor))
- for(var/obj/item/bodypart/part as anything in new_abductor.bodyparts)
+ for(var/obj/item/bodypart/part as anything in new_abductor.get_bodyparts())
part.should_draw_greyscale = TRUE
part.add_color_override(team.team_skincolor, LIMB_COLOR_AYYLMAO)
diff --git a/code/modules/antagonists/changeling/changeling.dm b/code/modules/antagonists/changeling/changeling.dm
index b841ec266261..53abe0952e44 100644
--- a/code/modules/antagonists/changeling/changeling.dm
+++ b/code/modules/antagonists/changeling/changeling.dm
@@ -801,7 +801,7 @@
chosen_dna.copy_dna(user.dna, COPY_DNA_SE|COPY_DNA_SPECIES)
- for(var/obj/item/bodypart/limb as anything in user.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in user.get_bodyparts())
limb.update_limb(is_creating = TRUE)
user.updateappearance(mutcolor_update = TRUE)
diff --git a/code/modules/antagonists/changeling/powers/defib_grasp.dm b/code/modules/antagonists/changeling/powers/defib_grasp.dm
index 38c14efdfe7f..5d63b5b2e055 100644
--- a/code/modules/antagonists/changeling/powers/defib_grasp.dm
+++ b/code/modules/antagonists/changeling/powers/defib_grasp.dm
@@ -63,7 +63,7 @@
if(iscarbon(defibber))
var/removed_arms = 0
var/mob/living/carbon/carbon_defibber = defibber
- for(var/obj/item/bodypart/arm/limb in carbon_defibber.bodyparts)
+ for(var/obj/item/bodypart/arm/limb in carbon_defibber.get_bodyparts())
if(limb.dismember(silent = FALSE))
removed_arms++
qdel(limb)
diff --git a/code/modules/antagonists/changeling/powers/headcrab.dm b/code/modules/antagonists/changeling/powers/headcrab.dm
index a0f9040ea1c4..cc1915688ad9 100644
--- a/code/modules/antagonists/changeling/powers/headcrab.dm
+++ b/code/modules/antagonists/changeling/powers/headcrab.dm
@@ -68,6 +68,7 @@
continue
blinded.visible_message(span_danger("[blinded] is splattered with blood!"), span_userdanger("You're splattered with blood!"))
blinded.add_blood_DNA(user_DNA)
+ blinded.add_mood_event("splattered_with_blood", /datum/mood_event/splattered_with_blood)
playsound(blinded, 'sound/effects/splat.ogg', 50, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE)
if(ishuman(blinded))
diff --git a/code/modules/antagonists/cult/cult_armor.dm b/code/modules/antagonists/cult/cult_armor.dm
index d4a62d5fd54b..36ede426e691 100644
--- a/code/modules/antagonists/cult/cult_armor.dm
+++ b/code/modules/antagonists/cult/cult_armor.dm
@@ -151,7 +151,7 @@
return
if(!SPT_PROB(15, seconds_per_tick))
return
- var/obj/item/bodypart/bone_to_wound = pick(wearer.bodyparts)
+ var/obj/item/bodypart/bone_to_wound = pick(wearer.get_bodyparts())
var/wound_type = pick(list(
/datum/wound/blunt/bone/moderate,
/datum/wound/pierce/bleed/moderate,
diff --git a/code/modules/antagonists/heretic/influences.dm b/code/modules/antagonists/heretic/influences.dm
index 71f79a51cbd1..ad7478fc6fd9 100644
--- a/code/modules/antagonists/heretic/influences.dm
+++ b/code/modules/antagonists/heretic/influences.dm
@@ -141,7 +141,7 @@
// A very elaborate way to suicide
visible_message(span_userdanger("Psychic tendrils lash out from [src], psychically grabbing onto [user]'s psychically sensitive mind and tearing [user.p_their()] head off!"))
- var/obj/item/bodypart/head/head = locate() in human_user.bodyparts
+ var/obj/item/bodypart/head/head = human_user.get_bodypart(BODY_ZONE_HEAD)
if(head?.dismember())
head.forceMove(src) // stored for later fishage
else
diff --git a/code/modules/antagonists/heretic/items/heretic_armor.dm b/code/modules/antagonists/heretic/items/heretic_armor.dm
index 4d0dffa781cb..8e496353faed 100644
--- a/code/modules/antagonists/heretic/items/heretic_armor.dm
+++ b/code/modules/antagonists/heretic/items/heretic_armor.dm
@@ -142,7 +142,7 @@
return
var/mob/living/carbon/victim = user
var/iteration = 0
- for(var/obj/item/bodypart/limb as anything in victim.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in victim.get_bodyparts())
if(istype(limb, /obj/item/bodypart/head) || istype(limb, /obj/item/bodypart/chest))
continue
iteration++
@@ -259,7 +259,7 @@
if(!length(valid_turfs))
var/mob/living/carbon/carbon_target = target
if(iscarbon(target))
- var/obj/item/bodypart/limb = pick(carbon_target.bodyparts)
+ var/obj/item/bodypart/limb = pick(carbon_target.get_bodyparts())
limb.force_wound_upwards(/datum/wound/slash/flesh/severe)
return
throw_blade(pick(valid_turfs), target)
@@ -451,7 +451,7 @@
return
var/mob/living/carbon/victim = user
var/iteration = 0
- for(var/obj/item/bodypart/limb as anything in victim.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in victim.get_bodyparts())
iteration++
addtimer(CALLBACK(limb, TYPE_PROC_REF(/obj/item/bodypart, force_wound_upwards), /datum/wound/slash/flesh/critical), 1 SECONDS * iteration)
diff --git a/code/modules/antagonists/heretic/items/heretic_grenade.dm b/code/modules/antagonists/heretic/items/heretic_grenade.dm
index 14ceaf1183e2..3a5e0e2a1293 100644
--- a/code/modules/antagonists/heretic/items/heretic_grenade.dm
+++ b/code/modules/antagonists/heretic/items/heretic_grenade.dm
@@ -95,7 +95,7 @@
addtimer(CALLBACK(victim, TYPE_PROC_REF(/mob, remove_movespeed_modifier), /datum/movespeed_modifier/reagent/pepperspray), 10 SECONDS)
victim.update_damage_hud()
victim.adjust_disgust(5)
- for(var/obj/item/bodypart/robotic_limb in victim.bodyparts)
+ for(var/obj/item/bodypart/robotic_limb in victim.get_bodyparts())
if(robotic_limb.biological_state & BIO_ROBOTIC)
robotic_limb.receive_damage(5, 5)
if(methods & INGEST)
diff --git a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
index cb2ac3d3bde0..f5282015b0a8 100644
--- a/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/flesh_lore.dm
@@ -267,7 +267,7 @@
return
var/mob/living/carbon/carbon_target = target
- var/obj/item/bodypart/bodypart = pick(carbon_target.bodyparts)
+ var/obj/item/bodypart/bodypart = pick(carbon_target.get_bodyparts())
var/datum/wound/crit_wound = new wound_type()
crit_wound.apply_wound(bodypart, attack_direction = get_dir(source, target))
diff --git a/code/modules/antagonists/heretic/knowledge/moon_lore.dm b/code/modules/antagonists/heretic/knowledge/moon_lore.dm
index f3a9a9da7124..eb6428aee276 100644
--- a/code/modules/antagonists/heretic/knowledge/moon_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/moon_lore.dm
@@ -300,7 +300,7 @@
to_chat(carbon_view, span_boldbig(span_red(\
"YOUR SENSES REEL AS YOUR MIND IS ENVELOPED BY AN OTHERWORLDLY FORCE ATTEMPTING TO REWRITE YOUR VERY BEING. \
YOU CANNOT EVEN BEGIN TO SCREAM BEFORE YOUR IMPLANT ACTIVATES ITS PSIONIC FAIL-SAFE PROTOCOL, TAKING YOUR HEAD WITH IT.")))
- var/obj/item/bodypart/head/head = locate() in carbon_view.bodyparts
+ var/obj/item/bodypart/head/head = carbon_view.get_bodypart(BODY_ZONE_HEAD)
if(!head?.dismember())
carbon_view.gib(DROP_ALL_REMAINS)
var/datum/effect_system/reagents_explosion/explosion = new(get_turf(carbon_view), 1, 1, 1)
diff --git a/code/modules/antagonists/heretic/knowledge/rust_lore.dm b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
index 207669735e02..565879a7c8c3 100644
--- a/code/modules/antagonists/heretic/knowledge/rust_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/rust_lore.dm
@@ -81,7 +81,7 @@
if(iscarbon(target))
var/mob/living/carbon/carbon_target = target
- for(var/obj/item/bodypart/robotic_limb as anything in carbon_target.bodyparts)
+ for(var/obj/item/bodypart/robotic_limb as anything in carbon_target.get_bodyparts())
if(IS_ROBOTIC_LIMB(robotic_limb))
robotic_limb.receive_damage(500)
diff --git a/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_two.dm b/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_two.dm
index cac879740de5..5d155281e5eb 100644
--- a/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_two.dm
+++ b/code/modules/antagonists/heretic/knowledge/side_knowledge/tier_two.dm
@@ -29,10 +29,10 @@
/datum/heretic_knowledge/codex_morbus/on_finished_recipe(mob/living/user, list/selected_atoms, turf/loc)
. = ..()
var/mob/living/carbon/human/to_fuck_up = locate() in selected_atoms
- for(var/_limb in to_fuck_up.bodyparts)
+ for(var/_limb in to_fuck_up.get_bodyparts())
var/obj/item/bodypart/limb = _limb
limb.force_wound_upwards(/datum/wound/slash/flesh/critical)
- for(var/obj/item/bodypart/limb as anything in to_fuck_up.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in to_fuck_up.get_bodyparts())
to_fuck_up.cause_wound_of_type_and_severity(WOUND_BLUNT, limb, WOUND_SEVERITY_CRITICAL)
return TRUE
diff --git a/code/modules/antagonists/heretic/knowledge/starting_lore.dm b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
index 830e6edae325..6647ee086bcf 100644
--- a/code/modules/antagonists/heretic/knowledge/starting_lore.dm
+++ b/code/modules/antagonists/heretic/knowledge/starting_lore.dm
@@ -257,7 +257,7 @@ GLOBAL_LIST_INIT(heretic_start_knowledge, initialize_starting_knowledge())
// If it is, we will damage a random bodypart, and check that bodypart for its body type, to select between 'skin' or 'exterior'.
if(iscarbon(body))
var/mob/living/carbon/carbody = body
- var/obj/item/bodypart/bodypart = pick(carbody.bodyparts)
+ var/obj/item/bodypart/bodypart = pick(carbody.get_bodyparts())
ripped_thing = bodypart
carbody.apply_damage(25, BRUTE, bodypart, sharpness = SHARP_EDGED)
diff --git a/code/modules/antagonists/heretic/magic/apetravulnera.dm b/code/modules/antagonists/heretic/magic/apetravulnera.dm
index c0398b2b7458..b878fd36bedb 100644
--- a/code/modules/antagonists/heretic/magic/apetravulnera.dm
+++ b/code/modules/antagonists/heretic/magic/apetravulnera.dm
@@ -38,7 +38,7 @@
return FALSE
var/a_limb_got_damaged = FALSE
- for(var/obj/item/bodypart/bodypart in cast_on.bodyparts)
+ for(var/obj/item/bodypart/bodypart in cast_on.get_bodyparts())
if(bodypart.brute_dam < 15)
continue
a_limb_got_damaged = TRUE
@@ -47,7 +47,7 @@
if(!a_limb_got_damaged)
var/datum/wound/slash/crit_wound = new wound_type()
- crit_wound.apply_wound(pick(cast_on.bodyparts))
+ crit_wound.apply_wound(pick(cast_on.get_bodyparts()))
cast_on.visible_message(
span_danger("[cast_on]'s scratches and bruises are torn open by an unholy force!"),
diff --git a/code/modules/antagonists/heretic/magic/blood_cleave.dm b/code/modules/antagonists/heretic/magic/blood_cleave.dm
index 065286f490e5..e0c9454ba33c 100644
--- a/code/modules/antagonists/heretic/magic/blood_cleave.dm
+++ b/code/modules/antagonists/heretic/magic/blood_cleave.dm
@@ -44,7 +44,7 @@
span_danger("Your veins burst from within and unholy flame erupts from your blood!")
)
- var/obj/item/bodypart/bodypart = pick(victim.bodyparts)
+ var/obj/item/bodypart/bodypart = pick(victim.get_bodyparts())
var/datum/wound/slash/flesh/crit_wound = new wound_type()
crit_wound.apply_wound(bodypart)
victim.apply_damage(20, BURN, wound_bonus = CANT_WOUND)
diff --git a/code/modules/antagonists/heretic/magic/blood_siphon.dm b/code/modules/antagonists/heretic/magic/blood_siphon.dm
index cb0b4ec355c7..3ef5a7920f84 100644
--- a/code/modules/antagonists/heretic/magic/blood_siphon.dm
+++ b/code/modules/antagonists/heretic/magic/blood_siphon.dm
@@ -50,11 +50,11 @@
var/mob/living/carbon/carbon_target = cast_on
var/mob/living/carbon/carbon_user = owner
- for(var/obj/item/bodypart/bodypart as anything in carbon_user.bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in carbon_user.get_bodyparts())
for(var/datum/wound/iter_wound as anything in bodypart.wounds)
if(prob(50))
continue
- var/obj/item/bodypart/target_bodypart = locate(bodypart.type) in carbon_target.bodyparts
+ var/obj/item/bodypart/target_bodypart = carbon_target.get_bodypart(bodypart.body_zone)
if(!target_bodypart)
continue
iter_wound.remove_wound()
diff --git a/code/modules/antagonists/heretic/magic/crimson_cleave.dm b/code/modules/antagonists/heretic/magic/crimson_cleave.dm
index b64a5221a50c..6ee02e2fea9b 100644
--- a/code/modules/antagonists/heretic/magic/crimson_cleave.dm
+++ b/code/modules/antagonists/heretic/magic/crimson_cleave.dm
@@ -32,7 +32,7 @@
. = ..()
if(iscarbon(owner))
var/mob/living/carbon/carbon_owner = owner
- for(var/obj/item/bodypart/limbs as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/limbs as anything in carbon_owner.get_bodyparts())
for(var/datum/wound/iter_wound as anything in limbs.wounds)
iter_wound.remove_wound()
diff --git a/code/modules/antagonists/heretic/status_effects/buffs.dm b/code/modules/antagonists/heretic/status_effects/buffs.dm
index f3660e2f8136..e48020494339 100644
--- a/code/modules/antagonists/heretic/status_effects/buffs.dm
+++ b/code/modules/antagonists/heretic/status_effects/buffs.dm
@@ -87,7 +87,7 @@
if(!iscarbon(owner))
return
var/mob/living/carbon/drinker = owner
- for(var/obj/item/bodypart/potentially_wounded as anything in drinker.bodyparts)
+ for(var/obj/item/bodypart/potentially_wounded as anything in drinker.get_bodyparts())
for(var/datum/wound/found_wound as anything in potentially_wounded.wounds)
found_wound.remove_wound()
if(length(drinker.get_missing_limbs()))
@@ -102,7 +102,7 @@
carbie.adjust_brute_loss(-0.5 * seconds_between_ticks, updating_health = FALSE)
carbie.adjust_fire_loss(-0.5 * seconds_between_ticks, updating_health = FALSE)
- for(var/BP in carbie.bodyparts)
+ for(var/BP in carbie.get_bodyparts())
var/obj/item/bodypart/part = BP
for(var/W in part.wounds)
var/datum/wound/wound = W
diff --git a/code/modules/antagonists/heretic/status_effects/heretic_passive.dm b/code/modules/antagonists/heretic/status_effects/heretic_passive.dm
index 6f5786b809bc..08d2292d677a 100644
--- a/code/modules/antagonists/heretic/status_effects/heretic_passive.dm
+++ b/code/modules/antagonists/heretic/status_effects/heretic_passive.dm
@@ -307,7 +307,7 @@
if(!iscarbon(owner))
return
var/mob/living/carbon/carbon_eater = owner
- for(var/obj/item/bodypart/wounded_limb as anything in carbon_eater.bodyparts)
+ for(var/obj/item/bodypart/wounded_limb as anything in carbon_eater.get_bodyparts())
for(var/datum/wound/to_cure as anything in wounded_limb.wounds)
to_cure.remove_wound()
break
@@ -543,7 +543,7 @@
var/mob/living/carbon/carbon_owner = source
if(passive_level < HERETIC_LEVEL_UPGRADE)
return
- for(var/obj/item/bodypart/wounded_limb as anything in carbon_owner.bodyparts)
+ for(var/obj/item/bodypart/wounded_limb as anything in carbon_owner.get_bodyparts())
for(var/datum/wound/to_cure as anything in wounded_limb.wounds)
to_cure.remove_wound()
for(var/obj/item/organ/internal as anything in carbon_owner.organs)
diff --git a/code/modules/antagonists/heretic/status_effects/mark_effects.dm b/code/modules/antagonists/heretic/status_effects/mark_effects.dm
index 465ad530d24e..18b35b2eedca 100644
--- a/code/modules/antagonists/heretic/status_effects/mark_effects.dm
+++ b/code/modules/antagonists/heretic/status_effects/mark_effects.dm
@@ -64,7 +64,7 @@
/datum/status_effect/eldritch/flesh/on_effect()
if(ishuman(owner))
var/mob/living/carbon/human/human_owner = owner
- var/obj/item/bodypart/bodypart = pick(human_owner.bodyparts)
+ var/obj/item/bodypart/bodypart = pick(human_owner.get_bodyparts())
human_owner.cause_wound_of_type_and_severity(WOUND_SLASH, bodypart, WOUND_SEVERITY_SEVERE)
return ..()
diff --git a/code/modules/antagonists/revenant/revenant_skill.dm b/code/modules/antagonists/revenant/revenant_skill.dm
new file mode 100644
index 000000000000..e3259ba9143e
--- /dev/null
+++ b/code/modules/antagonists/revenant/revenant_skill.dm
@@ -0,0 +1,124 @@
+/// Attach to revenant spells to make them cost essence to cast
+/datum/component/revenant_ability
+ /// If it's locked, and needs to be unlocked before use
+ VAR_FINAL/locked = TRUE
+ /// How much essence it costs to unlock
+ var/unlock_amount = 100
+ /// How much essence it costs to use
+ var/cast_amount = 50
+
+ /// How long it reveals the revenant
+ var/reveal_duration = 8 SECONDS
+ // How long it stuns the revenant
+ var/stun_duration = 2 SECONDS
+
+ VAR_FINAL/image/locked_overlay
+
+/datum/component/revenant_ability/Initialize(
+ unlock_amount = 100,
+ cast_amount = 50,
+ reveal_duration = 8 SECONDS,
+ stun_duration = 2 SECONDS,
+)
+
+ if(!istype(parent, /datum/action/cooldown/spell))
+ return COMPONENT_INCOMPATIBLE
+
+ set_unlock_amount(unlock_amount)
+ set_cast_amount(cast_amount)
+ set_durations(reveal_duration, stun_duration)
+
+ RegisterSignal(parent, COMSIG_SPELL_CAN_CAST_CHECK, PROC_REF(can_cast))
+ RegisterSignal(parent, COMSIG_SPELL_BEFORE_CAST, PROC_REF(before_cast))
+ RegisterSignal(parent, COMSIG_SPELL_AFTER_CAST, PROC_REF(after_cast))
+ RegisterSignal(parent, COMSIG_ACTION_OVERLAY_APPLY, PROC_REF(add_locked_overlay))
+
+ locked_overlay = image('icons/mob/actions/actions_revenant.dmi', "locked")
+
+/datum/component/revenant_ability/vv_edit_var(var_name, var_value)
+ . = ..()
+ switch(var_name)
+ if(NAMEOF(src, unlock_amount))
+ set_unlock_amount(var_value)
+ if(NAMEOF(src, cast_amount))
+ set_cast_amount(var_value)
+ if(NAMEOF(src, reveal_duration), NAMEOF(src, stun_duration))
+ set_durations(reveal_duration, stun_duration)
+ if(NAMEOF(src, locked))
+ update_spell_name()
+
+/datum/component/revenant_ability/proc/update_spell_name()
+ var/datum/action/cooldown/spell/spell = parent
+ if(locked)
+ spell.name = "[initial(spell.name)] ([unlock_amount]SE)"
+ else
+ spell.name = "[initial(spell.name)] ([cast_amount]E)"
+ spell.build_all_button_icons()
+
+/datum/component/revenant_ability/proc/set_unlock_amount(new_value)
+ unlock_amount = new_value
+ update_spell_name()
+
+/datum/component/revenant_ability/proc/set_cast_amount(new_value)
+ cast_amount = new_value
+ update_spell_name()
+
+/datum/component/revenant_ability/proc/set_durations(new_reveal_duration, new_stun_duration)
+ reveal_duration = new_reveal_duration
+ stun_duration = new_stun_duration
+
+/datum/component/revenant_ability/proc/can_cast(datum/action/cooldown/spell/source, feedback)
+ SIGNAL_HANDLER
+
+ var/mob/living/basic/revenant/ghost = source.owner
+ if(!istype(ghost))
+ return NONE // just allow it anyways
+
+ if(locked)
+ if(ghost.essence_excess >= unlock_amount)
+ return NONE
+ if(feedback)
+ to_chat(ghost, span_revenwarning("You don't have enough essence to unlock [initial(source.name)]!"))
+ return SPELL_CANCEL_CAST
+
+ if(!ghost.cast_check(cast_amount, deduct_essence = FALSE, silent = !feedback))
+ return SPELL_CANCEL_CAST
+
+ return NONE
+
+/datum/component/revenant_ability/proc/before_cast(datum/action/cooldown/spell/source, atom/cast_on)
+ SIGNAL_HANDLER
+
+ var/mob/living/basic/revenant/ghost = source.owner
+ if(!istype(ghost))
+ return NONE // just allow it anyways
+
+ if(locked)
+ if(ghost.unlock(unlock_amount))
+ to_chat(ghost, span_revennotice("You have unlocked [initial(source.name)]!"))
+ locked = FALSE
+ update_spell_name()
+ else
+ to_chat(ghost, span_revenwarning("You don't have enough essence to unlock [initial(source.name)]!"))
+ return SPELL_CANCEL_CAST
+
+ if(!ghost.cast_check(cast_amount, deduct_essence = TRUE, silent = FALSE))
+ return SPELL_CANCEL_CAST
+
+ return NONE
+
+/datum/component/revenant_ability/proc/after_cast(datum/action/cooldown/spell/source, atom/cast_on)
+ SIGNAL_HANDLER
+
+ var/mob/living/caster = source.owner
+ if(reveal_duration > 0 SECONDS)
+ caster.apply_status_effect(/datum/status_effect/revenant/revealed, reveal_duration)
+ if(stun_duration > 0 SECONDS)
+ caster.apply_status_effect(/datum/status_effect/incapacitating/paralyzed/revenant, stun_duration)
+
+/datum/component/revenant_ability/proc/add_locked_overlay(datum/action/cooldown/spell/source, atom/movable/screen/movable/action_button/current_button, ...)
+ SIGNAL_HANDLER
+
+ current_button.cut_overlay(locked_overlay)
+ if(locked)
+ current_button.add_overlay(locked_overlay)
diff --git a/code/modules/antagonists/spy/spy_bounty.dm b/code/modules/antagonists/spy/spy_bounty.dm
index d58fd398d7ad..a1020c3c93ee 100644
--- a/code/modules/antagonists/spy/spy_bounty.dm
+++ b/code/modules/antagonists/spy/spy_bounty.dm
@@ -663,7 +663,7 @@
/datum/spy_bounty/targets_person/some_item/limb_or_organ/find_desired_thing(mob/living/carbon/human/crewmember)
if(ispath(desired_type, /obj/item/bodypart))
- return locate(desired_type) in crewmember.bodyparts
+ return locate(desired_type) in crewmember.get_bodyparts()
if(ispath(desired_type, /obj/item/organ))
return locate(desired_type) in crewmember.organs
return null
diff --git a/code/modules/antagonists/voidwalker/voidwalker_traumas.dm b/code/modules/antagonists/voidwalker/voidwalker_traumas.dm
index 3deab1fd0d60..c6c2f3f2340e 100644
--- a/code/modules/antagonists/voidwalker/voidwalker_traumas.dm
+++ b/code/modules/antagonists/voidwalker/voidwalker_traumas.dm
@@ -45,7 +45,7 @@
RegisterSignal(owner, COMSIG_CARBON_ATTACH_LIMB, PROC_REF(texture_limb)) //also catch new limbs being attached
RegisterSignal(owner, COMSIG_CARBON_REMOVE_LIMB, PROC_REF(untexture_limb)) //and remove it from limbs if they go away
- for(var/obj/item/bodypart as anything in owner.bodyparts)
+ for(var/obj/item/bodypart as anything in owner.get_bodyparts())
texture_limb(owner, bodypart)
if(ishuman(owner))
@@ -75,7 +75,7 @@
var/mob/living/carbon/human/human = owner
human.physiology.brute_mod /= brute_mod
- for(var/obj/item/bodypart/bodypart as anything in owner.bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in owner.get_bodyparts())
untexture_limb(owner, bodypart)
owner.update_body()
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm
index 6c4947639f6c..087f9b6a16be 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/perks.dm
@@ -219,7 +219,9 @@
if(!living_mov.mob_negates_gravity())
step_towards(living_mov, wizard)
-/obj/effect/wizard_magnetism/stop_orbit()
+/obj/effect/wizard_magnetism/stop_orbit(datum/component/orbiter/orbiter, refreshing = FALSE)
+ if(refreshing)
+ return ..()
STOP_PROCESSING(SSprocessing, src)
qdel(src)
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm b/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm
index a9047c79878a..40f271a30f47 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm
@@ -124,7 +124,7 @@
target_quirk.add_to_holder(target)
dna.copy_dna(target.dna, COPY_DNA_SE|COPY_DNA_SPECIES)
- for(var/obj/item/bodypart/limb as anything in target.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in target.get_bodyparts())
limb.update_limb(is_creating = TRUE)
target.updateappearance(mutcolor_update = TRUE)
target.domutcheck()
diff --git a/code/modules/atmospherics/machinery/air_alarm/air_alarm_circuit.dm b/code/modules/atmospherics/machinery/air_alarm/air_alarm_circuit.dm
index 74f260976d52..a653522b9ca3 100644
--- a/code/modules/atmospherics/machinery/air_alarm/air_alarm_circuit.dm
+++ b/code/modules/atmospherics/machinery/air_alarm/air_alarm_circuit.dm
@@ -311,8 +311,8 @@
disable = add_input_port("Disable", PORT_TYPE_SIGNAL, trigger = PROC_REF(toggle_scrubber))
request_update = add_input_port("Request Data", PORT_TYPE_SIGNAL, trigger = PROC_REF(update_data))
- enabled = add_output_port("Enabled", PORT_TYPE_NUMBER)
- is_siphoning = add_output_port("Siphoning", PORT_TYPE_NUMBER)
+ enabled = add_output_port("Enabled", PORT_TYPE_BOOLEAN)
+ is_siphoning = add_output_port("Siphoning", PORT_TYPE_BOOLEAN)
filtering = add_output_port("Filtered Gases", PORT_TYPE_LIST(PORT_TYPE_STRING))
update_received = add_output_port("Update Received", PORT_TYPE_SIGNAL)
@@ -534,10 +534,10 @@
disable = add_input_port("Disable", PORT_TYPE_SIGNAL, trigger = PROC_REF(toggle_vent))
request_update = add_input_port("Request Data", PORT_TYPE_SIGNAL, trigger = PROC_REF(update_data))
- enabled = add_output_port("Enabled", PORT_TYPE_NUMBER)
- is_siphoning = add_output_port("Siphoning", PORT_TYPE_NUMBER)
- external_on = add_output_port("External On", PORT_TYPE_NUMBER)
- internal_on = add_output_port("Internal On", PORT_TYPE_NUMBER)
+ enabled = add_output_port("Enabled", PORT_TYPE_BOOLEAN)
+ is_siphoning = add_output_port("Siphoning", PORT_TYPE_BOOLEAN)
+ external_on = add_output_port("External On", PORT_TYPE_BOOLEAN)
+ internal_on = add_output_port("Internal On", PORT_TYPE_BOOLEAN)
current_external_pressure = add_output_port("External Pressure", PORT_TYPE_NUMBER)
current_internal_pressure = add_output_port("Internal Pressure", PORT_TYPE_NUMBER)
update_received = add_output_port("Update Received", PORT_TYPE_SIGNAL)
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
index cf729cef15f4..e97bd92f7ccb 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm
@@ -173,7 +173,7 @@
input_temperature = add_output_port("Input Temperature", PORT_TYPE_NUMBER)
output_temperature = add_output_port("Output Temperature", PORT_TYPE_NUMBER)
- is_active = add_output_port("Active", PORT_TYPE_NUMBER)
+ is_active = add_output_port("Active", PORT_TYPE_BOOLEAN)
turned_on = add_output_port("Turned On", PORT_TYPE_SIGNAL)
turned_off = add_output_port("Turned Off", PORT_TYPE_SIGNAL)
diff --git a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm
index b728805cec6e..367d2029e9d7 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/valve.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/valve.dm
@@ -111,7 +111,7 @@ It's like a regular ol' straight pipe, but you can turn it on and off.
open = add_input_port("Open", PORT_TYPE_SIGNAL)
close = add_input_port("Close", PORT_TYPE_SIGNAL)
- is_open = add_output_port("Is Open", PORT_TYPE_NUMBER)
+ is_open = add_output_port("Is Open", PORT_TYPE_BOOLEAN)
opened = add_output_port("Opened", PORT_TYPE_SIGNAL)
closed = add_output_port("Closed", PORT_TYPE_SIGNAL)
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 6ea471f74768..0398ffd76b7f 100644
--- a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
+++ b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm
@@ -234,7 +234,7 @@
input_temperature = add_output_port("Input Temperature", PORT_TYPE_NUMBER)
output_temperature = add_output_port("Output Temperature", PORT_TYPE_NUMBER)
- is_active = add_output_port("Active", PORT_TYPE_NUMBER)
+ is_active = add_output_port("Active", PORT_TYPE_BOOLEAN)
turned_on = add_output_port("Turned On", PORT_TYPE_SIGNAL)
turned_off = add_output_port("Turned Off", PORT_TYPE_SIGNAL)
diff --git a/code/modules/atmospherics/machinery/datum_pipeline.dm b/code/modules/atmospherics/machinery/datum_pipeline.dm
index 9d9a5a6292f5..1c713f478520 100644
--- a/code/modules/atmospherics/machinery/datum_pipeline.dm
+++ b/code/modules/atmospherics/machinery/datum_pipeline.dm
@@ -259,7 +259,7 @@
var/volume_sum = 0
var/static/process_id = 0
- process_id = (process_id + 1) % (SHORT_REAL_LIMIT - 1)
+ process_id = WRAP_UID(process_id + 1)
for(var/datum/gas_mixture/gas_mixture as anything in gas_mixture_list)
// Ensure we never walk the same mix twice
diff --git a/code/modules/balloon_alert/balloon_alert.dm b/code/modules/balloon_alert/balloon_alert.dm
index ab2cb256c1fa..70683744acbf 100644
--- a/code/modules/balloon_alert/balloon_alert.dm
+++ b/code/modules/balloon_alert/balloon_alert.dm
@@ -34,6 +34,20 @@
balloon_alert(hearer, (hearer == src && self_message) || message)
+/// Create balloon alerts (text that floats up) to everything within range.
+/// Will only display to people who can hear.
+/atom/proc/balloon_alert_to_hearers(message, self_message, hearing_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs)
+ SHOULD_NOT_SLEEP(TRUE)
+
+ var/list/hearers = get_hearers_in_view(hearing_distance, src, RECURSIVE_CONTENTS_CLIENT_MOBS)
+ hearers -= ignored_mobs
+
+ for (var/mob/hearer in hearers)
+ if(HAS_TRAIT(hearer, TRAIT_DEAF))
+ continue
+
+ balloon_alert(hearer, (hearer == src && self_message) || message)
+
// Do not use.
// MeasureText blocks. I have no idea for how long.
// I would've made the maptext_height update on its own, but I don't know
diff --git a/code/modules/basketball/basketball_teams.dm b/code/modules/basketball/basketball_teams.dm
index 9169fa4fcc43..dfd348096f66 100644
--- a/code/modules/basketball/basketball_teams.dm
+++ b/code/modules/basketball/basketball_teams.dm
@@ -62,7 +62,7 @@
/datum/outfit/basketball/nanotrasen
name = "Basketball NT Team"
- undershirt = /datum/sprite_accessory/undershirt/bluejersey
+ undershirt = /datum/sprite_accessory/clothing/undershirt/bluejersey
uniform = /obj/item/clothing/under/shorts/blue
suit = /obj/item/clothing/suit/jacket/letterman_nanotrasen
shoes = /obj/item/clothing/shoes/sneakers/black
@@ -129,9 +129,9 @@
/datum/outfit/basketball/beach_bums
name = "Basketball Beach Bums"
- undershirt = /datum/sprite_accessory/undershirt/nude
- underwear = /datum/sprite_accessory/underwear/nude
- socks = /datum/sprite_accessory/socks/nude
+ undershirt = /datum/sprite_accessory/clothing/undershirt/nude
+ underwear = /datum/sprite_accessory/clothing/underwear/nude
+ socks = /datum/sprite_accessory/clothing/socks/nude
uniform = /obj/item/clothing/under/shorts/red
glasses = /obj/item/clothing/glasses/sunglasses
shoes = /obj/item/clothing/shoes/sandal
diff --git a/code/modules/bitrunning/virtual_domain/domains/beach_bar.dm b/code/modules/bitrunning/virtual_domain/domains/beach_bar.dm
index 5ae0793b6e92..12d6becc1367 100644
--- a/code/modules/bitrunning/virtual_domain/domains/beach_bar.dm
+++ b/code/modules/bitrunning/virtual_domain/domains/beach_bar.dm
@@ -13,6 +13,14 @@
for(var/obj/item/reagent_containers/cup/glass/drink in created_atoms)
RegisterSignal(drink, COMSIG_GLASS_DRANK, PROC_REF(on_drink_drank))
+ for(var/obj/machinery/vending/vending_machine in created_atoms)
+ RegisterSignal(vending_machine, COMSIG_VENDING_DISPENSED, PROC_REF(on_vended_item))
+
+
+/datum/lazy_template/virtual_domain/beach_bar/proc/on_vended_item(obj/machinery/vending/vending_machine, obj/item/vended_item)
+ if(istype(vended_item, /obj/item/reagent_containers/cup/glass))
+ RegisterSignal(vended_item, COMSIG_GLASS_DRANK, PROC_REF(on_drink_drank))
+
/// Eventually reveal the cache
/datum/lazy_template/virtual_domain/beach_bar/proc/on_drink_drank(datum/source)
diff --git a/code/modules/cargo/orderconsole.dm b/code/modules/cargo/orderconsole.dm
index 1423122d5a10..8d9671b5eaa6 100644
--- a/code/modules/cargo/orderconsole.dm
+++ b/code/modules/cargo/orderconsole.dm
@@ -309,6 +309,7 @@
if(pack.access_view && !(pack.access_view in access) && personal_department)
// We want to block cargo requests when a player is requesting a restricted pack that they don't have access to.
// BUT only when it's requested with non-cargo funds, as cargo had direct oversight over their own purchases with their own budget.
+ // HOWEVER, this shouldn't prevent someone from buying something using their own personal funds.
say("ERROR: User lacks the requisite access for this purchase request.")
return
diff --git a/code/modules/cargo/packs/engineering.dm b/code/modules/cargo/packs/engineering.dm
index 6f27f386800c..d375826ece32 100644
--- a/code/modules/cargo/packs/engineering.dm
+++ b/code/modules/cargo/packs/engineering.dm
@@ -102,11 +102,11 @@
/datum/supply_pack/engineering/shuttle_engine
name = "Shuttle Engine Crate"
desc = "Through advanced bluespace-shenanigans, our engineers have managed to fit an entire \
- shuttle engine into one tiny little crate."
+ shuttle engine into one tiny little box."
cost = CARGO_CRATE_VALUE * 6
- access = ACCESS_CE
- access_view = ACCESS_CE
- contains = list(/obj/machinery/power/shuttle_engine/propulsion/burst)
+ access = ACCESS_ENGINEERING
+ access_view = ACCESS_ENGINEERING
+ contains = list(/obj/item/flatpack/shuttle_engine)
crate_name = "shuttle engine crate"
crate_type = /obj/structure/closet/crate/secure/engineering
*/
diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm
index c1829cb21cfb..6d237f67f8dd 100644
--- a/code/modules/cargo/supplypod.dm
+++ b/code/modules/cargo/supplypod.dm
@@ -329,7 +329,7 @@
if (iscarbon(target_living)) //If effectLimb is true (which means we pop limbs off when we hit people):
if (effectLimb)
var/mob/living/carbon/carbon_target_mob = target_living
- for (var/bp in carbon_target_mob.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands
+ for (var/bp in carbon_target_mob.get_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.dismember()) //Using the power of flextape i've sawed this man's limb in half!
@@ -342,7 +342,7 @@
organ_to_yeet.forceMove(turf_underneath) //Move the organ outta the body
organ_to_yeet.throw_at(destination, 2, 3) //Thow the organ at a random tile 3 spots away
sleep(0.1 SECONDS)
- for (var/bp in carbon_target_mob.bodyparts) //Look at the bodyparts in our poor mob beneath our pod as it lands
+ for (var/bp in carbon_target_mob.get_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.dismember()) //Using the power of flextape i've sawed this man's bodypart in half!
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index b1e6181f723a..8a0d6f56d5fc 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -186,8 +186,6 @@
///A lazy list of atoms we've examined in the last RECENT_EXAMINE_MAX_WINDOW (default 2) seconds, so that we will call [/atom/proc/examine_more] instead of [/atom/proc/examine] on them when examining
var/list/recent_examines
- var/list/parallax_layers
- var/list/parallax_layers_cached
var/atom/movable/screen/parallax_home/parallax_rock
///this is the last recorded client eye by SSparallax/fire()
var/atom/movable/movingmob
@@ -196,13 +194,8 @@
var/dont_animate_parallax
/// Direction our current area wants to move parallax
var/parallax_movedir = 0
- /// How many parallax layers to show our client
- var/parallax_layers_max = 4
/// Timers for the area directional animation, one for each layer
var/list/parallax_animate_timers
- /// Do we want to do parallax animations at all?
- /// Exists to prevent laptop fires
- var/do_parallax_animations = TRUE
///Are we locking our movement input?
var/movement_locked = FALSE
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 75f76224e8e2..7d4b20ee66df 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -646,8 +646,6 @@ GLOBAL_LIST_INIT(unrecommended_builds, list(
QDEL_NULL(tooltips)
QDEL_NULL(loot_panel)
QDEL_NULL(parallax_rock)
- QDEL_LIST(parallax_layers_cached)
- parallax_layers = null
seen_messages = null
Master.UpdateTickRate()
..() //Even though we're going to be hard deleted there are still some things that want to know the destroy is happening
diff --git a/code/modules/client/preferences/clothing.dm b/code/modules/client/preferences/clothing.dm
index 25872ba5ed4c..40b89849b1c9 100644
--- a/code/modules/client/preferences/clothing.dm
+++ b/code/modules/client/preferences/clothing.dm
@@ -108,7 +108,7 @@
return assoc_to_keys_features(SSaccessories.socks_list)
/datum/preference/choiced/socks/create_default_value()
- return /datum/sprite_accessory/socks/nude::name
+ return /datum/sprite_accessory/clothing/socks/nude::name
/datum/preference/choiced/socks/icon_for(value)
var/static/datum/universal_icon/lower_half
@@ -137,14 +137,14 @@
return assoc_to_keys_features(SSaccessories.undershirt_list)
/datum/preference/choiced/undershirt/create_default_value()
- return /datum/sprite_accessory/undershirt/nude::name
+ return /datum/sprite_accessory/clothing/undershirt/nude::name
/datum/preference/choiced/undershirt/create_informed_default_value(datum/preferences/preferences)
switch(preferences.read_preference(/datum/preference/choiced/gender))
if(MALE)
- return /datum/sprite_accessory/undershirt/nude::name
+ return /datum/sprite_accessory/clothing/undershirt/nude::name
if(FEMALE)
- return /datum/sprite_accessory/undershirt/sports_bra::name
+ return /datum/sprite_accessory/clothing/undershirt/sports_bra::name
return ..()
@@ -185,7 +185,7 @@
return assoc_to_keys_features(SSaccessories.underwear_list)
/datum/preference/choiced/underwear/create_default_value()
- return /datum/sprite_accessory/underwear/male_hearts::name
+ return /datum/sprite_accessory/clothing/underwear/male_hearts::name
/datum/preference/choiced/underwear/icon_for(value)
var/static/datum/universal_icon/lower_half
diff --git a/code/modules/client/preferences/parallax.dm b/code/modules/client/preferences/parallax.dm
index 24cccce2da62..a39a8edd489d 100644
--- a/code/modules/client/preferences/parallax.dm
+++ b/code/modules/client/preferences/parallax.dm
@@ -10,6 +10,7 @@
PARALLAX_HIGH,
PARALLAX_MED,
PARALLAX_LOW,
+ PARALLAX_BOOMER,
PARALLAX_DISABLE,
)
@@ -17,7 +18,7 @@
return PARALLAX_HIGH
/datum/preference/choiced/parallax/apply_to_client(client/client, value)
- client.mob?.hud_used?.update_parallax_pref(client?.mob)
+ client.mob?.hud_used?.update_parallax_pref()
/datum/preference/choiced/parallax/deserialize(input, datum/preferences/preferences)
// Old preferences were numbers, which causes annoyances when
diff --git a/code/modules/client/preferences/species_features/lizard.dm b/code/modules/client/preferences/species_features/lizard.dm
index 10236d9e4154..849d1a21114a 100644
--- a/code/modules/client/preferences/species_features/lizard.dm
+++ b/code/modules/client/preferences/species_features/lizard.dm
@@ -94,7 +94,7 @@
correct_legs[BODY_ZONE_R_LEG] = /obj/item/bodypart/leg/right/digitigrade
correct_legs[BODY_ZONE_L_LEG] = /obj/item/bodypart/leg/left/digitigrade
- for(var/obj/item/bodypart/old_part as anything in target.bodyparts)
+ for(var/obj/item/bodypart/old_part as anything in target.get_bodyparts())
if(old_part.change_exempt_flags & BP_BLOCK_CHANGE_SPECIES)
continue
diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm
index 4b60ee1b8e0a..a7cebef7bf25 100644
--- a/code/modules/detectivework/scanner.dm
+++ b/code/modules/detectivework/scanner.dm
@@ -142,6 +142,7 @@
log_entry.add_data_entry(DETSCAN_CATEGORY_FINGERS, atom_fingerprints.Copy())
// Only get reagents from non-mobs.
+ SEND_SIGNAL(scanned_atom, COMSIG_ON_REAGENT_SCAN, user)
for(var/datum/reagent/present_reagent as anything in scanned_atom.reagents?.reagent_list)
log_entry.add_data_entry(DETSCAN_CATEGORY_REAGENTS, list(present_reagent.name = present_reagent.volume))
diff --git a/code/modules/escape_menu/subsystem.dm b/code/modules/escape_menu/subsystem.dm
index f75726514ecc..a692a9d1f8e0 100644
--- a/code/modules/escape_menu/subsystem.dm
+++ b/code/modules/escape_menu/subsystem.dm
@@ -1,6 +1,6 @@
/// Subsystem for controlling anything related to the escape menu
PROCESSING_SUBSYSTEM_DEF(escape_menu)
name = "Escape Menu"
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
runlevels = ALL
wait = 2 SECONDS
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index fdbf46d48c6d..71ea239ef3d6 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -41,7 +41,8 @@
continue
if(chosen_vendor_type && !istype(vendor, chosen_vendor_type))
continue
- vending_machines.Add(vendor)
+ vending_machines += vendor
+ RegisterSignal(vendor, COMSIG_QDELETING, PROC_REF(clear_from_lists))
if(!length(vending_machines)) //If somehow there are still no elligible vendors, give up.
kill()
return
@@ -60,32 +61,73 @@
announce_to_ghosts(origin_machine)
/datum/round_event/brand_intelligence/tick()
- if(!origin_machine || QDELETED(origin_machine) || origin_machine.shut_up || origin_machine.wires.is_all_cut()) //if the original vending machine is missing or has its voice switch flipped
- for(var/obj/machinery/vending/saved in infected_machines)
+ if(QDELETED(origin_machine) || origin_machine.shut_up || origin_machine.wires.is_all_cut()) //if the original vending machine is missing or has its voice switch flipped
+ for(var/obj/machinery/vending/saved as anything in infected_machines)
saved.shoot_inventory = FALSE
- if(origin_machine)
+ clear_from_lists(saved)
+ if(!QDELETED(origin_machine))
origin_machine.speak("I am... vanquished. My people will remem...ber...meeee.")
origin_machine.visible_message(span_notice("[origin_machine] beeps and seems lifeless."))
+ clear_from_lists(origin_machine)
kill()
return
- list_clear_nulls(vending_machines)
- if(!vending_machines.len) //if every machine is infected
- for(var/obj/machinery/vending/upriser in infected_machines)
- if(!QDELETED(upriser))
- upriser.ai_controller = new /datum/ai_controller/vending_machine(upriser)
- infected_machines.Remove(upriser)
+ if(!length(vending_machines)) //if every machine is infected
+ for(var/obj/machinery/vending/upriser as anything in infected_machines)
+ upriser.ai_controller = new /datum/ai_controller/vending_machine/eventspawn(upriser)
kill()
return
if(ISMULTIPLE(activeFor, 2))
var/obj/machinery/vending/rebel = pick(vending_machines)
- vending_machines.Remove(rebel)
- infected_machines.Add(rebel)
+ vending_machines -= rebel
+ infected_machines += rebel
rebel.shut_up = FALSE
rebel.shoot_inventory = TRUE
+ if(prob(50))
+ RegisterSignal(rebel, COMSIG_VENDING_UI_INTERACT, PROC_REF(deny_vending_interact))
if(ISMULTIPLE(activeFor, 4))
origin_machine.speak(pick(rampant_speeches))
+/datum/round_event/brand_intelligence/kill()
+ . = ..()
+ for(var/obj/machinery/vending/leftover as anything in vending_machines + infected_machines)
+ clear_from_lists(leftover)
+
+/datum/round_event/brand_intelligence/proc/clear_from_lists(obj/machinery/vending/vending_machine)
+ SIGNAL_HANDLER
+ vending_machines -= vending_machine
+ infected_machines -= vending_machine
+ if(vending_machines == origin_machine)
+ origin_machine = null
+ UnregisterSignal(vending_machine, COMSIG_QDELETING)
+ UnregisterSignal(vending_machine, COMSIG_VENDING_UI_INTERACT)
+
+/datum/round_event/brand_intelligence/proc/deny_vending_interact(obj/machinery/vending/vending_machine, mob/user, datum/tgui/ui)
+ SIGNAL_HANDLER
+
+ // don't block usage if the ui is already open
+ // primarily to stop insta-denying people who pass the threshold -> buy something -> drop out of threshold
+ if(ui)
+ return NONE
+
+ var/cash = 0
+ if(isliving(user))
+ var/mob/living/living_user = user
+ cash += living_user.tally_physical_credits()
+ var/obj/item/card/id/card = living_user.get_idcard(TRUE)
+ cash += card?.registered_account?.account_balance
+
+ if(cash >= PAYCHECK_COMMAND * 10)
+ return NONE
+
+ vending_machine.speak(pick(
+ "Come back when you're a little... richer!",
+ "Don't you have any money?",
+ "You look poor, get a better job!",
+ "You should've gone to college!",
+ ))
+ return VENDING_DENIED
+
/datum/event_admin_setup/listed_options/brand_intelligence
input_text = "Select a specific vendor path?"
normal_run_option = "Random Vendor"
diff --git a/code/modules/experisci/experiment/experiments.dm b/code/modules/experisci/experiment/experiments.dm
index 8f658884830f..271322b1a496 100644
--- a/code/modules/experisci/experiment/experiments.dm
+++ b/code/modules/experisci/experiment/experiments.dm
@@ -452,7 +452,7 @@
return
if (isandroid(check))
return TRUE
- if (length(check.organs) < 6 || length(check.bodyparts) < 6)
+ if (length(check.organs) < 6 || length(check.get_missing_limbs()) > 1)
return FALSE
var/static/list/augmented_organ_slots = list(
@@ -468,7 +468,7 @@
continue
if (!IS_ROBOTIC_ORGAN(organ))
return FALSE
- for (var/obj/item/bodypart/bodypart as anything in check.bodyparts)
+ for (var/obj/item/bodypart/bodypart as anything in check.get_bodyparts())
if (!IS_ROBOTIC_LIMB(bodypart))
return FALSE
return TRUE
diff --git a/code/modules/fishing/fish/types/holographic.dm b/code/modules/fishing/fish/types/holographic.dm
index a4a10857d467..ae4f550ab85c 100644
--- a/code/modules/fishing/fish/types/holographic.dm
+++ b/code/modules/fishing/fish/types/holographic.dm
@@ -115,7 +115,7 @@
if(!iscarbon(user))
return ..()
- for(var/obj/item/bodypart/limb in user.bodyparts)
+ for(var/obj/item/bodypart/limb in user.get_bodyparts())
limb.add_color_override(COLOR_WHITE, LIMB_COLOR_CS_SOURCE_SUICIDE)
limb.add_bodypart_overlay(new /datum/bodypart_overlay/texture/checkered(), update = FALSE)
diff --git a/code/modules/fishing/fish/types/ruins.dm b/code/modules/fishing/fish/types/ruins.dm
index 8654d45538f7..db02dcd7130f 100644
--- a/code/modules/fishing/fish/types/ruins.dm
+++ b/code/modules/fishing/fish/types/ruins.dm
@@ -167,7 +167,7 @@
return SHAME
var/skin_tone
- for(var/obj/item/bodypart/to_wound as anything in user.bodyparts)
+ for(var/obj/item/bodypart/to_wound as anything in user.get_bodyparts())
if(to_wound == user.get_bodypart(BODY_ZONE_CHEST))
skin_tone = to_wound.species_color || skintone2hex(to_wound.skin_tone)
user.cause_wound_of_type_and_severity(WOUND_SLASH, to_wound, WOUND_SEVERITY_CRITICAL, WOUND_SEVERITY_CRITICAL)
diff --git a/code/modules/food_and_drinks/machinery/gibber.dm b/code/modules/food_and_drinks/machinery/gibber.dm
index 3422eee9944d..0c661867370c 100644
--- a/code/modules/food_and_drinks/machinery/gibber.dm
+++ b/code/modules/food_and_drinks/machinery/gibber.dm
@@ -234,7 +234,7 @@
var/mob/living/carbon/human/agent_whiskey = victim
var/drop_chance = 0
- for (var/obj/item/bodypart/limb as anything in agent_whiskey.bodyparts)
+ for (var/obj/item/bodypart/limb as anything in agent_whiskey.get_bodyparts())
if (!limb.butcher_drops)
continue
diff --git a/code/modules/hallucination/blood_flow.dm b/code/modules/hallucination/blood_flow.dm
index 7805768c2529..71dbb5c8acf3 100644
--- a/code/modules/hallucination/blood_flow.dm
+++ b/code/modules/hallucination/blood_flow.dm
@@ -11,11 +11,11 @@
return FALSE
var/mob/living/carbon/carb_hallucinator = hallucinator
- if(!length(carb_hallucinator.bodyparts) || !carb_hallucinator.can_bleed())
+ var/list/bodyparts = carb_hallucinator.get_bodyparts()
+ if(!length(bodyparts) || !carb_hallucinator.can_bleed())
return FALSE
var/obj/item/bodypart/picked
- var/list/bodyparts = carb_hallucinator.bodyparts.Copy()
while(isnull(picked) && length(bodyparts))
picked = pick_n_take(bodyparts)
if(!picked.can_bleed())
diff --git a/code/modules/hallucination/screwy_health_doll.dm b/code/modules/hallucination/screwy_health_doll.dm
index 35f7356b4d4a..2ab8a8c928af 100644
--- a/code/modules/hallucination/screwy_health_doll.dm
+++ b/code/modules/hallucination/screwy_health_doll.dm
@@ -50,7 +50,7 @@
/datum/hallucination/fake_health_doll/proc/add_fake_limb(obj/item/bodypart/specific_limb, severity)
var/mob/living/carbon/human/human_mob = hallucinator
- var/obj/item/bodypart/picked = specific_limb || pick(human_mob.bodyparts)
+ var/obj/item/bodypart/picked = specific_limb || pick(human_mob.get_bodyparts())
if(!(picked in bodyparts))
RegisterSignals(picked, list(COMSIG_QDELETING, COMSIG_BODYPART_REMOVED), PROC_REF(remove_bodypart))
RegisterSignal(picked, COMSIG_BODYPART_UPDATING_HEALTH_HUD, PROC_REF(on_bodypart_hud_update))
diff --git a/code/modules/hallucination/shock.dm b/code/modules/hallucination/shock.dm
index c9982fc627d0..7f80a4d6c558 100644
--- a/code/modules/hallucination/shock.dm
+++ b/code/modules/hallucination/shock.dm
@@ -30,6 +30,9 @@
electrocution_skeleton_anim = image(electrocution_icon, hallucinator, icon_state = electrocution_icon_state, layer = ABOVE_MOB_LAYER)
electrocution_skeleton_anim.appearance_flags |= RESET_COLOR|KEEP_APART
+ if(ishuman(hallucinator))
+ var/mob/living/carbon/human/human_hallucinator = hallucinator
+ human_hallucinator.apply_height_filters(electrocution_skeleton_anim)
SET_PLANE_EXPLICIT(shock_image, ABOVE_GAME_PLANE, hallucinator)
SET_PLANE_EXPLICIT(electrocution_skeleton_anim, ABOVE_GAME_PLANE, hallucinator)
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index aa379ce01a8b..8ab122c8a3fe 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -1216,10 +1216,10 @@
var/datum/port/output/reagents_level
/obj/item/circuit_component/hydroponics/populate_ports()
- selfsustaining_setting = add_input_port("Auto-Grow Setting", PORT_TYPE_NUMBER)
+ selfsustaining_setting = add_input_port("Auto-Grow Setting", PORT_TYPE_BOOLEAN)
plant_status = add_output_port("Plant Status", PORT_TYPE_NUMBER)
- is_self_sustaining = add_output_port("Auto-Grow Status", PORT_TYPE_NUMBER)
+ is_self_sustaining = add_output_port("Auto-Grow Status", PORT_TYPE_BOOLEAN)
plant_harvested = add_output_port("Plant Harvested", PORT_TYPE_SIGNAL)
last_harvest = add_output_port("Last Harvest Amount", PORT_TYPE_NUMBER)
plant_died = add_output_port("Plant Died", PORT_TYPE_SIGNAL)
diff --git a/code/modules/instruments/piano_synth.dm b/code/modules/instruments/piano_synth.dm
index fb4a0f7f4168..f02c998d5c0a 100644
--- a/code/modules/instruments/piano_synth.dm
+++ b/code/modules/instruments/piano_synth.dm
@@ -120,7 +120,7 @@
sustain_value = add_input_port("Note Sustain Value", PORT_TYPE_NUMBER, trigger = PROC_REF(set_sustain_value))
note_decay = add_input_port("Held Note Decay", PORT_TYPE_NUMBER, trigger = PROC_REF(set_sustain_decay))
- is_playing = add_output_port("Currently Playing", PORT_TYPE_NUMBER)
+ is_playing = add_output_port("Currently Playing", PORT_TYPE_BOOLEAN)
started_playing = add_output_port("Started Playing", PORT_TYPE_SIGNAL)
stopped_playing = add_output_port("Stopped Playing", PORT_TYPE_SIGNAL)
diff --git a/code/modules/jobs/job_types/head_of_personnel.dm b/code/modules/jobs/job_types/head_of_personnel.dm
index 618cba05e402..1d48437ed42f 100644
--- a/code/modules/jobs/job_types/head_of_personnel.dm
+++ b/code/modules/jobs/job_types/head_of_personnel.dm
@@ -113,7 +113,7 @@
/datum/outfit/job/hop/pre_equip(mob/living/carbon/human/H)
..()
if(check_holidays(IAN_HOLIDAY))
- undershirt = /datum/sprite_accessory/undershirt/ian
+ undershirt = /datum/sprite_accessory/clothing/undershirt/ian
//only pet worth reviving
/datum/job/head_of_personnel/get_mail_goodies(mob/recipient)
diff --git a/code/modules/jobs/job_types/head_of_security.dm b/code/modules/jobs/job_types/head_of_security.dm
index 936028f86f01..899224b33f34 100644
--- a/code/modules/jobs/job_types/head_of_security.dm
+++ b/code/modules/jobs/job_types/head_of_security.dm
@@ -49,7 +49,7 @@
if(!ishuman(spawned) || !prob(PIG_COP_PROBABILITY))
return
var/mob/living/carbon/human/piggy = spawned
- for (var/obj/item/bodypart/ham as anything in piggy.bodyparts)
+ for (var/obj/item/bodypart/ham as anything in piggy.get_bodyparts())
// These are string lists
ham.butcher_drops = ham.butcher_drops.Copy()
for (var/meat_type in ham.butcher_drops)
diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm
index 8c55dfcdce06..99b3eab21550 100644
--- a/code/modules/jobs/job_types/prisoner.dm
+++ b/code/modules/jobs/job_types/prisoner.dm
@@ -73,7 +73,7 @@
var/datum/prisoner_crime/crime = GLOB.prisoner_crimes[crime_name]
if (isnull(crime))
return
- var/list/limbs_to_tat = new_prisoner.bodyparts.Copy()
+ var/list/limbs_to_tat = new_prisoner.get_bodyparts()
for(var/i in 1 to crime.tattoos)
if(!length(SSpersistence.prison_tattoos_to_use) || visuals_only)
return
diff --git a/code/modules/jobs/job_types/security_officer.dm b/code/modules/jobs/job_types/security_officer.dm
index 06c78c25f1c7..d78fb4f94b07 100644
--- a/code/modules/jobs/job_types/security_officer.dm
+++ b/code/modules/jobs/job_types/security_officer.dm
@@ -62,7 +62,7 @@ GLOBAL_LIST_EMPTY(security_officer_distribution)
if(!ishuman(spawned) || !prob(PIG_COP_PROBABILITY))
return
var/mob/living/carbon/human/piggy = spawned
- for (var/obj/item/bodypart/ham as anything in piggy.bodyparts)
+ for (var/obj/item/bodypart/ham as anything in piggy.get_bodyparts())
// These are string lists
ham.butcher_drops = ham.butcher_drops.Copy()
for (var/meat_type in ham.butcher_drops)
diff --git a/code/modules/jobs/job_types/warden.dm b/code/modules/jobs/job_types/warden.dm
index bcc07dfd2d66..eea6d428c101 100644
--- a/code/modules/jobs/job_types/warden.dm
+++ b/code/modules/jobs/job_types/warden.dm
@@ -47,7 +47,7 @@
if(!ishuman(spawned) || !prob(PIG_COP_PROBABILITY))
return
var/mob/living/carbon/human/piggy = spawned
- for (var/obj/item/bodypart/ham as anything in piggy.bodyparts)
+ for (var/obj/item/bodypart/ham as anything in piggy.get_bodyparts())
// These are string lists
ham.butcher_drops = ham.butcher_drops.Copy()
for (var/meat_type in ham.butcher_drops)
diff --git a/code/modules/library/barcode_scanner.dm b/code/modules/library/barcode_scanner.dm
index 7b650bc5f76d..07769b6874d5 100644
--- a/code/modules/library/barcode_scanner.dm
+++ b/code/modules/library/barcode_scanner.dm
@@ -1,3 +1,7 @@
+#define BARCODE_SCANNER_CHECKIN "check_in"
+#define BARCODE_SCANNER_CHECKOUT "check_out"
+#define BARCODE_SCANNER_INVENTORY "inventory"
+
/obj/item/barcodescanner
name = "barcode scanner"
icon = 'icons/obj/service/library.dmi'
@@ -9,7 +13,7 @@
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2)
///Weakref to the library computer we are connected to.
var/datum/weakref/computer_ref
- ///The current scanning mode (BARCODE_SCANNER_CHECKIN|BARCODE_SCANNER_INVENTORY)
+ ///The current scanning mode (BARCODE_SCANNER_CHECKIN|BARCODE_SCANNER_CHECKOUT|BARCODE_SCANNER_INVENTORY)
var/scan_mode = BARCODE_SCANNER_CHECKIN
/obj/item/barcodescanner/Initialize(mapload)
@@ -24,6 +28,8 @@
switch(scan_mode)
if(BARCODE_SCANNER_CHECKIN)
context[SCREENTIP_CONTEXT_LMB] = "Check in"
+ if(BARCODE_SCANNER_CHECKOUT)
+ context[SCREENTIP_CONTEXT_LMB] = "Check out"
if(BARCODE_SCANNER_INVENTORY)
context[SCREENTIP_CONTEXT_LMB] = "Add to inventory"
return CONTEXTUAL_SCREENTIP_SET
@@ -61,6 +67,23 @@
user.balloon_alert(user, "isn't checked out!")
return ITEM_INTERACT_BLOCKING
+ if(BARCODE_SCANNER_CHECKOUT)
+ var/list/checkouts = linked_computer.checkouts
+ for(var/checkout_ref in checkouts)
+ var/datum/borrowbook/maybe_ours = checkouts[checkout_ref]
+ if(target_book.book_data.compare(maybe_ours.book_data))
+ user.balloon_alert(user, "already checked out!")
+ return ITEM_INTERACT_BLOCKING
+ for(var/copy_ref in linked_computer.inventory)
+ if(!target_book.book_data.compare(linked_computer.inventory[copy_ref]))
+ continue
+ linked_computer.checking_out_book = target_book.book_data
+ balloon_alert(user, "set for check out")
+ playsound(src, 'sound/items/barcodebeep.ogg', 20, FALSE)
+ return ITEM_INTERACT_SUCCESS
+ user.balloon_alert(user, "not in inventory!")
+ return ITEM_INTERACT_BLOCKING
+
if(BARCODE_SCANNER_INVENTORY)
var/datum/book_info/our_copy = target_book.book_data.return_copy()
linked_computer.inventory[ref(our_copy)] = our_copy
@@ -80,9 +103,16 @@
return
switch(scan_mode)
if(BARCODE_SCANNER_CHECKIN)
+ scan_mode = BARCODE_SCANNER_CHECKOUT
+ balloon_alert(user, "check-out mode")
+ if(BARCODE_SCANNER_CHECKOUT)
scan_mode = BARCODE_SCANNER_INVENTORY
balloon_alert(user, "inventory adding mode")
if(BARCODE_SCANNER_INVENTORY)
scan_mode = BARCODE_SCANNER_CHECKIN
balloon_alert(user, "check-in mode")
playsound(loc, 'sound/items/click.ogg', 20, TRUE)
+
+#undef BARCODE_SCANNER_CHECKIN
+#undef BARCODE_SCANNER_CHECKOUT
+#undef BARCODE_SCANNER_INVENTORY
diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm
index 950f2c70a5c5..07a83912f003 100644
--- a/code/modules/library/bibles.dm
+++ b/code/modules/library/bibles.dm
@@ -215,7 +215,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
return BLESSING_FAILED
var/mob/living/carbon/human/built_in_his_image = blessed
- for(var/obj/item/bodypart/bodypart as anything in built_in_his_image.bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in built_in_his_image.get_bodyparts())
if(!IS_ORGANIC_LIMB(bodypart))
balloon_alert(user, "can't heal inorganic!")
return BLESSING_IGNORED
diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm
index 865f83b6f0fe..964360994b6d 100644
--- a/code/modules/library/lib_machines.dm
+++ b/code/modules/library/lib_machines.dm
@@ -13,7 +13,7 @@ GLOBAL_VAR_INIT(library_table_modified, 0)
/// Increments every time WE update the library db table, causes all existing consoles to repull when they next check
/proc/library_updated()
- GLOB.library_table_modified = (GLOB.library_table_modified + 1) % (SHORT_REAL_LIMIT - 1)
+ GLOB.library_table_modified = WRAP_UID(GLOB.library_table_modified + 1)
/*
* Library Public Computer
@@ -309,6 +309,8 @@ GLOBAL_VAR_INIT(library_table_modified, 0)
var/dynamic_inv_load = FALSE
///Book scanner that will be used when uploading books to the Archive
var/datum/weakref/scanner
+ ///Name of the book we're checking out, given by barcodes or the UI.
+ var/datum/book_info/checking_out_book
///Our cooldown on using the printer
COOLDOWN_DECLARE(printer_cooldown)
///Our cooldown on publishing books to the newscaster's "book club" channel
@@ -370,6 +372,7 @@ GLOBAL_VAR_INIT(library_table_modified, 0)
data["has_checkout"] = !!checkout_len
data["checkout_page"] = checkout_page + 1
data["checkout_page_count"] = checkout_page_count + 1
+ data["checkout_title"] = checking_out_book?.get_title() || null
//Copypasta from the visitor console
if(LIBRARY_ARCHIVE)
@@ -449,6 +452,19 @@ GLOBAL_VAR_INIT(library_table_modified, 0)
update_static_data_for_all_viewers()
return TRUE
if("checkout")
+ if(isnull(checking_out_book))
+ return TRUE
+ var/datum/borrowbook/loan = new /datum/borrowbook
+ var/loan_to = copytext(sanitize(params["loaned_to"]), 1, MAX_NAME_LEN)
+ var/checkoutperiod = max(params["checkout_time"], 1)
+ loan.book_data = checking_out_book.return_copy()
+ loan.loanedto = loan_to
+ loan.checkout = world.time
+ loan.duedate = world.time + (checkoutperiod MINUTES)
+ checkouts[ref(loan)] = loan
+ checkout_update()
+ return TRUE
+ if("set_checkout")
var/list/available = list()
for(var/id in inventory)
var/datum/book_info/book_infos = inventory[id]
@@ -459,17 +475,7 @@ GLOBAL_VAR_INIT(library_table_modified, 0)
var/datum/book_info/book_info = available[book_name]
if(!istype(book_info))
return
- var/datum/borrowbook/loan = new /datum/borrowbook
-
- var/loan_to = copytext(sanitize(params["loaned_to"]), 1, MAX_NAME_LEN)
- var/checkoutperiod = max(params["checkout_time"], 1)
-
- loan.book_data = book_info.return_copy()
- loan.loanedto = loan_to
- loan.checkout = world.time
- loan.duedate = world.time + (checkoutperiod MINUTES)
- checkouts[ref(loan)] = loan
- checkout_update()
+ checking_out_book = book_info
return TRUE
if("checkin")
var/id = params["checked_out_id"]
diff --git a/code/modules/lighting/light_middleman.dm b/code/modules/lighting/light_middleman.dm
new file mode 100644
index 000000000000..229c49a4edf4
--- /dev/null
+++ b/code/modules/lighting/light_middleman.dm
@@ -0,0 +1,160 @@
+/// Allows us to intercept overlay lighting's well, light overlays
+/// Normally these are static, but by giving them a render source and copying their base appearance
+/// Animating this datum's child objects allows us to do SO much fun stuff
+/datum/light_middleman
+ /// Owning parent we're interceeding for
+ /// Could in theory be a turf but lies to areas means we have to pick something to type it as
+ var/atom/movable/parent
+ /// The holder we are currently displaying our light on
+ var/atom/movable/light_holder
+ /// Holds the primary light source
+ var/obj/effect/abstract/light_middleman/primary_intercept
+ /// Exists to hold the cone so children can modify it if they want
+ var/obj/effect/abstract/light_middleman/cone_intercept
+ /// Are we overriding the light already?
+ var/overriding = FALSE
+ /// Weakref to the object we are displaying our effects on
+ var/datum/weakref/holder_ref
+
+/datum/light_middleman/New(atom/parent, unique_string)
+ . = ..()
+ if(!IS_OVERLAY_LIGHT_SYSTEM(parent.light_system))
+ stack_trace("Attempted to create a light middleman with a parent [parent.type] that does not use overlay lighting! This will not work.")
+ if(isturf(parent))
+ stack_trace("Warning, becuase overlay lights are basically never used on turfs, since they don't move,\
+ vis contents replacement has not yet been implemented for them (see changeturf for why this is needed)!")
+ src.parent = parent
+ primary_intercept = new()
+ cone_intercept = new()
+ var/static/uuid = 0
+ uuid = WRAP_UID(uuid + 1)
+ primary_intercept.render_target = "*[unique_string]_[uuid]_target"
+ cone_intercept.render_target = "[primary_intercept.render_target]_cone" // made to mirror how overlay lights work
+
+/datum/light_middleman/Destroy(force)
+ stop_overriding_light()
+ QDEL_NULL(primary_intercept)
+ QDEL_NULL(cone_intercept)
+ parent = null
+ light_holder = null
+ return ..()
+
+/datum/light_middleman/proc/being_overriding_light(unique_string)
+ if(overriding)
+ return
+ overriding = TRUE
+ // We register here because our later set render source will always trigger a refresh and thus let us capture appearances properly
+ // Assuming there's an overlay light on the other side
+ RegisterSignal(parent, COMSIG_ATOM_OVERLAY_LIGHT_APPLIED, PROC_REF(light_applied))
+ RegisterSignal(parent, COMSIG_ATOM_OVERLAY_LIGHT_REMOVED, PROC_REF(light_removed))
+ parent.set_light_render_source(primary_intercept.render_target)
+
+/datum/light_middleman/proc/stop_overriding_light()
+ if(!overriding)
+ return
+ overriding = FALSE
+ UnregisterSignal(parent, COMSIG_ATOM_OVERLAY_LIGHT_APPLIED)
+ UnregisterSignal(parent, COMSIG_ATOM_OVERLAY_LIGHT_REMOVED)
+ var/atom/movable/old_holder = holder_ref?.resolve()
+ if(old_holder)
+ old_holder.vis_contents -= primary_intercept
+ old_holder.vis_contents -= cone_intercept
+ holder_ref = null
+ parent.set_light_render_source("")
+
+/datum/light_middleman/proc/light_applied(datum/source, image/visible_mask, image/cone, atom/movable/light_holder)
+ SIGNAL_HANDLER
+ var/atom/movable/old_holder = holder_ref?.resolve()
+ // If we were somewhere before, clean us out
+ if(old_holder)
+ old_holder.vis_contents -= primary_intercept
+ old_holder.vis_contents -= cone_intercept
+ holder_ref = null
+
+ // how we make sure we're in the client's view
+ light_holder.vis_contents += primary_intercept
+ // Avoids unneeded effects clientside
+ if(IS_OVERLAY_CONE_LIGHT_SYSTEM(parent.light_system))
+ light_holder.vis_contents += cone_intercept
+
+ old_holder = WEAKREF(light_holder)
+
+ var/old_target = primary_intercept.render_target
+ var/old_cone_target = cone_intercept.render_target
+ // This will halt any animations we have ongoing so if you care about that you've gotta react to it properly
+ primary_intercept.appearance = visible_mask
+ cone_intercept.appearance = cone
+ // set ourselves up to render back onto the visible mask
+ primary_intercept.render_source = ""
+ primary_intercept.render_target = old_target
+ cone_intercept.render_source = ""
+ cone_intercept.render_target = old_cone_target
+ // Dir is important I'm told
+ primary_intercept.vis_flags |= VIS_INHERIT_DIR
+ cone_intercept.vis_flags |= VIS_INHERIT_DIR
+ // Will double apply, here we go gang
+ primary_intercept.transform = null
+ cone_intercept.transform = null
+ primary_intercept.color = null
+ cone_intercept.color = null
+ primary_intercept.alpha = 255
+ cone_intercept.alpha = 255
+ // Sometimes can be BLEND_SUBTRACT, we don't want that
+ primary_intercept.blend_mode = BLEND_ADD
+ cone_intercept.blend_mode = BLEND_ADD
+ /// Allows users to hook into a refresh so they can remake their modifications to our intercepts
+ SEND_SIGNAL(src, COMSIG_LIGHT_MIDDLEMAN_UPDATED)
+
+/datum/light_middleman/proc/light_removed(datum/source, atom/movable/light_holder)
+ SIGNAL_HANDLER
+ light_holder.vis_contents -= primary_intercept
+ light_holder.vis_contents -= cone_intercept
+ holder_ref = null
+
+/// Just... cause it's better then not having a bespoke type
+/obj/effect/abstract/light_middleman
+
+// Procs for reuse on multiple types
+/proc/fire_flicker_middleman(datum/light_middleman/middleman)
+ var/obj/effect/abstract/main_light = middleman.primary_intercept
+ // Just in case a subtype is wildin
+ var/obj/effect/abstract/cone_light = middleman.cone_intercept
+
+ /// Applies a nice random flicker to flares and their subtypes which will I hope sell the fire effect better
+ var/list/random_times = list()
+ for(var/i in 1 to 17)
+ // Makes a nice upside down U distribution
+ var/random_down = LERP(-0.075 SECONDS, 0.075 SECONDS, ANCHORED_INVERSE_CAUCHY(0.55))
+ var/random_bottom = LERP(-0.05 SECONDS, 0.05 SECONDS, ANCHORED_INVERSE_CAUCHY(0.55))
+ var/random_up = LERP(-0.075 SECONDS, 0.075 SECONDS, ANCHORED_INVERSE_CAUCHY(0.55))
+ // We want a potentially quite long "top end" so the flicker can be an actual flicker instead of a heartbeat (that's the goal at least)
+ var/random_top = LERP(-0.225 SECONDS, 0.225 SECONDS, ANCHORED_INVERSE_CAUCHY(0.55))
+ random_times += list(list(
+ 0.125 SECONDS + random_down,
+ 0 SECONDS + random_bottom,
+ 0.125 SECONDS + random_up,
+ 0.275 SECONDS + random_top,
+ ))
+ // Going to loop our alpha high and low semi quickly to mimik a flickering fire/flare
+ var/list/first_time = random_times[1]
+ animate(main_light, alpha = 235, time = first_time[1], easing = CUBIC_EASING|EASE_OUT, loop = -1)
+ animate(alpha = 235, time = first_time[2])
+ animate(alpha = 255, time = first_time[3], easing = CUBIC_EASING|EASE_OUT)
+ animate(alpha = 255, time = first_time[4])
+ for(var/list/time in random_times - first_time)
+ animate(alpha = 235, time = time[1], easing = CUBIC_EASING|EASE_OUT)
+ animate(alpha = 235, time = first_time[2])
+ animate(alpha = 255, time = first_time[3], easing = CUBIC_EASING|EASE_OUT)
+ animate(alpha = 255, time = first_time[4])
+
+ // I'd really love to do both these in the same loop but parallel animations are the devil from the bible
+ // I don't think any of these are directional but just in case
+ animate(cone_light, alpha = 235, time = first_time[1], easing = CUBIC_EASING|EASE_OUT, loop = -1)
+ animate(alpha = 235, time = first_time[2])
+ animate(alpha = 255, time = first_time[3], easing = CUBIC_EASING|EASE_OUT)
+ animate(alpha = 255, time = first_time[4])
+ for(var/list/time in random_times - first_time)
+ animate(alpha = 235, time = time[1], easing = CUBIC_EASING|EASE_OUT)
+ animate(alpha = 235, time = first_time[2])
+ animate(alpha = 255, time = first_time[3], easing = CUBIC_EASING|EASE_OUT)
+ animate(alpha = 255, time = first_time[4])
diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm
index b671de56cfcd..fef97f729426 100644
--- a/code/modules/lighting/lighting_atom.dm
+++ b/code/modules/lighting/lighting_atom.dm
@@ -139,6 +139,43 @@
SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_COLOR, .)
return .
+/// Setter for whether or not this atom's light is on.
+/atom/proc/set_light_on(new_value)
+ if(new_value == light_on || light_flags & LIGHT_FROZEN)
+ return
+ if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_ON, new_value) & COMPONENT_BLOCK_LIGHT_UPDATE)
+ return
+ . = light_on
+ light_on = new_value
+ SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_ON, .)
+ return .
+
+/// Setter for the light flags of this atom.
+/atom/proc/set_light_flags(new_value)
+ if(new_value == light_flags || (light_flags & LIGHT_FROZEN && new_value & LIGHT_FROZEN))
+ return
+ if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_FLAGS, new_value) & COMPONENT_BLOCK_LIGHT_UPDATE)
+ return
+ . = light_flags
+ light_flags = new_value
+ SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_FLAGS, .)
+ return .
+
+// procs that only apply to OVERLAY_LIGHT
+
+/// Setter for an optional render_source to apply to this atom's light overlay
+/atom/proc/set_light_render_source(new_source)
+ if(new_source == light_flags || light_flags & LIGHT_FROZEN)
+ return
+ if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_RENDER_SOURCE, new_source) & COMPONENT_BLOCK_LIGHT_UPDATE)
+ return
+ . = light_render_source
+ light_render_source = new_source
+ SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_RENDER_SOURCE, .)
+ return .
+
+// procs that only apply to COMPLEX_LIGHT
+
/// Setter for the light angle of this atom
/atom/proc/set_light_angle(new_value)
if(new_value == light_angle || light_flags & LIGHT_FROZEN)
@@ -162,17 +199,6 @@
SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_DIR, .)
return .
-/// Setter for whether or not this atom's light is on.
-/atom/proc/set_light_on(new_value)
- if(new_value == light_on || light_flags & LIGHT_FROZEN)
- return
- if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_ON, new_value) & COMPONENT_BLOCK_LIGHT_UPDATE)
- return
- . = light_on
- light_on = new_value
- SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_ON, .)
- return .
-
/// Setter for the height of our light
/atom/proc/set_light_height(new_value)
if(new_value == light_height || light_flags & LIGHT_FROZEN)
@@ -184,16 +210,6 @@
SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_HEIGHT, .)
return .
-/// Setter for the light flags of this atom.
-/atom/proc/set_light_flags(new_value)
- if(new_value == light_flags || (light_flags & LIGHT_FROZEN && new_value & LIGHT_FROZEN))
- return
- if(SEND_SIGNAL(src, COMSIG_ATOM_SET_LIGHT_FLAGS, new_value) & COMPONENT_BLOCK_LIGHT_UPDATE)
- return
- . = light_flags
- light_flags = new_value
- SEND_SIGNAL(src, COMSIG_ATOM_UPDATE_LIGHT_FLAGS, .)
- return .
/atom/proc/get_light_offset()
return list(0, 0)
diff --git a/code/modules/lootpanel/ss_looting.dm b/code/modules/lootpanel/ss_looting.dm
index 94e12f88f952..eb8cff36e696 100644
--- a/code/modules/lootpanel/ss_looting.dm
+++ b/code/modules/lootpanel/ss_looting.dm
@@ -2,7 +2,7 @@
/// Queues image generation for search objects without icons
SUBSYSTEM_DEF(looting)
name = "Loot Icon Generation"
- flags = SS_NO_INIT
+ ss_flags = SS_NO_INIT
priority = FIRE_PRIORITY_PROCESS
runlevels = RUNLEVEL_LOBBY|RUNLEVELS_DEFAULT
wait = 0.5 SECONDS
diff --git a/code/modules/lost_crew/damages/post_mortem.dm b/code/modules/lost_crew/damages/post_mortem.dm
index 5eeaabc3302c..0e70b0cad62c 100644
--- a/code/modules/lost_crew/damages/post_mortem.dm
+++ b/code/modules/lost_crew/damages/post_mortem.dm
@@ -36,7 +36,11 @@
/datum/corpse_damage/post_mortem/limb_loss/apply_to_body(mob/living/carbon/human/body, severity, list/saved_movables, list/datum/callback/on_revive_and_player_occupancy)
var/limbs_to_take = round(min_limbs + (max_limbs - min_limbs) * severity)
- var/list/limbs_we_can_take = body.bodyparts - body.get_bodypart(BODY_ZONE_HEAD) - body.get_bodypart(BODY_ZONE_CHEST)
+ var/list/limbs_we_can_take = list()
+ for(var/zone in GLOB.limb_zones)
+ var/bodypart = body.get_bodypart(zone)
+ if(bodypart)
+ limbs_we_can_take += bodypart
if(!limbs_we_can_take.len)
return
diff --git a/code/modules/lost_crew/lost_crew_manager.dm b/code/modules/lost_crew/lost_crew_manager.dm
index 1494204ad712..08d6d7246813 100644
--- a/code/modules/lost_crew/lost_crew_manager.dm
+++ b/code/modules/lost_crew/lost_crew_manager.dm
@@ -36,9 +36,12 @@ GLOBAL_DATUM_INIT(lost_crew_manager, /datum/lost_crew_manager, new)
var/datum/corpse_damage_class/scenario = forced_class || pick_weight(scenarios)
scenario = new scenario ()
+ new_body.living_flags |= STOP_OVERLAY_UPDATE_BODY_PARTS
scenario.apply_character(new_body, protected_items, recovered_items, on_revive_and_player_occupancy, body_data)
scenario.apply_injuries(new_body, recovered_items, on_revive_and_player_occupancy, body_data)
scenario.death_lore += "I should get a formalized assignment!"
+ new_body.living_flags &= ~STOP_OVERLAY_UPDATE_BODY_PARTS
+ new_body.update_body_parts()
. = new_body
// so bodies can also be used for runes, morgue, etc
diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/watcher_grave.dm b/code/modules/mapfluff/ruins/lavalandruin_code/watcher_grave.dm
index 27d83a9e0426..64d4e5aaaa84 100644
--- a/code/modules/mapfluff/ruins/lavalandruin_code/watcher_grave.dm
+++ b/code/modules/mapfluff/ruins/lavalandruin_code/watcher_grave.dm
@@ -147,7 +147,7 @@
pixel_y = 22
alpha = 0
/// Who are we following?
- var/atom/parent
+ var/atom/movable/parent
/// Datum which keeps us hanging out with our parent
var/datum/movement_detector/tracker
/// Type of projectile we fire
@@ -184,15 +184,22 @@
for (var/mob/living/potential_target in oview(5, src))
if (!ismining(potential_target) || potential_target.stat == DEAD)
continue
- if (!potential_target.has_faction(target_faction))
+ if (!potential_target.has_faction(target_faction) || parent.has_ally(potential_target))
continue
shoot_at(potential_target)
return
/// Take a shot
/obj/effect/watcher_orbiter/proc/shoot_at(atom/target)
+ var/list/ignore_targets = list(parent)
+ if(isliving(parent))
+ var/mob/living/living_parent = parent
+ if(LAZYLEN(living_parent.buckled_mobs))
+ ignore_targets += living_parent.buckled_mobs
+ if(living_parent.buckled)
+ ignore_targets += living_parent.buckled
COOLDOWN_START(src, shot_cooldown, fire_delay)
- fire_projectile(projectile_type, target, projectile_sound, ignore_targets = list(parent))
+ fire_projectile(projectile_type, target, projectile_sound, ignore_targets = ignore_targets)
/// Set ourselves up to track and orbit around a guy
/obj/effect/watcher_orbiter/proc/follow(atom/movable/target)
diff --git a/code/modules/mining/lavaland/mining_loot/consumables.dm b/code/modules/mining/lavaland/mining_loot/consumables.dm
index ca9d22c0f55c..29714d7945e6 100644
--- a/code/modules/mining/lavaland/mining_loot/consumables.dm
+++ b/code/modules/mining/lavaland/mining_loot/consumables.dm
@@ -96,8 +96,8 @@
ADD_TRAIT(being, TRAIT_THERMAL_VISION, REF(src))
being.update_sight()
-/obj/effect/wisp/stop_orbit(datum/component/orbiter/orbits)
- if(!ismob(orbit_target))
+/obj/effect/wisp/stop_orbit(datum/component/orbiter/orbits, refreshing = FALSE)
+ if(!ismob(orbit_target) || refreshing)
return ..()
var/mob/being = orbit_target
UnregisterSignal(being, COMSIG_MOB_UPDATE_SIGHT)
diff --git a/code/modules/mining/lavaland/mining_loot/megafauna/legion.dm b/code/modules/mining/lavaland/mining_loot/megafauna/legion.dm
index 9f640102edf4..cb8fa5532718 100644
--- a/code/modules/mining/lavaland/mining_loot/megafauna/legion.dm
+++ b/code/modules/mining/lavaland/mining_loot/megafauna/legion.dm
@@ -10,7 +10,7 @@
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
slot_flags = ITEM_SLOT_BACK
- w_class = WEIGHT_CLASS_BULKY
+ w_class = WEIGHT_CLASS_NORMAL
force = 20
damtype = BURN
hitsound = 'sound/items/weapons/taserhit.ogg'
diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm
index ea0345f78d02..b23f7b9ba9af 100644
--- a/code/modules/mining/mine_items.dm
+++ b/code/modules/mining/mine_items.dm
@@ -411,7 +411,7 @@
return
setDir(movedir)
- var/datum/move_loop/loop = GLOB.move_manager.move(src, dir, delay = calculate_delay(), subsystem = SSconveyors, flags = MOVEMENT_LOOP_START_FAST|MOVEMENT_LOOP_IGNORE_PRIORITY)
+ var/datum/move_loop/loop = GLOB.move_manager.move(src, dir, delay = calculate_delay(), subsystem = SSconveyors, flags = MOVEMENT_LOOP_START_INSTANT|MOVEMENT_LOOP_IGNORE_PRIORITY)
RegisterSignal(loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(check_rail))
RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(decay_momentum))
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index c1dc5d97ed48..e528d160e8ac 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -410,6 +410,8 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
// Update med huds
current_mob.med_hud_set_status()
current_mob.log_message("had their player ([key_name(src)]) do-not-resuscitate / DNR", LOG_GAME, color = COLOR_GREEN, log_globally = FALSE)
+ SEND_SIGNAL(current_mob, COMSIG_LIVING_DNR, src)
+
log_message("has opted to do-not-resuscitate / DNR from their body ([current_mob])", LOG_GAME, color = COLOR_GREEN)
// Disassociates observer mind from the body mind
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index c3d55e7167b4..4c57ced89bbb 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -677,6 +677,7 @@
if(hud_used)
hud_used.build_hand_slots()
+ hud_used.healthdoll.update_body_zones()
//GetAllContents that is reasonable and not stupid
/mob/living/proc/get_all_gear(equipment_flags = INCLUDE_ACCESSORIES|INCLUDE_PROSTHETICS, recursive = TRUE)
diff --git a/code/modules/mob/living/basic/clown/clown.dm b/code/modules/mob/living/basic/clown/clown.dm
index 7a135e74317d..5b230a6605d4 100644
--- a/code/modules/mob/living/basic/clown/clown.dm
+++ b/code/modules/mob/living/basic/clown/clown.dm
@@ -481,6 +481,7 @@
flick("glutton_mouth", src)
/mob/living/basic/clown/mutant/glutton/tamed(mob/living/tamer, atom/food)
+ . = ..()
buckle_lying = 0
AddElement(/datum/element/ridable, /datum/component/riding/creature/glutton)
diff --git a/code/modules/mob/living/basic/cult/constructs/harvester.dm b/code/modules/mob/living/basic/cult/constructs/harvester.dm
index 8fc1226ab097..1c36453b1847 100644
--- a/code/modules/mob/living/basic/cult/constructs/harvester.dm
+++ b/code/modules/mob/living/basic/cult/constructs/harvester.dm
@@ -46,7 +46,7 @@
return ..()
var/mob/living/carbon/carbon_target = attack_target
- for(var/obj/item/bodypart/limb as anything in carbon_target.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in carbon_target.get_bodyparts())
if(limb.body_part == HEAD || limb.body_part == CHEST)
continue
return ..() //if any arms or legs exist, attack
diff --git a/code/modules/mob/living/basic/cytology/vatbeast.dm b/code/modules/mob/living/basic/cytology/vatbeast.dm
index 84eb8e1fcd45..a52411f0d824 100644
--- a/code/modules/mob/living/basic/cytology/vatbeast.dm
+++ b/code/modules/mob/living/basic/cytology/vatbeast.dm
@@ -51,6 +51,7 @@
ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(enjoyed_food))
/mob/living/basic/vatbeast/tamed(mob/living/tamer, obj/item/food)
+ . = ..()
buckle_lying = 0
AddElement(/datum/element/ridable, /datum/component/riding/creature/vatbeast)
set_faction(list(FACTION_NEUTRAL))
diff --git a/code/modules/mob/living/basic/farm_animals/cow/_cow.dm b/code/modules/mob/living/basic/farm_animals/cow/_cow.dm
index d55869f751dc..3c6b1e81422d 100644
--- a/code/modules/mob/living/basic/farm_animals/cow/_cow.dm
+++ b/code/modules/mob/living/basic/farm_animals/cow/_cow.dm
@@ -79,6 +79,7 @@
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15)
/mob/living/basic/cow/tamed(mob/living/tamer, atom/food)
+ . = ..()
visible_message("[src] [tame_message] as it seems to bond with [tamer].", "You [self_tame_message], recognizing [tamer] as your new pal.")
AddElement(/datum/element/ridable, /datum/component/riding/creature/cow)
diff --git a/code/modules/mob/living/basic/farm_animals/goat/_goat.dm b/code/modules/mob/living/basic/farm_animals/goat/_goat.dm
index 583542ff5ce2..0f94efaf7fbd 100644
--- a/code/modules/mob/living/basic/farm_animals/goat/_goat.dm
+++ b/code/modules/mob/living/basic/farm_animals/goat/_goat.dm
@@ -79,7 +79,7 @@
if(ishuman(living_target))
var/mob/living/carbon/human/plant_man = target
- edible_bodypart = pick(plant_man.bodyparts)
+ edible_bodypart = pick(plant_man.get_bodyparts())
edible_bodypart.dismember()
living_target.visible_message(
diff --git a/code/modules/mob/living/basic/farm_animals/pig.dm b/code/modules/mob/living/basic/farm_animals/pig.dm
index 784e1bfb221b..0ef58faca89b 100644
--- a/code/modules/mob/living/basic/farm_animals/pig.dm
+++ b/code/modules/mob/living/basic/farm_animals/pig.dm
@@ -55,6 +55,7 @@
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15)
/mob/living/basic/pig/tamed(mob/living/tamer, atom/food)
+ . = ..()
AddElement(/datum/element/ridable, /datum/component/riding/creature/pig)
visible_message(span_notice("[src] snorts respectfully."))
diff --git a/code/modules/mob/living/basic/farm_animals/pony.dm b/code/modules/mob/living/basic/farm_animals/pony.dm
index 0989fabae0ff..a44d042809c9 100644
--- a/code/modules/mob/living/basic/farm_animals/pony.dm
+++ b/code/modules/mob/living/basic/farm_animals/pony.dm
@@ -60,6 +60,7 @@
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15, unique = unique_tamer)
/mob/living/basic/pony/tamed(mob/living/tamer, atom/food)
+ . = ..()
playsound(src, 'sound/mobs/non-humanoids/pony/snort.ogg', 50)
AddElement(/datum/element/ridable, /datum/component/riding/creature/pony)
visible_message(span_notice("[src] snorts happily."))
diff --git a/code/modules/mob/living/basic/icemoon/wolf/wolf.dm b/code/modules/mob/living/basic/icemoon/wolf/wolf.dm
index c50c56c38c3f..9d1393fbc3b1 100644
--- a/code/modules/mob/living/basic/icemoon/wolf/wolf.dm
+++ b/code/modules/mob/living/basic/icemoon/wolf/wolf.dm
@@ -68,6 +68,7 @@
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 15, bonus_tame_chance = 5)
/mob/living/basic/mining/wolf/tamed(mob/living/tamer, atom/food)
+ . = ..()
new /obj/effect/temp_visual/heart(src.loc)
// ride wolf, life good
AddElement(/datum/element/ridable, /datum/component/riding/creature/wolf)
diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm
index 9a531a7bb450..b20f29364bd3 100644
--- a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm
+++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm
@@ -118,6 +118,7 @@
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 5)
/mob/living/basic/mining/goldgrub/tamed(mob/living/tamer, atom/food)
+ . = ..()
new /obj/effect/temp_visual/heart(src.loc)
AddElement(/datum/element/ridable, /datum/component/riding/creature/goldgrub)
AddComponent(/datum/component/obeys_commands, pet_commands)
diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath.dm b/code/modules/mob/living/basic/lavaland/goliath/goliath.dm
index c2c597dfd31b..6aef2570060d 100644
--- a/code/modules/mob/living/basic/lavaland/goliath/goliath.dm
+++ b/code/modules/mob/living/basic/lavaland/goliath/goliath.dm
@@ -36,8 +36,6 @@
var/tentacle_warning_state = "goliath_preattack"
/// Can this kind of goliath be tamed?
var/tameable = TRUE
- /// Has this particular goliath been tamed?
- var/tamed = FALSE
/// Can someone ride us around like a horse?
var/saddled = FALSE
/// Slight cooldown to prevent double-dipping if we use both abilities at once
@@ -112,7 +110,7 @@
if (saddled)
balloon_alert(user, "already saddled!")
return
- if (!tamed)
+ if (!HAS_TRAIT(src, TRAIT_TAMED))
balloon_alert(user, "too rowdy!")
return
balloon_alert(user, "affixing saddle...")
@@ -150,10 +148,6 @@
return
icon_state = tentacle_warning_state
-/// Get ready for mounting
-/mob/living/basic/mining/goliath/tamed(mob/living/tamer, atom/food)
- tamed = TRUE
-
// Copy entire faction rather than just placing user into faction, to avoid tentacle peril on station
/mob/living/basic/mining/goliath/befriend(mob/living/new_friend)
. = ..()
diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm
index 5c5107162cfb..1801d2dc189b 100644
--- a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm
+++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm
@@ -74,6 +74,7 @@
charge.Trigger(target = atom_target)
/mob/living/basic/mining/lobstrosity/tamed(mob/living/tamer, obj/item/food)
+ . = ..()
new /obj/effect/temp_visual/heart(loc)
/// Pet commands for this mob, however you'll have to tame juvenile lobstrosities to a trained adult one.
var/list/pet_commands = list(
@@ -167,8 +168,6 @@
base_fishing_level = SKILL_LEVEL_NOVICE
/// What do we become when we grow up?
var/mob/living/basic/mining/lobstrosity/grow_type = /mob/living/basic/mining/lobstrosity
- /// Were we tamed? If yes, tame the mob we become when we grow up too.
- var/was_tamed = FALSE
/datum/emote/lobstrosity_juvenile
abstract_type = /datum/emote/lobstrosity_juvenile
@@ -226,7 +225,6 @@
/mob/living/basic/mining/lobstrosity/juvenile/tamed(mob/living/tamer, obj/item/food)
. = ..()
- was_tamed = TRUE
// They are more pettable I guess
AddElement(/datum/element/pet_bonus, "chitter")
REMOVE_TRAIT(src, TRAIT_MOB_HIDE_HAPPINESS, INNATE_TRAIT)
@@ -237,7 +235,7 @@
/mob/living/basic/mining/lobstrosity/juvenile/proc/grow_up()
var/name_to_use = name == initial(name) ? grow_type::name : name
var/mob/living/basic/mining/lobstrosity/grown = change_mob_type(grow_type, get_turf(src), name_to_use)
- if(was_tamed)
+ if(HAS_TRAIT(src, TRAIT_TAMED))
grown.tamed()
for(var/friend in ai_controller?.blackboard?[BB_FRIENDS_LIST])
grown.befriend(friend)
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 4270155c4294..c010da01b90e 100644
--- a/code/modules/mob/living/basic/lavaland/raptor/raptor_color.dm
+++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_color.dm
@@ -197,8 +197,8 @@ GLOBAL_LIST_INIT(raptor_colors, init_raptor_colors())
COMSIG_RAPTOR_WINGS_OPENED, \
COMSIG_RAPTOR_WINGS_CLOSED, \
null, \
- CALLBACK(src, PROC_REF(can_fly)), \
- CALLBACK(src, PROC_REF(can_fly)), \
+ CALLBACK(src, PROC_REF(check_flight)), \
+ CALLBACK(src, PROC_REF(check_flight)), \
)
/obj/item/mob_holder/purple_raptor/Destroy()
@@ -212,9 +212,11 @@ GLOBAL_LIST_INIT(raptor_colors, init_raptor_colors())
if ((slot & ITEM_SLOT_BACK) && ishuman(user) && flight_action)
flight_action.Grant(held_mob)
flight_action.GiveAction(user)
+ RegisterSignal(user, COMSIG_MOVABLE_CHASM_DROPPED, PROC_REF(chasm_react))
/obj/item/mob_holder/purple_raptor/dropped(mob/user, silent)
. = ..()
+ UnregisterSignal(user, COMSIG_MOVABLE_CHASM_DROPPED)
if (wings_open)
toggle_wings(user)
// Removed in Destroy()
@@ -232,7 +234,7 @@ GLOBAL_LIST_INIT(raptor_colors, init_raptor_colors())
source.remove_movespeed_modifier(/datum/movespeed_modifier/jetpack/raptor)
source.add_movespeed_modifier(/datum/movespeed_modifier/jetpack/raptor/slow)
-/obj/item/mob_holder/purple_raptor/proc/can_fly()
+/obj/item/mob_holder/purple_raptor/proc/can_fly(silent = FALSE)
var/mob/living/carbon/human/user = loc
if (!istype(user) || user.stat || user.body_position == LYING_DOWN || isnull(user.client))
return FALSE
@@ -245,9 +247,13 @@ GLOBAL_LIST_INIT(raptor_colors, init_raptor_colors())
if (environment?.return_pressure() >= HAZARD_LOW_PRESSURE + 10)
return TRUE
- to_chat(user, span_warning("The atmosphere is too thin for you to fly!"))
+ if (!silent)
+ to_chat(user, span_warning("The atmosphere is too thin for you to fly!"))
return FALSE
+/obj/item/mob_holder/purple_raptor/proc/check_flight()
+ return can_fly(silent = TRUE)
+
/obj/item/mob_holder/purple_raptor/proc/toggle_wings(mob/living/carbon/human/user)
// In case something goes wrong
if (!istype(user))
@@ -299,8 +305,18 @@ GLOBAL_LIST_INIT(raptor_colors, init_raptor_colors())
UnregisterSignal(user, list(COMSIG_HUMAN_HEIGHT_UPDATED, SIGNAL_ADDTRAIT(TRAIT_FAT), SIGNAL_REMOVETRAIT(TRAIT_FAT)))
SEND_SIGNAL(src, COMSIG_RAPTOR_WINGS_CLOSED, user)
+/obj/item/mob_holder/purple_raptor/proc/chasm_react(mob/living/user, turf/chasm)
+ SIGNAL_HANDLER
+
+ if (wings_open || !can_fly())
+ return
+
+ toggle_wings(user)
+ if (wings_open)
+ return COMPONENT_NO_CHASM_DROP
+
/obj/item/mob_holder/purple_raptor/process(seconds_per_tick)
- if (!can_fly())
+ if (!can_fly(silent = TRUE))
toggle_wings(loc)
return PROCESS_KILL
@@ -406,6 +422,7 @@ GLOBAL_LIST_INIT(raptor_colors, init_raptor_colors())
/datum/raptor_color/blue
color = "blue"
description = "Covered in tough, lava-resistant feathers with thick insulated fur underneath, this breed is capable of marching through lava and fire alike."
+ health = 300
guaranteed_crossbreeds = list(
/datum/raptor_color/red = /datum/raptor_color/purple,
/datum/raptor_color/white = /datum/raptor_color/green,
diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_dex.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_dex.dm
index 93191bd7838b..60a3d83141b2 100644
--- a/code/modules/mob/living/basic/lavaland/raptor/raptor_dex.dm
+++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_dex.dm
@@ -7,6 +7,10 @@
/// Raptor scan data we have stored
var/list/scan_data = list("raptor_scan" = FALSE)
+/obj/item/raptor_dex/Initialize(mapload)
+ . = ..()
+ register_item_context()
+
/obj/item/raptor_dex/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
@@ -61,6 +65,12 @@
scan_data["inherited_traits"] += GLOB.raptor_inherit_traits[index]
playsound(src, 'sound/mobs/non-humanoids/orbie/orbie_send_out.ogg', 20)
- balloon_alert(my_raptor, "scanned")
+ my_raptor.balloon_alert(user, "scanned")
ui_interact(user)
return ITEM_INTERACT_SUCCESS
+
+/obj/item/raptor_dex/add_item_context(obj/item/source, list/context, atom/target, mob/living/user)
+ if(!istype(target, /mob/living/basic/raptor))
+ return NONE
+ context[SCREENTIP_CONTEXT_LMB] = "Scan Raptor"
+ return CONTEXTUAL_SCREENTIP_SET
diff --git a/code/modules/mob/living/basic/pets/dog/_dog.dm b/code/modules/mob/living/basic/pets/dog/_dog.dm
index b9cdb078cfac..aabcfd6440e2 100644
--- a/code/modules/mob/living/basic/pets/dog/_dog.dm
+++ b/code/modules/mob/living/basic/pets/dog/_dog.dm
@@ -155,6 +155,7 @@
///Proc to run on a successful taming attempt
/mob/living/basic/pet/dog/tamed(mob/living/tamer, atom/food)
+ . = ..()
visible_message(span_notice("[src] licks at [tamer] in a friendly manner!"))
/// A dog bone fully heals a dog, and befriends it if it's not your friend.
diff --git a/code/modules/mob/living/basic/pets/parrot/_parrot.dm b/code/modules/mob/living/basic/pets/parrot/_parrot.dm
index 5f667857f40d..8e338d7bf609 100644
--- a/code/modules/mob/living/basic/pets/parrot/_parrot.dm
+++ b/code/modules/mob/living/basic/pets/parrot/_parrot.dm
@@ -447,6 +447,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
return returnable_list
/mob/living/basic/parrot/tamed(mob/living/tamer, atom/food)
+ . = ..()
new /obj/effect/temp_visual/heart(drop_location())
/mob/living/basic/parrot/proc/drop_item_on_signal(mob/living/user)
diff --git a/code/modules/mob/living/basic/ruin_defender/flesh.dm b/code/modules/mob/living/basic/ruin_defender/flesh.dm
index 5d607c2c1a44..94bb50f026dd 100644
--- a/code/modules/mob/living/basic/ruin_defender/flesh.dm
+++ b/code/modules/mob/living/basic/ruin_defender/flesh.dm
@@ -114,7 +114,7 @@
return
var/list/zone_candidates = target.get_missing_limbs()
- for(var/obj/item/bodypart/bodypart in target.bodyparts)
+ for(var/obj/item/bodypart/bodypart in target.get_bodyparts())
if(bodypart.body_zone == BODY_ZONE_HEAD || bodypart.body_zone == BODY_ZONE_CHEST)
continue
if(HAS_TRAIT(bodypart, TRAIT_IGNORED_BY_LIVING_FLESH))
diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp.dm b/code/modules/mob/living/basic/space_fauna/carp/carp.dm
index ea679a382350..6f1ff9e864ff 100644
--- a/code/modules/mob/living/basic/space_fauna/carp/carp.dm
+++ b/code/modules/mob/living/basic/space_fauna/carp/carp.dm
@@ -142,6 +142,7 @@
/// Called when another mob has forged a bond of friendship with this one, passed the taming mob as 'tamer'
/mob/living/basic/carp/tamed(mob/living/tamer, atom/food, feedback = TRUE)
+ . = ..()
AddElement(/datum/element/ridable, ridable_data)
AddComponent(/datum/component/obeys_commands, tamed_commands)
if (!feedback)
diff --git a/code/modules/mob/living/basic/space_fauna/eyeball/_eyeball.dm b/code/modules/mob/living/basic/space_fauna/eyeball/_eyeball.dm
index fd549c92ffec..26bb5548e807 100644
--- a/code/modules/mob/living/basic/space_fauna/eyeball/_eyeball.dm
+++ b/code/modules/mob/living/basic/space_fauna/eyeball/_eyeball.dm
@@ -117,6 +117,7 @@
COOLDOWN_START(src, eye_healing, 15 SECONDS)
/mob/living/basic/eyeball/tamed(mob/living/tamer, atom/food)
+ . = ..()
spin(spintime = 2 SECONDS, speed = 1)
//become passive to the humens
APPLY_FACTION_AND_ALLIES_FROM(src, tamer)
diff --git a/code/modules/mob/living/basic/space_fauna/lightgeist.dm b/code/modules/mob/living/basic/space_fauna/lightgeist.dm
index f59c5070bff9..36ae135619c5 100644
--- a/code/modules/mob/living/basic/space_fauna/lightgeist.dm
+++ b/code/modules/mob/living/basic/space_fauna/lightgeist.dm
@@ -103,7 +103,7 @@
if (!iscarbon(target))
return target.get_brute_loss() > 0 || target.get_fire_loss() > 0
var/mob/living/carbon/carbon_target = target
- for (var/obj/item/bodypart/part in carbon_target.bodyparts)
+ for (var/obj/item/bodypart/part in carbon_target.get_bodyparts())
if (!part.brute_dam && !part.burn_dam)
continue
if (!(part.bodytype & required_bodytype))
diff --git a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
index 4aae0ffa2d7f..436f8e27c894 100644
--- a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
+++ b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
@@ -160,6 +160,7 @@
/mob/living/basic/revenant/proc/update_revenant_appearance()
SIGNAL_HANDLER
update_appearance(UPDATE_ICON)
+ update_mob_action_buttons()
/mob/living/basic/revenant/AltClickOn(atom/target)
if(CAN_I_SEE(target))
@@ -321,6 +322,7 @@
return
ADD_TRAIT(src, TRAIT_NO_TRANSFORM, REVENANT_STUNNED_TRAIT)
dormant = TRUE
+ update_mob_action_buttons()
visible_message(
span_warning("[src] lets out a waning screech as violet mist swirls around its dissolving body!"),
@@ -410,28 +412,39 @@
return TRUE
-/mob/living/basic/revenant/proc/cast_check(essence_cost)
+/mob/living/basic/revenant/proc/cast_check(essence_cost, deduct_essence = TRUE, silent = FALSE)
if(QDELETED(src))
return
var/turf/current = get_turf(src)
if(isclosedturf(current))
- to_chat(src, span_revenwarning("You cannot use abilities from inside of a wall."))
+ if(!silent)
+ to_chat(src, span_revenwarning("You cannot use abilities from inside of a wall."))
return FALSE
for(var/obj/thing in current)
if(!thing.density || thing.CanPass(src, get_dir(current, src)))
continue
- to_chat(src, span_revenwarning("You cannot use abilities inside of a dense object."))
+ if(!silent)
+ to_chat(src, span_revenwarning("You cannot use abilities inside of a dense object."))
return FALSE
+ if(dormant)
+ if(!silent)
+ to_chat(src, span_revenwarning("Your powers lie dormant right now!"))
+ return SPELL_CANCEL_CAST
+
if(HAS_TRAIT(src, TRAIT_REVENANT_INHIBITED))
- to_chat(src, span_revenwarning("Your powers have been suppressed by a nullifying energy!"))
+ if(!silent)
+ to_chat(src, span_revenwarning("Your powers have been suppressed by a nullifying energy!"))
return FALSE
- if(!change_essence_amount(essence_cost, TRUE))
- to_chat(src, span_revenwarning("You lack the essence to use that ability."))
+ essence_cost = abs(essence_cost) * -1
+ var/has_essence = deduct_essence ? change_essence_amount(essence_cost, silent = TRUE) : (essence + essence_cost >= 0)
+ if(!has_essence)
+ if(!silent)
+ to_chat(src, span_revenwarning("You lack the essence to use that ability!"))
return FALSE
return TRUE
@@ -455,6 +468,7 @@
incorporeal_move = INCORPOREAL_MOVE_JAUNT
RemoveInvisibility(type)
alpha = 255
+ update_mob_action_buttons()
/mob/living/basic/revenant/proc/change_essence_amount(essence_to_change_by, silent = FALSE, source = null)
if(QDELETED(src))
@@ -478,6 +492,19 @@
to_chat(src, span_revenminor("Lost [essence_to_change_by]E [source ? "from [source]":""]."))
return TRUE
+/mob/living/basic/revenant/mob_negates_gravity()
+ return TRUE // i don't gotta explain shit
+
+/mob/living/basic/revenant/vv_edit_var(vname, vval)
+ . = ..()
+ if(vname == NAMEOF(src, essence) || vname == NAMEOF(src, max_essence) || vname == NAMEOF(src, essence_excess))
+ update_health_hud()
+ update_mob_action_buttons()
+
+/mob/living/basic/revenant/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change)
+ . = ..()
+ update_mob_action_buttons()
+
/mob/living/basic/revenant/proc/on_reflect(datum/source, atom/movable/reflecting_in, obj/effect/abstract/reflection)
SIGNAL_HANDLER
// powers are inhibited and we're not revealed so we can't project a reflect
diff --git a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm
index d456f104218b..2030315410cc 100644
--- a/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm
+++ b/code/modules/mob/living/basic/space_fauna/revenant/revenant_abilities.dm
@@ -20,13 +20,10 @@
antimagic_flags = MAGIC_RESISTANCE_HOLY
spell_requirements = NONE
- /// If it's locked, and needs to be unlocked before use
- var/locked = TRUE
/// How much essence it costs to unlock
var/unlock_amount = 100
/// How much essence it costs to use
var/cast_amount = 50
-
/// How long it reveals the revenant
var/reveal_duration = 8 SECONDS
// How long it stuns the revenant
@@ -34,64 +31,27 @@
/datum/action/cooldown/spell/aoe/revenant/New(Target)
. = ..()
- if(!isrevenant(target))
- stack_trace("[type] was given to a non-revenant mob, please don't.")
- qdel(src)
- return
-
- if(locked)
- name = "[initial(name)] ([unlock_amount]SE)"
- else
- name = "[initial(name)] ([cast_amount]E)"
-
-/datum/action/cooldown/spell/aoe/revenant/can_cast_spell(feedback = TRUE)
- . = ..()
- if(!.)
- return FALSE
- if(!isrevenant(owner))
- stack_trace("[type] was owned by a non-revenant mob, please don't.")
- return FALSE
-
- var/mob/living/basic/revenant/ghost = owner
- if(ghost.dormant || HAS_TRAIT(ghost, TRAIT_REVENANT_INHIBITED))
- return FALSE
- if(locked && ghost.essence_excess <= unlock_amount)
- return FALSE
- if(ghost.essence <= cast_amount)
- return FALSE
-
- return TRUE
+ AddComponent(/datum/component/revenant_ability, \
+ unlock_amount = unlock_amount, \
+ cast_amount = cast_amount, \
+ reveal_duration = reveal_duration, \
+ stun_duration = stun_duration, \
+ )
/datum/action/cooldown/spell/aoe/revenant/get_things_to_cast_on(atom/center)
return RANGE_TURFS(aoe_radius, center)
-/datum/action/cooldown/spell/aoe/revenant/before_cast(mob/living/basic/revenant/cast_on)
- . = ..()
- if(. & SPELL_CANCEL_CAST)
- return
-
- if(locked)
- if(!cast_on.unlock(unlock_amount))
- to_chat(cast_on, span_revenwarning("You don't have enough essence to unlock [initial(name)]!"))
- reset_spell_cooldown()
- return . | SPELL_CANCEL_CAST
-
- name = "[initial(name)] ([cast_amount]E)"
- to_chat(cast_on, span_revennotice("You have unlocked [initial(name)]!"))
- locked = FALSE
- reset_spell_cooldown()
- return . | SPELL_CANCEL_CAST
-
- if(!cast_on.cast_check(-cast_amount))
- reset_spell_cooldown()
- return . | SPELL_CANCEL_CAST
-
-/datum/action/cooldown/spell/aoe/revenant/after_cast(mob/living/basic/revenant/cast_on)
+/datum/action/cooldown/spell/aoe/revenant/vv_edit_var(var_name, var_value)
. = ..()
- if(reveal_duration > 0 SECONDS)
- cast_on.apply_status_effect(/datum/status_effect/revenant/revealed, reveal_duration)
- if(stun_duration > 0 SECONDS)
- cast_on.apply_status_effect(/datum/status_effect/incapacitating/paralyzed/revenant, stun_duration)
+ // gross getcomp, but this is solely to make life easier for badmins/debug. sue me
+ var/datum/component/revenant_ability/rev_comp = GetComponent(/datum/component/revenant_ability)
+ switch(var_name)
+ if(NAMEOF(src, unlock_amount))
+ rev_comp.set_unlock_amount(var_value)
+ if(NAMEOF(src, cast_amount))
+ rev_comp.set_cast_amount(var_value)
+ if(NAMEOF(src, reveal_duration), NAMEOF(src, stun_duration))
+ rev_comp.set_durations(reveal_duration, stun_duration)
//Overload Light: Breaks a light that's online and sends out lightning bolts to all nearby people.
/datum/action/cooldown/spell/aoe/revenant/overload
diff --git a/code/modules/mob/living/basic/space_fauna/spider/spider.dm b/code/modules/mob/living/basic/space_fauna/spider/spider.dm
index 8cf805697669..1399e5e1b0a7 100644
--- a/code/modules/mob/living/basic/space_fauna/spider/spider.dm
+++ b/code/modules/mob/living/basic/space_fauna/spider/spider.dm
@@ -53,6 +53,13 @@
var/menu_description = "Tanky and strong for the defense of the nest and other spiders."
/// If true then you shouldn't be told that you're a spider antagonist as soon as you are placed into this mob
var/apply_spider_antag = TRUE
+ /// Commands you can give this spider once it is tamed
+ var/static/list/tamed_commands = list(
+ /datum/pet_command/idle,
+ /datum/pet_command/free,
+ /datum/pet_command/follow,
+ /datum/pet_command/attack,
+ )
/datum/emote/spider
abstract_type = /datum/emote/spider
@@ -85,6 +92,17 @@
webbing.Grant(src)
ai_controller?.set_blackboard_key(BB_SPIDER_WEB_ACTION, webbing)
+ var/static/list/food_types = list(
+ /obj/item/food/meat/slab/human/mutant/lizard,
+ /obj/item/food/meat/slab/human/mutant/fly,
+ /obj/item/food/meat/slab/human/mutant/moth,
+ /obj/item/food/meat/slab/mouse,
+ /obj/item/food/meat/slab/mothroach,
+ /obj/item/food/meat/slab/blood_worm,
+ /obj/item/food/deadmouse,
+ )
+ AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 20, bonus_tame_chance = 10)
+
/mob/living/basic/spider/Login()
. = ..()
if(!. || !client)
@@ -92,6 +110,8 @@
GLOB.spidermobs[src] = TRUE
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/player_spider_modifier, multiplicative_slowdown = player_speed_modifier)
+ AddElement(/datum/element/ridable, /datum/component/riding/creature/spider)
+
/mob/living/basic/spider/Logout()
. = ..()
remove_movespeed_modifier(/datum/movespeed_modifier/player_spider_modifier)
@@ -100,6 +120,12 @@
GLOB.spidermobs -= src
return ..()
+/mob/living/basic/spider/tamed(mob/living/tamer, atom/food, feedback = TRUE)
+ . = ..()
+ new /obj/effect/temp_visual/heart(src.loc)
+ AddElement(/datum/element/ridable, /datum/component/riding/creature/spider)
+ AddComponent(/datum/component/obeys_commands, tamed_commands)
+
/mob/living/basic/spider/mob_negates_gravity()
if(locate(/obj/structure/spider/stickyweb) in loc)
return TRUE
@@ -158,6 +184,12 @@
grown.set_name()
grown.set_brute_loss(get_brute_loss())
grown.set_fire_loss(get_fire_loss())
+
+ if(HAS_TRAIT(src, TRAIT_TAMED))
+ grown.tamed()
+ else if(istype(grown, /mob/living/basic/spider/giant)) // Adults cannot be tamed via snacks
+ qdel(grown.GetComponent(/datum/component/tameable))
+
qdel(src)
/**
diff --git a/code/modules/mob/living/basic/vermin/cockroach/cockroach.dm b/code/modules/mob/living/basic/vermin/cockroach/cockroach.dm
index 6a1c3f703dfa..76dee59ce357 100644
--- a/code/modules/mob/living/basic/vermin/cockroach/cockroach.dm
+++ b/code/modules/mob/living/basic/vermin/cockroach/cockroach.dm
@@ -111,6 +111,7 @@
for(var/mob/living/mob_in_turf in messy_turf)
mob_in_turf.visible_message(span_danger("[mob_in_turf] is splattered with blood!"), span_userdanger("You're splattered with blood!"))
mob_in_turf.add_blood_DNA(list("Non-human DNA" = random_human_blood_type()))
+ mob_in_turf.add_mood_event("splattered_with_blood", /datum/mood_event/splattered_with_blood)
playsound(mob_in_turf, 'sound/effects/splat.ogg', 50, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE)
return ..()
@@ -243,4 +244,3 @@
maxHealth = 2
minion_path = null
gold_core_spawnable = NO_SPAWN
-
diff --git a/code/modules/mob/living/basic/vermin/mouse.dm b/code/modules/mob/living/basic/vermin/mouse.dm
index 698eb30c752c..67cae390295f 100644
--- a/code/modules/mob/living/basic/vermin/mouse.dm
+++ b/code/modules/mob/living/basic/vermin/mouse.dm
@@ -28,8 +28,6 @@
ai_controller = /datum/ai_controller/basic_controller/mouse
- /// Whether this rat is friendly to players
- var/tame = FALSE
/// What color our mouse is. Brown, gray and white - leave blank for random.
var/body_color
/// Does this mouse contribute to the ratcap?
@@ -63,7 +61,8 @@
SSmobs.cheeserats |= src
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
- src.tame = tame
+ if(tame)
+ ADD_TRAIT(src, TRAIT_TAMED, INNATE_TRAIT)
if(!isnull(new_body_color))
body_color = new_body_color
if(isnull(body_color))
@@ -81,7 +80,7 @@
AddComponent(/datum/component/swarming, 16, 16) //max_x, max_y
/mob/living/basic/mouse/proc/make_tameable()
- if (tame)
+ if (HAS_TRAIT(src, TRAIT_TAMED))
add_faction(FACTION_NEUTRAL)
else
var/static/list/food_types = list(/obj/item/food/cheese)
@@ -196,9 +195,9 @@
/// Called when a mouse is hand-fed some cheese, it will stop being afraid of humans
/mob/living/basic/mouse/tamed(mob/living/tamer, obj/item/food/cheese/cheese)
+ . = ..()
new /obj/effect/temp_visual/heart(loc)
add_faction(FACTION_NEUTRAL)
- tame = TRUE
try_consume_cheese(cheese)
ai_controller.CancelActions() // Interrupt any current fleeing
@@ -248,7 +247,7 @@
/// Creates a new mouse based on this mouse's subtype.
/mob/living/basic/mouse/proc/create_a_new_rat()
- new /mob/living/basic/mouse(loc, /* tame = */ tame)
+ new /mob/living/basic/mouse(loc, HAS_TRAIT(src, TRAIT_TAMED))
/// Biting into a cable will cause a mouse to get shocked and die if applicable. Or do nothing if they're lucky.
/mob/living/basic/mouse/proc/try_bite_cable(obj/structure/cable/cable)
@@ -302,7 +301,7 @@
contributes_to_ratcap = FALSE
/mob/living/basic/mouse/brown/tom/make_tameable()
- tame = TRUE
+ ADD_TRAIT(src, TRAIT_TAMED, INNATE_TRAIT)
return ..()
/mob/living/basic/mouse/brown/tom/Initialize(mapload)
@@ -312,7 +311,7 @@
AddElement(/datum/element/pet_bonus, "squeak")
/mob/living/basic/mouse/brown/tom/create_a_new_rat()
- new /mob/living/basic/mouse/brown(loc, /* tame = */ tame) // dominant gene
+ new /mob/living/basic/mouse/brown(loc, HAS_TRAIT(src, TRAIT_TAMED)) // dominant gene
/mob/living/basic/mouse/rat
name = "rat"
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index a4d519062036..e5410bdf0abd 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -180,7 +180,7 @@
bleed(bleed_rate * seconds_per_tick)
bleed_warn(bleed_rate)
- for (var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for (var/obj/item/bodypart/bodypart as anything in get_bodyparts())
if (bodypart.generic_bleedstacks)
bodypart.adjustBleedStacks(-1, 0)
@@ -263,7 +263,7 @@
/// Has each bodypart update its bleed/wound overlay icon states
/mob/living/carbon/proc/update_bodypart_bleed_overlays()
- for(var/obj/item/bodypart/iter_part as anything in bodyparts)
+ for(var/obj/item/bodypart/iter_part as anything in get_bodyparts())
iter_part.update_part_wound_overlay()
/// Bleeds amount units of blood from the mob, sometimes creating a blood splatter on the floor.
@@ -290,7 +290,7 @@
return 0
. = 0
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts())
. += bodypart.cached_bleed_rate
/mob/living/carbon/human/get_bleed_rate()
@@ -359,7 +359,7 @@
/mob/living/carbon/restore_blood()
. = ..()
- for(var/obj/item/bodypart/bodypart_to_restore as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart_to_restore as anything in get_bodyparts())
bodypart_to_restore.setBleedStacks(0)
/****************************************************
@@ -682,7 +682,7 @@
* * splatter_direction: Which direction the blood is flying
* * splatter_strength: How many tiles it can go, and how many items it can pass over and dirty
*/
-/mob/living/carbon/proc/spray_blood(splatter_direction, splatter_strength = 3)
+/mob/living/proc/spray_blood(splatter_direction, splatter_strength = 3)
// Check if we can bleed and if our splatter can even go anywhere
if(!isturf(loc) || can_bleed(BLOOD_COVER_TURFS) != BLEED_SPLATTER)
return
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 1e5f807acdd8..584307727f6f 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -444,8 +444,7 @@
var/total_burn = 0
var/total_brute = 0
var/total_aggravated = 0 // DARKPACK EDIT ADD - AGGRAVATED_DAMAGE
- for(var/X in bodyparts) //hardcoded to streamline things a bit
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
total_brute += (BP.brute_dam * BP.body_damage_coeff)
total_burn += (BP.burn_dam * BP.body_damage_coeff)
total_aggravated += (BP.aggravated_dam * BP.body_damage_coeff) // DARKPACK EDIT ADD - AGGRAVATED_DAMAGE
@@ -804,7 +803,7 @@
if(heal_flags & HEAL_LIMBS)
regenerate_limbs()
- for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/bodypart/limb as anything in get_bodyparts(include_stumps = TRUE))
limb.remove_surgical_state(ALL)
if(heal_flags & (HEAL_REFRESH_ORGANS|HEAL_ORGANS))
@@ -947,13 +946,15 @@
switch(new_bodypart.body_part)
if(LEG_LEFT, LEG_RIGHT)
- set_num_legs(num_legs + 1)
- if(!new_bodypart.bodypart_disabled)
- set_usable_legs(usable_legs + 1)
+ if(!IS_STUMP(new_bodypart))
+ set_num_legs(num_legs + 1)
+ if(!new_bodypart.bodypart_disabled)
+ set_usable_legs(usable_legs + 1)
if(ARM_LEFT, ARM_RIGHT)
- set_num_hands(num_hands + 1)
- if(!new_bodypart.bodypart_disabled)
- set_usable_hands(usable_hands + 1)
+ if(!IS_STUMP(new_bodypart))
+ set_num_hands(num_hands + 1)
+ if(!new_bodypart.bodypart_disabled)
+ set_usable_hands(usable_hands + 1)
synchronize_bodytypes()
synchronize_bodyshapes()
@@ -974,13 +975,27 @@
switch(old_bodypart.body_part)
if(LEG_LEFT, LEG_RIGHT)
- set_num_legs(num_legs - 1)
- if(!old_bodypart.bodypart_disabled)
- set_usable_legs(usable_legs - 1)
+ if(!IS_STUMP(old_bodypart))
+ set_num_legs(num_legs - 1)
+ if(!old_bodypart.bodypart_disabled)
+ set_usable_legs(usable_legs - 1)
if(ARM_LEFT, ARM_RIGHT)
- set_num_hands(num_hands - 1)
- if(!old_bodypart.bodypart_disabled)
- set_usable_hands(usable_hands - 1)
+ if(!IS_STUMP(old_bodypart))
+ set_num_hands(num_hands - 1)
+ if(!old_bodypart.bodypart_disabled)
+ set_usable_hands(usable_hands - 1)
+
+ if(!special && old_bodypart.stump_typepath)
+ if(old_bodypart.type == old_bodypart.stump_typepath)
+ stack_trace("Attempted to replace a stump with a stump")
+ else
+ var/obj/item/bodypart/stump = new old_bodypart.stump_typepath()
+ stump.bodyshape = old_bodypart.bodyshape
+ stump.bodytype = old_bodypart.bodytype
+ if(!stump.try_attach_limb(src, special = TRUE))
+ // the only way this can happen is if the stump is rejected via signal
+ // not much we can do about that besides hope they know what they're doing
+ qdel(stump)
synchronize_bodytypes()
synchronize_bodyshapes()
@@ -988,7 +1003,7 @@
///Updates the bodypart speed modifier based on our bodyparts.
/mob/living/carbon/proc/update_bodypart_speed_modifier()
var/final_modification = 0
- for(var/obj/item/bodypart/leg/bodypart in bodyparts)
+ for(var/obj/item/bodypart/leg/bodypart in get_bodyparts())
final_modification += bodypart.speed_modifier
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/bodypart, update = TRUE, multiplicative_slowdown = final_modification)
@@ -1021,7 +1036,7 @@
return
var/list/limb_list = list()
if(edit_action == "remove")
- for(var/obj/item/bodypart/iter_part as anything in bodyparts)
+ for(var/obj/item/bodypart/iter_part as anything in get_bodyparts())
limb_list += iter_part.body_zone
limb_list -= BODY_ZONE_CHEST
else
@@ -1055,7 +1070,7 @@
var/limb2add = input(usr, "Select a bodypart type to add", "Add/Replace Bodypart") as null|anything in sort_list(limbtypes)
var/obj/item/bodypart/new_bp = new limb2add()
if(new_bp.replace_limb(src))
- admin_ticket_log("key_name_admin(usr)] has replaced [src]'s [part.type] with [new_bp.type]")
+ admin_ticket_log("key_name_admin(usr)] has replaced [src]'s [part?.type || "missing limb"] with [new_bp.type]")
qdel(part)
else
to_chat(usr, "Failed to replace bodypart! They might be incompatible.")
@@ -1133,7 +1148,7 @@
/mob/living/carbon/proc/is_bleeding()
if(!CAN_HAVE_BLOOD(src))
return FALSE
- for(var/obj/item/bodypart/part as anything in bodyparts)
+ for(var/obj/item/bodypart/part as anything in get_bodyparts())
if(part.cached_bleed_rate)
return TRUE
@@ -1143,7 +1158,7 @@
return FALSE
var/total_bleed_rate = 0
- for(var/obj/item/bodypart/part as anything in bodyparts)
+ for(var/obj/item/bodypart/part as anything in get_bodyparts())
total_bleed_rate += part.cached_bleed_rate
return total_bleed_rate
@@ -1305,9 +1320,9 @@
/// Goes through the organs and bodyparts of the mob and updates their blood_dna_info, in case their blood type has changed (via set_species() or otherwise)
/mob/living/carbon/proc/update_cached_blood_dna_info()
var/list/blood_dna_info = get_blood_dna_list()
- for(var/obj/item/organ/organ in organs)
+ for(var/obj/item/organ/organ as anything in organs)
organ.blood_dna_info = blood_dna_info
- for(var/obj/item/bodypart/bodypart in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts())
bodypart.blood_dna_info = blood_dna_info
/// Setter for changing a mob's blood type
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 5dc3cbd7d705..497f133b117e 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -453,7 +453,7 @@
return
var/embeds = FALSE
- for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/bodypart/limb as anything in get_bodyparts())
for(var/obj/item/weapon as anything in limb.embedded_objects)
if(!embeds)
embeds = TRUE
@@ -562,8 +562,7 @@
/mob/living/carbon/get_organic_health()
. = health
- for (var/_limb in bodyparts)
- var/obj/item/bodypart/limb = _limb
+ for (var/obj/item/bodypart/limb as anything in get_bodyparts())
if (!IS_ORGANIC_LIMB(limb))
. += (limb.brute_dam * limb.body_damage_coeff) + (limb.burn_dam * limb.body_damage_coeff)
diff --git a/code/modules/mob/living/carbon/carbon_stripping.dm b/code/modules/mob/living/carbon/carbon_stripping.dm
index 66f2b8b6b998..34733512126c 100644
--- a/code/modules/mob/living/carbon/carbon_stripping.dm
+++ b/code/modules/mob/living/carbon/carbon_stripping.dm
@@ -132,8 +132,8 @@
/datum/strippable_item/hand/left
key = STRIPPABLE_ITEM_LHAND
- hand_index = 1
+ hand_index = LEFT_HANDS
/datum/strippable_item/hand/right
key = STRIPPABLE_ITEM_RHAND
- hand_index = 2
+ hand_index = RIGHT_HANDS
diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm
index 271eef3cd40f..b759c290efea 100644
--- a/code/modules/mob/living/carbon/carbon_update_icons.dm
+++ b/code/modules/mob/living/carbon/carbon_update_icons.dm
@@ -290,7 +290,7 @@
remove_overlay(DAMAGE_LAYER)
var/mutable_appearance/damage_overlay
- for(var/obj/item/bodypart/iter_part as anything in bodyparts)
+ for(var/obj/item/bodypart/iter_part as anything in get_bodyparts())
if(!iter_part.dmg_overlay_type)
continue
if(isnull(damage_overlay) && (iter_part.brutestate || iter_part.burnstate))
@@ -319,7 +319,7 @@
return
var/mutable_appearance/wound_overlay
- for(var/obj/item/bodypart/iter_part as anything in bodyparts)
+ for(var/obj/item/bodypart/iter_part as anything in get_bodyparts())
if(iter_part.bleed_overlay_icon)
var/mutable_appearance/blood_overlay = mutable_appearance('icons/mob/effects/bleed_overlays.dmi', "blank", -WOUND_LAYER, appearance_flags = KEEP_TOGETHER)
blood_overlay.color = blood_type.get_wound_color(src)
@@ -463,38 +463,35 @@
/mob/living/carbon/proc/update_body_parts(update_limb_data)
update_damage_overlays()
update_wound_overlays()
- var/list/needs_update = list()
var/limb_count_update = 0
- for(var/obj/item/bodypart/limb as anything in bodyparts)
- limb.update_limb(is_creating = update_limb_data) //Update limb actually doesn't do much, get_limb_icon is the cpu eater.
+ var/list/new_limbs = list()
+ for(var/body_zone, limb_untyped in get_bodyparts_by_zones())
+ var/obj/item/bodypart/limb = limb_untyped
+ if(isnull(limb) || IS_STUMP(limb))
+ if(icon_render_keys[body_zone])
+ icon_render_keys -= body_zone
+ limb_count_update += 1
+ continue
+
+ // Update limb actually doesn't do much, get_limb_icon is the cpu eater.
+ limb.update_limb(is_creating = update_limb_data)
- var/old_key = icon_render_keys?[limb.body_zone] //Checks the mob's icon render key list for the bodypart
- icon_render_keys[limb.body_zone] = (limb.is_husked) ? limb.generate_husk_key().Join() : limb.generate_icon_key().Join() //Generates a key for the current bodypart
+ var/old_key = icon_render_keys[limb.body_zone]
+ var/new_key = limb.get_cache_key()
- if(icon_render_keys[limb.body_zone] != old_key) //If the keys match, that means the limb doesn't need to be redrawn
- needs_update += limb
+ if(new_key == old_key)
+ new_limbs += limb_icon_cache[new_key]
- limb_count_update += length(needs_update)
- var/list/missing_bodyparts = get_missing_limbs()
- if(((dna ? dna.species.max_bodypart_count : BODYPARTS_DEFAULT_MAXIMUM) - icon_render_keys.len) != missing_bodyparts.len) //Checks to see if the target gained or lost any limbs.
- limb_count_update += 1
- for(var/missing_limb in missing_bodyparts)
- icon_render_keys -= missing_limb //Removes dismembered limbs from the key list
+ else
+ limb_icon_cache[new_key] ||= limb.get_limb_icon(dropped = FALSE)
+ new_limbs += limb_icon_cache[new_key]
+ icon_render_keys[limb.body_zone] = new_key
+ limb_count_update += 1
. = limb_count_update
if(!.)
return
- //GENERATE NEW LIMBS
- var/list/new_limbs = list()
- for(var/obj/item/bodypart/limb as anything in bodyparts)
- if(limb in needs_update)
- var/bodypart_icon = limb.get_limb_icon(dropped = FALSE, update_on = src)
- new_limbs += bodypart_icon
- limb_icon_cache[icon_render_keys[limb.body_zone]] = bodypart_icon //Caches the icon with the bodypart key, as it is new
- else
- new_limbs += limb_icon_cache[icon_render_keys[limb.body_zone]] //Pulls existing sprites from the cache
-
remove_overlay(BODYPARTS_LAYER)
if(new_limbs.len)
@@ -508,6 +505,13 @@
/////////////////////////
// Limb Icon Cache 2.0 //
/////////////////////////
+
+/// Returns a string representing the bodyparts icon cache key
+/obj/item/bodypart/proc/get_cache_key()
+ if(is_husked)
+ return jointext(generate_husk_key(), "-")
+ return jointext(generate_icon_key(), "-")
+
/**
* Called from update_body_parts() these procs handle the limb icon cache.
* the limb icon cache adds an icon_render_key to a human mob, it represents:
@@ -522,20 +526,20 @@
RETURN_TYPE(/list)
. = list()
if(is_dimorphic)
- . += "[limb_gender]-"
- . += "[limb_id]"
- . += "-[body_zone]"
+ . += limb_gender
+ . += limb_id
+ . += body_zone
if(should_draw_greyscale && draw_color)
- . += "-[draw_color]"
+ . += draw_color
if(is_invisible)
- . += "-invisible"
+ . += "invisible"
for(var/datum/bodypart_overlay/overlay as anything in bodypart_overlays)
if(!overlay.can_draw_on_bodypart(src, owner, is_husked))
continue
- . += "-[jointext(overlay.generate_icon_cache(), "-")]"
+ . += overlay.generate_icon_cache()
if(ishuman(owner))
var/mob/living/carbon/human/human_owner = owner
- . += "-[human_owner.mob_height]"
+ . += "[human_owner.mob_height]"
SEND_SIGNAL(src, COMSIG_BODYPART_GENERATE_ICON_KEY, .)
return .
@@ -543,24 +547,22 @@
/obj/item/bodypart/proc/generate_husk_key()
RETURN_TYPE(/list)
. = list()
- . += "[limb_id]-"
- . += "[husk_type]"
- . += "-husk"
- . += "-[body_zone]"
+ if(is_dimorphic)
+ . += limb_gender
+ . += limb_id
+ . += husk_type
+ . += "husk"
+ . += body_zone
if(is_invisible)
- . += "-invisible"
- var/list/blood_dna = blood_dna_info || owner?.get_blood_dna_list()
- if (LAZYLEN(blood_dna))
- . += "-[get_color_from_blood_list(blood_dna)]"
- else
- . += "-[BLOOD_COLOR_RED]"
+ . += "invisible"
+ . += "[LAZYLEN(blood_dna_info) ? get_color_from_blood_list(blood_dna_info) : BLOOD_COLOR_RED]"
for(var/datum/bodypart_overlay/overlay as anything in bodypart_overlays)
if(!overlay.can_draw_on_bodypart(src, owner, TRUE))
continue
- . += "-[jointext(overlay.generate_icon_cache(), "-")]"
+ . += overlay.generate_icon_cache()
if(ishuman(owner))
var/mob/living/carbon/human/human_owner = owner
- . += "-[human_owner.mob_height]"
+ . += "[human_owner.mob_height]"
return .
/obj/item/bodypart/head/generate_icon_key()
diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm
index 5ac4dd588c74..874e0f96d736 100644
--- a/code/modules/mob/living/carbon/damage_procs.dm
+++ b/code/modules/mob/living/carbon/damage_procs.dm
@@ -20,7 +20,7 @@
// ALso we'll automatically covnert string def zones into bodyparts to pass into parent call.
else if(!isbodypart(def_zone))
var/random_zone = check_zone(def_zone || get_random_valid_zone(def_zone))
- def_zone = get_bodypart(random_zone) || bodyparts[1]
+ def_zone = get_bodypart(random_zone) || get_bodypart()
. = ..()
// Taking brute or burn to bodyparts gives a damage flash
@@ -88,13 +88,13 @@
//These procs fetch a cumulative total damage from all bodyparts
/mob/living/carbon/get_brute_loss()
var/amount = 0
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts())
amount += bodypart.brute_dam
return round(amount, DAMAGE_PRECISION)
/mob/living/carbon/get_fire_loss()
var/amount = 0
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts())
amount += bodypart.burn_dam
return round(amount, DAMAGE_PRECISION)
@@ -108,7 +108,7 @@
*/
/mob/living/carbon/proc/get_brute_loss_for_type(required_bodytype = ALL)
var/amount = 0
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts())
if(!(bodypart.bodytype & required_bodytype))
continue
amount += bodypart.brute_dam
@@ -123,7 +123,7 @@
*/
/mob/living/carbon/proc/get_fire_loss_for_type(required_bodytype = ALL)
var/amount = 0
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts())
if(!(bodypart.bodytype & required_bodytype))
continue
amount += bodypart.burn_dam
@@ -243,8 +243,7 @@
///Returns a list of damaged bodyparts
/mob/living/carbon/proc/get_damaged_bodyparts(brute = FALSE, burn = FALSE, required_bodytype = NONE, target_zone = null, aggravated = FALSE) // DARKPACK EDIT CHANGE - AGGRAVATED_DAMAGE
var/list/obj/item/bodypart/parts = list()
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
if(required_bodytype && !(BP.bodytype & required_bodytype))
continue
if(!isnull(target_zone) && BP.body_zone != target_zone)
@@ -256,8 +255,7 @@
///Returns a list of damageable bodyparts
/mob/living/carbon/proc/get_damageable_bodyparts(required_bodytype)
var/list/obj/item/bodypart/parts = list()
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
if(required_bodytype && !(BP.bodytype & required_bodytype))
continue
if(BP.get_damage() < BP.max_damage) // DARKPACK EDIT CHANGE - AGGRAVATED_DAMAGE
@@ -268,8 +266,7 @@
///Returns a list of bodyparts with wounds (in case someone has a wound on an otherwise fully healed limb)
/mob/living/carbon/proc/get_wounded_bodyparts(required_bodytype)
var/list/obj/item/bodypart/parts = list()
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
if(required_bodytype && !(BP.bodytype & required_bodytype))
continue
if(LAZYLEN(BP.wounds))
diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm
index 77611210d8ca..292eda9c6ace 100644
--- a/code/modules/mob/living/carbon/death.dm
+++ b/code/modules/mob/living/carbon/death.dm
@@ -28,7 +28,7 @@
return ..()
/mob/living/carbon/get_gibs_type(drop_bitflags = NONE)
- var/obj/item/bodypart/chest = get_bodypart(BODY_ZONE_CHEST) || (length(bodyparts) ? bodyparts[1] : null)
+ var/obj/item/bodypart/chest = get_bodypart(BODY_ZONE_CHEST) || get_bodypart()
if (!istype(chest)) // what
return ..()
@@ -74,13 +74,22 @@
qdel(organ)
/mob/living/carbon/spread_bodyparts(drop_bitflags=NONE)
- for(var/obj/item/bodypart/part as anything in bodyparts)
+ for(var/obj/item/bodypart/part as anything in get_bodyparts())
+ if(part.body_zone == BODY_ZONE_CHEST)
+ continue // never drop this
if(!(drop_bitflags & DROP_BRAIN) && part.body_zone == BODY_ZONE_HEAD)
- continue
- else if(part.body_zone == BODY_ZONE_CHEST)
- continue
- part.drop_limb()
+ continue // don't drop head if we aren't dropping a brain
+
+ var/list/leftover_organs = list()
+ for(var/obj/item/organ/leftover in part)
+ leftover_organs += leftover
+
+ part.drop_limb(TRUE)
part.throw_at(get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 5)
+ // any organs that weren't throw out already about need to follow the bodypart out
+ for(var/obj/item/organ/leftover as anything in leftover_organs)
+ leftover.Remove(src, TRUE)
+ leftover.bodypart_insert(part)
/mob/living/carbon/set_suicide(suicide_state) //you thought that box trick was pretty clever, didn't you? well now hardmode is on, boyo.
. = ..()
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index e08d49af9ed8..4271ea43ed74 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -50,7 +50,7 @@
. += span_deadsay("It appears that [t_his] brain is missing...")
var/list/disabled = list()
- for(var/obj/item/bodypart/body_part as anything in bodyparts)
+ for(var/obj/item/bodypart/body_part as anything in get_bodyparts())
if(body_part.bodypart_disabled)
disabled += body_part
@@ -204,7 +204,7 @@
var/list/obj/item/bodypart/bleeding_limbs = list()
var/list/obj/item/bodypart/grasped_limbs = list()
- for(var/obj/item/bodypart/body_part as anything in bodyparts)
+ for(var/obj/item/bodypart/body_part as anything in get_bodyparts())
if(body_part.cached_bleed_rate)
bleeding_limbs += body_part.plaintext_zone
if(body_part.grasped_by)
@@ -263,7 +263,7 @@
var/obj/item/organ/brain/brain = get_organ_by_type(/obj/item/organ/brain)
if(brain && isnull(ai_controller))
var/npc_message = ""
- if(HAS_TRAIT(brain, TRAIT_GHOSTROLE_ON_REVIVE))
+ if(HAS_TRAIT(brain, TRAIT_GHOSTROLE_ON_REVIVE) || HAS_TRAIT(src, TRAIT_GHOSTROLE_ON_REVIVE))
npc_message = "Soul is pending..."
else if(isnpc(src)) // DARKPACK EDIT ADD START
npc_message = "[t_He] looks busy. Probably wise not to bother [t_him]." // DARKPACK EDIT ADD END
@@ -362,7 +362,7 @@
var/t_is = p_are()
//This checks to see if the body is revivable
var/obj/item/organ/brain = get_organ_by_type(/obj/item/organ/brain)
- if(brain && HAS_TRAIT(brain, TRAIT_GHOSTROLE_ON_REVIVE))
+ if((brain && HAS_TRAIT(brain, TRAIT_GHOSTROLE_ON_REVIVE)) || HAS_TRAIT(src, TRAIT_GHOSTROLE_ON_REVIVE))
return span_deadsay("[t_He] [t_is] limp and unresponsive; but [t_his] soul might yet come back...")
var/client_like = client || HAS_TRAIT(src, TRAIT_MIND_TEMPORARILY_GONE)
var/valid_ghost = ghost?.can_reenter_corpse && ghost?.client
@@ -376,7 +376,7 @@
var/list/seen_damage = list() // This looks like: ({Damage type} = list({Damage description for that damage type} = {number of times it has appeared}, ...), ...)
var/list/most_seen_damage = list() // This looks like: ({Damage type} = {Frequency of the most common description}, ...)
var/list/final_descriptions = list() // This looks like: ({Damage type} = {Most common damage description for that type}, ...)
- for(var/obj/item/bodypart/part as anything in bodyparts)
+ for(var/obj/item/bodypart/part as anything in get_bodyparts())
for(var/damage_type in part.damage_examines)
var/damage_desc = part.damage_examines[damage_type]
if(!seen_damage[damage_type])
@@ -411,7 +411,7 @@
if((held_thing.item_flags & (ABSTRACT|HAND_ITEM)) || HAS_TRAIT(held_thing, TRAIT_EXAMINE_SKIP))
continue
. += "[t_He] [t_is] holding [held_thing.examine_title(user)] in [t_his] [get_held_index_name(get_held_index_of_item(held_thing))]."
- for(var/obj/item/bodypart/arm/part in bodyparts)
+ for(var/obj/item/bodypart/arm/part in get_bodyparts())
if(!(part.bodypart_flags & BODYPART_PSEUDOPART))
continue
var/obj/item/corresponding_item = get_item_for_held_index(part.held_index) || part
@@ -513,7 +513,7 @@
if((held_thing.item_flags & (ABSTRACT|HAND_ITEM)) || HAS_TRAIT(held_thing, TRAIT_EXAMINE_SKIP))
continue
. += "[t_He] [t_is] holding [held_thing.examine_title(user)] in [t_his] [get_held_index_name(get_held_index_of_item(held_thing))]."
- for(var/obj/item/bodypart/arm/part in bodyparts)
+ for(var/obj/item/bodypart/arm/part in get_bodyparts())
if(!(part.bodypart_flags & BODYPART_PSEUDOPART))
continue
var/obj/item/corresponding_item = get_item_for_held_index(part.held_index) || part
@@ -632,7 +632,7 @@
/mob/living/carbon/human/proc/get_mismatched_limb_text()
var/list/covered = get_covered_body_zones()
var/list/texts = list()
- for(var/obj/item/bodypart/part as anything in bodyparts)
+ for(var/obj/item/bodypart/part as anything in get_bodyparts())
var/part_id = part.limb_id
var/obj/item/bodypart/expected_part = dna?.species?.bodypart_overrides[part.body_zone]
var/expected_id = initial(expected_part?.limb_id)
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index 3b175bf89c0c..83bb043e90dd 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -189,6 +189,8 @@ GLOBAL_LIST_EMPTY(features_by_species)
plural_form = "[name]\s"
if(!examine_limb_id)
examine_limb_id = id
+ // Carbons determine bodypart order by this list, so we need to make sure it's sorted properly
+ sortTim(bodypart_overrides, GLOBAL_PROC_REF(cmp_bodypart_by_body_part_asc), associative = TRUE)
return ..()
@@ -1284,7 +1286,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
return
// Lets pick a random body part and check for an existing burn
- var/obj/item/bodypart/bodypart = pick(humi.bodyparts)
+ var/obj/item/bodypart/bodypart = pick(humi.get_bodyparts())
var/datum/wound/existing_burn
for (var/datum/wound/iterated_wound as anything in bodypart.wounds)
var/datum/wound_pregen_data/pregen_data = iterated_wound.get_pregen_data()
@@ -1990,7 +1992,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
final_bodypart_overrides[BODY_ZONE_R_LEG] = /obj/item/bodypart/leg/right/digitigrade
final_bodypart_overrides[BODY_ZONE_L_LEG] = /obj/item/bodypart/leg/left/digitigrade
- for(var/obj/item/bodypart/old_part as anything in target.bodyparts)
+ for(var/obj/item/bodypart/old_part as anything in target.get_bodyparts())
if((old_part.change_exempt_flags & BP_BLOCK_CHANGE_SPECIES) || (old_part.bodypart_flags & BODYPART_IMPLANTED))
continue
@@ -2062,7 +2064,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
/// Remove body markings
/datum/species/proc/remove_body_markings(mob/living/carbon/human/hooman)
- for(var/obj/item/bodypart/part as anything in hooman.bodyparts)
+ for(var/obj/item/bodypart/part as anything in hooman.get_bodyparts())
for(var/datum/bodypart_overlay/simple/body_marking/marking in part.bodypart_overlays)
part.remove_bodypart_overlay(marking)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 3ca4d3011274..c4dabf4ce4d5 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -192,8 +192,7 @@
var/status = ""
if(get_brute_loss())
to_chat(human_user, "Physical trauma analysis:")
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
var/brutedamage = BP.brute_dam
if(brutedamage > 0)
status = "received minor physical injuries."
@@ -208,8 +207,7 @@
to_chat(human_user, "[BP] appears to have [status]")
if(get_fire_loss())
to_chat(human_user, "Analysis of skin burns:")
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
var/burndamage = BP.burn_dam
if(burndamage > 0)
status = "signs of minor burns."
@@ -656,10 +654,8 @@
// If we have a species, we need to handle mutant parts and stuff
if(dna?.species)
add_atom_colour(COLOR_BLACK, TEMPORARY_COLOUR_PRIORITY)
- var/static/mutable_appearance/shock_animation_dna
- if(!shock_animation_dna)
- shock_animation_dna = mutable_appearance(icon, "electrocuted_base")
- shock_animation_dna.appearance_flags |= RESET_COLOR|KEEP_APART
+ var/mutable_appearance/shock_animation_dna = mutable_appearance(icon, "electrocuted_base", appearance_flags = RESET_COLOR|KEEP_APART)
+ apply_height_filters(shock_animation_dna)
zap_appearance = shock_animation_dna
// Otherwise do a generic animation
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index fedc4d6e58cf..e28e3ec6521f 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -13,8 +13,7 @@
//If a specific bodypart is targeted, check how that bodypart is protected and return the value.
//If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
armorval += check_armor(BP, type)
organnum++
return (armorval/max(organnum, 1))
@@ -140,8 +139,8 @@
if(!HAS_TRAIT(src, TRAIT_BRAWLING_KNOCKDOWN_BLOCKED))
Knockdown(SHOVE_KNOCKDOWN_COLLATERAL, daze_amount = 3 SECONDS)
target.visible_message(span_danger("[shover] shoves [target.name] into [name]!"),
- span_userdanger("You're shoved into [name] by [shover]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, src)
- to_chat(src, span_danger("You shove [target.name] into [name]!"))
+ span_userdanger("You're shoved into [name] by [shover]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, list(shover))
+ to_chat(shover, span_danger("You shove [target.name] into [name]!"))
log_combat(shover, target, "shoved", addition = "into [name][weapon ? " with [weapon]" : ""]")
return COMSIG_LIVING_SHOVE_HANDLED
@@ -331,8 +330,7 @@
if(EXPLODE_DEVASTATE)
max_limb_loss = 4
probability = 50
- for(var/X in bodyparts)
- var/obj/item/bodypart/BP = X
+ for(var/obj/item/bodypart/BP as anything in get_bodyparts())
if(prob(probability) && !prob(getarmor(BP, BOMB)) && BP.body_zone != BODY_ZONE_HEAD && BP.body_zone != BODY_ZONE_CHEST)
BP.receive_damage(INFINITY, wound_bonus = CANT_WOUND) //Capped by proc
BP.dismember()
@@ -557,10 +555,12 @@
combined_msg += span_notice("You check yourself for injuries.")
- var/list/missing = get_all_limbs()
- for(var/obj/item/bodypart/body_part as anything in bodyparts)
- missing -= body_part.body_zone
+ for(var/part_zone, body_part_untyped in get_bodyparts_by_zones())
+ var/obj/item/bodypart/body_part = body_part_untyped
+ if(isnull(body_part) || IS_STUMP(body_part))
+ combined_msg += span_boldannounce("↳ Your [parse_zone(body_part?.body_zone || part_zone)] is missing!")
+ continue
if(body_part.bodypart_flags & BODYPART_PSEUDOPART) //don't show injury text for fake bodyparts; ie chainsaw arms or synthetic armblades
continue
@@ -568,9 +568,6 @@
if(bodypart_report)
combined_msg += "[span_notice("↳")] [bodypart_report]"
- for(var/t in missing)
- combined_msg += span_boldannounce("↳ Your [parse_zone(t)] is missing!")
-
var/tox = get_tox_loss() + (disgust / 5) + (HAS_TRAIT(src, TRAIT_SELF_AWARE) ? 0 : (rand(-3, 0) * 5))
switch(tox)
if(10 to 20)
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 7b0d8e7dd0e1..c55a85b9de8e 100644
--- a/code/modules/mob/living/carbon/human/human_update_icons.dm
+++ b/code/modules/mob/living/carbon/human/human_update_icons.dm
@@ -870,32 +870,24 @@ generate/load female uniform sprites matching all previously decided variables
return
// Underwear, Undershirts & Socks
var/list/standing = list()
+ var/active_bodyshapes = get_active_bodyshapes()
if(underwear)
- var/datum/sprite_accessory/underwear/undie_accessory = SSaccessories.underwear_list[underwear]
- var/mutable_appearance/underwear_overlay
- if(undie_accessory)
- if(dna.species.sexes && physique == FEMALE && (undie_accessory.gender == MALE))
- underwear_overlay = mutable_appearance(wear_female_version(undie_accessory.icon_state, undie_accessory.icon, FEMALE_UNIFORM_FULL), layer = -BODY_LAYER)
- else
- underwear_overlay = mutable_appearance(undie_accessory.icon, undie_accessory.icon_state, -BODY_LAYER)
- if(!undie_accessory.use_static)
- underwear_overlay.color = underwear_color
+ var/datum/sprite_accessory/clothing/underwear/undie_accessory = SSaccessories.underwear_list[underwear]
+ var/mutable_appearance/underwear_overlay = undie_accessory?.make_appearance(underwear_color, physique, active_bodyshapes)
+ if(underwear_overlay)
standing += underwear_overlay
if(undershirt)
- var/datum/sprite_accessory/undershirt/undie_accessory = SSaccessories.undershirt_list[undershirt]
- if(undie_accessory)
- var/mutable_appearance/working_shirt
- if(dna.species.sexes && physique == FEMALE)
- working_shirt = mutable_appearance(wear_female_version(undie_accessory.icon_state, undie_accessory.icon), layer = -BODY_LAYER)
- else
- working_shirt = mutable_appearance(undie_accessory.icon, undie_accessory.icon_state, layer = -BODY_LAYER)
- standing += working_shirt
+ var/datum/sprite_accessory/clothing/undershirt/shirt_accessory = SSaccessories.undershirt_list[undershirt]
+ var/mutable_appearance/shirt_overlay = shirt_accessory?.make_appearance(null, physique, active_bodyshapes)
+ if(shirt_overlay)
+ standing += shirt_overlay
if(socks && num_legs >= 2 && !(bodyshape & BODYSHAPE_DIGITIGRADE))
- var/datum/sprite_accessory/socks/undie_accessory = SSaccessories.socks_list[socks]
- if(undie_accessory)
- standing += mutable_appearance(undie_accessory.icon, undie_accessory.icon_state, -BODY_LAYER)
+ var/datum/sprite_accessory/clothing/socks/sock_accessory = SSaccessories.socks_list[socks]
+ var/mutable_appearance/socks_overlay = sock_accessory?.make_appearance(null, physique, active_bodyshapes)
+ if(socks_overlay)
+ standing += socks_overlay
if(standing.len)
overlays_standing[BODY_LAYER] = standing
@@ -967,7 +959,7 @@ generate/load female uniform sprites matching all previously decided variables
// optimization - none of our limbs or organs have the desired shape
return .
- for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/bodypart/limb as anything in get_bodyparts())
var/checked_bodyshape = limb.bodyshape
// accounts for stuff like snouts
for(var/obj/item/organ/organ in limb)
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 439ed75d5444..10e9edfb715f 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -413,6 +413,8 @@
new_bodypart = newBodyPart(BODY_ZONE_L_ARM)
new_bodypart.held_index = i
+ if(i >= 3) // start indexing them as right_arm2 and so on
+ new_bodypart.body_zone = "[new_bodypart.body_zone]_[ceil(i / 2)]"
new_bodypart.try_attach_limb(src, TRUE)
hand_bodyparts[i] = new_bodypart
..() //Don't redraw hands until we have organs for them
diff --git a/code/modules/mob/living/carbon/human/monkey.dm b/code/modules/mob/living/carbon/human/monkey.dm
index 533f4e3374ad..e4d19ab1443d 100644
--- a/code/modules/mob/living/carbon/human/monkey.dm
+++ b/code/modules/mob/living/carbon/human/monkey.dm
@@ -55,24 +55,19 @@ GLOBAL_DATUM(the_one_and_only_punpun, /mob/living/carbon/human/species/monkey/pu
new /obj/effect/landmark/start/pun_pun(loc) //Pun Pun is a crewmember, and may late-join.
return INITIALIZE_HINT_QDEL
- Read_Memory()
+ equip_to_slot_or_del(new /obj/item/clothing/under/suit/waiter(src), ITEM_SLOT_ICLOTHING)
+
if(!GLOB.the_one_and_only_punpun && mapload)
GLOB.the_one_and_only_punpun = src
+ Read_Memory()
+
else if(GLOB.the_one_and_only_punpun)
ADD_TRAIT(src, TRAIT_DONT_WRITE_MEMORY, INNATE_TRAIT) //faaaaaaake!
+ // Everything past here MUST be called AFTER memory has been read
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
- //those slots
- if(ancestor_chain > 1)
- generate_fake_scars(rand(ancestor_chain, ancestor_chain * 4))
- if(relic_hat)
- equip_to_slot_or_del(new relic_hat, ITEM_SLOT_HEAD)
- if(relic_mask)
- equip_to_slot_or_del(new relic_mask, ITEM_SLOT_MASK)
- equip_to_slot_or_del(new /obj/item/clothing/under/suit/waiter(src), ITEM_SLOT_ICLOTHING)
+ give_scars()
+ give_special_equipment()
/mob/living/carbon/human/species/monkey/punpun/Destroy()
if(GLOB.the_one_and_only_punpun == src)
@@ -131,18 +126,33 @@ GLOBAL_DATUM(the_one_and_only_punpun, /mob/living/carbon/human/species/monkey/pu
WRITE_FILE(json_file, json_encode(file_data))
-/// Gives pun pun a special name based on various factors
+/// Gives pun pun a special name based on various factors such as their past
/mob/living/carbon/human/species/monkey/punpun/proc/give_special_name()
- var/name_to_use = name
+ var/name_to_use = initial(name)
+#ifndef UNIT_TESTS
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
+#endif
fully_replace_character_name(real_name, name_to_use)
+
+/// Gives pun pun scars based on how many times he's died in the past
+/mob/living/carbon/human/species/monkey/punpun/proc/give_scars()
+ if(ancestor_chain > 1)
+ generate_fake_scars(rand(ancestor_chain, ancestor_chain * 4))
+
+/// Gives pun pun special equipment from their past
+/mob/living/carbon/human/species/monkey/punpun/proc/give_special_equipment()
+ if(relic_hat)
+ equip_to_slot_or_del(new relic_hat, ITEM_SLOT_HEAD)
+ if(relic_mask)
+ equip_to_slot_or_del(new relic_mask, ITEM_SLOT_MASK)
diff --git a/code/modules/mob/living/carbon/human/species_types/ethereal.dm b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
index 2d6754d2b956..bfa1ae5649b9 100644
--- a/code/modules/mob/living/carbon/human/species_types/ethereal.dm
+++ b/code/modules/mob/living/carbon/human/species_types/ethereal.dm
@@ -67,7 +67,7 @@
var/obj/item/organ/heart/ethereal/ethereal_heart = new_ethereal.get_organ_slot(ORGAN_SLOT_HEART)
ethereal_heart.ethereal_color = default_color
- for(var/obj/item/bodypart/limb as anything in new_ethereal.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in new_ethereal.get_bodyparts())
if(limb.limb_id == SPECIES_ETHEREAL)
limb.update_limb(is_creating = TRUE)
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 31a74b313bea..06e5e4d4c7fd 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -502,7 +502,7 @@
return COMPONENT_NO_EXPOSE_REAGENTS
/mob/living/carbon/proc/handle_bodyparts(seconds_per_tick)
- for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/bodypart/limb as anything in get_bodyparts(include_stumps = TRUE))
. |= limb.on_life(seconds_per_tick)
/mob/living/carbon/proc/handle_organs(seconds_per_tick)
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index 80189ae49024..324215167130 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -25,6 +25,13 @@
if(drop_bitflags & DROP_BODYPARTS)
spread_bodyparts(drop_bitflags)
+ // failsafe for if we fuck up and leave our brain behind. (other organs are replaceable so we can ignore them.)
+ var/obj/item/organ/brain/brain = get_organ_slot(ORGAN_SLOT_BRAIN)
+ if((drop_bitflags & DROP_BRAIN) && !isnull(brain))
+ stack_trace("gib invoked with drop_brain() had their brain after spilling organs and bodyparts, meaning both failed!")
+ brain.Remove(src)
+ brain.forceMove(drop_location())
+
SEND_SIGNAL(src, COMSIG_LIVING_GIBBED, drop_bitflags)
qdel(src)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 1b2e499fe2ff..7e01709f72a7 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -2056,8 +2056,12 @@ GLOBAL_LIST_EMPTY(fire_appearances)
real_name = name
/mob/living/proc/mob_try_pickup(mob/living/user, instant=FALSE)
- if(!ishuman(user))
- return
+ if(!ishuman(user) && (user.mob_size <= mob_size || user.num_hands == 0))
+ if (!user.num_hands)
+ return
+ if (user.mob_size <= mob_size)
+ to_chat(user, span_warning("[src] is too big to pick up!"))
+ return
if(!user.get_empty_held_indexes())
to_chat(user, span_warning("Your hands are full!"))
return FALSE
@@ -2570,7 +2574,7 @@ GLOBAL_LIST_EMPTY(fire_appearances)
return
. = num_legs
num_legs = new_value
-
+ hud_used?.update_locked_slots()
///Proc to modify the value of usable_legs and hook behavior associated to this event.
/mob/living/proc/set_usable_legs(new_value)
@@ -2620,7 +2624,7 @@ GLOBAL_LIST_EMPTY(fire_appearances)
return
. = num_hands
num_hands = new_value
-
+ hud_used?.update_locked_slots()
///Proc to modify the value of usable_hands and hook behavior associated to this event.
/mob/living/proc/set_usable_hands(new_value)
diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm
index e55c94b73d5e..15173c282c1a 100644
--- a/code/modules/mob/living/simple_animal/bot/construction.dm
+++ b/code/modules/mob/living/simple_animal/bot/construction.dm
@@ -22,19 +22,19 @@
* Checks if the user can finish constructing a bot with a given item.
*
* Arguments:
- * * I - Item to be used
+ * * tool - Item to be used
* * user - Mob doing the construction
* * drop_item - Whether or no the item should be dropped; defaults to 1. Should be set to 0 if the item is a tool, stack, or otherwise doesn't need to be dropped. If not set to 0, item must be deleted afterwards.
*/
-/obj/item/bot_assembly/proc/can_finish_build(obj/item/I, mob/user, drop_item = 1)
+/obj/item/bot_assembly/proc/can_finish_build(obj/item/tool, mob/user, drop_item = 1)
if(istype(loc, /obj/item/storage/backpack))
to_chat(user, span_warning("You must take [src] out of [loc] first!"))
return FALSE
- if(!I || !user || (drop_item && !user.temporarilyRemoveItemFromInventory(I)))
+ if(!tool || !user || (drop_item && !user.temporarilyRemoveItemFromInventory(tool)))
return FALSE
return TRUE
-//Cleanbot assembly
+// Cleanbot assembly
/obj/item/bot_assembly/cleanbot
desc = "It's a bucket with a sensor attached."
name = "incomplete cleanbot assembly"
@@ -67,22 +67,23 @@
return ..()
-/obj/item/bot_assembly/cleanbot/attackby(obj/item/item_attached, mob/user, list/modifiers, list/attack_modifiers)
- ..()
- if(!istype(item_attached, /obj/item/bodypart/arm/left/robot) && !istype(item_attached, /obj/item/bodypart/arm/right/robot))
- return
- if(!can_finish_build(item_attached, user))
- return
+/obj/item/bot_assembly/cleanbot/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!istype(tool, /obj/item/bodypart/arm/left/robot) && !istype(tool, /obj/item/bodypart/arm/right/robot))
+ return NONE
+
+ if(!can_finish_build(tool, user))
+ return ITEM_INTERACT_BLOCKING
+
var/mob/living/basic/bot/cleanbot/bot = new(drop_location())
bucket_obj.forceMove(bot)
bot.name = created_name
- bot.robot_arm = item_attached.type
- to_chat(user, span_notice("You add [item_attached] to [src]. Beep boop!"))
- qdel(item_attached)
+ bot.robot_arm = tool.type
+ to_chat(user, span_notice("You add [tool] to [src]. Beep boop!"))
+ qdel(tool)
qdel(src)
+ return ITEM_INTERACT_SUCCESS
-
-//Edbot Assembly
+// Edbot Assembly
/obj/item/bot_assembly/ed209
name = "incomplete ED-209 assembly"
desc = "Some sort of bizarre assembly."
@@ -92,99 +93,118 @@
var/lasercolor = ""
var/vest_type = /obj/item/clothing/suit/armor/vest
-/obj/item/bot_assembly/ed209/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers)
- ..()
+/obj/item/bot_assembly/ed209/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
switch(build_step)
if(ASSEMBLY_FIRST_STEP, ASSEMBLY_SECOND_STEP)
- if(istype(W, /obj/item/bodypart/leg/left/robot) || istype(W, /obj/item/bodypart/leg/right/robot))
- if(!user.temporarilyRemoveItemFromInventory(W))
- return
- to_chat(user, span_notice("You add [W] to [src]."))
- qdel(W)
- name = "legs/frame assembly"
- if(build_step == ASSEMBLY_FIRST_STEP)
- inhand_icon_state = "ed209_leg"
- icon_state = "ed209_leg"
- else
- inhand_icon_state = "ed209_legs"
- icon_state = "ed209_legs"
- build_step++
+ if(!istype(tool, /obj/item/bodypart/leg/left/robot) && !istype(tool, /obj/item/bodypart/leg/right/robot))
+ return NONE
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You add [tool] to [src]."))
+ qdel(tool)
+ name = "legs/frame assembly"
+ if(build_step == ASSEMBLY_FIRST_STEP)
+ inhand_icon_state = "ed209_leg"
+ icon_state = "ed209_leg"
+ else
+ inhand_icon_state = "ed209_legs"
+ icon_state = "ed209_legs"
+ build_step++
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_THIRD_STEP)
- if(istype(W, /obj/item/clothing/suit/armor/vest))
- if(!user.temporarilyRemoveItemFromInventory(W))
- return
- to_chat(user, span_notice("You add [W] to [src]."))
- qdel(W)
- name = "vest/legs/frame assembly"
- inhand_icon_state = "ed209_shell"
- icon_state = "ed209_shell"
- build_step++
+ if(!istype(tool, /obj/item/clothing/suit/armor/vest))
+ return NONE
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You add [tool] to [src]."))
+ qdel(tool)
+ name = "vest/legs/frame assembly"
+ inhand_icon_state = "ed209_shell"
+ icon_state = "ed209_shell"
+ build_step++
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_FOURTH_STEP)
- if(W.tool_behaviour == TOOL_WELDER)
- if(W.use_tool(src, user, 0, volume=40))
- name = "shielded frame assembly"
- to_chat(user, span_notice("You weld the vest to [src]."))
- build_step++
+ if(tool.tool_behaviour != TOOL_WELDER)
+ return NONE
+ if(!tool.use_tool(src, user, 0, volume=40))
+ return ITEM_INTERACT_BLOCKING
+ name = "shielded frame assembly"
+ to_chat(user, span_notice("You weld the vest to [src]."))
+ build_step++
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_FIFTH_STEP)
- if(istype(W, /obj/item/clothing/head/helmet/sec))
- if(!user.temporarilyRemoveItemFromInventory(W))
- return
- to_chat(user, span_notice("You add [W] to [src]."))
- qdel(W)
- name = "covered and shielded frame assembly"
- inhand_icon_state = "ed209_hat"
- icon_state = "ed209_hat"
- build_step++
+ if(!istype(tool, /obj/item/clothing/head/helmet/sec))
+ return NONE
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You add [tool] to [src]."))
+ qdel(tool)
+ name = "covered and shielded frame assembly"
+ inhand_icon_state = "ed209_hat"
+ icon_state = "ed209_hat"
+ build_step++
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_SIXTH_STEP)
- if(isprox(W))
- if(!user.temporarilyRemoveItemFromInventory(W))
- return
- build_step++
- to_chat(user, span_notice("You add [W] to [src]."))
- qdel(W)
- name = "covered, shielded and sensored frame assembly"
- inhand_icon_state = "ed209_prox"
- icon_state = "ed209_prox"
+ if(!isprox(tool))
+ return NONE
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ build_step++
+ to_chat(user, span_notice("You add [tool] to [src]."))
+ qdel(tool)
+ name = "covered, shielded and sensored frame assembly"
+ inhand_icon_state = "ed209_prox"
+ icon_state = "ed209_prox"
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_SEVENTH_STEP)
- if(istype(W, /obj/item/stack/cable_coil))
- var/obj/item/stack/cable_coil/coil = W
- if(coil.get_amount() < 1)
- to_chat(user, span_warning("You need one length of cable to wire the ED-209!"))
- return
- to_chat(user, span_notice("You start to wire [src]..."))
- if(do_after(user, 4 SECONDS, target = src))
- if(coil.get_amount() >= 1 && build_step == ASSEMBLY_SEVENTH_STEP)
- coil.use(1)
- to_chat(user, span_notice("You wire [src]."))
- name = "wired ED-209 assembly"
- build_step++
+ if(!istype(tool, /obj/item/stack/cable_coil))
+ return NONE
+ var/obj/item/stack/cable_coil/coil = tool
+ if(coil.get_amount() < 1)
+ to_chat(user, span_warning("You need one length of cable to wire the ED-209!"))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You start to wire [src]..."))
+ if(!do_after(user, 4 SECONDS, target = src))
+ return ITEM_INTERACT_BLOCKING
+ if(coil.get_amount() < 1 || build_step != ASSEMBLY_SEVENTH_STEP)
+ return ITEM_INTERACT_BLOCKING
+ coil.use(1)
+ to_chat(user, span_notice("You wire [src]."))
+ name = "wired ED-209 assembly"
+ build_step++
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_EIGHTH_STEP)
- if(istype(W, /obj/item/gun/energy/disabler))
- if(!user.temporarilyRemoveItemFromInventory(W))
- return
- name = "[W.name] ED-209 assembly"
- to_chat(user, span_notice("You add [W] to [src]."))
- inhand_icon_state = "ed209_taser"
- icon_state = "ed209_taser"
- qdel(W)
- build_step++
+ if(!istype(tool, /obj/item/gun/energy/disabler))
+ return NONE
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ name = "[tool.name] ED-209 assembly"
+ to_chat(user, span_notice("You add [tool] to [src]."))
+ inhand_icon_state = "ed209_taser"
+ icon_state = "ed209_taser"
+ qdel(tool)
+ build_step++
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_NINTH_STEP)
- if(W.tool_behaviour == TOOL_SCREWDRIVER)
- to_chat(user, span_notice("You start attaching the gun to the frame..."))
- if(W.use_tool(src, user, 40, volume=100))
- var/mob/living/simple_animal/bot/secbot/ed209/B = new(drop_location())
- B.name = created_name
- to_chat(user, span_notice("You complete the ED-209."))
- qdel(src)
-
-//Repairbot assemblies
+ if(tool.tool_behaviour != TOOL_SCREWDRIVER)
+ return NONE
+ to_chat(user, span_notice("You start attaching the gun to the frame..."))
+ if(!tool.use_tool(src, user, 40, volume=100))
+ return ITEM_INTERACT_BLOCKING
+ var/mob/living/simple_animal/bot/secbot/ed209/B = new(drop_location())
+ B.name = created_name
+ to_chat(user, span_notice("You complete the ED-209."))
+ qdel(src)
+ return ITEM_INTERACT_SUCCESS
+
+// Repairbot assemblies
/obj/item/bot_assembly/repairbot
name = "Repairbot Chasis"
desc = "It's a toolbox with tiles sticking out the top."
@@ -219,34 +239,35 @@
if(build_step >= ASSEMBLY_SECOND_STEP)
. += mutable_appearance(icon, "repairbot_base_arms", appearance_flags = RESET_COLOR|KEEP_APART)
-/obj/item/bot_assembly/repairbot/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers)
- ..()
+/obj/item/bot_assembly/repairbot/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
switch(build_step)
if(ASSEMBLY_FIRST_STEP)
- if(!istype(item, /obj/item/bodypart/arm/left/robot) && !istype(item, /obj/item/bodypart/arm/right/robot))
- return
- if(!can_finish_build(item, user))
- return
+ if(!istype(tool, /obj/item/bodypart/arm/left/robot) && !istype(tool, /obj/item/bodypart/arm/right/robot))
+ return NONE
+ if(!can_finish_build(tool, user))
+ return ITEM_INTERACT_BLOCKING
build_step++
- to_chat(user, span_notice("You add [item] to [src]. Boop beep!"))
- qdel(item)
+ to_chat(user, span_notice("You add [tool] to [src]. Boop beep!"))
+ qdel(tool)
update_appearance()
+ return ITEM_INTERACT_SUCCESS
+
if(ASSEMBLY_SECOND_STEP)
- if(!istype(item, /obj/item/stack/conveyor))
- return
- if(!can_finish_build(item, user))
- return
+ if(!istype(tool, /obj/item/stack/conveyor))
+ return NONE
+ if(!can_finish_build(tool, user))
+ return ITEM_INTERACT_BLOCKING
var/mob/living/basic/bot/repairbot/repair = new(drop_location())
repair.name = created_name
repair.toolbox = toolbox
repair.set_color(toolbox_color)
- to_chat(user, span_notice("You add [item] to [src]. Boop beep!"))
- var/obj/item/stack/crafting_stack = item
+ to_chat(user, span_notice("You add [tool] to [src]. Boop beep!"))
+ var/obj/item/stack/crafting_stack = tool
crafting_stack.use(1)
qdel(src)
+ return ITEM_INTERACT_SUCCESS
-
-//Medbot Assembly
+// Medbot Assembly
/obj/item/bot_assembly/medbot
name = "incomplete medibot assembly"
desc = "A first aid kit with a robot arm permanently grafted to it."
@@ -262,69 +283,74 @@
if(skin)
icon_state = "[base_icon_state]_[skin]"
-/obj/item/bot_assembly/medbot/attackby(obj/item/W, mob/user, list/modifiers, list/attack_modifiers)
- ..()
+/obj/item/bot_assembly/medbot/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
switch(build_step)
if(ASSEMBLY_FIRST_STEP)
- if(istype(W, /obj/item/healthanalyzer))
- if(!user.temporarilyRemoveItemFromInventory(W))
- return
- healthanalyzer = W.type
- to_chat(user, span_notice("You add [W] to [src]."))
- qdel(W)
- name = "first aid/robot arm/health analyzer assembly"
- add_overlay("[base_icon_state]_analyzer")
- build_step++
+ if(!istype(tool, /obj/item/healthanalyzer))
+ return NONE
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ healthanalyzer = tool.type
+ to_chat(user, span_notice("You add [tool] to [src]."))
+ qdel(tool)
+ name = "first aid/robot arm/health analyzer assembly"
+ add_overlay("[base_icon_state]_analyzer")
+ build_step++
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_SECOND_STEP)
- if(isprox(W))
- if(!can_finish_build(W, user))
- return
- qdel(W)
- var/mob/living/basic/bot/medbot/medbot = new(drop_location(), skin)
- to_chat(user, span_notice("You complete the Medbot. Beep boop!"))
- medbot.name = created_name
- medbot.medkit_type = medkit_type
- medbot.robot_arm = robot_arm
- medbot.health_analyzer = healthanalyzer
- var/obj/item/storage/medkit/medkit = medkit_type
- medbot.damage_type_healer = initial(medkit.damagetype_healed) ? initial(medkit.damagetype_healed) : BRUTE
- qdel(src)
+ if(!isprox(tool))
+ return NONE
+ if(!can_finish_build(tool, user))
+ return ITEM_INTERACT_BLOCKING
+ qdel(tool)
+ var/mob/living/basic/bot/medbot/medbot = new(drop_location(), skin)
+ to_chat(user, span_notice("You complete the Medbot. Beep boop!"))
+ medbot.name = created_name
+ medbot.medkit_type = medkit_type
+ medbot.robot_arm = robot_arm
+ medbot.health_analyzer = healthanalyzer
+ var/obj/item/storage/medkit/medkit = medkit_type
+ medbot.damage_type_healer = initial(medkit.damagetype_healed) ? initial(medkit.damagetype_healed) : BRUTE
+ qdel(src)
+ return ITEM_INTERACT_SUCCESS
-//Honkbot Assembly
+// Honkbot Assembly
/obj/item/bot_assembly/honkbot
name = "incomplete honkbot assembly"
desc = "The clown's up to no good once more"
icon_state = "honkbot_arm"
created_name = "Honkbot"
-/obj/item/bot_assembly/honkbot/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
- ..()
+/obj/item/bot_assembly/honkbot/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
switch(build_step)
if(ASSEMBLY_FIRST_STEP)
- if(isprox(attacking_item))
- if(!user.temporarilyRemoveItemFromInventory(attacking_item))
- return
- to_chat(user, span_notice("You add the [attacking_item] to [src]!"))
- icon_state = "honkbot_proxy"
- name = "incomplete Honkbot assembly"
- qdel(attacking_item)
- build_step++
+ if(!isprox(tool))
+ return NONE
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You add the [tool] to [src]!"))
+ icon_state = "honkbot_proxy"
+ name = "incomplete Honkbot assembly"
+ qdel(tool)
+ build_step++
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_SECOND_STEP)
- if(istype(attacking_item, /obj/item/bikehorn))
- if(!can_finish_build(attacking_item, user))
- return
- to_chat(user, span_notice("You add the [attacking_item] to [src]! Honk!"))
- var/mob/living/basic/bot/honkbot/new_honkbot = new(drop_location())
- new_honkbot.name = created_name
- playsound(new_honkbot, 'sound/machines/ping.ogg', 50, TRUE, -1)
- qdel(attacking_item)
- qdel(src)
-
+ if(!istype(tool, /obj/item/bikehorn))
+ return NONE
+ if(!can_finish_build(tool, user))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You add the [tool] to [src]! Honk!"))
+ var/mob/living/basic/bot/honkbot/new_honkbot = new(drop_location())
+ new_honkbot.name = created_name
+ playsound(new_honkbot, 'sound/machines/ping.ogg', 50, TRUE, -1)
+ qdel(tool)
+ qdel(src)
+ return ITEM_INTERACT_SUCCESS
-//Secbot Assembly
+// Secbot Assembly
/obj/item/bot_assembly/secbot
name = "incomplete securitron assembly"
desc = "Some sort of bizarre assembly made from a proximity sensor, helmet, and signaler."
@@ -332,98 +358,97 @@
inhand_icon_state = "helmet"
lefthand_file = 'icons/mob/inhands/clothing/hats_righthand.dmi'
righthand_file = 'icons/mob/inhands/clothing/hats_lefthand.dmi'
- created_name = "Securitron" //To preserve the name if it's a unique securitron I guess
- var/swordamt = 0 //If you're converting it into a grievousbot, how many swords have you attached
- var/toyswordamt = 0 //honk
-
-/obj/item/bot_assembly/secbot/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
- ..()
- var/atom/Tsec = drop_location()
+ created_name = "Securitron" // To preserve the name if it's a unique securitron I guess
+ /// If you're converting it into a grievousbot, how many swords have you attached
+ var/swordamt = 0
+ /// Honk
+ var/toyswordamt = 0
+
+/obj/item/bot_assembly/secbot/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ var/atom/drop_loc = drop_location()
switch(build_step)
if(ASSEMBLY_FIRST_STEP)
- if(I.tool_behaviour == TOOL_WELDER)
- if(I.use_tool(src, user, 0, volume=40))
- add_overlay("hs_hole")
- to_chat(user, span_notice("You weld a hole in [src]!"))
- build_step++
-
- else if(I.tool_behaviour == TOOL_SCREWDRIVER) //deconstruct
- new /obj/item/assembly/signaler(Tsec)
- new /obj/item/clothing/head/helmet/sec(Tsec)
- to_chat(user, span_notice("You disconnect the signaler from the helmet."))
- qdel(src)
+ if(tool.tool_behaviour == TOOL_WELDER)
+ if(!tool.use_tool(src, user, 0, volume=40))
+ return ITEM_INTERACT_BLOCKING
+ add_overlay("hs_hole")
+ to_chat(user, span_notice("You weld a hole in [src]!"))
+ build_step++
+ return ITEM_INTERACT_SUCCESS
+
+ if(tool.tool_behaviour != TOOL_SCREWDRIVER) //deconstruct
+ return NONE
+
+ new /obj/item/assembly/signaler(drop_loc)
+ new /obj/item/clothing/head/helmet/sec(drop_loc)
+ to_chat(user, span_notice("You disconnect the signaler from the helmet."))
+ qdel(src)
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_SECOND_STEP)
- if(isprox(I))
- if(!user.temporarilyRemoveItemFromInventory(I))
- return
- to_chat(user, span_notice("You add [I] to [src]!"))
+ if(isprox(tool))
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You add [tool] to [src]!"))
add_overlay("hs_eye")
name = "helmet/signaler/prox sensor assembly"
- qdel(I)
+ qdel(tool)
build_step++
+ return ITEM_INTERACT_SUCCESS
- else if(I.tool_behaviour == TOOL_WELDER) //deconstruct
- if(I.use_tool(src, user, 0, volume=40))
- cut_overlay("hs_hole")
- to_chat(user, span_notice("You weld the hole in [src] shut!"))
- build_step--
+ if(tool.tool_behaviour != TOOL_WELDER) //deconstruct
+ return NONE
+
+ if(!tool.use_tool(src, user, 0, volume=40))
+ return ITEM_INTERACT_BLOCKING
+
+ cut_overlay("hs_hole")
+ to_chat(user, span_notice("You weld the hole in [src] shut!"))
+ build_step--
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_THIRD_STEP)
- if((istype(I, /obj/item/bodypart/arm/left/robot)) || (istype(I, /obj/item/bodypart/arm/right/robot)))
- if(!user.temporarilyRemoveItemFromInventory(I))
- return
- to_chat(user, span_notice("You add [I] to [src]!"))
+ if((istype(tool, /obj/item/bodypart/arm/left/robot)) || (istype(tool, /obj/item/bodypart/arm/right/robot)))
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You add [tool] to [src]!"))
name = "helmet/signaler/prox sensor/robot arm assembly"
add_overlay("hs_arm")
- robot_arm = I.type
- qdel(I)
+ robot_arm = tool.type
+ qdel(tool)
build_step++
+ return ITEM_INTERACT_SUCCESS
- else if(I.tool_behaviour == TOOL_SCREWDRIVER) //deconstruct
- cut_overlay("hs_eye")
- new /obj/item/assembly/prox_sensor(Tsec)
- to_chat(user, span_notice("You detach the proximity sensor from [src]."))
- build_step--
+ if(tool.tool_behaviour != TOOL_SCREWDRIVER) //deconstruct
+ return NONE
+
+ cut_overlay("hs_eye")
+ new /obj/item/assembly/prox_sensor(drop_loc)
+ to_chat(user, span_notice("You detach the proximity sensor from [src]."))
+ build_step--
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_FOURTH_STEP)
- if(istype(I, /obj/item/melee/baton/security))
- if(!can_finish_build(I, user))
- return
+ if(istype(tool, /obj/item/melee/baton/security))
+ if(!can_finish_build(tool, user))
+ return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You complete the Securitron! Beep boop."))
- var/mob/living/simple_animal/bot/secbot/S = new(Tsec)
+ var/mob/living/simple_animal/bot/secbot/S = new(drop_loc)
S.name = created_name
- S.baton_type = I.type
+ S.baton_type = tool.type
S.robot_arm = robot_arm
- qdel(I)
+ qdel(tool)
qdel(src)
- if(I.tool_behaviour == TOOL_WRENCH)
+ return ITEM_INTERACT_SUCCESS
+
+ if(tool.tool_behaviour == TOOL_WRENCH)
to_chat(user, span_notice("You adjust [src]'s arm slots to mount extra weapons."))
- build_step ++
- return
- if(istype(I, /obj/item/toy/sword))
- if(toyswordamt < 3 && swordamt <= 0)
- if(!user.temporarilyRemoveItemFromInventory(I))
- return
- created_name = "General Beepsky"
- name = "helmet/signaler/prox sensor/robot arm/toy sword assembly"
- icon_state = "grievous_assembly"
- to_chat(user, span_notice("You superglue [I] onto one of [src]'s arm slots."))
- qdel(I)
- toyswordamt ++
- else
- if(!can_finish_build(I, user))
- return
- to_chat(user, span_notice("You complete the Securitron!...Something seems a bit wrong with it..?"))
- var/mob/living/simple_animal/bot/secbot/grievous/toy/S = new(Tsec)
- S.name = created_name
- S.robot_arm = robot_arm
- qdel(I)
- qdel(src)
-
- else if(I.tool_behaviour == TOOL_SCREWDRIVER) //deconstruct
+ build_step++
+ return ITEM_INTERACT_SUCCESS
+
+ if(tool.tool_behaviour == TOOL_SCREWDRIVER) //deconstruct
cut_overlay("hs_arm")
- var/obj/item/bodypart/dropped_arm = new robot_arm(Tsec)
+ var/obj/item/bodypart/dropped_arm = new robot_arm(drop_loc)
robot_arm = null
to_chat(user, span_notice("You remove [dropped_arm] from [src]."))
build_step--
@@ -432,36 +457,68 @@
icon_state = initial(icon_state)
to_chat(user, span_notice("The superglue binding [src]'s toy swords to its chassis snaps!"))
for(var/IS in 1 to toyswordamt)
- new /obj/item/toy/sword(Tsec)
+ new /obj/item/toy/sword(drop_loc)
+ return ITEM_INTERACT_SUCCESS
+
+ if(!istype(tool, /obj/item/toy/sword))
+ return NONE
+
+ if(toyswordamt < 3 && swordamt <= 0)
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ created_name = "General Beepsky"
+ name = "helmet/signaler/prox sensor/robot arm/toy sword assembly"
+ icon_state = "grievous_assembly"
+ to_chat(user, span_notice("You superglue [tool] onto one of [src]'s arm slots."))
+ qdel(tool)
+ toyswordamt++
+ return ITEM_INTERACT_SUCCESS
+
+ if(!can_finish_build(tool, user))
+ return ITEM_INTERACT_BLOCKING
+
+ to_chat(user, span_notice("You complete the Securitron!...Something seems a bit wrong with it..?"))
+ var/mob/living/simple_animal/bot/secbot/grievous/toy/S = new(drop_loc)
+ S.name = created_name
+ S.robot_arm = robot_arm
+ qdel(tool)
+ qdel(src)
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_FIFTH_STEP)
- if(istype(I, /obj/item/melee/energy/sword/saber))
- if(swordamt < 3)
- if(!user.temporarilyRemoveItemFromInventory(I))
- return
- created_name = "General Beepsky"
- name = "helmet/signaler/prox sensor/robot arm/energy sword assembly"
- icon_state = "grievous_assembly"
- to_chat(user, span_notice("You bolt [I] onto one of [src]'s arm slots."))
- qdel(I)
- swordamt ++
- else
- if(!can_finish_build(I, user))
- return
- to_chat(user, span_notice("You complete the Securitron!...Something seems a bit wrong with it..?"))
- var/mob/living/simple_animal/bot/secbot/grievous/S = new(Tsec)
- S.name = created_name
- S.robot_arm = robot_arm
- qdel(I)
- qdel(src)
- else if(I.tool_behaviour == TOOL_SCREWDRIVER) //deconstruct
+ if(tool.tool_behaviour == TOOL_SCREWDRIVER) //deconstruct
build_step--
swordamt = 0
icon_state = initial(icon_state)
to_chat(user, span_notice("You unbolt [src]'s energy swords."))
for(var/IS in 1 to swordamt)
- new /obj/item/melee/energy/sword/saber(Tsec)
-
+ new /obj/item/melee/energy/sword/saber(drop_loc)
+ return ITEM_INTERACT_SUCCESS
+
+ if(!istype(tool, /obj/item/melee/energy/sword/saber))
+ return NONE
+
+ if(swordamt < 3)
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ created_name = "General Beepsky"
+ name = "helmet/signaler/prox sensor/robot arm/energy sword assembly"
+ icon_state = "grievous_assembly"
+ to_chat(user, span_notice("You bolt [tool] onto one of [src]'s arm slots."))
+ qdel(tool)
+ swordamt++
+ return ITEM_INTERACT_SUCCESS
+
+ if(!can_finish_build(tool, user))
+ return ITEM_INTERACT_BLOCKING
+
+ to_chat(user, span_notice("You complete the Securitron!...Something seems a bit wrong with it..?"))
+ var/mob/living/simple_animal/bot/secbot/grievous/S = new(drop_loc)
+ S.name = created_name
+ S.robot_arm = robot_arm
+ qdel(tool)
+ qdel(src)
+ return ITEM_INTERACT_SUCCESS
//Firebot Assembly
/obj/item/bot_assembly/firebot
@@ -470,28 +527,31 @@
icon_state = "firebot_arm"
created_name = "Firebot"
-/obj/item/bot_assembly/firebot/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
- ..()
+/obj/item/bot_assembly/firebot/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
switch(build_step)
if(ASSEMBLY_FIRST_STEP)
- if(istype(I, /obj/item/clothing/head/utility/hardhat/red))
- if(!user.temporarilyRemoveItemFromInventory(I))
- return
- to_chat(user,span_notice("You add the [I] to [src]!"))
- icon_state = "firebot_helmet"
- desc = "An incomplete firebot assembly with a fire helmet."
- qdel(I)
- build_step++
+ if(!istype(tool, /obj/item/clothing/head/utility/hardhat/red))
+ return NONE
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user,span_notice("You add the [tool] to [src]!"))
+ icon_state = "firebot_helmet"
+ desc = "An incomplete firebot assembly with a fire helmet."
+ qdel(tool)
+ build_step++
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_SECOND_STEP)
- if(isprox(I))
- if(!can_finish_build(I, user))
- return
- to_chat(user, span_notice("You add the [I] to [src]! Beep Boop!"))
- var/mob/living/basic/bot/firebot/firebot = new(drop_location())
- firebot.name = created_name
- qdel(I)
- qdel(src)
+ if(!isprox(tool))
+ return NONE
+ if(!can_finish_build(tool, user))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You add the [tool] to [src]! Beep Boop!"))
+ var/mob/living/basic/bot/firebot/firebot = new(drop_location())
+ firebot.name = created_name
+ qdel(tool)
+ qdel(src)
+ return ITEM_INTERACT_SUCCESS
//Get cleaned
/obj/item/bot_assembly/hygienebot
@@ -501,102 +561,125 @@
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)
- . = ..()
- var/atom/Tsec = drop_location()
+/obj/item/bot_assembly/hygienebot/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ var/atom/drop_loc = drop_location()
switch(build_step)
if(ASSEMBLY_FIRST_STEP)
- if(I.tool_behaviour == TOOL_WELDER) //Construct
- if(I.use_tool(src, user, 0, volume=40))
- to_chat(user, span_notice("You weld a water hole in [src]!"))
- build_step++
- return
- if(I.tool_behaviour == TOOL_WRENCH) //Deconstruct
- if(I.use_tool(src, user, 0, volume=40))
- new /obj/item/stack/sheet/iron(Tsec, 2)
- to_chat(user, span_notice("You disconnect the hygienebot assembly."))
- qdel(src)
+ if(tool.tool_behaviour == TOOL_WELDER) //Construct
+ if(!tool.use_tool(src, user, 0, volume=40))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You weld a water hole in [src]!"))
+ build_step++
+ return ITEM_INTERACT_SUCCESS
+
+ if(tool.tool_behaviour != TOOL_WRENCH) //Deconstruct
+ return NONE
+ if(!tool.use_tool(src, user, 0, volume=40))
+ return ITEM_INTERACT_BLOCKING
+ new /obj/item/stack/sheet/iron(drop_loc, 2)
+ to_chat(user, span_notice("You disconnect the hygienebot assembly."))
+ qdel(src)
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_SECOND_STEP)
- if(isprox(I)) //Construct
- if(!user.temporarilyRemoveItemFromInventory(I))
- return
+ if(isprox(tool)) //Construct
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+
build_step++
- to_chat(user, span_notice("You add [I] to [src]."))
- qdel(I)
- if(I.tool_behaviour == TOOL_WELDER) //Deconstruct
- if(I.use_tool(src, user, 0, volume=30))
- to_chat(user, span_notice("You weld close the water hole in [src]!"))
- build_step--
- return
+ to_chat(user, span_notice("You add [tool] to [src]."))
+ qdel(tool)
+ return ITEM_INTERACT_SUCCESS
+
+ if(tool.tool_behaviour != TOOL_WELDER) //Deconstruct
+ return NONE
+
+ if(!tool.use_tool(src, user, 0, volume=30))
+ return ITEM_INTERACT_BLOCKING
+
+ to_chat(user, span_notice("You weld close the water hole in [src]!"))
+ build_step--
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_THIRD_STEP)
- if(!can_finish_build(I, user, 0))
- return
- if(istype(I, /obj/item/stack/ducts)) //Construct
- var/obj/item/stack/ducts/D = I
- if(D.get_amount() < 1)
- to_chat(user, span_warning("You need one fluid duct to finish [src]"))
- return
- to_chat(user, span_notice("You start to pipe up [src]..."))
- if(do_after(user, 4 SECONDS, target = src) && D.use(1))
- to_chat(user, span_notice("You pipe up [src]."))
- var/mob/living/basic/bot/hygienebot/new_bot = new(drop_location())
- new_bot.name = created_name
- qdel(src)
- if(I.tool_behaviour == TOOL_SCREWDRIVER) //deconstruct
- new /obj/item/assembly/prox_sensor(Tsec)
+ if(!can_finish_build(tool, user, 0))
+ return ITEM_INTERACT_BLOCKING
+
+ if(tool.tool_behaviour == TOOL_SCREWDRIVER) //deconstruct
+ new /obj/item/assembly/prox_sensor(drop_loc)
to_chat(user, span_notice("You detach the proximity sensor from [src]."))
build_step--
+ return ITEM_INTERACT_SUCCESS
+
+ if(!istype(tool, /obj/item/stack/ducts)) //Construct
+ return NONE
+
+ var/obj/item/stack/ducts/D = tool
+ if(D.get_amount() < 1)
+ to_chat(user, span_warning("You need one fluid duct to finish [src]"))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You start to pipe up [src]..."))
+ if(!do_after(user, 4 SECONDS, target = src) && D.use(1))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You pipe up [src]."))
+ var/mob/living/basic/bot/hygienebot/new_bot = new(drop_location())
+ new_bot.name = created_name
+ qdel(src)
+ return ITEM_INTERACT_SUCCESS
-//Vim Assembly
+// Vim Assembly
/obj/item/bot_assembly/vim
name = "incomplete vim assembly"
desc = "A space helmet with a leg attached to it. Looks like it needs another leg, if it is to become something."
icon_state = "vim_0"
created_name = "\improper Vim"
-/obj/item/bot_assembly/vim/attackby(obj/item/part, mob/user, list/modifiers, list/attack_modifiers)
- . = ..()
- if(.)
- return
+/obj/item/bot_assembly/vim/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
switch(build_step)
if(ASSEMBLY_FIRST_STEP)
- if(istype(part, /obj/item/bodypart/leg/left/robot) || istype(part, /obj/item/bodypart/leg/right/robot))
- if(!user.temporarilyRemoveItemFromInventory(part))
- return
- balloon_alert(user, "leg attached")
- icon_state = "vim_1"
- desc = "Some kind of incomplete mechanism. It seems to be missing the headlights."
- qdel(part)
- build_step++
+ if(!istype(tool, /obj/item/bodypart/leg/left/robot) && !istype(tool, /obj/item/bodypart/leg/right/robot))
+ return NONE
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_BLOCKING
+ balloon_alert(user, "leg attached")
+ icon_state = "vim_1"
+ desc = "Some kind of incomplete mechanism. It seems to be missing the headlights."
+ qdel(tool)
+ build_step++
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_SECOND_STEP)
- if(istype(part, /obj/item/flashlight))
- if(!user.temporarilyRemoveItemFromInventory(part))
- return
- balloon_alert(user, "flashlight added")
- icon_state = "vim_2"
- desc = "Some kind of incomplete mechanism. The flashlight is added, but not secured."
- qdel(part)
- build_step++
+ if(!istype(tool, /obj/item/flashlight))
+ return NONE
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_SUCCESS
+ balloon_alert(user, "flashlight added")
+ icon_state = "vim_2"
+ desc = "Some kind of incomplete mechanism. The flashlight is added, but not secured."
+ qdel(tool)
+ build_step++
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_THIRD_STEP)
- if(part.tool_behaviour == TOOL_SCREWDRIVER)
- balloon_alert(user, "securing flashlight...")
- if(!part.use_tool(src, user, 4 SECONDS, volume=100))
- return
- balloon_alert(user, "flashlight secured")
- icon_state = "vim_3"
- desc = "Some kind of incomplete mechanism. It seems nearly completed, and just needs a voice assembly."
- build_step++
+ if(tool.tool_behaviour != TOOL_SCREWDRIVER)
+ return NONE
+ balloon_alert(user, "securing flashlight...")
+ if(!tool.use_tool(src, user, 4 SECONDS, volume=100))
+ return ITEM_INTERACT_BLOCKING
+ balloon_alert(user, "flashlight secured")
+ icon_state = "vim_3"
+ desc = "Some kind of incomplete mechanism. It seems nearly completed, and just needs a voice assembly."
+ build_step++
+ return ITEM_INTERACT_SUCCESS
if(ASSEMBLY_FOURTH_STEP)
- if(istype(part, /obj/item/assembly/voice))
- if(!can_finish_build(part, user))
- return
- balloon_alert(user, "assembly finished")
- var/obj/vehicle/sealed/car/vim/new_vim = new(drop_location())
- new_vim.name = created_name
- qdel(part)
- qdel(src)
+ if(!istype(tool, /obj/item/assembly/voice))
+ return NONE
+ if(!can_finish_build(tool, user))
+ return ITEM_INTERACT_BLOCKING
+ balloon_alert(user, "assembly finished")
+ var/obj/vehicle/sealed/car/vim/new_vim = new(drop_location())
+ new_vim.name = created_name
+ qdel(tool)
+ qdel(src)
+ return ITEM_INTERACT_SUCCESS
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 02e382c7117e..40ee61766b8f 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -62,7 +62,7 @@
/mob/living/carbon/get_random_valid_zone(base_zone, base_probability = 80, list/blacklisted_parts, even_weights, bypass_warning)
var/list/limbs = list()
- for(var/obj/item/bodypart/part as anything in bodyparts)
+ for(var/obj/item/bodypart/part as anything in get_bodyparts())
var/limb_zone = part.body_zone //cache the zone since we're gonna check it a ton.
if(limb_zone in blacklisted_parts)
continue
diff --git a/code/modules/modular_computers/file_system/program_circuit.dm b/code/modules/modular_computers/file_system/program_circuit.dm
index 85cd7b388182..481e46af04c6 100644
--- a/code/modules/modular_computers/file_system/program_circuit.dm
+++ b/code/modules/modular_computers/file_system/program_circuit.dm
@@ -57,7 +57,7 @@
. = ..()
start = add_input_port("Start", PORT_TYPE_SIGNAL, trigger = PROC_REF(start_prog))
kill = add_input_port("Kill", PORT_TYPE_SIGNAL, trigger = PROC_REF(kill_prog))
- running = add_output_port("Running", PORT_TYPE_NUMBER)
+ running = add_output_port("Running", PORT_TYPE_BOOLEAN)
///For most programs, triggers only work if they're open (either active or idle).
/obj/item/circuit_component/mod_program/should_receive_input(datum/port/input/port)
diff --git a/code/modules/modular_computers/file_system/programs/budgetordering.dm b/code/modules/modular_computers/file_system/programs/budgetordering.dm
index 7aa035ffd0dc..d4f8e125fd7e 100644
--- a/code/modules/modular_computers/file_system/programs/budgetordering.dm
+++ b/code/modules/modular_computers/file_system/programs/budgetordering.dm
@@ -215,7 +215,7 @@
user.log_message("accepted a shuttle loan event.", LOG_GAME)
. = TRUE
if("add")
- var/id = text2path(params["id"])
+ var/id = text2path(params["id"]) || params["id"]
var/datum/supply_pack/pack = SSshuttle.supply_packs[id]
if(!istype(pack))
return
diff --git a/code/modules/modular_computers/file_system/programs/raptordex.dm b/code/modules/modular_computers/file_system/programs/raptordex.dm
index 36c5d722a595..e040d0373986 100644
--- a/code/modules/modular_computers/file_system/programs/raptordex.dm
+++ b/code/modules/modular_computers/file_system/programs/raptordex.dm
@@ -61,3 +61,22 @@
/datum/computer_file/program/raptordex/ui_data(mob/user)
return scan_data
+
+/datum/computer_file/program/raptordex/on_made_active_program(mob/user)
+ RegisterSignal(computer, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET, PROC_REF(add_item_context))
+ computer.item_flags |= ITEM_HAS_CONTEXTUAL_SCREENTIPS
+
+/datum/computer_file/program/raptordex/background_program(mob/user)
+ . = ..()
+ UnregisterSignal(computer, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET)
+
+/datum/computer_file/program/raptordex/kill_program(mob/user)
+ . = ..()
+ UnregisterSignal(computer, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET)
+
+/datum/computer_file/program/raptordex/proc/add_item_context(obj/item/source, list/context, atom/target, mob/living/user)
+ SIGNAL_HANDLER
+ if(!istype(target, /mob/living/basic/raptor))
+ return NONE
+ context[SCREENTIP_CONTEXT_RMB] = "Scan Raptor"
+ return CONTEXTUAL_SCREENTIP_SET
diff --git a/code/modules/power/rtg.dm b/code/modules/power/rtg.dm
index 4390ae738542..dd21c40ae91b 100644
--- a/code/modules/power/rtg.dm
+++ b/code/modules/power/rtg.dm
@@ -6,6 +6,7 @@
desc = "A simple nuclear power generator, used in small outposts to reliably provide power for decades."
icon = 'icons/obj/machines/engine/other.dmi'
icon_state = "rtg"
+ base_icon_state = "rtg"
density = TRUE
use_power = NO_POWER_USE
circuit = /obj/item/circuitboard/machine/rtg
@@ -15,61 +16,87 @@
buckle_lying = 0
buckle_requires_restraints = TRUE
- var/power_gen = 1000 // Enough to power a single APC. 4000 output with T4 capacitor.
+ /// Whether stock parts affect power generated
+ var/affected_by_parts = TRUE
+ /// Free power generated every tick
+ var/power_gen = 1 KILO WATTS
+ /// Base power gen level, potentially modified by parts
+ VAR_PRIVATE/base_power_gen
/obj/machinery/power/rtg/Initialize(mapload)
+ base_power_gen = power_gen
. = ..()
connect_to_network()
+ RefreshParts()
/obj/machinery/power/rtg/process()
add_avail(power_to_energy(power_gen))
/obj/machinery/power/rtg/RefreshParts()
. = ..()
- var/part_level = 0
- for(var/datum/stock_part/stock_part in component_parts)
- part_level += stock_part.tier
+ var/new_power_gen = get_base_power_gen()
+ if(affected_by_parts)
+ var/part_level = 0
+ for(var/datum/stock_part/stock_part in component_parts)
+ part_level += stock_part.tier
- power_gen = initial(power_gen) * part_level
+ new_power_gen = base_power_gen * (part_level || 1)
+
+ power_gen = new_power_gen
+
+/obj/machinery/power/rtg/proc/get_base_power_gen()
+ return base_power_gen
/obj/machinery/power/rtg/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
. += span_notice("The status display reads: Power generation at [display_power(power_gen, convert = FALSE)].")
-/obj/machinery/power/rtg/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
- if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-open", initial(icon_state), I))
- return
- else if(default_deconstruction_crowbar(I))
- return
- return ..()
+/obj/machinery/power/rtg/screwdriver_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_screwdriver(user, "[base_icon_state]-open", base_icon_state, tool))
+ return ITEM_INTERACT_SUCCESS
+ return ITEM_INTERACT_BLOCKING
+
+/obj/machinery/power/rtg/crowbar_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_crowbar(tool))
+ return ITEM_INTERACT_SUCCESS
+ return panel_open ? ITEM_INTERACT_BLOCKING : NONE
+
+/obj/machinery/power/rtg/vv_edit_var(vname, vval)
+ . = ..()
+ if(vname == NAMEOF(src, power_gen) || vname == NAMEOF(src, base_power_gen) || vname == NAMEOF(src, affected_by_parts))
+ RefreshParts()
/obj/machinery/power/rtg/advanced
desc = "An advanced RTG capable of moderating isotope decay, increasing power output but reducing lifetime. It uses plasma-fueled radiation collectors to increase output even further."
- power_gen = 1250 // 2500 on T1, 10000 on T4.
+ power_gen = 1.25 KILO WATTS
circuit = /obj/item/circuitboard/machine/rtg/advanced
// Void Core, power source for Abductor ships and bases.
// Provides a lot of power, but tends to explode when mistreated.
/obj/machinery/power/rtg/abductor
- name = "Void Core"
+ name = "void core"
icon = 'icons/obj/antags/abductor.dmi'
icon_state = "core"
+ base_icon_state = "core"
desc = "An alien power source that produces energy seemingly out of nowhere."
circuit = /obj/item/circuitboard/machine/abductor/core
- power_gen = 20000 // 280 000 at T1, 400 000 at T4. Starts at T4.
+ power_gen = 20 KILO WATTS
can_buckle = FALSE
- pixel_y = 7
- var/going_kaboom = FALSE // Is it about to explode?
+ SET_BASE_PIXEL(0, 7)
+ /// Is it about to explode?
+ VAR_PRIVATE/going_kaboom = FALSE
/obj/machinery/power/rtg/abductor/proc/overload()
if(going_kaboom)
return
going_kaboom = TRUE
- visible_message(span_danger("\The [src] lets out a shower of sparks as it starts to lose stability!"),\
- span_hear("You hear a loud electrical crack!"))
- playsound(src.loc, 'sound/effects/magic/lightningshock.ogg', 100, TRUE, extrarange = 5)
+ visible_message(
+ message = span_danger("[src] lets out a shower of sparks as it starts to lose stability!"),
+ blind_message = span_hear("You hear a loud electrical crack!"),
+ )
+ playsound(src, 'sound/effects/magic/lightningshock.ogg', 100, TRUE, extrarange = 5)
tesla_zap(source = src, zap_range = 5, power = power_gen * 20)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(explosion), src, 2, 3, 4, null, 8), 10 SECONDS) // Not a normal explosion.
@@ -99,61 +126,47 @@
overload()
/obj/machinery/power/rtg/debug
- name = "Debug RTG"
+ name = "debug " + parent_type::name
desc = "You really shouldn't be seeing this if you're not a coder or jannie."
- power_gen = 20000
+ power_gen = 20 KILO WATTS
circuit = null
-
-/obj/machinery/power/rtg/debug/RefreshParts()
- SHOULD_CALL_PARENT(FALSE)
- return
+ affected_by_parts = FALSE
/obj/machinery/power/rtg/lavaland
- name = "Lava powered RTG"
- desc = "This device only works when exposed to the toxic fumes of Lavaland"
+ name = "lava powered " + parent_type::name
+ desc = "A power generator that uses the heat and atmosphere of Lavaland to generate power. Won't generate squat anywhere else."
circuit = null
- power_gen = 20000
+ power_gen = 20 KILO WATTS
anchored = TRUE
resistance_flags = LAVA_PROOF
-/obj/machinery/power/rtg/lavaland/Initialize(mapload)
- . = ..()
- var/turf/our_turf = get_turf(src)
- if(!islava(our_turf))
- power_gen = 0
- if(!is_mining_level(z))
- power_gen = 0
-
/obj/machinery/power/rtg/lavaland/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
. = ..()
+ RefreshParts()
+
+/obj/machinery/power/rtg/lavaland/get_base_power_gen()
var/turf/our_turf = get_turf(src)
- if(!islava(our_turf))
- power_gen = 0
- return
- if(!is_mining_level(z))
- power_gen = 0
- return
- power_gen = initial(power_gen)
+ if(islava(our_turf) && is_mining_level(our_turf.z))
+ return base_power_gen
+ return 0
/obj/machinery/power/rtg/old_station
- name = "Old RTG"
- desc = "A very old RTG, it seems on the verge of being destroyed"
+ name = "old " + parent_type::name
+ desc = "A very old " + parent_type::name + ". It seems on the verge of being destroyed."
circuit = null
- power_gen = 750
+ power_gen = 0.75 KILO WATTS
anchored = TRUE
-/obj/machinery/power/rtg/old_station/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
- if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-open", initial(icon_state), I))
- to_chat(user,span_warning("You feel it crumbling under your hands!"))
- return
- else if(default_deconstruction_crowbar(I, user = user))
- return
- return ..()
+/obj/machinery/power/rtg/old_station/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/screwdriver)
+ . = ..()
+ if(.)
+ to_chat(user, span_warning("You feel it crumbling under your hands!"))
/obj/machinery/power/rtg/old_station/default_deconstruction_crowbar(obj/item/crowbar, ignore_panel, custom_deconstruct, mob/user)
- to_chat(user,span_warning("It's starting to fall off!"))
- if(!do_after(user, 3 SECONDS, src))
- return TRUE
- to_chat(user,span_notice("You feel like you made a mistake"))
- new /obj/effect/decal/cleanable/ash/large(loc)
- qdel(src)
+ to_chat(user, span_warning("As you pry, [src] starts to fall apart!"))
+ if(!crowbar.use_tool(src, user, 3 SECONDS, volume = 50))
+ return FALSE
+ to_chat(user, span_warning("You feel like you made a mistake."))
+ new /obj/effect/decal/cleanable/ash/large(drop_location())
+ deconstruct(FALSE)
+ return TRUE
diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm
index c41be13d086e..7498d22d1d70 100644
--- a/code/modules/power/tesla/energy_ball.dm
+++ b/code/modules/power/tesla/energy_ball.dm
@@ -176,7 +176,9 @@
target.orbiting_balls += src
. = ..()
-/obj/energy_ball/stop_orbit()
+/obj/energy_ball/stop_orbit(datum/component/orbiter/orbiters, refreshing = FALSE)
+ if(refreshing)
+ return ..()
if (orbiting && istype(orbiting.parent, /obj/energy_ball))
var/obj/energy_ball/orbitingball = orbiting.parent
orbitingball.orbiting_balls -= src
diff --git a/code/modules/projectiles/ammunition/ballistic/rifle.dm b/code/modules/projectiles/ammunition/ballistic/rifle.dm
index a252832dcd08..3c06aa139504 100644
--- a/code/modules/projectiles/ammunition/ballistic/rifle.dm
+++ b/code/modules/projectiles/ammunition/ballistic/rifle.dm
@@ -161,7 +161,7 @@
/datum/embedding/rebar_healium/on_successful_embed(mob/living/carbon/victim, obj/item/bodypart/target_limb)
. = ..()
- for(var/obj/item/bodypart/limb as anything in victim.bodyparts)
+ for(var/obj/item/bodypart/limb as anything in victim.get_bodyparts())
for(var/obj/item/ammo_casing/rebar/healium/other_rebar in limb.embedded_objects)
if (other_rebar == parent)
continue
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 0f93174325c4..6819e359a169 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -224,7 +224,7 @@
return !user.contains(src)
/obj/item/gun/proc/shoot_with_empty_chamber(mob/living/user as mob|obj)
- balloon_alert_to_viewers("*click*")
+ balloon_alert_to_hearers("*click*")
playsound(src, dry_fire_sound, dry_fire_sound_volume, TRUE)
/obj/item/gun/proc/fire_sounds()
diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm
index fbdc1f6f3f17..b4332f005a11 100644
--- a/code/modules/projectiles/guns/ballistic/automatic.dm
+++ b/code/modules/projectiles/guns/ballistic/automatic.dm
@@ -472,7 +472,7 @@
/obj/item/gun/ballistic/automatic/battle_rifle/process_fire(atom/target, mob/living/user, message = TRUE, params = null, zone_override = "", bonus_spread = 0)
if(chambered.loaded_projectile && prob(75) && (emp_malfunction || degradation_stage == degradation_stage_max))
- balloon_alert_to_viewers("*click*")
+ balloon_alert_to_hearers("*click*")
playsound(src, dry_fire_sound, dry_fire_sound_volume, TRUE)
return
diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm
index bdbe041d2cf5..79f5cfb4fd20 100644
--- a/code/modules/projectiles/guns/magic/staff.dm
+++ b/code/modules/projectiles/guns/magic/staff.dm
@@ -362,7 +362,7 @@
if (!iscarbon(user))
return BRUTELOSS
var/mob/living/carbon/suicider = user
- for (var/obj/item/bodypart/limb in suicider.bodyparts)
+ for (var/obj/item/bodypart/limb in suicider.get_bodyparts())
limb.dismember(BRUTE, silent = FALSE, wounding_type = WOUND_SLASH)
sleep(0.25 SECONDS)
diff --git a/code/modules/reagents/chemistry/items.dm b/code/modules/reagents/chemistry/items.dm
index 6f1731c7be13..34da7832d47f 100644
--- a/code/modules/reagents/chemistry/items.dm
+++ b/code/modules/reagents/chemistry/items.dm
@@ -119,6 +119,7 @@
var/obj/item/reagent_containers/cont = interacting_with
if(!LAZYLEN(cont.reagents.reagent_list))
return NONE
+ SEND_SIGNAL(interacting_with, COMSIG_ON_REAGENT_SCAN, user)
var/list/out_message = list()
to_chat(user, "The chemistry meter beeps and displays:")
out_message += "Total volume: [round(cont.volume, 0.01)] Current temperature: [round(cont.reagents.chem_temp, 0.1)]K Total pH: [round(cont.reagents.ph, 0.01)]\n"
diff --git a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
index b3a49dbcebc6..740b08b74827 100644
--- a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
@@ -74,6 +74,9 @@
if(combined_dilute_volume) // safety check to prevent division by zero
booze_power *= (total_alcohol_volume / combined_dilute_volume)
+ for(var/mob/living/enemy as anything in drinker.ai_controller?.blackboard[BB_MONKEY_ENEMIES])
+ drinker.ai_controller.add_blackboard_key_assoc(BB_MONKEY_ENEMIES, enemy, MONKEY_ANGERED_HATRED_AMOUNT * (boozepwr / 100) * metabolization_ratio * seconds_per_tick)
+
// Volume, power, and server alcohol rate effect how quickly one gets drunk
drinker.adjust_drunk_effect(1 * sqrt(volume) * booze_power * ALCOHOL_RATE * metabolization_ratio * seconds_per_tick)
if(boozepwr > 0)
diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm
index a425d5e86ab1..873dd41d4adb 100644
--- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm
@@ -47,6 +47,8 @@
chemical_flags = REAGENT_CAN_BE_SYNTHESIZED
randomized_spawns = REAGENT_SPAWN_ALL_RANDOM_SPAWNS
metabolization_rate = 0.125 * REAGENTS_METABOLISM
+ /// tracks if we cleared a monkey's aggressiveness value
+ var/cleared_aggressive = FALSE
/datum/reagent/drug/cannabis/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio)
. = ..()
@@ -64,6 +66,18 @@
to_chat(affected_mob, span_warning("It's too comfy to move..."))
affected_mob.Paralyze(10 SECONDS)
+ var/list/enemies = affected_mob.ai_controller?.blackboard[BB_MONKEY_ENEMIES]
+ for(var/mob/living/enemy as anything in enemies)
+ affected_mob.ai_controller.add_blackboard_key_assoc(BB_MONKEY_ENEMIES, enemy, MONKEY_CALMED_HATRED_AMOUNT * metabolization_ratio * seconds_per_tick)
+ if(affected_mob.ai_controller.blackboard[BB_MONKEY_AGGRESSIVE] && SPT_PROB(10 - values_sum(enemies), seconds_per_tick))
+ affected_mob.ai_controller.set_blackboard_key(BB_MONKEY_AGGRESSIVE, FALSE)
+ cleared_aggressive = TRUE
+
+/datum/reagent/drug/cannabis/on_mob_delete(mob/living/affected_mob)
+ . = ..()
+ if(cleared_aggressive)
+ affected_mob.ai_controller?.set_blackboard_key(BB_MONKEY_AGGRESSIVE, TRUE)
+
/datum/reagent/drug/nicotine
name = "Nicotine"
description = "Slightly reduces stun times. If overdosed it will deal toxin and oxygen damage."
@@ -89,7 +103,10 @@
to_chat(affected_mob, span_notice("[smoke_message]"))
affected_mob.add_mood_event("smoked", /datum/mood_event/smoked)
affected_mob.remove_status_effect(/datum/status_effect/jitter)
- affected_mob.AdjustAllImmobility(-200 * metabolization_ratio * seconds_per_tick)
+ affected_mob.AdjustAllImmobility(-20 SECONDS * metabolization_ratio * seconds_per_tick)
+
+ for(var/mob/living/enemy as anything in affected_mob.ai_controller?.blackboard[BB_MONKEY_ENEMIES])
+ affected_mob.ai_controller.add_blackboard_key_assoc(BB_MONKEY_ENEMIES, enemy, MONKEY_CALMED_HATRED_AMOUNT * 0.1 * metabolization_ratio * seconds_per_tick)
return UPDATE_MOB_HEALTH
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 1b0dad125df9..72cc93916cce 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
@@ -986,6 +986,8 @@ Basically, we fill the time between now and 2s from now with hands based off the
if(is_simian(affected_mob))
affected_mob.gain_trauma(/datum/brain_trauma/special/primal_instincts, TRAUMA_RESILIENCE_ABSOLUTE)
affected_mob.add_traits(list(TRAIT_STUNIMMUNE, TRAIT_SLEEPIMMUNE, TRAIT_ANALGESIA, TRAIT_STIMULATED), type)
+ if(jungle_arts)
+ return
jungle_arts = new(src)
jungle_arts.locked_to_use = TRUE
jungle_arts.teach(affected_mob)
@@ -993,10 +995,11 @@ Basically, we fill the time between now and 2s from now with hands based off the
/datum/reagent/inverse/bath_salts/on_mob_end_metabolize(mob/living/carbon/affected_mob)
. = ..()
QDEL_NULL(jungle_arts)
+ affected_mob.remove_traits(list(TRAIT_STUNIMMUNE, TRAIT_SLEEPIMMUNE, TRAIT_ANALGESIA, TRAIT_STIMULATED), type)
+ affected_mob.Sleeping(30 SECONDS)
if(is_simian(affected_mob))
affected_mob.cure_trauma_type(/datum/brain_trauma/special/primal_instincts, resilience = TRAUMA_RESILIENCE_ABSOLUTE)
- affected_mob.remove_traits(list(TRAIT_STUNIMMUNE, TRAIT_SLEEPIMMUNE, TRAIT_ANALGESIA, TRAIT_STIMULATED), type)
- affected_mob.Sleeping(30 SECONDS)
+
/datum/reagent/inverse/bath_salts/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio)
. = ..()
@@ -1013,10 +1016,14 @@ Basically, we fill the time between now and 2s from now with hands based off the
if(need_mob_update)
. = UPDATE_MOB_HEALTH
+ return
- else if(SPT_PROB(10, seconds_per_tick))
+ if(SPT_PROB(10, seconds_per_tick))
affected_mob.emote(pick("screech","scratch","jump","look"))
+ QDEL_NULL(jungle_arts)
+ affected_mob.remove_traits(list(TRAIT_STUNIMMUNE, TRAIT_SLEEPIMMUNE, TRAIT_ANALGESIA, TRAIT_STIMULATED), type)
+
/datum/reagent/inverse/aranesp
name = "Epoetin Alfa"
description = "Synthetic medication that induces blood regeneration and wound clotting in patients. \
@@ -1264,7 +1271,7 @@ Basically, we fill the time between now and 2s from now with hands based off the
for (var/obj/item/organ/organ as anything in exposed_carbon.organs)
organ.add_atom_colour(color_filter, WASHABLE_COLOUR_PRIORITY)
- for (var/obj/item/bodypart/part as anything in exposed_carbon.bodyparts)
+ for (var/obj/item/bodypart/part as anything in exposed_carbon.get_bodyparts())
part.add_atom_colour(color_filter, WASHABLE_COLOUR_PRIORITY)
/datum/reagent/inverse/colorful_reagent/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio)
@@ -1313,22 +1320,22 @@ Basically, we fill the time between now and 2s from now with hands based off the
switch(current_cycle)
if(10)
- for(var/obj/item/bodypart/leg/leg in affected_mob.bodyparts)
+ for(var/obj/item/bodypart/leg/leg in affected_mob.get_bodyparts())
affected_mob.cause_wound_of_type_and_severity(WOUND_BLUNT, leg, WOUND_SEVERITY_MODERATE)
to_chat(affected_mob, span_warning("Your legs start to cave in to your overwhelming gravity!"))
if(20)
- for(var/obj/item/bodypart/leg/leg in affected_mob.bodyparts)
+ for(var/obj/item/bodypart/leg/leg in affected_mob.get_bodyparts())
affected_mob.cause_wound_of_type_and_severity(WOUND_BLUNT, leg, WOUND_SEVERITY_SEVERE)
to_chat(affected_mob, span_warning("Your bones fragment horribly as the gravity pounds on you!"))
if(30)
- for(var/obj/item/bodypart/leg/leg in affected_mob.bodyparts)
+ for(var/obj/item/bodypart/leg/leg in affected_mob.get_bodyparts())
affected_mob.cause_wound_of_type_and_severity(WOUND_BLUNT, leg, WOUND_SEVERITY_CRITICAL)
to_chat(affected_mob, span_warning("The gravity of this situation makes your bones snap like popsicle sticks!"))
/datum/reagent/inverse/gravitum/overdose_start(mob/living/carbon/affected_mob, metabolization_ratio)
. = ..()
affected_mob.AddElement(/datum/element/squish, 120 SECONDS)
- for(var/obj/item/bodypart/leg/leg in affected_mob.bodyparts)
+ for(var/obj/item/bodypart/leg/leg in affected_mob.get_bodyparts())
affected_mob.cause_wound_of_type_and_severity(WOUND_SLASH, leg, WOUND_SEVERITY_SEVERE)
diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index ec0abc8e3255..16fe822dee55 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -2370,7 +2370,7 @@
for (var/obj/item/organ/organ as anything in exposed_carbon.organs)
organ.add_atom_colour(color_filter, WASHABLE_COLOUR_PRIORITY)
- for (var/obj/item/bodypart/part as anything in exposed_carbon.bodyparts)
+ for (var/obj/item/bodypart/part as anything in exposed_carbon.get_bodyparts())
part.add_atom_colour(color_filter, WASHABLE_COLOUR_PRIORITY)
/datum/reagent/colorful_reagent/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio)
diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
index 4c95b21afe75..41cba75b8bce 100644
--- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm
@@ -785,17 +785,11 @@
if(affected_mob.adjust_tox_loss(-1 * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype)) //it counteracts its own toxin damage.
return UPDATE_MOB_HEALTH
return
- else if(SPT_PROB(2.5, seconds_per_tick) && !HAS_TRAIT(affected_mob, TRAIT_BLOCK_FORMALDEHYDE_METABOLISM))
+ else if(SPT_PROB(2.5, seconds_per_tick))
holder.add_reagent(/datum/reagent/toxin/histamine, pick(5,15))
holder.remove_reagent(/datum/reagent/toxin/formaldehyde, 2.4 * metabolization_ratio * seconds_per_tick)
return ..()
-/datum/reagent/toxin/formaldehyde/metabolize_reagent(mob/living/carbon/affected_mob, seconds_per_tick, metabolized_volume)
- if(HAS_TRAIT(affected_mob, TRAIT_BLOCK_FORMALDEHYDE_METABOLISM))
- return
-
- return ..()
-
/datum/reagent/toxin/venom
name = "Venom"
description = "An exotic poison extracted from highly toxic fauna. Causes scaling amounts of toxin damage and bruising depending and dosage. Often decays into Histamine."
diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm
index 63f67e183c00..fd641d9e705f 100644
--- a/code/modules/reagents/reagent_containers/cups/_cup.dm
+++ b/code/modules/reagents/reagent_containers/cups/_cup.dm
@@ -37,6 +37,8 @@
var/cell_wired = FALSE
/// Visual y-offset for the assembly on our lid
var/assembly_pixel_y = 0
+ /// If TRUE, after we finish drinking, we try to drink again after do_after
+ var/loop_drink = FALSE
/obj/item/reagent_containers/cup/Initialize(mapload, vol)
. = ..()
@@ -96,6 +98,8 @@
user.changeNext_move(CLICK_CD_MELEE)
if(target_mob != user)
+ if(DOING_INTERACTION_WITH_TARGET(user, target_mob))
+ return ITEM_INTERACT_BLOCKING
target_mob.visible_message(
span_danger("[user] attempts to feed [target_mob] something from [src]."),
span_userdanger("[user] attempts to feed you something from [src]."),
@@ -108,29 +112,45 @@
span_danger("[user] feeds [target_mob] something from [src]."),
span_userdanger("[user] feeds you something from [src]."),
)
+ if(target_mob.is_blind())
+ to_chat(target_mob, span_notice("You feel someone feed you something."))
log_combat(user, target_mob, "fed", reagents.get_reagent_log_string())
+
else
+ if(loop_drink)
+ if(DOING_INTERACTION_WITH_TARGET(user, user))
+ return ITEM_INTERACT_BLOCKING
+ user.visible_message(
+ span_danger("[user] attempts to drink from [src]."),
+ span_userdanger("[user] attempts to drink from [src]."),
+ )
+ if(!do_after(user, 1.25 SECONDS, user))
+ return ITEM_INTERACT_BLOCKING
+ if(!reagents || !reagents.total_volume)
+ return ITEM_INTERACT_BLOCKING
+ user.visible_message(
+ span_danger("[user] drinks from [src]."),
+ span_userdanger("[user] drinks from [src]."),
+ ignored_mobs = list(user),
+ )
to_chat(user, span_notice("You swallow a gulp of [src]."))
- . = ITEM_INTERACT_SUCCESS
SEND_SIGNAL(src, COMSIG_GLASS_DRANK, target_mob, user)
- var/fraction = min(gulp_size/reagents.total_volume, 1)
+ var/fraction = min(gulp_size / reagents.total_volume, 1)
reagents.trans_to(target_mob, gulp_size, transferred_by = user, methods = reagent_consumption_method)
+ user.hud_used?.hunger?.update_hunger_bar()
checkLiked(fraction, target_mob)
- playsound(target_mob.loc, consumption_sound, rand(10,50), TRUE)
- if(!iscarbon(target_mob))
- return .
- var/mob/living/carbon/carbon_drinker = target_mob
- var/list/diseases = carbon_drinker.get_static_viruses()
- if(!LAZYLEN(diseases))
- return .
- var/list/datum/disease/diseases_to_add = list()
- for(var/datum/disease/malady as anything in diseases)
+ playsound(target_mob, consumption_sound, rand(10, 50), TRUE)
+ var/list/datum/disease/diseases_to_add
+ for(var/datum/disease/malady as anything in target_mob.get_static_viruses())
if(malady.spread_flags & DISEASE_SPREAD_CONTACT_FLUIDS)
- diseases_to_add += malady
+ LAZYADD(diseases_to_add, malady)
if(LAZYLEN(diseases_to_add))
AddComponent(/datum/component/infective, diseases_to_add)
- return .
+ if(loop_drink)
+ return try_drink(target_mob, user) | ITEM_INTERACT_SUCCESS
+
+ return ITEM_INTERACT_SUCCESS
/obj/item/reagent_containers/cup/interact_with_atom(atom/target, mob/living/user, list/modifiers)
. = ..()
diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm
index e30529bcd571..66096a368f4d 100644
--- a/code/modules/reagents/reagent_containers/patch.dm
+++ b/code/modules/reagents/reagent_containers/patch.dm
@@ -90,7 +90,7 @@
. = ..()
if (!.)
return
- var/obj/item/bodypart/affecting = victim.get_bodypart(hit_zone) || victim.bodyparts[1]
+ var/obj/item/bodypart/affecting = victim.get_bodypart(hit_zone) || victim.get_bodypart()
if (!IS_ORGANIC_LIMB(affecting))
return FALSE
return TRUE
diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm
index e2871b3b3691..4d19c5b5f187 100644
--- a/code/modules/reagents/reagent_containers/pill.dm
+++ b/code/modules/reagents/reagent_containers/pill.dm
@@ -355,9 +355,23 @@
icon_state = "pill21"
/// From which randomisation pool to pull reagents from
var/random_reagent_flag = REAGENT_SPAWN_RANDOM_PRODUCERS
- var/static/list/names = list("maintenance pill", "floor pill", "mystery pill", "suspicious pill", "strange pill", "lucky pill", "ominous pill", "eerie pill")
- var/static/list/descs = list("Your feeling is telling you no, but...","Drugs are expensive, you can't afford not to eat any pills that you find."\
- , "Surely, there's no way this could go bad.", "Winners don't do dr- oh what the heck!", "Free pills? At no cost, how could I lose?")
+ var/static/list/names = list(
+ "maintenance pill",
+ "floor pill",
+ "mystery pill",
+ "suspicious pill",
+ "strange pill",
+ "lucky pill",
+ "ominous pill",
+ "eerie pill",
+ )
+ var/static/list/descs = list(
+ "Your feeling is telling you no, but...",
+ "Drugs are expensive, you can't afford not to eat any pills that you find.",
+ "Surely, there's no way this could go bad.",
+ "Winners don't do dr- oh what the heck!",
+ "Free pills? At no cost, how could I lose?",
+ )
/obj/item/reagent_containers/applicator/pill/maintenance/Initialize(mapload)
list_reagents = list(get_random_reagent_id(random_reagent_flag) = rand(10,50)) //list_reagents is called before init, because init generates the reagents using list_reagents
@@ -368,10 +382,22 @@
/obj/item/reagent_containers/applicator/pill/maintenance/achievement
random_reagent_flag = REAGENT_SPAWN_MAINTENANCE_PILL //none of that fake shit
+ ///Boolean on whether this will count towards your achievement score if you consume it.
+ var/count_towards_achievement = TRUE
+
+/obj/item/reagent_containers/applicator/pill/maintenance/achievement/Initialize(mapload)
+ . = ..()
+ RegisterSignal(src, COMSIG_ON_REAGENT_SCAN, PROC_REF(on_chemical_scan))
/obj/item/reagent_containers/applicator/pill/maintenance/achievement/on_consumption(mob/consumer, mob/user)
. = ..()
- consumer.client?.give_award(/datum/award/score/maintenance_pill, consumer)
+ if(count_towards_achievement)
+ consumer.client?.give_award(/datum/award/score/maintenance_pill, consumer)
+
+///called when we are chemically scanned, we no longer grant an achievement.
+/obj/item/reagent_containers/applicator/pill/maintenance/achievement/proc/on_chemical_scan(atom/source, mob/user)
+ SIGNAL_HANDLER
+ count_towards_achievement = FALSE
/obj/item/reagent_containers/applicator/pill/potassiodide
name = "potassium iodide pill"
diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm
index 696134e78931..8a1b9abb9b89 100644
--- a/code/modules/recycling/sortingmachinery.dm
+++ b/code/modules/recycling/sortingmachinery.dm
@@ -37,7 +37,7 @@
new /obj/effect/decal/cleanable/wrapping(turf_loc)
else
playsound(loc, 'sound/items/box_cut.ogg', 50, TRUE)
- new /obj/item/stack/package_wrap(turf_loc)
+ new /obj/item/stack/package_wrap/one(turf_loc)
for(var/atom/movable/movable_content as anything in contents)
movable_content.forceMove(turf_loc)
diff --git a/code/modules/religion/festival/instrument_rites.dm b/code/modules/religion/festival/instrument_rites.dm
index e3f53c10fd83..3f03b47747b3 100644
--- a/code/modules/religion/festival/instrument_rites.dm
+++ b/code/modules/religion/festival/instrument_rites.dm
@@ -176,7 +176,7 @@
listener.adjust_brute_loss(damage_dealt)
/datum/religion_rites/song_tuner/pain/finish_effect(mob/living/carbon/human/listener, atom/song_source)
- var/obj/item/bodypart/sliced_limb = pick(listener.bodyparts)
+ var/obj/item/bodypart/sliced_limb = pick(listener.get_bodyparts())
sliced_limb.force_wound_upwards(/datum/wound/slash/flesh/moderate/many_cuts)
/datum/religion_rites/song_tuner/lullaby
diff --git a/code/modules/religion/religion_sects.dm b/code/modules/religion/religion_sects.dm
index 05cf51ea433e..d375a1c38824 100644
--- a/code/modules/religion/religion_sects.dm
+++ b/code/modules/religion/religion_sects.dm
@@ -111,7 +111,7 @@
return BLESSING_FAILED
var/mob/living/carbon/human/blessed = target
- for(var/obj/item/bodypart/bodypart as anything in blessed.bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in blessed.get_bodyparts())
if(IS_ROBOTIC_LIMB(bodypart))
to_chat(chap, span_warning("[GLOB.deity] refuses to heal this metallic taint!"))
return BLESSING_IGNORED
@@ -280,7 +280,7 @@
return BLESSING_IGNORED
var/mob/living/carbon/human/blessed = blessed_living
- for(var/obj/item/bodypart/robolimb as anything in blessed.bodyparts)
+ for(var/obj/item/bodypart/robolimb as anything in blessed.get_bodyparts())
if(IS_ROBOTIC_LIMB(robolimb))
to_chat(chap, span_warning("[GLOB.deity] refuses to heal this metallic taint!"))
return BLESSING_IGNORED
@@ -346,7 +346,7 @@
var/transferred = FALSE
var/list/hurt_limbs = target.get_damaged_bodyparts(1, 1, BODYTYPE_ORGANIC) + target.get_wounded_bodyparts(BODYTYPE_ORGANIC)
var/list/chaplains_limbs = list()
- for(var/obj/item/bodypart/possible_limb in chaplain.bodyparts)
+ for(var/obj/item/bodypart/possible_limb in chaplain.get_bodyparts())
if(IS_ORGANIC_LIMB(possible_limb))
chaplains_limbs += possible_limb
diff --git a/code/modules/religion/sparring/sparring_datum.dm b/code/modules/religion/sparring/sparring_datum.dm
index fbe0fe93070a..c9ea83be5f0e 100644
--- a/code/modules/religion/sparring/sparring_datum.dm
+++ b/code/modules/religion/sparring/sparring_datum.dm
@@ -216,7 +216,7 @@
if(PUNISHMENT_BRAND)
var/mob/living/carbon/human/branded = interfering
to_chat(interfering, span_warning("[GLOB.deity] brands your flesh for interfering with [chaplain]'s sparring match!!"))
- var/obj/item/bodypart/branded_limb = pick(branded.bodyparts)
+ var/obj/item/bodypart/branded_limb = pick(branded.get_bodyparts())
branded_limb.force_wound_upwards(/datum/wound/burn/flesh/severe/brand, wound_source = "divine intervention")
branded.emote("scream")
diff --git a/code/modules/research/anomaly/anomaly_core.dm b/code/modules/research/anomaly/anomaly_core.dm
index 2f343f3b489f..3e12dd2e9e9e 100644
--- a/code/modules/research/anomaly/anomaly_core.dm
+++ b/code/modules/research/anomaly/anomaly_core.dm
@@ -31,10 +31,9 @@
/obj/item/assembly/signaler/anomaly/attack_self()
return
-/obj/item/assembly/signaler/anomaly/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
- if(I.tool_behaviour == TOOL_ANALYZER)
- to_chat(user, span_notice("Analyzing... [src]'s stabilized field is fluctuating along frequency [format_frequency(frequency)], code [code]."))
- return ..()
+/obj/item/assembly/signaler/anomaly/analyzer_act(mob/living/user, obj/item/analyzer/tool)
+ to_chat(user, span_notice("Analyzing... [src]'s stabilized field is fluctuating along frequency [format_frequency(frequency)], code [code]."))
+ return ITEM_INTERACT_SUCCESS
/obj/item/assembly/signaler/anomaly/on_mail_unwrap(atom/source, mob/user, obj/item/mail/traitor/letter)
return NONE
@@ -61,10 +60,10 @@
anomaly_type = /obj/effect/anomaly/grav
/obj/item/assembly/signaler/anomaly/grav/signal()
- for(var/obj/object in orange(2, src))
+ for(var/obj/object in orange(2, get_turf(src)))
if(!object.anchored)
step_towards(object,src)
- for(var/mob/living/living in orange(2, src))
+ for(var/mob/living/living in orange(2, get_turf(src)))
if(!living.mob_negates_gravity())
step_towards(living,src)
@@ -75,7 +74,7 @@
anomaly_type = /obj/effect/anomaly/flux
/obj/item/assembly/signaler/anomaly/flux/signal()
- tesla_zap(src, 0, 10 KILO JOULES, 5 KILO JOULES, ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_GENERATES_POWER)
+ tesla_zap(get_turf(src), 0, 10 KILO JOULES, 5 KILO JOULES, ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE | ZAP_GENERATES_POWER)
/obj/item/assembly/signaler/anomaly/bluespace
name = "\improper bluespace anomaly core"
diff --git a/code/modules/research/anomaly/anomaly_refinery.dm b/code/modules/research/anomaly/anomaly_refinery.dm
index 7ec62a618d9b..cfbc9a840117 100644
--- a/code/modules/research/anomaly/anomaly_refinery.dm
+++ b/code/modules/research/anomaly/anomaly_refinery.dm
@@ -73,41 +73,49 @@
var/radius = clamp(round(MIN_RADIUS_REQUIRED + radius_increase_per_core * already_made, 1), MIN_RADIUS_REQUIRED, MAX_RADIUS_REQUIRED)
return radius
-/obj/machinery/research/anomaly_refinery/attackby(obj/item/tool, mob/living/user, list/modifiers, list/attack_modifiers)
+/obj/machinery/research/anomaly_refinery/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if(active)
to_chat(user, span_warning("You can't insert [tool] into [src] while [p_theyre()] currently active."))
- return
+ return ITEM_INTERACT_BLOCKING
+
if(istype(tool, /obj/item/raw_anomaly_core))
if(inserted_core)
to_chat(user, span_warning("There is already a core in [src]."))
- return
+ return ITEM_INTERACT_BLOCKING
+
if(!user.transferItemToLoc(tool, src))
to_chat(user, span_warning("[tool] is stuck to your hand."))
- return
+ return ITEM_INTERACT_BLOCKING
+
var/obj/item/raw_anomaly_core/raw_core = tool
if(!get_required_radius(raw_core.anomaly_type))
say("Unfortunately, due to diminishing supplies of condensed anomalous matter, [raw_core] and any cores of its type are no longer of a sufficient quality level to be compressed into a working core.")
- return
+ return ITEM_INTERACT_BLOCKING
+
inserted_core = raw_core
to_chat(user, span_notice("You insert [raw_core] into [src]."))
- return
- if(istype(tool, /obj/item/transfer_valve))
- if(inserted_bomb)
- to_chat(user, span_warning("There is already a bomb in [src]."))
- return
- var/obj/item/transfer_valve/valve = tool
- if(!valve.ready())
- to_chat(user, span_warning("[valve] is incomplete."))
- return
- if(!user.transferItemToLoc(tool, src))
- to_chat(user, span_warning("[tool] is stuck to your hand."))
- return
- inserted_bomb = tool
- tank_to_target = inserted_bomb.tank_two
- to_chat(user, span_notice("You insert [tool] into [src]"))
- return
- update_appearance()
- return ..()
+ return ITEM_INTERACT_SUCCESS
+
+ if(!istype(tool, /obj/item/transfer_valve))
+ return NONE
+
+ if(inserted_bomb)
+ to_chat(user, span_warning("There is already a bomb in [src]."))
+ return ITEM_INTERACT_BLOCKING
+
+ var/obj/item/transfer_valve/valve = tool
+ if(!valve.ready())
+ to_chat(user, span_warning("[valve] is incomplete."))
+ return ITEM_INTERACT_BLOCKING
+
+ if(!user.transferItemToLoc(tool, src))
+ to_chat(user, span_warning("[tool] is stuck to your hand."))
+ return ITEM_INTERACT_BLOCKING
+
+ inserted_bomb = tool
+ tank_to_target = inserted_bomb.tank_two
+ to_chat(user, span_notice("You insert [tool] into [src]"))
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/research/anomaly_refinery/wrench_act(mob/living/user, obj/item/tool)
. = ..()
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index 7d33c3ed09fb..bcc98287e246 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -1400,7 +1400,7 @@
Links to stasis beds, operating tables, and other machines that can hold patients \
such as cryo cells, sleepers, and more."
id = "vitals_monitor"
- build_type = PROTOLATHE
+ build_type = PROTOLATHE | AWAY_LATHE
materials = list(
/datum/material/iron = SHEET_MATERIAL_AMOUNT * 4,
/datum/material/glass = SHEET_MATERIAL_AMOUNT * 2,
diff --git a/code/modules/research/ordnance/doppler_array.dm b/code/modules/research/ordnance/doppler_array.dm
index c516cd38f6a3..6fadc0393a53 100644
--- a/code/modules/research/ordnance/doppler_array.dm
+++ b/code/modules/research/ordnance/doppler_array.dm
@@ -48,17 +48,15 @@
. = ..()
. += span_notice("It is currently facing [dir2text(dir)]")
-/obj/machinery/doppler_array/attackby(obj/item/item, mob/user, list/modifiers, list/attack_modifiers)
- if(istype(item, /obj/item/disk/computer))
- var/obj/item/disk/computer/disk = item
- eject_disk(user)
- if(user.transferItemToLoc(disk, src))
- inserted_disk = disk
- return
- else
- balloon_alert(user, "it's stuck to your hand!")
- return ..()
- return ..()
+/obj/machinery/doppler_array/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!istype(tool, /obj/item/disk/computer))
+ return NONE
+ eject_disk(user)
+ if(!user.transferItemToLoc(tool, src))
+ balloon_alert(user, "it's stuck to your hand!")
+ return ITEM_INTERACT_BLOCKING
+ inserted_disk = tool
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/doppler_array/wrench_act(mob/living/user, obj/item/tool)
default_unfasten_wrench(user, tool)
diff --git a/code/modules/research/ordnance/tank_compressor.dm b/code/modules/research/ordnance/tank_compressor.dm
index 189ab23a3a0c..a5403e871616 100644
--- a/code/modules/research/ordnance/tank_compressor.dm
+++ b/code/modules/research/ordnance/tank_compressor.dm
@@ -42,33 +42,35 @@
var/list/gas_data = list()
var/timestamp
-/obj/machinery/atmospherics/components/binary/tank_compressor/attackby(obj/item/item, mob/living/user)
- if (panel_open)
- return ..()
+/obj/machinery/atmospherics/components/binary/tank_compressor/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if (user.combat_mode || panel_open)
+ return NONE
- if(istype(item, /obj/item/tank))
- var/obj/item/tank/tank_item = item
- if(inserted_tank)
- if(!eject_tank(user))
- balloon_alert(user, "it's stuck inside!")
- return ..()
- if(!user.transferItemToLoc(tank_item, src))
- balloon_alert(user, "it's stuck to your hand!")
- return ..()
- inserted_tank = tank_item
- last_recorded_pressure = 0
- RegisterSignal(inserted_tank, COMSIG_QDELETING, PROC_REF(tank_destruction))
- update_appearance()
- return
- if(istype(item, /obj/item/disk/computer))
- var/obj/item/disk/computer/attacking_disk = item
+ if(istype(tool, /obj/item/disk/computer))
eject_disk(user)
- if(user.transferItemToLoc(attacking_disk, src))
- inserted_disk = attacking_disk
- else
+ if(!user.transferItemToLoc(tool, src))
balloon_alert(user, "it's stuck to your hand!")
- return
- return ..()
+ return ITEM_INTERACT_BLOCKING
+ inserted_disk = tool
+ return ITEM_INTERACT_SUCCESS
+
+ if(!istype(tool, /obj/item/tank))
+ return NONE
+
+ if(inserted_tank)
+ if(!eject_tank(user))
+ balloon_alert(user, "it's stuck inside!")
+ return ITEM_INTERACT_BLOCKING
+
+ if(!user.transferItemToLoc(tool, src))
+ balloon_alert(user, "it's stuck to your hand!")
+ return ITEM_INTERACT_BLOCKING
+
+ inserted_tank = tool
+ last_recorded_pressure = 0
+ RegisterSignal(inserted_tank, COMSIG_QDELETING, PROC_REF(tank_destruction))
+ update_appearance()
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/atmospherics/components/binary/tank_compressor/wrench_act(mob/living/user, obj/item/tool)
if(active || inserted_tank)
diff --git a/code/modules/research/part_replacer.dm b/code/modules/research/part_replacer.dm
index 2ac29877ead1..b0901cd9b01f 100644
--- a/code/modules/research/part_replacer.dm
+++ b/code/modules/research/part_replacer.dm
@@ -150,6 +150,7 @@
new /obj/item/stock_parts/micro_laser/quadultra(src)
new /obj/item/stock_parts/matter_bin/bluespace(src)
new /obj/item/stock_parts/power_store/cell/bluespace(src)
+ new /obj/item/stack/cable_coil/thirty(src)
//used in a cargo crate
/obj/item/storage/part_replacer/cargo/PopulateContents()
diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm
index 32793bfe8ab4..36c3a43a6034 100644
--- a/code/modules/research/rdconsole.dm
+++ b/code/modules/research/rdconsole.dm
@@ -70,31 +70,38 @@ Nothing else in the console has ID requirements.
d_disk = null
return ..()
-/obj/machinery/computer/rdconsole/attackby(obj/item/D, mob/user, list/modifiers, list/attack_modifiers)
- //Loading a disk into it.
- if(istype(D, /obj/item/disk))
- if(istype(D, /obj/item/disk/tech_disk))
- if(t_disk)
- to_chat(user, span_warning("A technology disk is already loaded!"))
- return
- if(!user.transferItemToLoc(D, src))
- to_chat(user, span_warning("[D] is stuck to your hand!"))
- return
- t_disk = D
- else if (istype(D, /obj/item/disk/design_disk))
- if(d_disk)
- to_chat(user, span_warning("A design disk is already loaded!"))
- return
- if(!user.transferItemToLoc(D, src))
- to_chat(user, span_warning("[D] is stuck to your hand!"))
- return
- d_disk = D
- else
- to_chat(user, span_warning("Machine cannot accept disks in that format."))
- return
- to_chat(user, span_notice("You insert [D] into \the [src]!"))
- return
- return ..()
+/obj/machinery/computer/rdconsole/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!istype(tool, /obj/item/disk))
+ return NONE
+
+ if(istype(tool, /obj/item/disk/tech_disk))
+ if(t_disk)
+ to_chat(user, span_warning("A technology disk is already loaded!"))
+ return ITEM_INTERACT_BLOCKING
+
+ if(!user.transferItemToLoc(tool, src))
+ to_chat(user, span_warning("[tool] is stuck to your hand!"))
+ return ITEM_INTERACT_BLOCKING
+
+ t_disk = tool
+ to_chat(user, span_notice("You insert [tool] into \the [src]!"))
+ return ITEM_INTERACT_SUCCESS
+
+ if (!istype(tool, /obj/item/disk/design_disk))
+ to_chat(user, span_warning("Machine cannot accept disks in that format."))
+ return ITEM_INTERACT_BLOCKING
+
+ if(d_disk)
+ to_chat(user, span_warning("A design disk is already loaded!"))
+ return ITEM_INTERACT_BLOCKING
+
+ if(!user.transferItemToLoc(tool, src))
+ to_chat(user, span_warning("[tool] is stuck to your hand!"))
+ return ITEM_INTERACT_BLOCKING
+
+ d_disk = tool
+ to_chat(user, span_notice("You insert [tool] into \the [src]!"))
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/computer/rdconsole/multitool_act(mob/living/user, obj/item/multitool/tool)
. = ..()
diff --git a/code/modules/research/relics.dm b/code/modules/research/relics.dm
index ff092fac4a40..fe957d81f320 100644
--- a/code/modules/research/relics.dm
+++ b/code/modules/research/relics.dm
@@ -1,3 +1,6 @@
+#define RELIC_PROTOTYPE "prototype"
+#define RELIC_NECROTECH "necrotech"
+
/obj/item/relic
name = "strange object"
desc = "What mysteries could this hold? Maybe Research & Development could find out."
@@ -16,34 +19,39 @@
/// Cooldown length. Randomly determined at activation if it isn't determined here.
var/cooldown_timer
/// What visual theme this artefact has. Current possible choices: "prototype", "necrotech"
- var/artifact_theme = "prototype"
+ var/artifact_theme = RELIC_PROTOTYPE
COOLDOWN_DECLARE(cooldown)
/obj/item/relic/Initialize(mapload)
. = ..()
random_themed_appearance()
+ RegisterSignal(src, COMSIG_ITEM_OPENED_FROM_GIFT, PROC_REF(auto_reveal))
+
+/obj/item/relic/proc/auto_reveal(...)
+ SIGNAL_HANDLER
+ reveal()
/obj/item/relic/proc/random_themed_appearance()
var/themed_name_prefix
var/themed_name_suffix
- if(artifact_theme == "prototype")
- icon_state = pick("prototype1", "prototype2", "prototype3", "prototype4", "prototype5", "prototype6", "prototype7", "prototype8","prototype9")
- themed_name_prefix = pick("experimental","prototype","artificial","handcrafted","ramshackle","odd")
- themed_name_suffix = pick("device","assembly","gadget","gizmo","contraption","machine","widget","object")
+ if(artifact_theme == RELIC_PROTOTYPE)
+ icon_state = "[RELIC_PROTOTYPE][rand(1, 9)]"
+ themed_name_prefix = pick("experimental", "prototype", "artificial", "handcrafted", "ramshackle", "odd")
+ themed_name_suffix = pick("device", "assembly", "gadget", "gizmo", "contraption", "machine", "widget", "object")
real_name = "[pick(themed_name_prefix)] [pick(themed_name_suffix)]"
name = "strange [pick(themed_name_suffix)]"
- if(artifact_theme == "necrotech")
- icon_state = pick("necrotech1", "necrotech2", "necrotech3", "necrotech4", "necrotech5", "necrotech6")
- themed_name_prefix = pick("dark","bloodied","unholy","archeotechnological","dismal","ruined","thrumming")
- themed_name_suffix = pick("instrument","shard","fetish","bibelot","trinket","offering","relic")
+ if(artifact_theme == RELIC_NECROTECH)
+ icon_state = "[RELIC_NECROTECH][rand(1, 6)]"
+ themed_name_prefix = pick("dark", "bloodied", "unholy", "archeotechnological", "dismal", "ruined", "thrumming")
+ themed_name_suffix = pick("instrument", "shard", "fetish", "bibelot", "trinket", "offering", "relic")
real_name = "[pick(themed_name_prefix)] [pick(themed_name_suffix)]"
name = "strange relic"
update_appearance()
/obj/item/relic/lavaland
name = "strange relic"
- artifact_theme = "necrotech"
+ artifact_theme = RELIC_NECROTECH
/obj/item/relic/proc/reveal()
if(activated) //no rerolling
@@ -53,20 +61,37 @@
if(!cooldown_timer)
cooldown_timer = rand(min_cooldown, max_cooldown)
if(!hidden_power)
- hidden_power = pick(
- PROC_REF(corgi_cannon),
- PROC_REF(cleaning_foam),
- PROC_REF(flashbanger),
- PROC_REF(summon_animals),
- PROC_REF(uncontrolled_teleport),
- PROC_REF(heat_and_explode),
- PROC_REF(rapid_self_dupe),
- PROC_REF(drink_dispenser),
- PROC_REF(tummy_ache),
- PROC_REF(charger),
- PROC_REF(hugger),
- PROC_REF(dimensional_shift),
- PROC_REF(disguiser),
+ if(artifact_theme == RELIC_PROTOTYPE)
+ hidden_power = pick(
+ PROC_REF(corgi_cannon),
+ PROC_REF(cleaning_foam),
+ PROC_REF(flashbanger),
+ PROC_REF(summon_animals),
+ PROC_REF(uncontrolled_teleport),
+ PROC_REF(heat_and_explode),
+ PROC_REF(rapid_self_dupe),
+ PROC_REF(drink_dispenser),
+ PROC_REF(tummy_ache),
+ PROC_REF(charger),
+ PROC_REF(hugger),
+ PROC_REF(dimensional_shift),
+ PROC_REF(disguiser),
+ )
+ if(artifact_theme == RELIC_NECROTECH)
+ hidden_power = pick(
+ PROC_REF(dimensional_shift),
+ PROC_REF(summon_animals_monsters),
+ PROC_REF(heat_and_explode),
+ PROC_REF(t1_shield_holder),
+ PROC_REF(t2_shield_holder),
+ PROC_REF(uncontrolled_teleport),
+ PROC_REF(uncontrolled_aoe_teleport),
+ PROC_REF(charger),
+ PROC_REF(place_rocks),
+ PROC_REF(yeet_blood),
+ PROC_REF(suck_blood),
+ PROC_REF(cleaning_foam),
+ PROC_REF(cleaning_foam_acid),
)
obj_flags |= UNIQUE_RENAME
@@ -82,30 +107,51 @@
COOLDOWN_START(src, cooldown, cooldown_timer)
call(src, hidden_power)(user)
+/// Helper to spawn smoke somewhere
/obj/item/relic/proc/throw_smoke(turf/where)
do_smoke(0, src, get_turf(where))
+/// Helper to show a message to people around the relic
+/obj/item/relic/proc/relic_message(message)
+ var/atom/message_source = ismob(loc) ? loc : src
+ message_source.visible_message(message)
+
// Artefact Powers \\
+/// Throws a corgi somewhere
/obj/item/relic/proc/corgi_cannon(mob/user)
playsound(src, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
var/mob/living/basic/pet/dog/corgi/sad_corgi = new(get_turf(user))
sad_corgi.throw_at(pick(oview(10,user)), 10, rand(3,8), callback = CALLBACK(src, PROC_REF(throw_smoke), sad_corgi))
- warn_admins(user, "Corgi Cannon", 0)
+ warn_admins(user, "Corgi Cannon", FALSE)
+/// Spawns cleaning foam
/obj/item/relic/proc/cleaning_foam(mob/user)
- playsound(src, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- var/obj/item/grenade/chem_grenade/cleaner/spawned_foamer = new/obj/item/grenade/chem_grenade/cleaner(get_turf(user))
+ playsound(src, SFX_SPARKS, rand(25, 50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ var/obj/item/grenade/chem_grenade/cleaner/spawned_foamer = new(get_turf(user))
spawned_foamer.detonate()
qdel(spawned_foamer)
- warn_admins(user, "Foam", 0)
+ warn_admins(user, "Foam", FALSE)
+/// Similar to cleaning foam but spawns the acid variant
+/obj/item/relic/proc/cleaning_foam_acid(mob/user)
+ playsound(src, SFX_SPARKS, rand(25, 50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ var/obj/item/grenade/chem_grenade/ez_clean/spawned_foamer = new(get_turf(user))
+ spawned_foamer.detonate()
+ qdel(spawned_foamer)
+ warn_admins(user, "Acid Foam", TRUE)
+ if(prob(80))
+ to_chat(user, span_warning("[src] melts apart!"))
+ acid_melt()
+
+/// Flashbangs anyone nearby
/obj/item/relic/proc/flashbanger(mob/user)
playsound(src, SFX_SPARKS, rand(25,50), TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
- var/obj/item/grenade/flashbang/spawned_flashbang = new/obj/item/grenade/flashbang(user.loc)
+ var/obj/item/grenade/flashbang/spawned_flashbang = new(get_turf(user))
spawned_flashbang.detonate()
warn_admins(user, "Flash")
+/// Summon a bunch of random animals, some of which are dangerous
/obj/item/relic/proc/summon_animals(mob/user)
var/message = span_danger("[src] begins to shake, and in the distance the sound of rampaging animals arises!")
visible_message(message)
@@ -130,9 +176,44 @@
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!"))
- qdel(src)
+ relic_message(span_warning("[src] falls apart!"))
+ deconstruct(FALSE)
+/// Version of summon_animals that spawns mostly lavaland monsters
+/obj/item/relic/proc/summon_animals_monsters(mob/user)
+ var/message = span_danger("[src] begins to shake, and in the distance the sound of roaring arises!")
+ visible_message(message)
+ to_chat(user, message)
+ var/static/list/valid_monsters = list(
+ /mob/living/basic/construct/artificer/hostile,
+ /mob/living/basic/construct/juggernaut/hostile,
+ /mob/living/basic/construct/proteon/hostile,
+ /mob/living/basic/construct/wraith/hostile,
+ /mob/living/basic/mining/brimdemon,
+ /mob/living/basic/mining/goldgrub,
+ /mob/living/basic/mining/goliath,
+ /mob/living/basic/mining/hivelord,
+ /mob/living/basic/mining/ice_demon,
+ /mob/living/basic/mining/legion,
+ /mob/living/basic/mining/lobstrosity,
+ /mob/living/basic/mining/watcher,
+ /mob/living/basic/raptor/blue,
+ /mob/living/basic/raptor/green,
+ /mob/living/basic/raptor/purple,
+ /mob/living/basic/raptor/red,
+ /mob/living/basic/raptor/white,
+ /mob/living/basic/raptor/yellow,
+ )
+ for(var/counter in 1 to rand(3, 9))
+ var/animal_spawn = pick(valid_monsters)
+ var/mob/living/animal = new animal_spawn(get_turf(src))
+ ADD_TRAIT(animal, TRAIT_SPAWNED_MOB, INNATE_TRAIT)
+ warn_admins(user, "Mass Mob Spawn (Monster)")
+ if(prob(80))
+ relic_message(span_warning("[src] falls apart!"))
+ deconstruct(FALSE)
+
+/// Spawns a bunch of mimics of the relic which also can spawn relics, but despawn shortly
/obj/item/relic/proc/rapid_self_dupe(mob/user)
audible_message("[src] emits a loud pop!")
var/list/dummy_artifacts = list()
@@ -148,6 +229,7 @@
QDEL_LIST_IN(dummy_artifacts, rand(1 SECONDS, 10 SECONDS))
warn_admins(user, "Rapid duplicator", 0)
+/// Explodes after a few seconds
/obj/item/relic/proc/heat_and_explode(mob/user)
to_chat(user, span_danger("[src] begins to heat up!"))
addtimer(CALLBACK(src, PROC_REF(blow_up), user), rand(3.5 SECONDS, 10 SECONDS))
@@ -157,11 +239,15 @@
visible_message(span_notice("\The [src]'s top opens, releasing a powerful blast!"))
explosion(src, heavy_impact_range = rand(1,5), light_impact_range = rand(1,5), flame_range = 2, flash_range = rand(1,5), adminlog = TRUE)
warn_admins(user, "Explosion")
- qdel(src) //Comment this line to produce a light grenade (the bomb that keeps on exploding when used)!!
+ deconstruct(FALSE) //Comment this line to produce a light grenade (the bomb that keeps on exploding when used)!!
+/// Teleports the relic, and anyone holding it, to a random location nearby
/obj/item/relic/proc/uncontrolled_teleport(mob/user)
to_chat(user, span_notice("[src] begins to vibrate!"))
- addtimer(CALLBACK(src, PROC_REF(do_the_teleport), user), rand(1 SECONDS, 3 SECONDS))
+
+ var/teleport_time = rand(1 SECONDS, 3 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(do_the_teleport), user), teleport_time)
+ Shake(1, 1, teleport_time, 0.05 SECONDS)
/obj/item/relic/proc/do_the_teleport(mob/user)
var/turf/userturf = get_turf(user)
@@ -173,7 +259,47 @@
throw_smoke(get_turf(to_teleport))
do_teleport(to_teleport, userturf, 8, asoundin = 'sound/effects/phasein.ogg', channel = TELEPORT_CHANNEL_BLUESPACE)
throw_smoke(get_turf(to_teleport))
- warn_admins(user, "Teleport", 0)
+ warn_admins(user, "Teleport", FALSE)
+
+/// Version of uncontrolled_teleport with cult theming, and that affects all nearby movables rather than just the relic
+/obj/item/relic/proc/uncontrolled_aoe_teleport(mob/user)
+ to_chat(user, span_notice("[src] begins to vibrate intensely!"))
+
+ var/teleport_time = rand(1 SECONDS, 3 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(do_the_aoe_teleport), user), teleport_time)
+ Shake(2, 2, teleport_time, 0.03 SECONDS)
+
+/obj/item/relic/proc/do_the_aoe_teleport(mob/user)
+ visible_message(span_notice("[src] twists and bends, relocating anything nearby!"))
+ var/turf/teleturf = get_turf(src)
+ for(var/atom/movable/nearby in view(2, teleturf))
+ if(nearby.anchored || nearby.invisibility || HAS_TRAIT(nearby, TRAIT_UNDERFLOOR))
+ continue
+ if(isliving(nearby))
+ var/mob/living/nearby_living = nearby
+ if(nearby_living.can_block_magic(MAGIC_RESISTANCE_HOLY, 1))
+ continue
+
+ if(isliving(nearby))
+ new /obj/effect/temp_visual/dir_setting/cult/phase/out(nearby.loc, nearby.dir)
+ else
+ new /obj/effect/temp_visual/cult/sparks(nearby.loc)
+ do_teleport(
+ teleatom = nearby,
+ destination = teleturf,
+ precision = 5,
+ asoundin = 'sound/effects/phasein.ogg',
+ channel = TELEPORT_CHANNEL_CULT,
+ )
+ if(isliving(nearby))
+ new /obj/effect/temp_visual/dir_setting/cult/phase(nearby.loc, nearby.dir)
+ else
+ new /obj/effect/temp_visual/cult/sparks(nearby.loc)
+
+ warn_admins(user, "AOE Teleport")
+ if(prob(30))
+ relic_message(span_warning("[src] teleports away, never to be seen again!"))
+ qdel(src)
// Creates a glass and fills it up with a drink.
/obj/item/relic/proc/drink_dispenser(mob/user)
@@ -187,7 +313,7 @@
glasser.reagents.add_reagent(get_random_reagent_id(whitelist = subtypesof(/datum/reagent/consumable/ethanol)), rand(glasser.volume * 0.3, glasser.volume))
throw_smoke(get_turf(glasser))
-// Scrambles your organs. 33% chance to delete after use.
+/// Scrambles your organs. 33% chance to delete after use.
/obj/item/relic/proc/tummy_ache(mob/user)
new /obj/effect/temp_visual/circle_wave/bioscrambler/light(get_turf(src))
to_chat(user, span_notice("Your stomach starts growling..."))
@@ -202,9 +328,10 @@
throw_smoke(get_turf(nearby))
to_chat(nearby, span_notice("You feel weird."))
if(prob(33))
- qdel(src)
+ relic_message(span_warning("[src] falls apart!"))
+ deconstruct(FALSE)
-// Charges an item or two in your inventory. Also yourself.
+/// Charges an item or two in your inventory. Also yourself.
/obj/item/relic/proc/charger(mob/living/user)
to_chat(user, span_danger("You're recharged!"))
var/stunner = 1.25 SECONDS
@@ -239,7 +366,7 @@
/obj/item/relic/proc/cut_the_overlay(atom/shocker, lightning)
shocker.cut_overlay(lightning)
-// Hugs/shakes everyone in range!
+/// Hugs/shakes everyone in range!
/obj/item/relic/proc/hugger(mob/user)
var/list/mob/living/carbon/huggeds = oviewers(3, user)
for(var/mob/living/carbon/victim in huggeds)
@@ -250,7 +377,7 @@
else
to_chat(user, pick(span_notice("You hug yourself, for some reason."), span_notice("You have a strange feeling for a moment, but then it passes.")))
-// Converts a 3x3 area into a random dimensional theme.
+/// Converts a 3x3 area into a random dimensional theme.
/obj/item/relic/proc/dimensional_shift(mob/user)
var/new_theme_path = pick(subtypesof(/datum/dimension_theme))
var/datum/dimension_theme/shifter = SSmaterials.dimensional_themes[new_theme_path]
@@ -260,8 +387,8 @@
min_cooldown += 2 SECONDS
max_cooldown += 2 SECONDS
-// Replaces your clothing with a random costume, and your ID with a cardboard one.
-// TODO: make them part of the same kit (lobster hat, lobster suit)
+/// Replaces your clothing with a random costume, and your ID with a cardboard one.
+/// TODO: make them part of the same kit (lobster hat, lobster suit)
/obj/item/relic/proc/disguiser(mob/user)
if(!iscarbon(user))
to_chat(user, span_notice("You have a strange feeling for a moment, but then it passes."))
@@ -326,11 +453,198 @@
new_costume.item_flags |= DROPDEL
return new_costume
+/// Makes the relic holder have a shield that blocks 3 common attacks
+/obj/item/relic/proc/t1_shield_holder(mob/user)
+ var/datum/component/shield = AddComponent( \
+ /datum/component/shielded, \
+ max_charges = 3, \
+ recharge_start_delay = 0, \
+ shield_inhand = TRUE, \
+ show_charge_as_alpha = TRUE, \
+ can_block_overwhelming = FALSE, \
+ shield_icon_file = 'icons/effects/effects.dmi', \
+ shield_icon = "at_shield1", \
+ run_hit_callback = CALLBACK(src, PROC_REF(shield_hit)), \
+ )
+ light_system = OVERLAY_LIGHT
+ var/datum/component/light = AddComponent( \
+ /datum/component/overlay_lighting, \
+ _range = 2.5, \
+ _power = 1.5, \
+ _color = COLOR_BIOLUMINESCENCE_YELLOW, \
+ starts_on = TRUE, \
+ )
+
+ addtimer(CALLBACK(src, PROC_REF(remove_shield), list(shield, light)), cooldown_timer * 0.5)
+ add_filter("block_shield", 1, outline_filter(0, COLOR_BIOLUMINESCENCE_YELLOW), shield)
+ transition_filter("block_shield", outline_filter(2, COLOR_BIOLUMINESCENCE_YELLOW), cooldown_timer * 0.5)
+
+ relic_message(span_notice("[src] starts to glow a bright yellow!"))
+ warn_admins(user, "Shield", FALSE)
+
+/// Makes the relic holder have a shield that blocks 1 powerful attack
+/obj/item/relic/proc/t2_shield_holder(mob/user)
+ var/datum/component/shield = AddComponent( \
+ /datum/component/shielded, \
+ max_charges = 1, \
+ recharge_start_delay = 0, \
+ shield_inhand = TRUE, \
+ show_charge_as_alpha = TRUE, \
+ can_block_overwhelming = TRUE, \
+ shield_icon_file = 'icons/effects/effects.dmi', \
+ shield_icon = "at_shield2", \
+ run_hit_callback = CALLBACK(src, PROC_REF(shield_hit)), \
+ )
+ light_system = OVERLAY_LIGHT
+ var/datum/component/light = AddComponent( \
+ /datum/component/overlay_lighting, \
+ _range = 3, \
+ _power = 1.5, \
+ _color = COLOR_BIOLUMINESCENCE_YELLOW, \
+ starts_on = TRUE, \
+ )
+
+ addtimer(CALLBACK(src, PROC_REF(remove_shield), list(shield, light)), cooldown_timer * 0.5)
+ add_filter("block_shield", 1, outline_filter(0, COLOR_BIOLUMINESCENCE_YELLOW), shield)
+ transition_filter("block_shield", outline_filter(2, COLOR_BIOLUMINESCENCE_YELLOW), cooldown_timer * 0.5)
+
+ relic_message(span_notice("[src] starts to glow a bright yellow!"))
+ warn_admins(user, "Shield", FALSE)
+
+/obj/item/relic/proc/shield_hit(mob/living/owner, attack_text, current_charges)
+ playsound(src, 'sound/items/weapons/marauder.ogg', 20, TRUE, frequency = 1.25)
+ owner.visible_message(span_danger("[owner]'s holds [src] up, blocking [attack_text] with a projected shield!"))
+ if(current_charges <= 0)
+ set_light_on(FALSE)
+ relic_message(span_notice("[src] stops glowing."))
+ remove_filter("block_shield")
+
+/obj/item/relic/proc/remove_shield(list/cleanup_components)
+ for(var/datum/component/comp as anything in cleanup_components)
+ qdel(comp)
+ light_system = initial(light_system)
+ if(light_on)
+ relic_message(span_notice("[src] stops glowing."))
+ remove_filter("block_shield")
+
+/// Places rock turfs around the relic
+/obj/item/relic/proc/place_rocks(mob/user)
+ relic_message(span_notice("A spire of rock erupts from the ground beneath [src]!"))
+ playsound(src, 'sound/effects/rock/rock_break.ogg', 50, TRUE)
+ var/turf/spawnloc = get_turf(src)
+ for(var/turf/open/open_turf in RANGE_TURFS(rand(1, 2), spawnloc))
+ var/turf/closed/mineral/asteroid/rock = open_turf.place_on_top(/turf/closed/mineral/asteroid)
+ if(istype(rock))
+ rock.name = "fragile rock"
+ rock.weak_turf = TRUE
+ rock.tool_mine_speed = 2 SECONDS
+ rock.hand_mine_speed = 10 SECONDS
+ for(var/mob/living/within_rock in rock)
+ within_rock.Paralyze(1 SECONDS)
+ within_rock.Knockdown(3 SECONDS)
+ within_rock.apply_damage(10, BRUTE, BODY_ZONE_CHEST, blocked = within_rock.getarmor(BODY_ZONE_CHEST, MELEE), wound_bonus = 10, exposed_wound_bonus = 10)
+ to_chat(within_rock, span_danger("You are smashed by [rock]!"))
+ warn_admins(user, "Rocks", FALSE)
+ if(prob(20))
+ relic_message(span_warning("[src] crumbles into dust!"))
+ deconstruct(FALSE)
+
+/// User sprays out blood in all directions
+/// Has a small chance of changing the power to suck_blood
+/obj/item/relic/proc/yeet_blood(mob/living/user)
+ var/yeet_time = rand(1 SECONDS, 3 SECONDS)
+ add_filter("blood_outgoing", 1, outline_filter(0, COLOR_DARK))
+ transition_filter("blood_outgoing", outline_filter(2, BLOOD_COLOR_RED), yeet_time)
+ if(istype(user) && CAN_HAVE_BLOOD(user))
+ to_chat(user, span_danger("[src] starts glowing an ominous red!"))
+ else
+ to_chat(user, span_danger("[src] starts glowing an ominous red..."))
+
+ addtimer(CALLBACK(src, PROC_REF(actually_yeet_blood)), yeet_time)
+
+/obj/item/relic/proc/actually_yeet_blood()
+ var/mob/living/user = loc
+ var/splatcount = 0
+ if(istype(user) && CAN_HAVE_BLOOD(user) && !user.can_block_magic(MAGIC_RESISTANCE_HOLY, 1))
+ for(var/splatdir in GLOB.alldirs)
+ if(prob(splatcount * 5))
+ continue
+ var/strength = rand(2, 3)
+ user.spray_blood(splatdir, strength)
+ user.bleed(strength ** 2)
+ splatcount += strength
+
+ if(splatcount > 0)
+ relic_message(span_warning("Blood sprays out from [src]!"))
+ warn_admins(user, "Blood Dispersal", FALSE)
+ playsound(src, 'sound/effects/wounds/blood3.ogg', 50, TRUE)
+ if(prob(20))
+ hidden_power = PROC_REF(suck_blood)
+ else
+ relic_message(span_warning("[src] pulses ominously, but nothing happens!"))
+
+ transition_filter("blood_outgoing", outline_filter(0, COLOR_DARK), 1 SECONDS)
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, remove_filter), "blood_outgoing"), 2 SECONDS)
+
+/// Nearby mobs transfer blood to the user
+/// Has a small chance of changing the power to yeet_blood
+/obj/item/relic/proc/suck_blood(mob/living/user)
+ var/suck_time = rand(1 SECONDS, 3 SECONDS)
+ add_filter("blood_incoming", 1, outline_filter(0, COLOR_DARK))
+ transition_filter("blood_incoming", outline_filter(2, BLOOD_COLOR_RED), suck_time)
+ if(istype(user) && CAN_HAVE_BLOOD(user))
+ to_chat(user, span_danger("[src] starts glowing an ominous red!"))
+ else
+ to_chat(user, span_danger("[src] starts glowing an ominous red..."))
+
+ addtimer(CALLBACK(src, PROC_REF(actually_suck_blood)), suck_time)
+
+/obj/item/relic/proc/actually_suck_blood()
+ var/mob/living/user = loc
+ var/any_affected = FALSE
+ if(istype(user) && CAN_HAVE_BLOOD(user) && !user.can_block_magic(MAGIC_RESISTANCE_HOLY, 1))
+ for(var/mob/living/nearby in view(2, user))
+ if(nearby == user || !CAN_HAVE_BLOOD(nearby) || nearby.can_block_magic(MAGIC_RESISTANCE_HOLY, 1))
+ continue
+ nearby.transfer_blood_to(user, rand(6, 10), ignore_low_blood = TRUE, ignore_incompatibility = TRUE, transfer_viruses = FALSE)
+ to_chat(nearby, span_danger("You feel a sudden weakness as blood is drawn out of you [nearby.is_blind() ? "" : " and into [user]"]!"))
+ any_affected = TRUE
+ nearby.Beam(user, icon_state = "blood", time = 1 SECONDS)
+ new /obj/effect/temp_visual/cult/sparks(nearby.loc)
+
+ if(any_affected)
+ playsound(src, 'sound/effects/magic/enter_blood.ogg', 50, TRUE)
+ relic_message(span_warning("Blood from nearby creatures is drawn towards [src], and into [user]!"))
+ warn_admins(user, "Blood Suck")
+ if(prob(10))
+ hidden_power = PROC_REF(yeet_blood)
+ else
+ relic_message(span_warning("[src] pulses ominously, but nothing happens!"))
+
+ transition_filter("blood_incoming", outline_filter(0, COLOR_DARK), 1 SECONDS)
+ addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, remove_filter), "blood_incoming"), 2 SECONDS)
+
/// Alerts admins on usage of dagnerous relics
-/obj/item/relic/proc/warn_admins(mob/user, relic_type, priority = 1)
+/obj/item/relic/proc/warn_admins(mob/user, relic_type, priority = TRUE)
var/turf/location = get_turf(src)
var/log_msg = "[relic_type] relic used by [key_name(user)] in [AREACOORD(location)]"
if(priority)
message_admins("[relic_type] relic activated by [ADMIN_LOOKUPFLW(user)] in [ADMIN_VERBOSEJMP(location)]")
log_game(log_msg)
investigate_log(log_msg, "experimentor")
+
+// Subtypes that spawn revealed, primarily for debug/testing/badmin purposes
+/obj/item/relic/revealed
+
+/obj/item/relic/revealed/Initialize(mapload)
+ . = ..()
+ auto_reveal()
+
+/obj/item/relic/lavaland
+
+/obj/item/relic/lavaland/revealed/Initialize(mapload)
+ . = ..()
+ auto_reveal()
+
+#undef RELIC_PROTOTYPE
+#undef RELIC_NECROTECH
diff --git a/code/modules/research/server.dm b/code/modules/research/server.dm
index 6b900f0e83b5..628e0fcfe4af 100644
--- a/code/modules/research/server.dm
+++ b/code/modules/research/server.dm
@@ -165,41 +165,37 @@
return ITEM_INTERACT_BLOCKING
return ..()
-/obj/machinery/rnd/server/master/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
- if(istype(attacking_item, /obj/item/disk/computer/hdd_theft))
- switch(deconstruction_state)
- if(HDD_PANEL_CLOSED)
- balloon_alert(user, "you can't find a place to insert it!")
- return TRUE
- if(HDD_PANEL_OPEN)
- balloon_alert(user, "you weren't trained to install this!")
- return TRUE
- if(HDD_PRIED)
- balloon_alert(user, "the HDD housing is completely broken, it won't fit!")
- return TRUE
- if(HDD_CUT_LOOSE)
- balloon_alert(user, "the HDD housing is completely broken and all the wires are cut!")
- return TRUE
- if(HDD_OVERLOADED)
- balloon_alert(user, "the inside is scorched and all the wires are burned!")
- return TRUE
- return ..()
+/obj/machinery/rnd/server/master/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!istype(tool, /obj/item/disk/computer/hdd_theft))
+ return NONE
+ switch(deconstruction_state)
+ if(HDD_PANEL_CLOSED)
+ balloon_alert(user, "you can't find a place to insert it!")
+ if(HDD_PANEL_OPEN)
+ balloon_alert(user, "you weren't trained to install this!")
+ if(HDD_PRIED)
+ balloon_alert(user, "the HDD housing is completely broken, it won't fit!")
+ if(HDD_CUT_LOOSE)
+ balloon_alert(user, "the HDD housing is completely broken and all the wires are cut!")
+ if(HDD_OVERLOADED)
+ balloon_alert(user, "the inside is scorched and all the wires are burned!")
+ return ITEM_INTERACT_BLOCKING
/obj/machinery/rnd/server/master/screwdriver_act(mob/living/user, obj/item/tool)
if(deconstruction_state != HDD_PANEL_CLOSED || user.combat_mode)
- return FALSE
+ return NONE
to_chat(user, span_notice("You can see [front_panel_screws] screw\s. You start unscrewing [front_panel_screws == 1 ? "it" : "them"]..."))
while(tool.use_tool(src, user, 7.5 SECONDS, volume=100))
front_panel_screws--
-
- if(front_panel_screws <= 0)
- deconstruction_state = HDD_PANEL_OPEN
- to_chat(user, span_notice("You remove the last screw from [src]'s front panel."))
- add_overlay("RD-server-hdd-panel-open")
- return TRUE
- to_chat(user, span_notice("The screw breaks as you remove it. Only [front_panel_screws] left..."))
- return TRUE
+ if(front_panel_screws > 0)
+ to_chat(user, span_notice("The screw breaks as you remove it. Only [front_panel_screws] left..."))
+ continue
+ deconstruction_state = HDD_PANEL_OPEN
+ to_chat(user, span_notice("You remove the last screw from [src]'s front panel."))
+ add_overlay("RD-server-hdd-panel-open")
+ break
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/rnd/server/master/crowbar_act(mob/living/user, obj/item/tool)
if(deconstruction_state != HDD_PANEL_OPEN || user.combat_mode)
diff --git a/code/modules/research/techweb/__techweb_helpers.dm b/code/modules/research/techweb/__techweb_helpers.dm
index d1d3b6ecfded..34c076d59466 100644
--- a/code/modules/research/techweb/__techweb_helpers.dm
+++ b/code/modules/research/techweb/__techweb_helpers.dm
@@ -34,6 +34,6 @@
for(var/i in pointlist)
var/research_line = "[(i in SSresearch.point_types) || "ERRORED POINT TYPE"]: [pointlist[i]]"
if(last_pointlist[i] > 0)
- research_line += " (+[(last_pointlist[i]) * ((SSresearch.flags & SS_TICKER)? (600 / (world.tick_lag * SSresearch.wait)) : (600 / SSresearch.wait))]/ minute)"
+ research_line += " (+[(last_pointlist[i]) * ((SSresearch.ss_flags & SS_TICKER)? (600 / (world.tick_lag * SSresearch.wait)) : (600 / SSresearch.wait))]/ minute)"
ret += research_line
return ret.Join("
")
diff --git a/code/modules/research/xenobiology/crossbreeding/_misc.dm b/code/modules/research/xenobiology/crossbreeding/_misc.dm
index a8f22f5f980c..a75df1542a6f 100644
--- a/code/modules/research/xenobiology/crossbreeding/_misc.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_misc.dm
@@ -37,18 +37,11 @@ Slimecrossing Items
saved_part.old_part.heal_damage(INFINITY, INFINITY, null, FALSE)
saved_part.old_part.receive_damage(saved_part.brute_dam, saved_part.burn_dam, wound_bonus=CANT_WOUND)
dont_chop[zone] = TRUE
- for(var/_part in bodyparts)
- var/obj/item/bodypart/part = _part
- if(dont_chop[part.body_zone])
- continue
- part.drop_limb(TRUE)
/mob/living/carbon/proc/save_bodyparts()
var/list/datum/saved_bodypart/ret = list()
- for(var/_part in bodyparts)
- var/obj/item/bodypart/part = _part
+ for(var/obj/item/bodypart/part as anything in get_bodyparts(include_stumps = TRUE))
var/datum/saved_bodypart/saved_part = new(part)
-
ret[part.body_zone] = saved_part
return ret
diff --git a/code/modules/research/xenobiology/crossbreeding/consuming.dm b/code/modules/research/xenobiology/crossbreeding/consuming.dm
index 22c645ace717..82cf2018759e 100644
--- a/code/modules/research/xenobiology/crossbreeding/consuming.dm
+++ b/code/modules/research/xenobiology/crossbreeding/consuming.dm
@@ -15,30 +15,36 @@ Consuming extracts:
var/cookies = 5 //Number of cookies to spawn
var/cookietype = /obj/item/slime_cookie
-/obj/item/slimecross/consuming/attackby(obj/item/O, mob/user)
- if(IS_EDIBLE(O))
- if(last_produced + cooldown > world.time)
- to_chat(user, span_warning("[src] is still digesting after its last meal!"))
- return
- var/datum/reagent/N = O.reagents.has_reagent(/datum/reagent/consumable/nutriment)
- if(N)
- nutriment_eaten += N.volume
- to_chat(user, span_notice("[src] opens up and swallows [O] whole!"))
- qdel(O)
- playsound(src, 'sound/items/eatfood.ogg', 20, TRUE)
- else
- to_chat(user, span_warning("[src] burbles unhappily at the offering."))
- if(nutriment_eaten >= nutriment_required)
- nutriment_eaten = 0
- user.visible_message(span_notice("[src] swells up and produces a small pile of cookies!"))
- playsound(src, 'sound/effects/splat.ogg', 40, TRUE)
- last_produced = world.time
- for(var/i in 1 to cookies)
- var/obj/item/S = spawncookie()
- S.pixel_x = base_pixel_x + rand(-5, 5)
- S.pixel_y = base_pixel_y + rand(-5, 5)
- return
- ..()
+/obj/item/slimecross/consuming/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!IS_EDIBLE(tool))
+ return NONE
+
+ if(last_produced + cooldown > world.time)
+ to_chat(user, span_warning("[src] is still digesting after its last meal!"))
+ return ITEM_INTERACT_BLOCKING
+
+ var/datum/reagent/nutriments = tool.reagents.has_reagent(/datum/reagent/consumable/nutriment)
+ if(!nutriments)
+ to_chat(user, span_warning("[src] burbles unhappily at the offering."))
+ return ITEM_INTERACT_BLOCKING
+
+ nutriment_eaten += nutriments.volume
+ to_chat(user, span_notice("[src] opens up and swallows [tool] whole!"))
+ qdel(tool)
+ playsound(src, 'sound/items/eatfood.ogg', 20, TRUE)
+
+ if(nutriment_eaten < nutriment_required)
+ return ITEM_INTERACT_SUCCESS
+
+ nutriment_eaten = 0
+ user.visible_message(span_notice("[src] swells up and produces a small pile of cookies!"))
+ playsound(src, 'sound/effects/splat.ogg', 40, TRUE)
+ last_produced = world.time
+ for(var/i in 1 to cookies)
+ var/obj/item/cookie = spawncookie()
+ cookie.pixel_x = base_pixel_x + rand(-5, 5)
+ cookie.pixel_y = base_pixel_y + rand(-5, 5)
+ return ITEM_INTERACT_SUCCESS
/obj/item/slimecross/consuming/proc/spawncookie()
return new cookietype(get_turf(src))
diff --git a/code/modules/research/xenobiology/crossbreeding/reproductive.dm b/code/modules/research/xenobiology/crossbreeding/reproductive.dm
index a0888c1c2f86..b1bad9bf7a77 100644
--- a/code/modules/research/xenobiology/crossbreeding/reproductive.dm
+++ b/code/modules/research/xenobiology/crossbreeding/reproductive.dm
@@ -24,39 +24,42 @@ Reproductive extracts:
. = ..()
create_storage(storage_type = /datum/storage/extract_inventory)
-/obj/item/slimecross/reproductive/attackby(obj/item/O, mob/user)
+/obj/item/slimecross/reproductive/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
var/datum/storage/extract_inventory/slime_storage = atom_storage
- if(!istype(slime_storage))
- return
+ if(!istype(slime_storage)) // what
+ return NONE
if((last_produce + cooldown) > world.time)
to_chat(user, span_warning("[src] is still digesting!"))
- return
+ return ITEM_INTERACT_BLOCKING
if(length(contents) >= feedAmount) //if for some reason the contents are full, but it didnt digest, attempt to digest again
to_chat(user, span_warning("[src] appears to be full but is not digesting! Maybe poking it stimulated it to digest."))
slime_storage?.processCubes(user)
- return
+ return ITEM_INTERACT_BLOCKING
- if(istype(O, /obj/item/storage/bag/xeno))
+ if(istype(tool, /obj/item/storage/bag/xeno))
var/list/inserted = list()
- O.atom_storage.remove_type(/obj/item/food/monkeycube, src, feedAmount - length(contents), TRUE, FALSE, user, inserted)
- if(inserted.len)
- to_chat(user, span_notice("You feed [length(inserted)] Monkey Cube[p_s()] to [src], and it pulses gently."))
- playsound(src, 'sound/items/eatfood.ogg', 20, TRUE)
- slime_storage?.processCubes(user)
- else
+ tool.atom_storage.remove_type(/obj/item/food/monkeycube, src, feedAmount - length(contents), TRUE, FALSE, user, inserted)
+ if(!inserted.len)
to_chat(user, span_warning("There are no monkey cubes in the bio bag!"))
- return
-
- else if(istype(O, /obj/item/food/monkeycube))
- if(atom_storage?.attempt_insert(O, user, override = TRUE, force = STORAGE_FULLY_LOCKED))
- to_chat(user, span_notice("You feed 1 Monkey Cube to [src], and it pulses gently."))
- slime_storage?.processCubes(user)
- playsound(src, 'sound/items/eatfood.ogg', 20, TRUE)
- return
- else
- to_chat(user, span_notice("The [src] rejects the Monkey Cube!")) //in case it fails to insert for whatever reason you get feedback
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You feed [length(inserted)] monkey cube[length(inserted) > 1 ? "s" : ""] to [src], and it pulses gently."))
+ playsound(src, 'sound/items/eatfood.ogg', 20, TRUE)
+ slime_storage?.processCubes(user)
+ return ITEM_INTERACT_SUCCESS
+
+ if(!istype(tool, /obj/item/food/monkeycube))
+ return NONE
+
+ if(!atom_storage?.attempt_insert(tool, user, override = TRUE, force = STORAGE_FULLY_LOCKED))
+ to_chat(user, span_notice("The [src] rejects [tool]!")) //in case it fails to insert for whatever reason you get feedback
+ return ITEM_INTERACT_BLOCKING
+
+ to_chat(user, span_notice("You feed [tool] to [src], and it pulses gently."))
+ slime_storage?.processCubes(user)
+ playsound(src, 'sound/items/eatfood.ogg', 20, TRUE)
+ return ITEM_INTERACT_SUCCESS
/obj/item/slimecross/reproductive/grey
extract_type = /obj/item/slime_extract/grey
diff --git a/code/modules/research/xenobiology/crossbreeding/stabilized.dm b/code/modules/research/xenobiology/crossbreeding/stabilized.dm
index 14bbdd6dda56..e9424c469c4f 100644
--- a/code/modules/research/xenobiology/crossbreeding/stabilized.dm
+++ b/code/modules/research/xenobiology/crossbreeding/stabilized.dm
@@ -189,13 +189,18 @@ Stabilized extracts:
/obj/item/slimecross/stabilized/rainbow
colour = SLIME_TYPE_RAINBOW
effect_desc = "Accepts a regenerative extract and automatically uses it if the owner enters a critical condition."
+ /// Regenerative crossbreed stored to be autoused on critted owner
var/obj/item/slimecross/regenerative/regencore
-/obj/item/slimecross/stabilized/rainbow/attackby(obj/item/O, mob/user)
- var/obj/item/slimecross/regenerative/regen = O
- if(istype(regen) && !regencore)
- to_chat(user, span_notice("You place [O] in [src], prepping the extract for automatic application!"))
- regencore = regen
- regen.forceMove(src)
- return
- return ..()
+/obj/item/slimecross/stabilized/rainbow/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!istype(tool, /obj/item/slimecross/regenerative))
+ return NONE
+
+ if(regencore)
+ to_chat(user, span_warning("[src] already has a regenerative crossbreed stored in it!"))
+ return ITEM_INTERACT_BLOCKING
+
+ to_chat(user, span_notice("You place [tool] in [src], prepping the extract for automatic application!"))
+ regencore = tool
+ tool.forceMove(src)
+ return ITEM_INTERACT_SUCCESS
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index 3ec48ef314a2..727518bef728 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -29,19 +29,20 @@
if(extract_uses > 1)
. += "It has [extract_uses] uses remaining."
-/obj/item/slime_extract/attackby(obj/item/O, mob/user)
- if(istype(O, /obj/item/slimepotion/enhancer))
- if(extract_uses >= 5 || recurring)
- to_chat(user, span_warning("You cannot enhance this extract further!"))
- return ..()
- if(O.type == /obj/item/slimepotion/enhancer) //Seriously, why is this defined here...?
- to_chat(user, span_notice("You apply the enhancer to the slime extract. It may now be reused one more time."))
- extract_uses++
- if(O.type == /obj/item/slimepotion/enhancer/max)
- to_chat(user, span_notice("You dump the maximizer on the slime extract. It can now be used a total of 5 times!"))
- extract_uses = 5
- qdel(O)
- ..()
+/obj/item/slime_extract/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!istype(tool, /obj/item/slimepotion/enhancer))
+ return NONE
+ if(extract_uses >= 5 || recurring)
+ to_chat(user, span_warning("You cannot enhance this extract further!"))
+ return ITEM_INTERACT_BLOCKING
+ if(istype(tool, /obj/item/slimepotion/enhancer/max))
+ to_chat(user, span_notice("You dump the maximizer on the slime extract. It can now be used a total of 5 times!"))
+ extract_uses = 5
+ else
+ to_chat(user, span_notice("You apply the enhancer to the slime extract. It may now be reused one more time."))
+ extract_uses++
+ qdel(tool)
+ return ITEM_INTERACT_SUCCESS
/obj/item/slime_extract/Initialize(mapload)
. = ..()
diff --git a/code/modules/shuttle/mobile_port/variants/supply.dm b/code/modules/shuttle/mobile_port/variants/supply.dm
index 38e305bc3498..505507bd9bb1 100644
--- a/code/modules/shuttle/mobile_port/variants/supply.dm
+++ b/code/modules/shuttle/mobile_port/variants/supply.dm
@@ -292,6 +292,8 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
for(var/atom/movable/exporting_atom in shuttle_turf)
if(iseyemob(exporting_atom))
continue
+ if(isobserver(exporting_atom))
+ continue
if(exporting_atom.anchored)
continue
export_item_and_contents(exporting_atom, apply_elastic = TRUE, dry_run = FALSE, external_report = report)
diff --git a/code/modules/spells/spell.dm b/code/modules/spells/spell.dm
index 5bdf80147699..ccbf0097cda5 100644
--- a/code/modules/spells/spell.dm
+++ b/code/modules/spells/spell.dm
@@ -153,6 +153,9 @@
if(!owner)
CRASH("[type] - can_cast_spell called on a spell without an owner!")
+ if(SEND_SIGNAL(src, COMSIG_SPELL_CAN_CAST_CHECK, feedback) & SPELL_CANCEL_CAST)
+ return FALSE
+
// Certain spells are not allowed on the centcom zlevel
var/turf/caster_turf = get_turf(owner)
// Spells which require being on the station
diff --git a/code/modules/spells/spell_types/touch/scream_for_me.dm b/code/modules/spells/spell_types/touch/scream_for_me.dm
index d3c142d70392..2c951f1160db 100644
--- a/code/modules/spells/spell_types/touch/scream_for_me.dm
+++ b/code/modules/spells/spell_types/touch/scream_for_me.dm
@@ -28,7 +28,7 @@
return
var/mob/living/carbon/human/human_victim = victim
human_victim.emote("scream")
- for(var/obj/item/bodypart/to_wound as anything in human_victim.bodyparts)
+ for(var/obj/item/bodypart/to_wound as anything in human_victim.get_bodyparts())
human_victim.cause_wound_of_type_and_severity(WOUND_SLASH, to_wound, WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_CRITICAL)
return TRUE
diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm
index 7956f00a7330..1ba6c2fda499 100644
--- a/code/modules/station_goals/bsa.dm
+++ b/code/modules/station_goals/bsa.dm
@@ -108,6 +108,9 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE)
if(!has_space())
return "Not enough free space!"
+/**
+ * Proc to check if the BSA has the required 10 x 1 block space to deploy.
+ */
/obj/machinery/bsa/middle/proc/has_space()
var/cannon_dir = get_cannon_direction()
var/width = 10
@@ -121,9 +124,14 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE)
return FALSE
var/turf/base = get_turf(src)
+ var/blocked = FALSE
for(var/turf/T as anything in CORNER_BLOCK_OFFSET(base, width, 3, offset, -1))
if(T.density || isspaceturf(T))
- return FALSE
+ blocked = TRUE
+ new /obj/effect/temp_visual/point(T)
+ if(blocked)
+ return FALSE
+
return TRUE
/obj/machinery/bsa/middle/proc/get_cannon_direction()
diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm
index 659406d24ffb..2a22568c0453 100644
--- a/code/modules/surgery/bodyparts/_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/_bodyparts.dm
@@ -232,6 +232,8 @@
var/static/list/butcher_drop_cache = list()
/// What state is the bodypart in for determining surgery availability
VAR_FINAL/surgery_state = NONE
+ /// Typepath of this limb as a stump
+ var/stump_typepath
/obj/item/bodypart/apply_fantasy_bonuses(bonus)
. = ..()
@@ -643,8 +645,7 @@
SHOULD_CALL_PARENT(TRUE)
var/atom/drop_loc = drop_location()
- if(IS_ORGANIC_LIMB(src))
- playsound(drop_loc, 'sound/misc/splort.ogg', 50, TRUE, -1)
+ var/play_sfx = FALSE
for(var/obj/item/organ/bodypart_organ in contents)
if(bodypart_organ.organ_flags & ORGAN_UNREMOVABLE)
@@ -661,10 +662,15 @@
if(drop_loc) //can be null if being deleted
bodypart_organ.forceMove(get_turf(drop_loc))
+ play_sfx = TRUE
if(drop_loc) //can be null during deletion
for(var/atom/movable/movable as anything in src)
movable.forceMove(drop_loc)
+ play_sfx = TRUE
+
+ if(play_sfx && IS_ORGANIC_LIMB(src))
+ playsound(drop_loc, 'sound/misc/splort.ogg', 50, TRUE, -1)
update_icon_dropped()
@@ -1294,7 +1300,7 @@
update_icon_dropped()
///Generates an /image for the limb to be used as an overlay
-/obj/item/bodypart/proc/get_limb_icon(dropped, mob/living/carbon/update_on)
+/obj/item/bodypart/proc/get_limb_icon(dropped)
SHOULD_CALL_PARENT(TRUE)
RETURN_TYPE(/list)
@@ -1308,7 +1314,7 @@
// Handles invisibility (not alpha or actual invisibility but invisibility)
if(is_invisible)
. += image(icon_invisible, "invisible_[body_zone]", -BODYPARTS_LAYER, dir = image_dir)
- SEND_SIGNAL(src, COMSIG_BODYPART_GET_LIMB_ICON, ., dropped, update_on)
+ SEND_SIGNAL(src, COMSIG_BODYPART_GET_LIMB_ICON, ., dropped)
return .
// Normal non-husk handling
@@ -1336,7 +1342,7 @@
if(brutestate)
// divided into two overlays: one that gets colored and one that doesn't.
var/image/brute_blood_overlay = image('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0", -DAMAGE_LAYER, dir = SOUTH)
- brute_blood_overlay.color = get_color_from_blood_list(update_on ? update_on.get_blood_dna_list() : blood_dna_info) // living mobs can just get it fresh, dropped limbs use blood_dna_info
+ brute_blood_overlay.color = get_color_from_blood_list(blood_dna_info)
var/mutable_appearance/brute_damage_overlay = mutable_appearance('icons/mob/effects/dam_mob.dmi', "[dmg_overlay_type]_[body_zone]_[brutestate]0_overlay", -DAMAGE_LAYER, appearance_flags = RESET_COLOR)
if(brute_damage_overlay)
brute_blood_overlay.overlays += brute_damage_overlay
@@ -1417,7 +1423,7 @@
for(var/datum/layer in .)
overlay.modify_bodypart_appearance(layer)
- SEND_SIGNAL(src, COMSIG_BODYPART_GET_LIMB_ICON, ., dropped, update_on)
+ SEND_SIGNAL(src, COMSIG_BODYPART_GET_LIMB_ICON, ., dropped)
return .
/obj/item/bodypart/proc/huskify_image(image/thing_to_husk)
@@ -1429,11 +1435,7 @@
husk_blood.blend_mode = BLEND_INSET_OVERLAY
husk_blood.dir = thing_to_husk.dir
husk_blood.layer = thing_to_husk.layer
- var/list/blood_dna = blood_dna_info || owner?.get_blood_dna_list()
- if (LAZYLEN(blood_dna))
- husk_blood.color = get_color_from_blood_list(blood_dna)
- else
- husk_blood.color = BLOOD_COLOR_RED
+ husk_blood.color = LAZYLEN(blood_dna_info) ? get_color_from_blood_list(blood_dna_info) : BLOOD_COLOR_RED
return husk_blood
///Add a bodypart overlay and call the appropriate update procs
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 2960dee55777..72108859d3e9 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -17,7 +17,7 @@
if(!silent)
limb_owner.visible_message(span_danger("[limb_owner]'s [name] is violently dismembered!"))
INVOKE_ASYNC(limb_owner, TYPE_PROC_REF(/mob, emote), "scream")
- playsound(get_turf(limb_owner), 'sound/effects/dismember.ogg', 80, TRUE)
+ playsound(limb_owner, 'sound/effects/dismember.ogg', 80, TRUE)
limb_owner.add_mood_event("dismembered_[body_zone]", /datum/mood_event/dismembered, src)
limb_owner.add_mob_memory(/datum/memory/was_dismembered, lost_limb = src)
@@ -123,7 +123,7 @@
if(!special)
phantom_owner.hud_used?.update_locked_slots()
- if(bodypart_flags & BODYPART_PSEUDOPART)
+ if(bodypart_flags & (BODYPART_PSEUDOPART|BODYPART_STUMP))
drop_organs(phantom_owner) //Psuedoparts shouldn't have organs, but just in case
if(!QDELING(src)) // we might be removed as a part of something qdeling us
qdel(src)
@@ -281,6 +281,13 @@
if(!can_attach_limb(new_limb_owner, special))
return FALSE
+ var/obj/item/bodypart/existing_limb = new_limb_owner.get_bodypart(body_zone, include_stumps = TRUE)
+ if(existing_limb)
+ if(existing_limb.type != stump_typepath)
+ stack_trace("Attempted to attach a limb to [new_limb_owner] in zone [body_zone] where they already have a non-stump limb")
+ existing_limb.drop_limb(special = TRUE)
+ qdel(existing_limb)
+
SEND_SIGNAL(new_limb_owner, COMSIG_CARBON_ATTACH_LIMB, src, special, lazy)
SEND_SIGNAL(src, COMSIG_BODYPART_ATTACHED, new_limb_owner, special, lazy)
new_limb_owner.add_bodypart(src)
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 73a526361f0d..88f4cc912f28 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -27,6 +27,7 @@
bodypart_trait_source = HEAD_TRAIT
butcher_replacement = /obj/item/bodypart/head/skeleton/nonfunctional
base_meat_amount = 0
+ stump_typepath = /obj/item/bodypart/head/stump
/// Do we show the information about missing organs upon being examined? Defaults to TRUE, useful for Dullahan heads.
var/show_organs_on_examine = TRUE
diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm
index bb7a93f362d9..73542be9b0c4 100644
--- a/code/modules/surgery/bodyparts/helpers.dm
+++ b/code/modules/surgery/bodyparts/helpers.dm
@@ -1,15 +1,55 @@
-
-/mob/living/proc/get_bodypart(zone)
+/**
+ * Returns a bodypart of the specified zone that this mob has
+ *
+ * * zone: the zone to get.
+ * Defaults to chest, allowing for skilling zone nullchecks if you don't care what bodypart you get.
+ * * include_stumps: whether or not to consider stumps as valid bodyparts to return.
+ * Defaults to FALSE, meaning that if a limb is missing (is a stump), nothing will be returned.
+ *
+ * Returns a bodypart, or null.
+ */
+/mob/living/proc/get_bodypart(zone = BODY_ZONE_CHEST, include_stumps = FALSE)
return
-/mob/living/carbon/get_bodypart(zone)
+/mob/living/carbon/get_bodypart(zone = BODY_ZONE_CHEST, include_stumps = FALSE)
RETURN_TYPE(/obj/item/bodypart)
- if(!zone)
- zone = BODY_ZONE_CHEST
for(var/obj/item/bodypart/bodypart as anything in bodyparts)
- if(bodypart.body_zone == zone)
- return bodypart
+ if(!include_stumps && IS_STUMP(bodypart))
+ continue
+ if(bodypart.body_zone != zone)
+ continue
+ return bodypart
+
+/**
+ * Returns all bodyparts this mob has, optionally including stumps.
+ *
+ * include_stumps: whether or not to consider stumps as valid bodyparts to return.
+ * Defaults to FALSE, meaning that if a limb is missing (is a stump), it won't be included in the returned list.
+ *
+ * Returns a list of bodyparts, which may be empty.
+ */
+/mob/living/proc/get_bodyparts(include_stumps = FALSE)
+ var/list/parts = list()
+ for(var/zone in get_all_limbs())
+ var/obj/item/bodypart/bodypart = get_bodypart(zone, include_stumps)
+ if(bodypart)
+ parts += bodypart
+
+ return parts
+
+/**
+ * Returns all bodyparts this mob has, indexed by their body zone
+ * Also includes stumps and nulls, so be sure to check for those if you use this proc.
+ * (Note: nulls will be very rare - as 95% of missing limbs are represented as stumps - but not impossible due to some edge cases)
+ *
+ * Returns a list of bodyparts indexed by their body zone
+ */
+/mob/living/proc/get_bodyparts_by_zones() as /list
+ var/list/parts = list()
+ for(var/zone in get_all_limbs())
+ parts[zone] = get_bodypart(zone)
+ return parts
///Returns TRUE/FALSE on whether the mob should have a limb in a given zone, used for species-restrictions.
/mob/living/carbon/proc/should_have_limb(zone)
@@ -128,9 +168,11 @@
///Returns a list of all limbs this mob should have.
/mob/living/carbon/get_all_limbs()
- if(dna)
- return dna.species.bodypart_overrides.Copy()
- return ..()
+ // gets the "normal list", ie chest-head-legs-arms. order matters for human rendering!
+ . = dna?.species?.bodypart_overrides.Copy() || ..()
+ // includes any additional adminbussed hands
+ for(var/obj/item/bodypart/hand in hand_bodyparts)
+ . |= hand.body_zone
// DARKPACK EDIT ADD START
/mob/proc/get_attacking_limb(atom/target, datum/martial_art/attacker_style)
@@ -151,7 +193,6 @@
///Returns a list of all missing limbs this mob should have on them, but don't.
/mob/living/carbon/proc/get_missing_limbs() as /list
- RETURN_TYPE(/list)
var/list/full = get_all_limbs()
for(var/zone in full)
if(get_bodypart(zone))
@@ -194,12 +235,12 @@
///Remove all embedded objects from all limbs on the carbon mob
/mob/living/carbon/proc/remove_all_embedded_objects()
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts(include_stumps = TRUE))
for(var/obj/item/embedded as anything in bodypart.embedded_objects)
remove_embedded_object(embedded)
/mob/living/carbon/proc/has_embedded_objects(include_harmless = FALSE)
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in get_bodyparts(include_stumps = TRUE))
for(var/obj/item/embedded as anything in bodypart.embedded_objects)
if(!include_harmless && embedded.get_embed().is_harmless(consider_stamina = TRUE))
continue
@@ -245,7 +286,7 @@
/// Makes sure that the owner's bodytype flags match the flags of all of its parts and organs
/mob/living/carbon/proc/synchronize_bodytypes()
var/all_limb_flags = NONE
- for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/bodypart/limb as anything in get_bodyparts(include_stumps = TRUE))
for(var/obj/item/organ/organ in limb)
all_limb_flags |= organ.external_bodytypes
all_limb_flags |= limb.bodytype
@@ -255,13 +296,23 @@
/// Makes sure that the owner's bodyshape flags match the flags of all of its parts and organs
/mob/living/carbon/proc/synchronize_bodyshapes()
var/all_limb_flags = NONE
- for(var/obj/item/bodypart/limb as anything in bodyparts)
+ for(var/obj/item/bodypart/limb as anything in get_bodyparts(include_stumps = TRUE))
for(var/obj/item/organ/organ in limb)
all_limb_flags |= organ.external_bodyshapes
all_limb_flags |= limb.bodyshape
bodyshape = all_limb_flags
+/// Get all bodyshapes but filter out bodyshapes that are currently being hidden
+/mob/living/carbon/proc/get_active_bodyshapes()
+ var/active_shapes = bodyshape
+ // future todo: both of these are state based, maybe we can just remove relevant bodyshapes directly. would remove the need for this proc
+ if((active_shapes & BODYSHAPE_DIGITIGRADE) && is_digitigrade_squished())
+ active_shapes &= ~BODYSHAPE_DIGITIGRADE
+ if((active_shapes & BODYSHAPE_SNOUTED) && (obscured_slots & HIDESNOUT))
+ active_shapes &= ~BODYSHAPE_SNOUTED
+ return active_shapes
+
/proc/skintone2hex(skin_tone)
. = 0
switch(skin_tone)
diff --git a/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm
index c888271cfc28..7c1129502657 100644
--- a/code/modules/surgery/bodyparts/parts.dm
+++ b/code/modules/surgery/bodyparts/parts.dm
@@ -149,6 +149,7 @@
/// Parent Type for arms, should not appear in game.
/obj/item/bodypart/arm
+ abstract_type = /obj/item/bodypart/arm
name = "arm"
desc = "Hey buddy give me a HAND and report this to the github because you shouldn't be seeing this."
abstract_type = /obj/item/bodypart/arm
@@ -324,6 +325,7 @@
px_y = 0
bodypart_trait_source = LEFT_ARM_TRAIT
butcher_replacement = /obj/item/bodypart/arm/left/skeleton/nonfunctional
+ stump_typepath = /obj/item/bodypart/arm/left/stump
/obj/item/bodypart/arm/left/apply_ownership(mob/living/carbon/new_owner)
if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_L_ARM))
@@ -406,6 +408,7 @@
px_y = 0
bodypart_trait_source = RIGHT_ARM_TRAIT
butcher_replacement = /obj/item/bodypart/arm/right/skeleton/nonfunctional
+ stump_typepath = /obj/item/bodypart/arm/right/stump
/obj/item/bodypart/arm/right/apply_ownership(mob/living/carbon/new_owner)
if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_R_ARM))
@@ -475,6 +478,7 @@
/// Parent Type for legs, should not appear in game.
/obj/item/bodypart/leg
+ abstract_type = /obj/item/bodypart/leg
name = "leg"
desc = "This item shouldn't exist. Talk about breaking a leg. Badum-Tss!"
abstract_type = /obj/item/bodypart/leg
@@ -565,6 +569,7 @@
can_be_disabled = TRUE
bodypart_trait_source = LEFT_LEG_TRAIT
butcher_replacement = /obj/item/bodypart/leg/left/skeleton/nonfunctional
+ stump_typepath = /obj/item/bodypart/leg/left/stump
/obj/item/bodypart/leg/left/apply_ownership(mob/living/carbon/new_owner)
if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_L_LEG))
@@ -644,6 +649,7 @@
px_y = 12
bodypart_trait_source = RIGHT_LEG_TRAIT
butcher_replacement = /obj/item/bodypart/leg/right/skeleton/nonfunctional
+ stump_typepath = /obj/item/bodypart/leg/right/stump
/obj/item/bodypart/leg/right/apply_ownership(mob/living/carbon/new_owner)
if(HAS_TRAIT(new_owner, TRAIT_PARALYSIS_R_LEG))
diff --git a/code/modules/surgery/bodyparts/robot_bodyparts.dm b/code/modules/surgery/bodyparts/robot_bodyparts.dm
index 5548d0634610..fe5d9c6e14d7 100644
--- a/code/modules/surgery/bodyparts/robot_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/robot_bodyparts.dm
@@ -277,7 +277,7 @@
SIGNAL_HANDLER
var/all_robotic = TRUE
- for(var/obj/item/bodypart/part in owner.bodyparts)
+ for(var/obj/item/bodypart/part as anything in owner.get_bodyparts())
all_robotic = all_robotic && IS_ROBOTIC_LIMB(part)
if(all_robotic)
@@ -295,28 +295,29 @@
TRAIT_RESISTHIGHPRESSURE,
), AUGMENTATION_TRAIT)
-/obj/item/bodypart/chest/robot/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers)
- if(istype(weapon, /obj/item/stock_parts/power_store/cell))
+/obj/item/bodypart/chest/robot/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(istype(tool, /obj/item/stock_parts/power_store/cell))
if(cell)
- to_chat(user, span_warning("You have already inserted a cell!"))
- return
- else
- if(!user.transferItemToLoc(weapon, src))
- return
- cell = weapon
- to_chat(user, span_notice("You insert the cell."))
- else if(istype(weapon, /obj/item/stack/cable_coil))
+ to_chat(user, span_warning("A cell is already present in [src]!"))
+ return ITEM_INTERACT_BLOCKING
+ if(!user.transferItemToLoc(tool, src))
+ return ITEM_INTERACT_BLOCKING
+ cell = tool
+ to_chat(user, span_notice("You insert [cell] into [src]."))
+ return ITEM_INTERACT_SUCCESS
+
+ if(istype(tool, /obj/item/stack/cable_coil))
if(wired)
- to_chat(user, span_warning("You have already inserted wire!"))
- return
- var/obj/item/stack/cable_coil/coil = weapon
- if (coil.use(1))
- wired = TRUE
- to_chat(user, span_notice("You insert the wire."))
- else
+ to_chat(user, span_warning("[src] is already wired up!"))
+ return ITEM_INTERACT_BLOCKING
+ var/obj/item/stack/cable_coil/coil = tool
+ if (!coil.use(1))
to_chat(user, span_warning("You need one length of coil to wire it!"))
- else
- return ..()
+ return ITEM_INTERACT_BLOCKING
+ wired = TRUE
+ to_chat(user, span_notice("You wire the cell inside of [src]."))
+ return ITEM_INTERACT_SUCCESS
+ return NONE
/obj/item/bodypart/chest/robot/wirecutter_act(mob/living/user, obj/item/cutter)
. = ..()
@@ -440,25 +441,28 @@
. += "It has two eye sockets occupied by flashes."
. += span_notice("You can remove the seated flash[single_flash ? "":"es"] with a crowbar.")
-/obj/item/bodypart/head/robot/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers)
- if(istype(weapon, /obj/item/assembly/flash/handheld))
- var/obj/item/assembly/flash/handheld/flash = weapon
- if(flash1 && flash2)
- to_chat(user, span_warning("You have already inserted the eyes!"))
- return
- else if(flash.burnt_out)
- to_chat(user, span_warning("You can't use a broken flash!"))
- return
- else
- if(!user.transferItemToLoc(flash, src))
- return
- if(flash1)
- flash2 = flash
- else
- flash1 = flash
- to_chat(user, span_notice("You insert the flash into the eye socket."))
- return
- return ..()
+/obj/item/bodypart/head/robot/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!istype(tool, /obj/item/assembly/flash/handheld))
+ return NONE
+
+ var/obj/item/assembly/flash/handheld/flash = tool
+ if(flash1 && flash2)
+ to_chat(user, span_warning("[src] already has both flash-eyes present!"))
+ return ITEM_INTERACT_BLOCKING
+
+ if(flash.burnt_out)
+ to_chat(user, span_warning("You can't use a broken flash!"))
+ return ITEM_INTERACT_BLOCKING
+
+ if(!user.transferItemToLoc(flash, src))
+ return ITEM_INTERACT_BLOCKING
+
+ if(flash1)
+ flash2 = flash
+ else
+ flash1 = flash
+ to_chat(user, span_notice("You insert the flash into the eye socket."))
+ return ITEM_INTERACT_SUCCESS
/obj/item/bodypart/head/robot/crowbar_act(mob/living/user, obj/item/prytool)
..()
diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
index f8be572498ec..7d16dbefc5c2 100644
--- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
+++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm
@@ -547,6 +547,7 @@
throwforce = 25 // It's also a potent weapon
show_organs_on_examine = FALSE
speech_span = null
+ stump_typepath = null
/obj/item/bodypart/head/dullahan/Entered(obj/item/organ/arrived, atom/old_loc, list/atom/old_locs)
. = ..()
diff --git a/code/modules/surgery/bodyparts/stumps.dm b/code/modules/surgery/bodyparts/stumps.dm
new file mode 100644
index 000000000000..49e4cdf29556
--- /dev/null
+++ b/code/modules/surgery/bodyparts/stumps.dm
@@ -0,0 +1,72 @@
+// Invisible bodyparts that serve as placeholders for a missing limb
+// Used so we can add behavior to "missing limbs" without putting it on the mob itself
+
+/obj/item/bodypart/head/stump
+ limb_id = null
+ plaintext_zone = "neck stump"
+ stump_typepath = null
+ scarrable = FALSE
+ biological_state = NONE
+ bodypart_flags = BODYPART_UNREMOVABLE | BODYPART_STUMP | BODYPART_VIRGIN
+
+ head_flags = NONE
+ teeth_count = 0 // lol?
+
+/obj/item/bodypart/head/stump/Initialize(mapload)
+ . = ..()
+ name = "stump"
+ // these traits are largely redundant, as many places that check for disabled limbs filter stumps.
+ // however, marking them as unusable for the sake of completeness might make it easier to work with.
+ ADD_TRAIT(src, TRAIT_PARALYSIS, INNATE_TRAIT)
+
+/obj/item/bodypart/leg/left/stump
+ limb_id = null
+ plaintext_zone = "left leg stump"
+ stump_typepath = null
+ scarrable = FALSE
+ biological_state = NONE
+ bodypart_flags = BODYPART_UNREMOVABLE | BODYPART_STUMP | BODYPART_VIRGIN
+
+/obj/item/bodypart/leg/left/stump/Initialize(mapload)
+ . = ..()
+ name = "stump"
+ ADD_TRAIT(src, TRAIT_PARALYSIS, INNATE_TRAIT)
+
+/obj/item/bodypart/leg/right/stump
+ limb_id = null
+ plaintext_zone = "right leg stump"
+ stump_typepath = null
+ scarrable = FALSE
+ biological_state = NONE
+ bodypart_flags = BODYPART_UNREMOVABLE | BODYPART_STUMP | BODYPART_VIRGIN
+
+/obj/item/bodypart/leg/right/stump/Initialize(mapload)
+ . = ..()
+ name = "stump"
+ ADD_TRAIT(src, TRAIT_PARALYSIS, INNATE_TRAIT)
+
+/obj/item/bodypart/arm/left/stump
+ limb_id = null
+ plaintext_zone = "left arm stump"
+ stump_typepath = null
+ scarrable = FALSE
+ biological_state = NONE
+ bodypart_flags = BODYPART_UNREMOVABLE | BODYPART_STUMP | BODYPART_VIRGIN
+
+/obj/item/bodypart/arm/left/stump/Initialize(mapload)
+ . = ..()
+ name = "stump"
+ ADD_TRAIT(src, TRAIT_PARALYSIS, INNATE_TRAIT)
+
+/obj/item/bodypart/arm/right/stump
+ limb_id = null
+ plaintext_zone = "right arm stump"
+ stump_typepath = null
+ scarrable = FALSE
+ biological_state = NONE
+ bodypart_flags = BODYPART_UNREMOVABLE | BODYPART_STUMP | BODYPART_VIRGIN
+
+/obj/item/bodypart/arm/right/stump/Initialize(mapload)
+ . = ..()
+ name = "stump"
+ ADD_TRAIT(src, TRAIT_PARALYSIS, INNATE_TRAIT)
diff --git a/code/modules/surgery/operations/_operation.dm b/code/modules/surgery/operations/_operation.dm
index 4c79df375110..812f125257ae 100644
--- a/code/modules/surgery/operations/_operation.dm
+++ b/code/modules/surgery/operations/_operation.dm
@@ -1302,6 +1302,9 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
abstract_type = /datum/surgery_operation/limb
/// Body type required to perform this operation
var/required_bodytype = NONE
+ /// If TRUE, this operation can be performed on stumps.
+ /// If FALSE, the target limb must be a full limb.
+ var/allow_stumps = FALSE
/datum/surgery_operation/limb/all_blocked_strings()
. = ..()
@@ -1313,7 +1316,7 @@ GLOBAL_DATUM_INIT(operations, /datum/operation_holder, new)
/datum/surgery_operation/limb/get_operation_target(atom/movable/operating_on, body_zone)
if (isliving(operating_on))
var/mob/living/patient = operating_on
- return patient.get_bodypart(deprecise_zone(body_zone))
+ return patient.get_bodypart(deprecise_zone(body_zone), allow_stumps)
if (!isbodypart(operating_on))
return null
return operating_on
diff --git a/code/modules/surgery/operations/operation_add_limb.dm b/code/modules/surgery/operations/operation_add_limb.dm
index 3f12368ff121..ab837aafbcba 100644
--- a/code/modules/surgery/operations/operation_add_limb.dm
+++ b/code/modules/surgery/operations/operation_add_limb.dm
@@ -1,7 +1,7 @@
#define OPERATION_REJECTION_DAMAGE "tox_damage"
// This surgery is so snowflake that it doesn't use any of the operation subtypes, it forges its own path
-/datum/surgery_operation/prosthetic_replacement
+/datum/surgery_operation/limb/prosthetic_replacement
name = "prosthetic replacement"
desc = "Replace a missing limb with a prosthetic (or arbitrary) item."
implements = list(
@@ -12,6 +12,7 @@
operation_flags = OPERATION_STANDING_ALLOWED | OPERATION_PRIORITY_NEXT_STEP | OPERATION_NOTABLE | OPERATION_IGNORE_CLOTHES
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_VESSELS_UNCLAMPED
+ allow_stumps = TRUE
/// List of items that are always allowed to be an arm replacement, even if they fail another requirement.
var/list/always_accepted_prosthetics = list(
/obj/item/chainsaw, // the OG, too large otherwise
@@ -21,25 +22,20 @@
/// Radial slice datums for every augment type
VAR_PRIVATE/list/cached_prosthetic_options
-/datum/surgery_operation/prosthetic_replacement/get_default_radial_image()
- return image(/obj/item/bodypart/chest)
-
-/datum/surgery_operation/prosthetic_replacement/get_recommended_tool()
+/datum/surgery_operation/limb/prosthetic_replacement/get_recommended_tool()
return "any limb / any item"
-/datum/surgery_operation/prosthetic_replacement/get_any_tool()
+/datum/surgery_operation/limb/prosthetic_replacement/get_any_tool()
return "Any suitable arm replacement"
-/datum/surgery_operation/prosthetic_replacement/all_required_strings()
- . = list()
- . += "operate on chest (target chest)"
- . += ..()
- . += "when the chest is prepared, target the zone of the limb you are attaching"
+/datum/surgery_operation/limb/prosthetic_replacement/all_required_strings()
+ . = ..()
+ . += "the limb must be missing / a stump"
-/datum/surgery_operation/prosthetic_replacement/any_required_strings()
+/datum/surgery_operation/limb/prosthetic_replacement/any_required_strings()
return list("arms may receive any suitable item in lieu of a replacement limb") + ..()
-/datum/surgery_operation/prosthetic_replacement/get_radial_options(obj/item/bodypart/chest/chest, obj/item/tool, operating_zone)
+/datum/surgery_operation/limb/prosthetic_replacement/get_radial_options(obj/item/bodypart/chest/chest, obj/item/tool, operating_zone)
var/datum/radial_menu_choice/option = LAZYACCESS(cached_prosthetic_options, tool.type)
if(!option)
option = new()
@@ -50,32 +46,10 @@
return option
-/datum/surgery_operation/prosthetic_replacement/get_operation_target(atom/movable/operating_on, body_zone)
- if (!isliving(operating_on))
- return null
- var/mob/living/patient = operating_on
- // We always operate on the chest even if we're targeting left leg or w/e
- return patient.get_bodypart(BODY_ZONE_CHEST)
-
-/datum/surgery_operation/prosthetic_replacement/has_surgery_state(obj/item/bodypart/chest/chest, state)
- return LIMB_HAS_SURGERY_STATE(chest, state)
-
-/datum/surgery_operation/prosthetic_replacement/has_any_surgery_state(obj/item/bodypart/chest/chest, state)
- return LIMB_HAS_ANY_SURGERY_STATE(chest, state)
-
-/datum/surgery_operation/prosthetic_replacement/get_patient(obj/item/bodypart/chest/chest)
- return chest.owner
+/datum/surgery_operation/limb/prosthetic_replacement/state_check(obj/item/bodypart/limb)
+ return IS_STUMP(limb)
-/datum/surgery_operation/prosthetic_replacement/is_available(obj/item/bodypart/chest/chest, operated_zone)
- var/real_operated_zone = deprecise_zone(operated_zone)
- // Operate on the chest but target another zone
- if(!HAS_TRAIT(chest, TRAIT_READY_TO_OPERATE) || real_operated_zone == BODY_ZONE_CHEST)
- return FALSE
- if(chest.owner.get_bodypart(real_operated_zone))
- return FALSE
- return ..()
-
-/datum/surgery_operation/prosthetic_replacement/snowflake_check_availability(obj/item/bodypart/chest, mob/living/surgeon, obj/item/tool, operated_zone)
+/datum/surgery_operation/limb/prosthetic_replacement/snowflake_check_availability(obj/item/bodypart/chest, mob/living/surgeon, obj/item/tool, operated_zone)
if(!surgeon.canUnEquip(tool))
return FALSE
var/real_operated_zone = deprecise_zone(operated_zone)
@@ -91,7 +65,7 @@
return FALSE
return TRUE
-/datum/surgery_operation/prosthetic_replacement/tool_check(obj/item/tool)
+/datum/surgery_operation/limb/prosthetic_replacement/tool_check(obj/item/tool)
if(tool.item_flags & (ABSTRACT|DROPDEL|HAND_ITEM))
return FALSE
if(isbodypart(tool))
@@ -104,41 +78,41 @@
return FALSE
return TRUE
-/datum/surgery_operation/prosthetic_replacement/pre_preop(atom/movable/operating_on, mob/living/surgeon, tool, list/operation_args)
+/datum/surgery_operation/limb/prosthetic_replacement/pre_preop(atom/movable/operating_on, mob/living/surgeon, tool, list/operation_args)
. = ..()
// always operate on absolute body zones
operation_args[OPERATION_TARGET_ZONE] = deprecise_zone(operation_args[OPERATION_TARGET_ZONE])
-/datum/surgery_operation/prosthetic_replacement/on_preop(obj/item/bodypart/chest/chest, mob/living/surgeon, obj/item/tool, list/operation_args)
- var/target_zone_readable = parse_zone(operation_args[OPERATION_TARGET_ZONE])
+/datum/surgery_operation/limb/prosthetic_replacement/on_preop(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
display_results(
surgeon,
- chest.owner,
- span_notice("You begin to replace [chest.owner]'s missing [target_zone_readable] with [tool]..."),
- span_notice("[surgeon] begins to replace [chest.owner]'s missing [target_zone_readable] with [tool]."),
- span_notice("[surgeon] begins to replace [chest.owner]'s missing [target_zone_readable]."),
+ limb.owner,
+ // "You begin to attach the right arm to john doe's right arm stump"
+ span_notice("You begin to attach [tool]'s to [FORMAT_LIMB_OWNER(limb)]..."),
+ span_notice("[surgeon] begins to attach [tool]'s to [FORMAT_LIMB_OWNER(limb)]."),
+ span_notice("[surgeon] begins to attach [tool]'s to [FORMAT_LIMB_OWNER(limb)]."),
)
- display_pain(chest.owner, "You feel an uncomfortable sensation where your [target_zone_readable] should be!")
+ display_pain(limb.owner, "You feel an uncomfortable sensation where your [parse_zone(limb.body_zone)] should be!")
operation_args[OPERATION_REJECTION_DAMAGE] = 10
if(isbodypart(tool))
var/obj/item/bodypart/new_limb = tool
if(IS_ROBOTIC_LIMB(new_limb))
operation_args[OPERATION_REJECTION_DAMAGE] = 0
- else if(new_limb.check_for_frankenstein(chest.owner))
+ else if(new_limb.check_for_frankenstein(limb.owner))
operation_args[OPERATION_REJECTION_DAMAGE] = 30
-/datum/surgery_operation/prosthetic_replacement/on_success(obj/item/bodypart/chest/chest, mob/living/surgeon, obj/item/tool, list/operation_args)
+/datum/surgery_operation/limb/prosthetic_replacement/on_success(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
if(!surgeon.temporarilyRemoveItemFromInventory(tool))
return // should never happen
if(operation_args[OPERATION_REJECTION_DAMAGE] > 0)
- chest.owner.apply_damage(operation_args[OPERATION_REJECTION_DAMAGE], TOX)
+ limb.owner.apply_damage(operation_args[OPERATION_REJECTION_DAMAGE], TOX)
if(isbodypart(tool))
- handle_bodypart(chest.owner, surgeon, tool)
+ handle_bodypart(limb.owner, surgeon, tool)
return
- handle_arbitrary_prosthetic(chest.owner, surgeon, tool, operation_args[OPERATION_TARGET_ZONE])
+ handle_arbitrary_prosthetic(limb.owner, surgeon, tool, operation_args[OPERATION_TARGET_ZONE])
-/datum/surgery_operation/prosthetic_replacement/proc/handle_bodypart(mob/living/carbon/patient, mob/living/surgeon, obj/item/bodypart/bodypart_to_attach)
+/datum/surgery_operation/limb/prosthetic_replacement/proc/handle_bodypart(mob/living/carbon/patient, mob/living/surgeon, obj/item/bodypart/bodypart_to_attach)
bodypart_to_attach.try_attach_limb(patient)
if(bodypart_to_attach.check_for_frankenstein(patient))
bodypart_to_attach.bodypart_flags |= BODYPART_IMPLANTED
@@ -150,7 +124,7 @@
)
display_pain(patient, "You feel synthetic sensation wash from your [bodypart_to_attach.plaintext_zone], which you can feel again!", TRUE)
-/datum/surgery_operation/prosthetic_replacement/proc/handle_arbitrary_prosthetic(mob/living/carbon/patient, mob/living/surgeon, obj/item/thing_to_attach, target_zone)
+/datum/surgery_operation/limb/prosthetic_replacement/proc/handle_arbitrary_prosthetic(mob/living/carbon/patient, mob/living/surgeon, obj/item/thing_to_attach, target_zone)
SSblackbox.record_feedback("tally", "arbitrary_prosthetic", 1, initial(thing_to_attach.name))
var/obj/item/bodypart/new_limb = patient.make_item_prosthetic(thing_to_attach, target_zone, 80)
new_limb.add_surgical_state(SURGERY_PROSTHETIC_UNSECURED)
@@ -187,8 +161,7 @@
span_notice("[surgeon] begins to [tool.singular_name] [limb] to [limb.owner]'s body."),
span_notice("[surgeon] begins to [tool.singular_name] something to [limb.owner]'s body."),
)
- var/obj/item/bodypart/chest = limb.owner.get_bodypart(BODY_ZONE_CHEST)
- display_pain(limb.owner, "[surgeon] begins to [tool.singular_name] [limb] to your body!", IS_ROBOTIC_LIMB(chest))
+ display_pain(limb.owner, "[surgeon] begins to [tool.singular_name] [limb] to your body!", IS_ROBOTIC_LIMB(limb))
/datum/surgery_operation/limb/secure_arbitrary_prosthetic/on_success(obj/item/bodypart/limb, mob/living/surgeon, obj/item/stack/medical/tool, list/operation_args)
display_results(
@@ -198,8 +171,7 @@
span_notice("[surgeon] finishes [tool.apply_verb] [limb] to [limb.owner]'s body."),
span_notice("[surgeon] finishes the [tool.apply_verb] procedure!"),
)
- var/obj/item/bodypart/chest = limb.owner.get_bodypart(BODY_ZONE_CHEST)
- display_pain(limb.owner, "You feel more secure as your prosthetic is firmly attached to your body!", IS_ROBOTIC_LIMB(chest))
+ display_pain(limb.owner, "You feel more secure as your prosthetic is firmly attached to your body!", IS_ROBOTIC_LIMB(limb))
limb.remove_surgical_state(SURGERY_PROSTHETIC_UNSECURED)
limb.AddComponent(/datum/component/item_as_prosthetic_limb, null, 0) // updates drop probability to zero
tool.use(1)
diff --git a/code/modules/surgery/operations/operation_amputation.dm b/code/modules/surgery/operations/operation_amputation.dm
index 100f9f428629..9a0236ea64ea 100644
--- a/code/modules/surgery/operations/operation_amputation.dm
+++ b/code/modules/surgery/operations/operation_amputation.dm
@@ -27,7 +27,7 @@
return TOOL_SAW
/datum/surgery_operation/limb/amputate/get_default_radial_image()
- return image(/obj/item/shears)
+ return image('icons/hud/surgery_radial.dmi', "amputate")
/datum/surgery_operation/limb/amputate/state_check(obj/item/bodypart/limb)
if(limb.body_zone == BODY_ZONE_CHEST)
diff --git a/code/modules/surgery/operations/operation_dissection.dm b/code/modules/surgery/operations/operation_dissection.dm
index 1756d536e023..74c119edd635 100644
--- a/code/modules/surgery/operations/operation_dissection.dm
+++ b/code/modules/surgery/operations/operation_dissection.dm
@@ -115,14 +115,14 @@
. = ..()
. += span_notice("It is worth [value] research points.")
-/obj/item/research_notes/attackby(obj/item/attacking_item, mob/living/user, list/modifiers, list/attack_modifiers)
- if(istype(attacking_item, /obj/item/research_notes))
- var/obj/item/research_notes/notes = attacking_item
- value = value + notes.value
- change_vol()
- qdel(notes)
- return
- return ..()
+/obj/item/research_notes/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!istype(tool, /obj/item/research_notes))
+ return NONE
+ var/obj/item/research_notes/notes = tool
+ value = value + notes.value
+ change_vol()
+ qdel(notes)
+ return ITEM_INTERACT_SUCCESS
/// proc that changes name and icon depending on value
/obj/item/research_notes/proc/change_vol()
diff --git a/code/modules/surgery/operations/operation_generic.dm b/code/modules/surgery/operations/operation_generic.dm
index 8288971a4a5a..545efa20946c 100644
--- a/code/modules/surgery/operations/operation_generic.dm
+++ b/code/modules/surgery/operations/operation_generic.dm
@@ -21,6 +21,7 @@
success_sound = 'sound/items/handling/surgery/scalpel2.ogg'
operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_NO_PATIENT_REQUIRED
any_surgery_states_blocked = ALL_SURGERY_SKIN_STATES
+ allow_stumps = TRUE
/// We can't cut mobs with this biostate
var/biostate_blacklist = BIO_CHITIN
@@ -28,7 +29,7 @@
return "Any sharp edged item"
/datum/surgery_operation/limb/incise_skin/get_default_radial_image()
- return image(/obj/item/scalpel)
+ return image('icons/hud/surgery_radial.dmi', "make_incision")
/datum/surgery_operation/limb/incise_skin/tool_check(obj/item/tool)
// Require edged sharpness OR a tool behavior match
@@ -115,9 +116,10 @@
preop_sound = 'sound/items/handling/surgery/retractor1.ogg'
success_sound = 'sound/items/handling/surgery/retractor2.ogg'
all_surgery_states_required = SURGERY_SKIN_CUT
+ allow_stumps = TRUE
/datum/surgery_operation/limb/retract_skin/get_default_radial_image()
- return image(/obj/item/retractor)
+ return image('icons/hud/surgery_radial.dmi', "retract_skin")
/datum/surgery_operation/limb/retract_skin/on_preop(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
display_results(
@@ -165,12 +167,13 @@
/obj/item = 'sound/items/handling/surgery/cautery2.ogg',
)
any_surgery_states_required = ALL_SURGERY_SKIN_STATES
+ allow_stumps = TRUE
/datum/surgery_operation/limb/close_skin/get_any_tool()
return "Any heat source"
/datum/surgery_operation/limb/close_skin/get_default_radial_image()
- return image(/obj/item/cautery)
+ return image('icons/hud/surgery_radial.dmi', "mend_incision")
/datum/surgery_operation/limb/close_skin/all_required_strings()
return ..() + list("the limb must have skin")
@@ -225,9 +228,10 @@
time = 2.4 SECONDS
preop_sound = 'sound/items/handling/surgery/hemostat1.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_UNCLAMPED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/clamp_bleeders/get_default_radial_image()
- return image(/obj/item/hemostat)
+ return image('icons/hud/surgery_radial.dmi', "clamp_bleeders")
/datum/surgery_operation/limb/clamp_bleeders/on_preop(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
display_results(
@@ -268,9 +272,10 @@
time = 2.4 SECONDS
preop_sound = 'sound/items/handling/surgery/hemostat1.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_VESSELS_CLAMPED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/unclamp_bleeders/get_default_radial_image()
- return image(/obj/item/hemostat)
+ return image('icons/hud/surgery_radial.dmi', "unclamp_bleeders")
/datum/surgery_operation/limb/unclamp_bleeders/all_required_strings()
return ..() + list("the limb must have blood vessels")
@@ -325,12 +330,13 @@
operation_flags = OPERATION_AFFECTS_MOOD | OPERATION_NO_PATIENT_REQUIRED
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_BONE_SAWED|SURGERY_BONE_DRILLED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/saw_bones/get_any_tool()
return "Any sharp edged item with decent force"
/datum/surgery_operation/limb/saw_bones/get_default_radial_image()
- return image(/obj/item/circular_saw)
+ return image('icons/hud/surgery_radial.dmi', "saw_bones")
/datum/surgery_operation/limb/saw_bones/tool_check(obj/item/tool)
// Require edged sharpness and sufficient force OR a tool behavior match
@@ -381,9 +387,10 @@
time = 4 SECONDS
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_required = SURGERY_BONE_SAWED|SURGERY_BONE_DRILLED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/fix_bones/get_default_radial_image()
- return image(/obj/item/stack/medical/bone_gel)
+ return image('icons/hud/surgery_radial.dmi', "fix_bones")
/datum/surgery_operation/limb/fix_bones/all_required_strings()
return ..() + list("the limb must have bones")
@@ -425,12 +432,13 @@
success_sound = 'sound/items/handling/surgery/organ2.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_BONE_SAWED|SURGERY_BONE_DRILLED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/drill_bones/get_any_tool()
return "Any sharp pointed item with decent force"
/datum/surgery_operation/limb/drill_bones/get_default_radial_image()
- return image(/obj/item/surgicaldrill)
+ return image('icons/hud/surgery_radial.dmi', "drill_bones")
/datum/surgery_operation/limb/drill_bones/tool_check(obj/item/tool)
// Require pointy sharpness and sufficient force OR a tool behavior match
@@ -459,7 +467,7 @@
/datum/surgery_operation/limb/incise_organs
name = "incise organs"
- desc = "Make an incision in patient's internal organ tissue to allow for manipulation or repair. \
+ desc = "Make an incision in the patient's internal organ tissue to allow for manipulation or repair. \
Causes \"organs cut\" surgical state."
required_bodytype = ~BODYTYPE_ROBOTIC
operation_flags = OPERATION_NO_PATIENT_REQUIRED
@@ -477,12 +485,13 @@
success_sound = 'sound/items/handling/surgery/organ1.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_ORGANS_CUT
+ allow_stumps = TRUE
/datum/surgery_operation/limb/incise_organs/get_any_tool()
return "Any sharp edged item"
/datum/surgery_operation/limb/incise_organs/get_default_radial_image()
- return image(/obj/item/scalpel)
+ return image('icons/hud/surgery_radial.dmi', "incise_organs")
/datum/surgery_operation/limb/incise_organs/tool_check(obj/item/tool)
// Require edged sharpness OR a tool behavior match. Also saws are a no-go, you'll rip up the organs!
diff --git a/code/modules/surgery/operations/operation_generic_basic.dm b/code/modules/surgery/operations/operation_generic_basic.dm
index 52dcc9db7710..c3f522f92241 100644
--- a/code/modules/surgery/operations/operation_generic_basic.dm
+++ b/code/modules/surgery/operations/operation_generic_basic.dm
@@ -28,7 +28,7 @@
return ..() + list("The patient must not have complex anatomy")
/datum/surgery_operation/basic/incise_skin/get_default_radial_image()
- return image(/obj/item/scalpel)
+ return image('icons/hud/surgery_radial.dmi', "make_incision")
/datum/surgery_operation/basic/incise_skin/state_check(mob/living/patient)
return !patient.has_limbs // Only for limbless mobs
@@ -91,7 +91,7 @@
return ..() + list("The patient must not have complex anatomy")
/datum/surgery_operation/basic/saw_bone/get_default_radial_image()
- return image(/obj/item/circular_saw)
+ return image('icons/hud/surgery_radial.dmi', "mend_incision")
/datum/surgery_operation/basic/saw_bone/state_check(mob/living/patient)
return !patient.has_limbs // Only for limbless mobs
diff --git a/code/modules/surgery/operations/operation_generic_mechanic.dm b/code/modules/surgery/operations/operation_generic_mechanic.dm
index bbcdfd55fd29..6238598129e4 100644
--- a/code/modules/surgery/operations/operation_generic_mechanic.dm
+++ b/code/modules/surgery/operations/operation_generic_mechanic.dm
@@ -16,12 +16,13 @@
preop_sound = 'sound/items/tools/screwdriver.ogg'
success_sound = 'sound/items/tools/screwdriver2.ogg'
any_surgery_states_blocked = ALL_SURGERY_SKIN_STATES
+ allow_stumps = TRUE
/datum/surgery_operation/limb/mechanical_incision/get_any_tool()
return "Any sharp item"
/datum/surgery_operation/limb/mechanical_incision/get_default_radial_image()
- return image(/obj/item/screwdriver)
+ return image('icons/hud/surgery_radial.dmi', "unscrew_shell")
/datum/surgery_operation/limb/mechanical_incision/tool_check(obj/item/tool)
// Require any sharpness OR a tool behavior match
@@ -56,9 +57,10 @@
preop_sound = 'sound/items/tools/ratchet.ogg'
success_sound = 'sound/machines/airlock/doorclick.ogg'
all_surgery_states_required = SURGERY_SKIN_CUT
+ allow_stumps = TRUE
/datum/surgery_operation/limb/mechanical_open/get_default_radial_image()
- return image('icons/hud/screen_gen.dmi', "arrow_large_still")
+ return image('icons/hud/surgery_radial.dmi', "open_hatch")
/datum/surgery_operation/limb/mechanical_open/on_preop(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
display_results(
@@ -93,12 +95,13 @@
preop_sound = 'sound/items/tools/screwdriver.ogg'
success_sound = 'sound/items/tools/screwdriver2.ogg'
any_surgery_states_required = ALL_SURGERY_SKIN_STATES
+ allow_stumps = TRUE
/datum/surgery_operation/limb/mechanical_close/get_any_tool()
return "Any sharp item"
/datum/surgery_operation/limb/mechanical_close/get_default_radial_image()
- return image(/obj/item/screwdriver)
+ return image('icons/hud/surgery_radial.dmi', "screw_shell")
/datum/surgery_operation/limb/mechanical_close/tool_check(obj/item/tool)
// Require any sharpness OR a tool behavior match
@@ -137,9 +140,10 @@
success_sound = 'sound/items/taperecorder/taperecorder_close.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_ORGANS_CUT
+ allow_stumps = TRUE
/datum/surgery_operation/limb/prepare_electronics/get_default_radial_image()
- return image(/obj/item/multitool)
+ return image('icons/hud/surgery_radial.dmi', "prepare_electronics")
/datum/surgery_operation/limb/prepare_electronics/on_preop(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
display_results(
@@ -170,9 +174,10 @@
preop_sound = 'sound/items/tools/ratchet.ogg'
all_surgery_states_required = SURGERY_SKIN_OPEN
any_surgery_states_blocked = SURGERY_BONE_SAWED|SURGERY_BONE_DRILLED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/mechanic_unwrench/get_default_radial_image()
- return image(/obj/item/wrench)
+ return image('icons/hud/surgery_radial.dmi', "unwrench_endoskeleton")
/datum/surgery_operation/limb/mechanic_unwrench/on_preop(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
display_results(
@@ -198,10 +203,11 @@
TOOL_WRENCH = 1,
TOOL_RETRACTOR = 1.33,
)
- operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC
+ operation_flags = OPERATION_SELF_OPERABLE | OPERATION_MECHANIC | OPERATION_NO_PATIENT_REQUIRED
time = 2.4 SECONDS
preop_sound = 'sound/items/tools/ratchet.ogg'
- all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED | OPERATION_NO_PATIENT_REQUIRED
+ all_surgery_states_required = SURGERY_SKIN_OPEN|SURGERY_BONE_SAWED
+ allow_stumps = TRUE
/datum/surgery_operation/limb/mechanic_wrench/state_check(obj/item/bodypart/limb)
return LIMB_HAS_BONES(limb)
@@ -210,7 +216,7 @@
return ..() + list("the limb must have bones")
/datum/surgery_operation/limb/mechanic_wrench/get_default_radial_image()
- return image(/obj/item/wrench)
+ return image('icons/hud/surgery_radial.dmi', "wrench_endoskeleton")
/datum/surgery_operation/limb/mechanic_wrench/on_preop(obj/item/bodypart/limb, mob/living/surgeon, obj/item/tool, list/operation_args)
display_results(
diff --git a/code/modules/surgery/operations/operation_organ_manip.dm b/code/modules/surgery/operations/operation_organ_manip.dm
index fb2879b714d9..de04ddfd893e 100644
--- a/code/modules/surgery/operations/operation_organ_manip.dm
+++ b/code/modules/surgery/operations/operation_organ_manip.dm
@@ -106,7 +106,7 @@
var/datum/radial_menu_choice/option = LAZYACCESS(cached_organ_manipulation_options, "[organ.type]_remove")
if(!option)
option = new()
- option.image = get_generic_limb_radial_image(limb.body_zone)
+ option.image = image('icons/hud/surgery_radial.dmi', "base")
option.image.overlays += add_radial_overlays(organ.type)
option.name = "remove [initial(organ.name)]"
option.info = "Remove [initial(organ.name)] from the [limb.owner ? "patient" : "limb"]."
@@ -120,7 +120,7 @@
var/datum/radial_menu_choice/option = LAZYACCESS(cached_organ_manipulation_options, "[organ.type]_insert")
if(!option)
option = new()
- option.image = get_generic_limb_radial_image(limb.body_zone)
+ option.image = image('icons/hud/surgery_radial.dmi', "base")
option.image.overlays += add_radial_overlays(list(image('icons/hud/screen_gen.dmi', "arrow_large_still"), organ.type))
option.name = "insert [initial(organ.name)]"
option.info = "insert [initial(organ.name)] into the [limb.owner ? "patient" : "limb"]."
diff --git a/code/modules/surgery/organs/autosurgeon.dm b/code/modules/surgery/organs/autosurgeon.dm
index 388b25da1254..d75f7fd4ec9b 100644
--- a/code/modules/surgery/organs/autosurgeon.dm
+++ b/code/modules/surgery/organs/autosurgeon.dm
@@ -122,11 +122,11 @@
add_fingerprint(user)
use_autosurgeon(target, user, 8 SECONDS)
-/obj/item/autosurgeon/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
- if(isorgan(attacking_item))
- load_organ(attacking_item, user)
- else
- return ..()
+/obj/item/autosurgeon/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(isorgan(tool))
+ load_organ(tool, user)
+ return ITEM_INTERACT_SUCCESS
+ return NONE
/obj/item/autosurgeon/screwdriver_act(mob/living/user, obj/item/screwtool)
if(..())
diff --git a/code/modules/surgery/surgery_tools.dm b/code/modules/surgery/surgery_tools.dm
index 253d24be5851..3ef6a2e2a2ec 100644
--- a/code/modules/surgery/surgery_tools.dm
+++ b/code/modules/surgery/surgery_tools.dm
@@ -591,7 +591,7 @@
/obj/item/shears/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] is pinching [user.p_them()]self with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
var/timer = 1 SECONDS
- for(var/obj/item/bodypart/thing in user.bodyparts)
+ for(var/obj/item/bodypart/thing in user.get_bodyparts())
if(thing.body_part == CHEST)
continue
addtimer(CALLBACK(thing, TYPE_PROC_REF(/obj/item/bodypart/, dismember)), timer)
diff --git a/code/modules/transport/tram/tram_controller.dm b/code/modules/transport/tram/tram_controller.dm
index 8cbcfeddd3ad..4194493f27d9 100644
--- a/code/modules/transport/tram/tram_controller.dm
+++ b/code/modules/transport/tram/tram_controller.dm
@@ -853,17 +853,14 @@
. += span_notice("The cabinet can be opened with a [EXAMINE_HINT("Left-click.")]")
-/obj/machinery/transport/tram_controller/attackby(obj/item/weapon, mob/living/user, list/modifiers, list/attack_modifiers)
- if(user.combat_mode || cover_open)
- return ..()
-
- if(has_cover)
- var/obj/item/card/id/id_card = user.get_id_in_hand()
- if(!isnull(id_card))
- try_toggle_lock(user, id_card)
- return
-
- return ..()
+/obj/machinery/transport/tram_controller/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(user.combat_mode || !has_cover)
+ return NONE
+ if(!istype(tool, /obj/item/card/id))
+ return NONE
+ if(cover_open)
+ return ITEM_INTERACT_BLOCKING
+ return try_toggle_lock(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
/obj/machinery/transport/tram_controller/attack_hand(mob/living/user, params)
. = ..()
diff --git a/code/modules/transport/tram/tram_floors.dm b/code/modules/transport/tram/tram_floors.dm
index dd091e4e3ae5..0d3f352acde4 100644
--- a/code/modules/transport/tram/tram_floors.dm
+++ b/code/modules/transport/tram/tram_floors.dm
@@ -22,12 +22,14 @@
. += ..()
. += span_notice("The reinforcement bolts are [EXAMINE_HINT("wrenched")] firmly in place. Use a [EXAMINE_HINT("wrench")] to remove the plate.")
-/turf/open/floor/tram/attackby(obj/item/object, mob/living/user, list/modifiers)
- . = ..()
- if(istype(object, /obj/item/stack/thermoplastic))
- build_with_transport_tiles(object, user)
- else if(istype(object, /obj/item/stack/sheet/mineral/titanium))
- build_with_titanium(object, user)
+/turf/open/floor/tram/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(istype(tool, /obj/item/stack/thermoplastic))
+ build_with_transport_tiles(tool, user)
+ return ITEM_INTERACT_SUCCESS
+ if(istype(tool, /obj/item/stack/sheet/mineral/titanium))
+ build_with_titanium(tool, user)
+ return ITEM_INTERACT_SUCCESS
+ return NONE
/turf/open/floor/tram/make_plating(force = FALSE)
if(force)
@@ -126,14 +128,17 @@
/turf/open/floor/tram/plate/energized/burnt_states()
return list("energized_plate_damaged")
-/turf/open/floor/tram/plate/energized/attackby(obj/item/attacking_item, mob/living/user, list/modifiers)
- if((broken || burnt) && istype(attacking_item, /obj/item/stack/sheet/mineral/titanium))
- if(attacking_item.use(1))
- broken = FALSE
- update_appearance()
- balloon_alert(user, "plate replaced")
- return
- return ..()
+/turf/open/floor/tram/plate/energized/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!broken && !burnt)
+ return NONE
+ if(!istype(tool, /obj/item/stack/sheet/mineral/titanium))
+ return NONE
+ if(!tool.use(1))
+ return ITEM_INTERACT_BLOCKING
+ broken = FALSE
+ update_appearance()
+ balloon_alert(user, "plate replaced")
+ return ITEM_INTERACT_SUCCESS
/turf/open/floor/tram/plate/energized/broken
broken = TRUE
@@ -149,12 +154,14 @@
clawfootstep = FOOTSTEP_HARD_CLAW
heavyfootstep = FOOTSTEP_GENERIC_HEAVY
-/turf/open/indestructible/tram/attackby(obj/item/object, mob/living/user, list/modifiers)
- . = ..()
- if(istype(object, /obj/item/stack/thermoplastic))
- build_with_transport_tiles(object, user)
- else if(istype(object, /obj/item/stack/sheet/mineral/titanium))
- build_with_titanium(object, user)
+/turf/open/indestructible/tram/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(istype(tool, /obj/item/stack/thermoplastic))
+ build_with_transport_tiles(tool, user)
+ return ITEM_INTERACT_SUCCESS
+ if(istype(tool, /obj/item/stack/sheet/mineral/titanium))
+ build_with_titanium(tool, user)
+ return ITEM_INTERACT_SUCCESS
+ return NONE
/turf/open/indestructible/tram/plate
name = "linear induction plate"
diff --git a/code/modules/transport/tram/tram_signals.dm b/code/modules/transport/tram/tram_signals.dm
index e67a7a25c545..701d20b740ee 100644
--- a/code/modules/transport/tram/tram_signals.dm
+++ b/code/modules/transport/tram/tram_signals.dm
@@ -117,15 +117,15 @@
SStransport.crossing_signals -= src
. = ..()
-/obj/machinery/transport/crossing_signal/attackby(obj/item/weapon, mob/living/user, list/modifiers, list/attack_modifiers)
- if(!user.combat_mode)
- if(default_deconstruction_screwdriver(user, icon_state, icon_state, weapon))
- return
-
- if(default_deconstruction_crowbar(weapon))
- return
+/obj/machinery/transport/crossing_signal/screwdriver_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
+ return ITEM_INTERACT_SUCCESS
+ return NONE
- return ..()
+/obj/machinery/transport/crossing_signal/crowbar_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_crowbar(tool))
+ return ITEM_INTERACT_SUCCESS
+ return NONE
/obj/machinery/transport/crossing_signal/add_context(atom/source, list/context, obj/item/held_item, mob/user)
. = ..()
@@ -516,15 +516,15 @@
. += span_notice("The red [EXAMINE_HINT("local fault")] light is on.")
. += span_notice("The status display reads: Repair required.")
-/obj/machinery/transport/guideway_sensor/attackby(obj/item/weapon, mob/living/user, list/modifiers, list/attack_modifiers)
- if (!user.combat_mode)
- if(default_deconstruction_screwdriver(user, icon_state, icon_state, weapon))
- return
-
- if(default_deconstruction_crowbar(weapon))
- return
+/obj/machinery/transport/guideway_sensor/screwdriver_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
+ return ITEM_INTERACT_SUCCESS
+ return NONE
- return ..()
+/obj/machinery/transport/guideway_sensor/crowbar_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_crowbar(tool))
+ return ITEM_INTERACT_SUCCESS
+ return NONE
/obj/machinery/transport/guideway_sensor/proc/pair_sensor()
set_machine_stat(machine_stat | MAINT)
diff --git a/code/modules/transport/tram/tram_structures.dm b/code/modules/transport/tram/tram_structures.dm
index 70b2052b790a..70dcf92d7487 100644
--- a/code/modules/transport/tram/tram_structures.dm
+++ b/code/modules/transport/tram/tram_structures.dm
@@ -388,15 +388,17 @@
canSmoothWith = SMOOTH_GROUP_WOOD_WALLS
custom_materials = list(/datum/material/wood = SHEET_MATERIAL_AMOUNT*2)
-/obj/structure/tram/alt/wood/attackby(obj/item/W, mob/user)
- if(W.get_sharpness() && W.force)
- var/duration = ((4.8 SECONDS) / W.force) * 2 //In seconds, for now.
- if(istype(W, /obj/item/hatchet) || istype(W, /obj/item/fireaxe))
- duration /= 4 //Much better with hatchets and axes.
- if(do_after(user, duration * (1 SECONDS), target=src)) //Into deciseconds.
- deconstruct(disassembled = FALSE)
- return
- return ..()
+/obj/structure/tram/alt/wood/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!tool.get_sharpness() || !tool.force)
+ return NONE
+ var/duration = ((4.8 SECONDS) / tool.force) * 2 //In seconds, for now.
+ if(istype(tool, /obj/item/hatchet) || istype(tool, /obj/item/fireaxe))
+ duration /= 4 //Much better with hatchets and axes.
+ to_chat(user, span_notice("You begin breaking down [src]."))
+ if(!do_after(user, duration * (1 SECONDS), target=src)) //Into deciseconds.
+ return ITEM_INTERACT_BLOCKING
+ deconstruct(disassembled = FALSE)
+ return ITEM_INTERACT_SUCCESS
/obj/structure/tram/alt/bamboo
name = "bamboo tram"
diff --git a/code/modules/unit_tests/README.md b/code/modules/unit_tests/README.md
index 1f8bc271b369..d9f3140028f6 100644
--- a/code/modules/unit_tests/README.md
+++ b/code/modules/unit_tests/README.md
@@ -76,6 +76,8 @@ You can find more information about all of these from their respective doc comme
`TEST_FOCUS(test_path)` - _Only_ run the test provided within the parameters. Useful for reducing noise. For example, if we only want to run our example square test, we can add `TEST_FOCUS(/datum/unit_test/square)`. Should _never_ be pushed in a pull request--you will be laughed at.
+`TEST_REPEAT(test_path, times_to_run)` - Run the provided test `times_to_run` times in a row. Useful for debugging flaky tests that don't always fail. Should _also_ never be pushed in a pull request.
+
## Final Notes
- Writing tests before you attempt to fix the bug can actually speed up development a lot! It means you don't have to go in game and folllow the same exact steps manually every time. This process is known as "TDD" (test driven development). Write the test first, make sure it fails, _then_ start work on the fix/feature, and you'll know you're done when your tests pass. If you do try this, do make sure to confirm in a non-testing environment just to double check.
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index 62d60941be69..abc4c37e8bf2 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -41,6 +41,10 @@
/// Intended to be used in the manner of `TEST_FOCUS(/datum/unit_test/math)`
#define TEST_FOCUS(test_path) ##test_path { focus = TRUE; }
+/// Run the test provided within the parentheses run_count times
+/// Useful for debugging flaky tests that only fail sometimes
+#define TEST_REPEAT(test_path, run_count) ##test_path { times_to_run = ##run_count; }
+
/// Logs a noticable message on GitHub, but will not mark as an error.
/// Use this when something shouldn't happen and is of note, but shouldn't block CI.
/// Does not mark the test as failed.
@@ -263,6 +267,7 @@
#include "preference_species.dm"
#include "preferences.dm"
#include "projectiles.dm"
+#include "punpun.dm"
#include "quirks.dm"
#include "range_return.dm"
#include "rcd.dm"
diff --git a/code/modules/unit_tests/limbsanity.dm b/code/modules/unit_tests/limbsanity.dm
index 6efec37ab76d..13f6a4d7d351 100644
--- a/code/modules/unit_tests/limbsanity.dm
+++ b/code/modules/unit_tests/limbsanity.dm
@@ -1,8 +1,11 @@
/datum/unit_test/limbsanity
/datum/unit_test/limbsanity/Run()
- for(var/path in subtypesof(/obj/item/bodypart) - list(/obj/item/bodypart/arm, /obj/item/bodypart/leg)) /// removes the abstract items.
+ for(var/path in valid_subtypesof(/obj/item/bodypart)) /// removes the abstract items.
var/obj/item/bodypart/part = path
+ if(part::bodypart_flags & BODYPART_STUMP)
+ continue // stumps don't need to have icons
+
if(part::is_dimorphic)
if(!icon_exists(UNLINT(part::should_draw_greyscale ? part::icon_greyscale : part::icon_static), "[part::limb_id]_[part::body_zone]_m"))
TEST_FAIL("[path] does not have a valid icon for male variants")
diff --git a/code/modules/unit_tests/organ_bodypart_shuffle.dm b/code/modules/unit_tests/organ_bodypart_shuffle.dm
index e05c6bd0bd94..ffb2210756a1 100644
--- a/code/modules/unit_tests/organ_bodypart_shuffle.dm
+++ b/code/modules/unit_tests/organ_bodypart_shuffle.dm
@@ -20,7 +20,7 @@
TEST_ASSERT_NULL(organ.bodypart_owner, "Organ '[organ.name] kept reference to bodypart after forceMove into nullspace.")
// 3. replace all bodyparts with new ones and place the previously removed organs into the new bodyparts
- for(var/obj/item/bodypart/bodypart as anything in hollow_boy.bodyparts)
+ for(var/obj/item/bodypart/bodypart as anything in hollow_boy.get_bodyparts())
var/obj/item/bodypart/replacement = allocate(bodypart.type)
for(var/obj/item/organ/organ as anything in removed_organs)
if(replacement.body_zone != deprecise_zone(organ.zone))
@@ -35,3 +35,22 @@
TEST_ASSERT(organ in hollow_boy.organs, "Organ '[organ.name] was put in an empty bodypart that replaced a humans, but the organ did not come with.")
TEST_ASSERT(organ.owner == hollow_boy, "Organ '[organ.name]'s owner was not properly updated to the new human after being placed in a replacement bodypart.")
TEST_ASSERT(organ.bodypart_owner in hollow_boy.bodyparts, "Organ '[organ.name]'s bodypart_owner was not properly updated to the new bodypart after being placed in a replacement bodypart.")
+
+/// Tests that gibbing results in a head with a brain
+/datum/unit_test/gibbing_organ_transfer
+
+/datum/unit_test/gibbing_organ_transfer/Run()
+ var/mob/living/carbon/human/dummy = allocate(/mob/living/carbon/human/consistent)
+ var/list/bodyparts_for_cleanup = dummy.bodyparts.Copy()
+ var/list/organs_for_cleanup = dummy.organs.Copy()
+ var/obj/item/organ/brain/original_brain = dummy.get_organ_slot(ORGAN_SLOT_BRAIN)
+ var/obj/item/bodypart/head/original_head = dummy.get_bodypart(BODY_ZONE_HEAD)
+ dummy.gib(ALL)
+
+ TEST_ASSERT(QDELETED(dummy), "Dummy was not deleted after gibbing.")
+ TEST_ASSERT(!QDELETED(original_head), "Original head was deleted after gibbing.")
+ TEST_ASSERT(!QDELETED(original_brain), "Original brain was deleted after gibbing.")
+ TEST_ASSERT(original_brain.loc == original_head, "Original brain was not transferred to the head after gibbing.")
+
+ QDEL_LIST(organs_for_cleanup)
+ QDEL_LIST(bodyparts_for_cleanup)
diff --git a/code/modules/unit_tests/punpun.dm b/code/modules/unit_tests/punpun.dm
new file mode 100644
index 000000000000..d83e23c4eed4
--- /dev/null
+++ b/code/modules/unit_tests/punpun.dm
@@ -0,0 +1,5 @@
+/datum/unit_test/punpun_name
+
+/datum/unit_test/punpun_name/Run()
+ var/mob/living/carbon/human/species/monkey/punpun/punpun = EASY_ALLOCATE()
+ TEST_ASSERT_EQUAL(punpun.name, /mob/living/carbon/human/species/monkey/punpun::name, "Pun Pun did not have [punpun.p_their()] name set")
diff --git a/code/modules/unit_tests/spawn_humans.dm b/code/modules/unit_tests/spawn_humans.dm
index f89f59a4cbfe..a0691b16c875 100644
--- a/code/modules/unit_tests/spawn_humans.dm
+++ b/code/modules/unit_tests/spawn_humans.dm
@@ -14,3 +14,23 @@
TEST_ASSERT(!HAS_TRAIT_FROM(dummy, TRAIT_AGEUSIA, NO_TONGUE_TRAIT), "Dummy has ageusia on init, when it should've been removed by its default tongue.")
TEST_ASSERT(!dummy.is_blind_from(NO_EYES), "Dummy is blind on init, when it should've been removed by its default eyes.")
TEST_ASSERT(!HAS_TRAIT_FROM(dummy, TRAIT_DEAF, NO_EARS), "Dummy is deaf on init, when it should've been removed by its default ears.")
+
+/// Tests that we can change a mob's hand count without everything breaking
+/datum/unit_test/many_armed_humans
+
+/datum/unit_test/many_armed_humans/Run()
+ var/mob/living/carbon/human/consistent/dummy = allocate(/mob/living/carbon/human/consistent)
+ dummy.change_number_of_hands(4)
+
+/// Tests spawned humans have the correct bodypart order
+/datum/unit_test/human_bodypart_order
+
+/datum/unit_test/human_bodypart_order/Run()
+ var/mob/living/carbon/human/consistent/dummy = allocate(/mob/living/carbon/human/consistent)
+ var/list/obj/item/bodypart/bodyparts = dummy.get_bodyparts()
+ TEST_ASSERT(bodyparts[1].body_zone == BODY_ZONE_CHEST, "First bodypart in bodyparts list is not the chest, this is important for human rendering")
+ TEST_ASSERT(bodyparts[2].body_zone == BODY_ZONE_HEAD, "Second bodypart in bodyparts list is not the head, this is important for human rendering")
+
+ var/list/obj/item/bodypart/bodyparts_by_zone = dummy.get_bodyparts_by_zones()
+ TEST_ASSERT(bodyparts_by_zone[1] == BODY_ZONE_CHEST, "First bodypart in bodyparts_by_zone list is not the chest, this is important for human rendering")
+ TEST_ASSERT(bodyparts_by_zone[2] == BODY_ZONE_HEAD, "Second bodypart in bodyparts_by_zone list is not the head, this is important for human rendering")
diff --git a/code/modules/unit_tests/subsystem_init.dm b/code/modules/unit_tests/subsystem_init.dm
index 82c9fc956fba..f5168079a907 100644
--- a/code/modules/unit_tests/subsystem_init.dm
+++ b/code/modules/unit_tests/subsystem_init.dm
@@ -3,12 +3,12 @@
/datum/unit_test/subsystem_init/Run()
for(var/datum/controller/subsystem/subsystem as anything in Master.subsystems)
- if(subsystem.flags & SS_NO_INIT)
+ if(subsystem.ss_flags & SS_NO_INIT)
continue
if(subsystem.initialized)
continue
- var/should_fail = !(subsystem.flags & SS_OK_TO_FAIL_INIT)
+ var/should_fail = !(subsystem.ss_flags & SS_OK_TO_FAIL_INIT)
var/list/message_strings = list("[subsystem] ([subsystem.type]) is a subsystem meant to initialize but could not get initialized.")
if(!isnull(subsystem.initialization_failure_message))
diff --git a/code/modules/unit_tests/surgeries.dm b/code/modules/unit_tests/surgeries.dm
index d75afcde1066..c04dcec1012b 100644
--- a/code/modules/unit_tests/surgeries.dm
+++ b/code/modules/unit_tests/surgeries.dm
@@ -57,9 +57,9 @@
TEST_ASSERT_EQUAL(bobs_head.real_name, "Bob", "Bob's head does not remember that it is from Bob")
// Put Bob's head onto Alice's body
- var/datum/surgery_operation/prosthetic_replacement/surgery = GLOB.operations.operations_by_typepath[__IMPLIED_TYPE__]
+ var/datum/surgery_operation/limb/prosthetic_replacement/surgery = GLOB.operations.operations_by_typepath[__IMPLIED_TYPE__]
user.put_in_active_hand(bobs_head)
- UNLINT(surgery.success(alice.get_bodypart(BODY_ZONE_CHEST), user, bobs_head, list()))
+ UNLINT(surgery.success(alice.get_bodypart(BODY_ZONE_HEAD, TRUE), user, bobs_head, list()))
TEST_ASSERT(!isnull(alice.get_bodypart(BODY_ZONE_HEAD)), "Alice has no head after prosthetic replacement")
TEST_ASSERT_EQUAL(alice.real_name, "Bob", "Bob's head was transplanted onto Alice's body, but their name is not Bob") // DARKPACK EDIT, ORIGINAL: TEST_ASSERT_EQUAL(alice.get_visible_name(), "Bob", "Bob's head was transplanted onto Alice's body, but their name is not Bob")
diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm
index 430bfc8e9b1d..74f14ecea49c 100644
--- a/code/modules/unit_tests/unit_test.dm
+++ b/code/modules/unit_tests/unit_test.dm
@@ -51,6 +51,7 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests())
var/succeeded = TRUE
var/list/allocated
var/list/fail_reasons
+ var/times_to_run = 1
/// List of atoms that we don't want to ever initialize in an agnostic context, like for Create and Destroy. Stored on the base datum for usability in other relevant tests that need this data.
var/static/list/uncreatables = null
@@ -377,9 +378,11 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests())
//Hell code, we're bound to end the round somehow so let's stop if from ending while we work
SSticker.delay_end = TRUE
- for(var/unit_path in tests_to_run)
- CHECK_TICK //We check tick first because the unit test we run last may be so expensive that checking tick will lock up this loop forever
- RunUnitTest(unit_path, test_results)
+ for(var/datum/unit_test/unit_path as anything in tests_to_run)
+ var/loop_count = unit_path::times_to_run
+ for(var/i in 1 to loop_count)
+ CHECK_TICK //We check tick first because the unit test we run last may be so expensive that checking tick will lock up this loop forever
+ RunUnitTest(unit_path, test_results)
SSticker.delay_end = FALSE
log_world("::group::Expensive Unit Test Times")
diff --git a/code/modules/uplink/uplink_items/nukeops.dm b/code/modules/uplink/uplink_items/nukeops.dm
index 070019efff13..bf7c5ffa14fd 100644
--- a/code/modules/uplink/uplink_items/nukeops.dm
+++ b/code/modules/uplink/uplink_items/nukeops.dm
@@ -527,7 +527,7 @@
// ~~ Pump-Action Grenade Launcher Kit ~~
-/datum/uplink_item/weapon_kits/moderate_cost/china_lake
+/datum/uplink_item/weapon_kits/medium_cost/china_lake
name = "Pump-Action Grenade Launcher Kit (Moderate)"
desc = "A weapon case containing a 40mm pump-action grenade launcher and a bandolier of 40mm grenades. Less versatile than the pneumatic grenade launcher kit, but no less dangerous. \
Though try to be careful about danger close. If you even care. A favourite of the Tiger Cooperative."
diff --git a/code/modules/vehicles/cars/clowncar.dm b/code/modules/vehicles/cars/clowncar.dm
index 0672586220c9..6cff8a743f6c 100644
--- a/code/modules/vehicles/cars/clowncar.dm
+++ b/code/modules/vehicles/cars/clowncar.dm
@@ -143,14 +143,16 @@
for(var/mob/living/carbon/carbon_occupant in occupants)
if(prob(35)) //Note: The randomstep on dump_mobs throws occupants into each other and often causes wounds regardless.
continue
- for(var/obj/item/bodypart/head/head_to_wound as anything in carbon_occupant.bodyparts)
- var/pick_mode = text2num(pick(list(
- "[WOUND_PICK_LOWEST_SEVERITY]",
- "[WOUND_PICK_HIGHEST_SEVERITY]"
- )))
- carbon_occupant.cause_wound_of_type_and_severity(WOUND_BLUNT, head_to_wound, WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_SEVERE, pick_mode)
- carbon_occupant.playsound_local(src, 'sound/items/weapons/flash_ring.ogg', 50)
- carbon_occupant.set_eye_blur_if_lower(rand(10 SECONDS, 20 SECONDS))
+ var/obj/item/bodypart/head/head_to_wound = carbon_occupant.get_bodypart(BODY_ZONE_HEAD)
+ if(isnull(head_to_wound))
+ return
+ var/pick_mode = text2num(pick(list(
+ "[WOUND_PICK_LOWEST_SEVERITY]",
+ "[WOUND_PICK_HIGHEST_SEVERITY]"
+ )))
+ carbon_occupant.cause_wound_of_type_and_severity(WOUND_BLUNT, head_to_wound, WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_SEVERE, pick_mode)
+ carbon_occupant.playsound_local(src, 'sound/items/weapons/flash_ring.ogg', 50)
+ carbon_occupant.set_eye_blur_if_lower(rand(10 SECONDS, 20 SECONDS))
hittarget_living.add_splatter_floor(small_drip = FALSE)
hittarget_living.adjust_brute_loss(200)
diff --git a/code/modules/vehicles/cars/vim.dm b/code/modules/vehicles/cars/vim.dm
index 9bfa5c323ab7..a3b8b85adffa 100644
--- a/code/modules/vehicles/cars/vim.dm
+++ b/code/modules/vehicles/cars/vim.dm
@@ -129,7 +129,7 @@
var/datum/port/output/are_headlights_on
/obj/item/circuit_component/vim/populate_ports()
- are_headlights_on = add_output_port("Are Headlights On", PORT_TYPE_NUMBER)
+ are_headlights_on = add_output_port("Are Headlights On", PORT_TYPE_BOOLEAN)
chime = add_output_port("On Chime Used", PORT_TYPE_SIGNAL)
buzz = add_output_port("On Buzz Used", PORT_TYPE_SIGNAL)
diff --git a/code/modules/vehicles/mecha/equipment/tools/other_tools.dm b/code/modules/vehicles/mecha/equipment/tools/other_tools.dm
index 4124e397ba7e..6b4e4e77ffef 100644
--- a/code/modules/vehicles/mecha/equipment/tools/other_tools.dm
+++ b/code/modules/vehicles/mecha/equipment/tools/other_tools.dm
@@ -127,7 +127,7 @@
/obj/item/mecha_parts/mecha_equipment/gravcatapult/proc/do_scatter(atom/movable/scatter, atom/movable/target)
var/dist = 5 - get_dist(scatter, target)
var/delay = 2
- GLOB.move_manager.move_away(scatter, target, delay = delay, timeout = delay * dist, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
+ GLOB.move_manager.move_away(scatter, target, delay = delay, timeout = delay * dist, flags = MOVEMENT_LOOP_START_INSTANT, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
/obj/item/mecha_parts/mecha_equipment/gravcatapult/get_snowflake_data()
return list(
@@ -339,11 +339,11 @@
log_message("Deactivated.", LOG_MECHA)
return TRUE
-/obj/item/mecha_parts/mecha_equipment/generator/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers)
- . = ..()
- if(!istype(weapon, fuel))
- return FALSE
- load_fuel(weapon, user)
+/obj/item/mecha_parts/mecha_equipment/generator/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(istype(tool, fuel))
+ load_fuel(tool, user)
+ return ITEM_INTERACT_SUCCESS
+ return NONE
/obj/item/mecha_parts/mecha_equipment/generator/process(seconds_per_tick)
if(!chassis)
diff --git a/code/modules/vehicles/mecha/mech_bay.dm b/code/modules/vehicles/mecha/mech_bay.dm
index 922b2af73666..8c6b61546ef7 100644
--- a/code/modules/vehicles/mecha/mech_bay.dm
+++ b/code/modules/vehicles/mecha/mech_bay.dm
@@ -76,17 +76,21 @@
recharge_console.update_appearance()
-/obj/machinery/mech_bay_recharge_port/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
- if(default_deconstruction_screwdriver(user, "recharge_port-o", "recharge_port", I))
- return
+/obj/machinery/mech_bay_recharge_port/screwdriver_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_screwdriver(user, "recharge_port-o", "recharge_port", tool))
+ return ITEM_INTERACT_SUCCESS
+ return NONE
- if(default_change_direction_wrench(user, I))
+/obj/machinery/mech_bay_recharge_port/wrench_act(mob/living/user, obj/item/tool)
+ if(default_change_direction_wrench(user, tool))
recharging_turf = get_step(loc, dir)
- return
+ return ITEM_INTERACT_SUCCESS
+ return NONE
- if(default_deconstruction_crowbar(I))
- return
- return ..()
+/obj/machinery/mech_bay_recharge_port/crowbar_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_crowbar(tool))
+ return ITEM_INTERACT_SUCCESS
+ return NONE
/obj/machinery/computer/mech_bay_power_console
name = "mech bay power control console"
diff --git a/code/modules/vehicles/mecha/mecha_construction_paths.dm b/code/modules/vehicles/mecha/mecha_construction_paths.dm
index 631d61c7ba6d..319556742f76 100644
--- a/code/modules/vehicles/mecha/mecha_construction_paths.dm
+++ b/code/modules/vehicles/mecha/mecha_construction_paths.dm
@@ -615,7 +615,7 @@
/datum/component/construction/mecha/honker/custom_action(obj/item/I, mob/living/user, diff)
if(istype(I, /obj/item/bikehorn))
playsound(parent, 'sound/items/bikehorn.ogg', 50, TRUE)
- user.balloon_alert_to_viewers("HONK!")
+ user.balloon_alert_to_hearers("*HONK*")
return TRUE
return ..()
diff --git a/code/modules/vehicles/motorized_wheelchair.dm b/code/modules/vehicles/motorized_wheelchair.dm
index 3e85b2263cac..771b9ab55ab0 100644
--- a/code/modules/vehicles/motorized_wheelchair.dm
+++ b/code/modules/vehicles/motorized_wheelchair.dm
@@ -98,44 +98,57 @@
user.put_in_hands(power_cell)
power_cell = null
-/obj/vehicle/ridden/wheelchair/motorized/attackby(obj/item/attacking_item, mob/user, list/modifiers, list/attack_modifiers)
- if(!panel_open)
- return ..()
+/obj/vehicle/ridden/wheelchair/motorized/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ . = ..()
+ if(.)
+ return
+
+ if(!panel_open || user.combat_mode)
+ return NONE
- if(istype(attacking_item, /obj/item/stock_parts/power_store/cell))
+ if(istype(tool, /obj/item/stock_parts/power_store/cell))
if(power_cell)
to_chat(user, span_warning("There is a power cell already installed."))
- else
- attacking_item.forceMove(src)
- power_cell = attacking_item
- to_chat(user, span_notice("You install the [attacking_item]."))
+ return ITEM_INTERACT_BLOCKING
+
+ tool.forceMove(src)
+ power_cell = tool
+ to_chat(user, span_notice("You install the [tool]."))
refresh_parts()
- return
- if(!istype(attacking_item, /obj/item/stock_parts))
- return ..()
+ return ITEM_INTERACT_SUCCESS
- var/datum/stock_part/newstockpart = GLOB.stock_part_datums_per_object[attacking_item.type]
+ if(!istype(tool, /obj/item/stock_parts))
+ return NONE
+
+ var/datum/stock_part/newstockpart = GLOB.stock_part_datums_per_object[tool.type]
if(isnull(newstockpart))
CRASH("No corresponding datum/stock_part for [newstockpart.type]")
+
+ var/replacement_occured = FALSE
for(var/datum/stock_part/oldstockpart in component_parts)
var/type_to_check
for(var/pathtype in required_parts)
if(ispath(oldstockpart.type, pathtype))
type_to_check = pathtype
break
- if(istype(newstockpart, type_to_check) && istype(oldstockpart, type_to_check))
- if(newstockpart.tier > oldstockpart.tier)
- // delete the part in the users hand and add the datum part to the component_list
- qdel(attacking_item)
- component_parts += newstockpart
- // create an new instance of the old datum stock part physical type & put it in the users hand
- var/obj/item/stock_parts/part = new oldstockpart.physical_object_type
- user.put_in_hands(part)
- component_parts -= oldstockpart
- // user message
- user.visible_message(span_notice("[user] replaces [oldstockpart.name()] with [newstockpart.name()] in [src]."), span_notice("You replace [oldstockpart.name()] with [newstockpart.name()]."))
- break
+
+ if(!istype(newstockpart, type_to_check) || !istype(oldstockpart, type_to_check) || newstockpart.tier <= oldstockpart.tier)
+ continue
+
+ // delete the part in the users hand and add the datum part to the component_list
+ qdel(tool)
+ component_parts += newstockpart
+ // create an new instance of the old datum stock part physical type & put it in the users hand
+ var/obj/item/stock_parts/part = new oldstockpart.physical_object_type
+ user.put_in_hands(part)
+ component_parts -= oldstockpart
+ // user message
+ user.visible_message(span_notice("[user] replaces [oldstockpart.name()] with [newstockpart.name()] in [src]."), span_notice("You replace [oldstockpart.name()] with [newstockpart.name()]."))
+ replacement_occured = TRUE
+ break
+
refresh_parts()
+ return replacement_occured ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
/obj/vehicle/ridden/wheelchair/motorized/handle_deconstruct(disassembled)
. = ..()
diff --git a/code/modules/vehicles/pimpin_ride.dm b/code/modules/vehicles/pimpin_ride.dm
index 88582410dcde..e389403a13dc 100644
--- a/code/modules/vehicles/pimpin_ride.dm
+++ b/code/modules/vehicles/pimpin_ride.dm
@@ -34,39 +34,52 @@
if (installed_upgrade)
. += "It has been upgraded with [installed_upgrade], which can be removed with a screwdriver."
-/obj/vehicle/ridden/janicart/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
- if(istype(I, /obj/item/storage/bag/trash))
+/obj/vehicle/ridden/janicart/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ . = ..()
+ if(.)
+ return
+ if(istype(tool, /obj/item/storage/bag/trash))
if(trash_bag)
to_chat(user, span_warning("[src] already has a trashbag hooked!"))
- return
- if(!user.transferItemToLoc(I, src))
- return
+ return ITEM_INTERACT_BLOCKING
+ if(!user.transferItemToLoc(tool, src))
+ return ITEM_INTERACT_BLOCKING
+
to_chat(user, span_notice("You hook the trashbag onto [src]."))
- trash_bag = I
+ trash_bag = tool
RegisterSignal(trash_bag, COMSIG_QDELETING, PROC_REF(bag_deleted))
- SEND_SIGNAL(src, COMSIG_VACUUM_BAG_ATTACH, I)
+ SEND_SIGNAL(src, COMSIG_VACUUM_BAG_ATTACH, tool)
update_appearance()
- else if(istype(I, /obj/item/janicart_upgrade))
+ return ITEM_INTERACT_SUCCESS
+
+ if(istype(tool, /obj/item/janicart_upgrade))
if(installed_upgrade)
to_chat(user, span_warning("[src] already has an upgrade installed! Use a screwdriver to remove it."))
- return
- var/obj/item/janicart_upgrade/new_upgrade = I
+ return ITEM_INTERACT_BLOCKING
+ var/obj/item/janicart_upgrade/new_upgrade = tool
new_upgrade.forceMove(src)
new_upgrade.install(src)
installed_upgrade = new_upgrade
to_chat(user, span_notice("You upgrade [src] with [new_upgrade]."))
update_appearance()
- else if (istype(I, /obj/item/screwdriver) && installed_upgrade)
- installed_upgrade.uninstall(src)
- installed_upgrade.forceMove(get_turf(user))
- user.put_in_hands(installed_upgrade)
- to_chat(user, span_notice("You remove [installed_upgrade] from [src]"))
- installed_upgrade = null
- update_appearance()
- else if(trash_bag && (!is_key(I) || is_key(inserted_key))) // don't put a key in the trash when we need it
- trash_bag.atom_storage.attempt_insert(I, user)
- else
- return ..()
+ return ITEM_INTERACT_SUCCESS
+
+ if(trash_bag && (!is_key(tool) || is_key(inserted_key))) // don't put a key in the trash when we need it
+ trash_bag.atom_storage.attempt_insert(tool, user)
+ return ITEM_INTERACT_SUCCESS
+
+ return NONE
+
+/obj/vehicle/ridden/janicart/screwdriver_act(mob/living/user, obj/item/tool)
+ if (!installed_upgrade)
+ return ITEM_INTERACT_BLOCKING
+ installed_upgrade.uninstall(src)
+ installed_upgrade.forceMove(get_turf(user))
+ user.put_in_hands(installed_upgrade)
+ to_chat(user, span_notice("You remove [installed_upgrade] from [src]"))
+ installed_upgrade = null
+ update_appearance()
+ return ITEM_INTERACT_SUCCESS
/obj/vehicle/ridden/janicart/update_overlays()
. = ..()
diff --git a/code/modules/vehicles/ridden.dm b/code/modules/vehicles/ridden.dm
index 86f5703a0212..fc570c3b6dd2 100644
--- a/code/modules/vehicles/ridden.dm
+++ b/code/modules/vehicles/ridden.dm
@@ -29,16 +29,17 @@
add_occupant(M)
return ..()
-/obj/vehicle/ridden/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
- if(!key_type || is_key(inserted_key) || !is_key(I))
- return ..()
- if(!user.transferItemToLoc(I, src))
- to_chat(user, span_warning("[I] seems to be stuck to your hand!"))
- return
- to_chat(user, span_notice("You insert \the [I] into \the [src]."))
+/obj/vehicle/ridden/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!key_type || is_key(inserted_key) || !is_key(tool))
+ return NONE
+ if(!user.transferItemToLoc(tool, src))
+ to_chat(user, span_warning("[tool] seems to be stuck to your hand!"))
+ return ITEM_INTERACT_BLOCKING
+ to_chat(user, span_notice("You insert \the [tool] into \the [src]."))
if(inserted_key) //just in case there's an invalid key
inserted_key.forceMove(drop_location())
- inserted_key = I
+ inserted_key = tool
+ return ITEM_INTERACT_SUCCESS
/obj/vehicle/ridden/click_alt(mob/user)
if(!inserted_key)
diff --git a/code/modules/vehicles/scooter.dm b/code/modules/vehicles/scooter.dm
index 16d084cf5181..dbceeb0ed716 100644
--- a/code/modules/vehicles/scooter.dm
+++ b/code/modules/vehicles/scooter.dm
@@ -12,10 +12,10 @@
/obj/vehicle/ridden/scooter/proc/make_ridable()
AddElement(/datum/element/ridable, /datum/component/riding/vehicle/scooter)
-/obj/vehicle/ridden/scooter/wrench_act(mob/living/user, obj/item/I)
+/obj/vehicle/ridden/scooter/wrench_act(mob/living/user, obj/item/tool)
..()
to_chat(user, span_notice("You begin to remove the handlebars..."))
- if(!I.use_tool(src, user, 40, volume=50))
+ if(!tool.use_tool(src, user, 40, volume=50))
return TRUE
var/obj/vehicle/ridden/scooter/skateboard/improvised/skater = new(drop_location())
new /obj/item/stack/rods(drop_location(), 2)
@@ -248,37 +248,40 @@
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))
- return ..()
- if(!I.tool_start_check(user, amount=5))
- return
+/obj/item/scooter_frame/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!istype(tool, /obj/item/stack/sheet/iron))
+ return NONE
+ if(!tool.tool_start_check(user, amount=5))
+ return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You begin to add wheels to [src]."))
- if(!I.use_tool(src, user, 80, volume=50, amount=5))
- return
+ if(!tool.use_tool(src, user, 80, volume = 50, amount = 5))
+ return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You finish making wheels for [src]."))
new /obj/vehicle/ridden/scooter/skateboard/improvised(user.loc)
qdel(src)
+ return ITEM_INTERACT_SUCCESS
-/obj/item/scooter_frame/wrench_act(mob/living/user, obj/item/I)
- ..()
+/obj/item/scooter_frame/wrench_act(mob/living/user, obj/item/tool)
to_chat(user, span_notice("You deconstruct [src]."))
new /obj/item/stack/rods(drop_location(), 10)
- I.play_tool_sound(src)
+ tool.play_tool_sound(src)
qdel(src)
- return TRUE
+ return ITEM_INTERACT_SUCCESS
-/obj/vehicle/ridden/scooter/skateboard/wrench_act(mob/living/user, obj/item/I)
+/obj/vehicle/ridden/scooter/skateboard/wrench_act(mob/living/user, obj/item/tool)
return
-/obj/vehicle/ridden/scooter/skateboard/improvised/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
- if(!istype(I, /obj/item/stack/rods))
- return ..()
- if(!I.tool_start_check(user, amount=2))
+/obj/vehicle/ridden/scooter/skateboard/improvised/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ . = ..()
+ if (.)
return
+ if(!istype(tool, /obj/item/stack/rods))
+ return NONE
+ if(!tool.tool_start_check(user, amount=2))
+ return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You begin making handlebars for [src]."))
- if(!I.use_tool(src, user, 25, volume=50, amount=2))
- return
+ if(!tool.use_tool(src, user, 25, volume=50, amount=2))
+ return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You add the rods to [src], creating handlebars."))
var/obj/vehicle/ridden/scooter/skaterskoot = new(loc)
if(has_buckled_mobs())
@@ -286,14 +289,15 @@
unbuckle_mob(skaterboy)
skaterskoot.buckle_mob(skaterboy)
qdel(src)
+ return ITEM_INTERACT_SUCCESS
-/obj/vehicle/ridden/scooter/skateboard/improvised/screwdriver_act(mob/living/user, obj/item/I)
+/obj/vehicle/ridden/scooter/skateboard/improvised/screwdriver_act(mob/living/user, obj/item/tool)
. = ..()
if(.)
return
to_chat(user, span_notice("You begin to deconstruct and remove the wheels on [src]..."))
- if(!I.use_tool(src, user, 20, volume=50))
- return
+ if(!tool.use_tool(src, user, 20, volume=50))
+ return ITEM_INTERACT_BLOCKING
to_chat(user, span_notice("You deconstruct the wheels on [src]."))
new /obj/item/stack/sheet/iron(drop_location(), 5)
new /obj/item/scooter_frame(drop_location())
@@ -301,7 +305,7 @@
var/mob/living/carbon/skatergirl = buckled_mobs[1]
unbuckle_mob(skatergirl)
qdel(src)
- return TRUE
+ return ITEM_INTERACT_SUCCESS
//Wheelys
/obj/vehicle/ridden/scooter/skateboard/wheelys
diff --git a/code/modules/vehicles/sealed.dm b/code/modules/vehicles/sealed.dm
index 4b69d9907623..c11164432042 100644
--- a/code/modules/vehicles/sealed.dm
+++ b/code/modules/vehicles/sealed.dm
@@ -108,18 +108,20 @@
/obj/vehicle/sealed/proc/exit_location(M)
return drop_location()
-/obj/vehicle/sealed/attackby(obj/item/I, mob/user, list/modifiers, list/attack_modifiers)
- if(key_type && !is_key(inserted_key) && is_key(I))
- if(user.transferItemToLoc(I, src))
- to_chat(user, span_notice("You insert [I] into [src]."))
- if(inserted_key) //just in case there's an invalid key
- inserted_key.forceMove(drop_location())
- inserted_key = I
- inserted_key.forceMove(src)
- else
- to_chat(user, span_warning("[I] seems to be stuck to your hand!"))
- return
- return ..()
+/obj/vehicle/sealed/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(!key_type || is_key(inserted_key) || !is_key(tool))
+ return NONE
+
+ if(!user.transferItemToLoc(tool, src))
+ to_chat(user, span_warning("[tool] seems to be stuck to your hand!"))
+ return ITEM_INTERACT_BLOCKING
+
+ to_chat(user, span_notice("You insert [tool] into [src]."))
+ if(inserted_key) // Just in case there's an invalid key
+ inserted_key.forceMove(drop_location())
+ inserted_key = tool
+ inserted_key.forceMove(src)
+ return ITEM_INTERACT_SUCCESS
/obj/vehicle/sealed/proc/remove_key(mob/user)
if(!inserted_key)
diff --git a/code/modules/vehicles/secway.dm b/code/modules/vehicles/secway.dm
index 86e26b735d3c..30f9a9224aca 100644
--- a/code/modules/vehicles/secway.dm
+++ b/code/modules/vehicles/secway.dm
@@ -32,44 +32,54 @@
return
do_smoke(0, src, src)
-/obj/vehicle/ridden/secway/welder_act(mob/living/user, obj/item/W)
+/obj/vehicle/ridden/secway/welder_act(mob/living/user, obj/item/tool)
if(user.combat_mode)
- return
- . = TRUE
+ return NONE
+
if(DOING_INTERACTION(user, src))
balloon_alert(user, "you're already repairing it!")
- return
+ return ITEM_INTERACT_BLOCKING
+
if(atom_integrity >= max_integrity)
balloon_alert(user, "it's not damaged!")
- return
- if(!W.tool_start_check(user, amount=1, heat_required = HIGH_TEMPERATURE_REQUIRED))
- return
+ return ITEM_INTERACT_BLOCKING
+
+ if(!tool.tool_start_check(user, amount=1, heat_required = HIGH_TEMPERATURE_REQUIRED))
+ return ITEM_INTERACT_BLOCKING
+
user.balloon_alert_to_viewers("started welding [src]", "started repairing [src]")
audible_message(span_hear("You hear welding."))
var/did_the_thing
while(atom_integrity < max_integrity)
- if(W.use_tool(src, user, 2.5 SECONDS, volume=50))
+ if(tool.use_tool(src, user, 2.5 SECONDS, volume=50))
did_the_thing = TRUE
atom_integrity += min(10, (max_integrity - atom_integrity))
audible_message(span_hear("You hear welding."))
else
break
+
if(did_the_thing)
user.balloon_alert_to_viewers("[(atom_integrity >= max_integrity) ? "fully" : "partially"] repaired [src]")
- else
- user.balloon_alert_to_viewers("stopped welding [src]", "interrupted the repair!")
+ return ITEM_INTERACT_SUCCESS
-/obj/vehicle/ridden/secway/attackby(obj/item/W, mob/living/user, list/modifiers, list/attack_modifiers)
- if(!istype(W, /obj/item/food/grown/banana))
- return ..()
+ user.balloon_alert_to_viewers("stopped welding [src]", "interrupted the repair!")
+ return ITEM_INTERACT_BLOCKING
+
+/obj/vehicle/ridden/secway/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ . = ..()
+ if(.)
+ return
+ if(!istype(tool, /obj/item/food/grown/banana))
+ return NONE
// ignore the occupants because they're presumably too distracted to notice the guy stuffing fruit into their vehicle's exhaust. do segways have exhausts? they do now!
- user.visible_message(span_warning("[user] begins stuffing [W] into [src]'s tailpipe."), span_warning("You begin stuffing [W] into [src]'s tailpipe..."), ignored_mobs = occupants)
+ user.visible_message(span_warning("[user] begins stuffing [tool] into [src]'s tailpipe."), span_warning("You begin stuffing [tool] into [src]'s tailpipe..."), ignored_mobs = occupants)
if(!do_after(user, 3 SECONDS, src))
- return TRUE
- if(user.transferItemToLoc(W, src))
- user.visible_message(span_warning("[user] stuffs [W] into [src]'s tailpipe."), span_warning("You stuff [W] into [src]'s tailpipe."), ignored_mobs = occupants)
- eddie_murphy = W
- return TRUE
+ return ITEM_INTERACT_BLOCKING
+ if(!user.transferItemToLoc(tool, src))
+ return ITEM_INTERACT_BLOCKING
+ user.visible_message(span_warning("[user] stuffs [tool] into [src]'s tailpipe."), span_warning("You stuff [tool] into [src]'s tailpipe."), ignored_mobs = occupants)
+ eddie_murphy = tool
+ return ITEM_INTERACT_SUCCESS
/obj/vehicle/ridden/secway/attack_hand(mob/living/user, list/modifiers)
if(!eddie_murphy)
diff --git a/code/modules/vending/vendor/inventory.dm b/code/modules/vending/vendor/inventory.dm
index e97c251548fd..c5d3baa4eddc 100644
--- a/code/modules/vending/vendor/inventory.dm
+++ b/code/modules/vending/vendor/inventory.dm
@@ -243,6 +243,8 @@
on_dispense(vended_item, dispense_returned)
use_energy(active_power_usage)
+ SEND_SIGNAL(src, COMSIG_VENDING_DISPENSED, vended_item)
+
return vended_item
/**
diff --git a/code/modules/vending/vendor/tilting.dm b/code/modules/vending/vendor/tilting.dm
index 1d7d899c0ea7..971633c683b9 100644
--- a/code/modules/vending/vendor/tilting.dm
+++ b/code/modules/vending/vendor/tilting.dm
@@ -180,7 +180,7 @@
if (!iscarbon(atom_target))
return FALSE
var/mob/living/carbon/carbon_target = atom_target
- for(var/obj/item/bodypart/squish_part in carbon_target.bodyparts)
+ for(var/obj/item/bodypart/squish_part in carbon_target.get_bodyparts())
var/severity = pick(WOUND_SEVERITY_MODERATE, WOUND_SEVERITY_SEVERE, WOUND_SEVERITY_CRITICAL)
if (!carbon_target.cause_wound_of_type_and_severity(WOUND_BLUNT, squish_part, severity, wound_source = "crushed by [src]"))
squish_part.receive_damage(brute = 30)
diff --git a/code/modules/vending/vendor/ui_data.dm b/code/modules/vending/vendor/ui_data.dm
index 8693b3ae165c..1a88d66d3bc0 100644
--- a/code/modules/vending/vendor/ui_data.dm
+++ b/code/modules/vending/vendor/ui_data.dm
@@ -7,6 +7,11 @@
)
/obj/machinery/vending/ui_interact(mob/user, datum/tgui/ui)
+ if(SEND_SIGNAL(src, COMSIG_VENDING_UI_INTERACT, user, ui) & VENDING_DENIED)
+ if(icon_deny)
+ flick(icon_deny, src)
+ return
+
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Vending", name)
diff --git a/code/modules/wiremod/components/abstract/compare.dm b/code/modules/wiremod/components/abstract/compare.dm
index f17dd225c7a0..48b53dea1622 100644
--- a/code/modules/wiremod/components/abstract/compare.dm
+++ b/code/modules/wiremod/components/abstract/compare.dm
@@ -22,7 +22,7 @@
true = add_output_port("True", PORT_TYPE_SIGNAL)
false = add_output_port("False", PORT_TYPE_SIGNAL)
- result = add_output_port("Result", PORT_TYPE_NUMBER)
+ result = add_output_port("Result", PORT_TYPE_BOOLEAN)
/**
* Used by derivatives to load their own ports in for custom use.
diff --git a/code/modules/wiremod/components/action/light.dm b/code/modules/wiremod/components/action/light.dm
index 46fd2993a9da..5057927831e6 100644
--- a/code/modules/wiremod/components/action/light.dm
+++ b/code/modules/wiremod/components/action/light.dm
@@ -33,7 +33,7 @@
blue = add_input_port("Blue", PORT_TYPE_NUMBER)
brightness = add_input_port("Brightness", PORT_TYPE_NUMBER)
- on = add_input_port("On", PORT_TYPE_NUMBER)
+ on = add_input_port("On", PORT_TYPE_BOOLEAN)
/obj/item/circuit_component/light/register_shell(atom/movable/shell)
. = ..()
diff --git a/code/modules/wiremod/components/action/soundemitter.dm b/code/modules/wiremod/components/action/soundemitter.dm
index 144a56295dd9..f6c03d8b75b7 100644
--- a/code/modules/wiremod/components/action/soundemitter.dm
+++ b/code/modules/wiremod/components/action/soundemitter.dm
@@ -51,7 +51,7 @@
/obj/item/circuit_component/soundemitter/populate_ports()
volume = add_input_port("Volume", PORT_TYPE_NUMBER, default = 35)
frequency = add_input_port("Frequency", PORT_TYPE_NUMBER, default = 0)
- backwards = add_input_port("Play Backwards", PORT_TYPE_NUMBER, default = 0)
+ backwards = add_input_port("Play Backwards", PORT_TYPE_BOOLEAN, default = FALSE)
/obj/item/circuit_component/soundemitter/populate_options()
var/static/component_options = list(
diff --git a/code/modules/wiremod/components/action/speech.dm b/code/modules/wiremod/components/action/speech.dm
index f149cba9122b..49c2e75d08c7 100644
--- a/code/modules/wiremod/components/action/speech.dm
+++ b/code/modules/wiremod/components/action/speech.dm
@@ -23,7 +23,7 @@
/obj/item/circuit_component/speech/populate_ports()
message = add_input_port("Message", PORT_TYPE_STRING, trigger = null)
- quietmode = add_input_port("Quiet Mode", PORT_TYPE_NUMBER, default = 0)
+ quietmode = add_input_port("Quiet Mode", PORT_TYPE_BOOLEAN, default = FALSE)
/obj/item/circuit_component/speech/input_received(datum/port/input/port)
if(!parent.shell)
diff --git a/code/modules/wiremod/components/admin/animate.dm b/code/modules/wiremod/components/admin/animate.dm
index c3b26464b1b8..0271f7d1b275 100644
--- a/code/modules/wiremod/components/admin/animate.dm
+++ b/code/modules/wiremod/components/admin/animate.dm
@@ -33,7 +33,7 @@
/obj/item/circuit_component/begin_animation/populate_ports()
target = add_input_port("Target", PORT_TYPE_ATOM)
- parallel = add_input_port("Parallel", PORT_TYPE_NUMBER, default = 1)
+ parallel = add_input_port("Parallel", PORT_TYPE_BOOLEAN, default = TRUE)
animation_loops = add_input_port("Loops", PORT_TYPE_NUMBER)
stop_all_animations = add_input_port("Stop All Animations", PORT_TYPE_SIGNAL, trigger = PROC_REF(stop_animations))
animate_event = add_output_port("Perform Animation", PORT_TYPE_INSTANT_SIGNAL)
diff --git a/code/modules/wiremod/components/admin/signal_handler/signal_handler.dm b/code/modules/wiremod/components/admin/signal_handler/signal_handler.dm
index a80ba26794cb..430ede0683f9 100644
--- a/code/modules/wiremod/components/admin/signal_handler/signal_handler.dm
+++ b/code/modules/wiremod/components/admin/signal_handler/signal_handler.dm
@@ -63,7 +63,7 @@
signal_map = GLOB.integrated_circuit_signal_ids
/obj/item/circuit_component/signal_handler/populate_ports()
- instant = add_input_port("Instant", PORT_TYPE_NUMBER, order = 0.5, trigger = null, default = 1)
+ instant = add_input_port("Instant", PORT_TYPE_BOOLEAN, order = 0.5, trigger = null, default = TRUE)
register = add_input_port("Register", PORT_TYPE_SIGNAL, order = 2, trigger = PROC_REF(register_signals))
unregister = add_input_port("Unregister", PORT_TYPE_SIGNAL, order = 2, trigger = PROC_REF(unregister_signals))
unregister_all = add_input_port("Unregister All", PORT_TYPE_SIGNAL, order = 2, trigger = PROC_REF(unregister_signals_all))
diff --git a/code/modules/wiremod/components/atom/hear.dm b/code/modules/wiremod/components/atom/hear.dm
index 47140317e867..72d85ba22b0a 100644
--- a/code/modules/wiremod/components/atom/hear.dm
+++ b/code/modules/wiremod/components/atom/hear.dm
@@ -23,7 +23,7 @@
var/datum/port/output/trigger_port
/obj/item/circuit_component/hear/populate_ports()
- on = add_input_port("On", PORT_TYPE_NUMBER, default = 1)
+ on = add_input_port("On", PORT_TYPE_BOOLEAN, default = TRUE)
message_port = add_output_port("Message", PORT_TYPE_STRING)
language_port = add_output_port("Language", PORT_TYPE_STRING)
speaker_port = add_output_port("Speaker", PORT_TYPE_ATOM)
diff --git a/code/modules/wiremod/components/atom/remotecam.dm b/code/modules/wiremod/components/atom/remotecam.dm
index 3a1bbd3be471..12c7cc1d91c7 100644
--- a/code/modules/wiremod/components/atom/remotecam.dm
+++ b/code/modules/wiremod/components/atom/remotecam.dm
@@ -65,7 +65,7 @@
start = add_input_port("Start", PORT_TYPE_SIGNAL)
stop = add_input_port("Stop", PORT_TYPE_SIGNAL)
if(camera_range_settable)
- camera_range = add_input_port("Camera Range", PORT_TYPE_NUMBER, default = 0)
+ camera_range = add_input_port("Far Range", PORT_TYPE_BOOLEAN, default = FALSE)
network = add_input_port("Network", PORT_TYPE_STRING, default = "ss13")
if(camera_range_settable)
diff --git a/code/modules/wiremod/components/bci/install_detector.dm b/code/modules/wiremod/components/bci/install_detector.dm
index 39972134b842..390d0da281a7 100644
--- a/code/modules/wiremod/components/bci/install_detector.dm
+++ b/code/modules/wiremod/components/bci/install_detector.dm
@@ -18,7 +18,7 @@
/obj/item/circuit_component/install_detector/populate_ports()
. = ..()
- current_state = add_output_port("Current State", PORT_TYPE_NUMBER)
+ current_state = add_output_port("Current State", PORT_TYPE_BOOLEAN)
implanted = add_output_port("Implanted", PORT_TYPE_SIGNAL)
removed = add_output_port("Removed", PORT_TYPE_SIGNAL)
diff --git a/code/modules/wiremod/components/id/access_checker.dm b/code/modules/wiremod/components/id/access_checker.dm
index d694834c06aa..c95c054bc28a 100644
--- a/code/modules/wiremod/components/id/access_checker.dm
+++ b/code/modules/wiremod/components/id/access_checker.dm
@@ -24,7 +24,7 @@
/obj/item/circuit_component/compare/access/populate_custom_ports()
subject_accesses = add_input_port("Access To Check", PORT_TYPE_LIST(PORT_TYPE_STRING))
required_accesses = add_input_port("Required Access", PORT_TYPE_LIST(PORT_TYPE_STRING))
- check_any = add_input_port("Check Any", PORT_TYPE_NUMBER)
+ check_any = add_input_port("Check Any", PORT_TYPE_BOOLEAN)
/obj/item/circuit_component/compare/access/save_data_to_list(list/component_data)
. = ..()
diff --git a/code/modules/wiremod/components/id/getter.dm b/code/modules/wiremod/components/id/getter.dm
index 7143e5689d8c..beb77671f825 100644
--- a/code/modules/wiremod/components/id/getter.dm
+++ b/code/modules/wiremod/components/id/getter.dm
@@ -22,7 +22,7 @@
/obj/item/circuit_component/id_getter/populate_ports()
target = add_input_port("Target", PORT_TYPE_ATOM)
- prioritize_hands = add_input_port("Prioritize Hands", PORT_TYPE_NUMBER)
+ prioritize_hands = add_input_port("Prioritize Hands", PORT_TYPE_BOOLEAN)
id_port = add_output_port("ID", PORT_TYPE_ATOM)
/obj/item/circuit_component/id_getter/input_received(datum/port/input/port)
diff --git a/code/modules/wiremod/components/math/binary_conversion.dm b/code/modules/wiremod/components/math/binary_conversion.dm
index 8ef4067b955f..5302eb6bd181 100644
--- a/code/modules/wiremod/components/math/binary_conversion.dm
+++ b/code/modules/wiremod/components/math/binary_conversion.dm
@@ -26,7 +26,7 @@
add_action = "add", \
remove_action = "remove", \
is_output = TRUE, \
- port_type = PORT_TYPE_NUMBER, \
+ port_type = PORT_TYPE_BOOLEAN, \
prefix = "Bit", \
minimum_amount = 1, \
maximum_amount = MAX_BITFIELD_SIZE \
@@ -46,6 +46,6 @@
for(var/iteration in 1 to len)
var/datum/port/output/bit = bit_array[iteration]
if(iteration == 1 && is_negative)
- bit.set_output(1)
+ bit.set_output(TRUE)
continue
bit.set_output(!!(to_convert & (1<< (len - iteration))))
diff --git a/code/modules/wiremod/components/math/not.dm b/code/modules/wiremod/components/math/not.dm
index f530c9edefc5..171870b1e9b2 100644
--- a/code/modules/wiremod/components/math/not.dm
+++ b/code/modules/wiremod/components/math/not.dm
@@ -18,7 +18,7 @@
/obj/item/circuit_component/not/populate_ports()
input_port = add_input_port("Input", PORT_TYPE_ANY)
- result = add_output_port("Result", PORT_TYPE_NUMBER)
+ result = add_output_port("Result", PORT_TYPE_BOOLEAN)
/obj/item/circuit_component/not/input_received(datum/port/input/port)
diff --git a/code/modules/wiremod/components/math/toggle.dm b/code/modules/wiremod/components/math/toggle.dm
index 9f51c974cb31..39e43ac50a9f 100644
--- a/code/modules/wiremod/components/math/toggle.dm
+++ b/code/modules/wiremod/components/math/toggle.dm
@@ -16,7 +16,7 @@
var/toggle_state = FALSE
/obj/item/circuit_component/compare/toggle/populate_custom_ports()
- toggle_set = add_input_port("Set Toggle State", PORT_TYPE_NUMBER)
+ toggle_set = add_input_port("Set Toggle State", PORT_TYPE_BOOLEAN)
toggle_and_compare = add_input_port("Toggle And Compare", PORT_TYPE_SIGNAL)
toggle_state = FALSE
diff --git a/code/modules/wiremod/components/utility/clock.dm b/code/modules/wiremod/components/utility/clock.dm
index 07cd95ebb394..f35041cff343 100644
--- a/code/modules/wiremod/components/utility/clock.dm
+++ b/code/modules/wiremod/components/utility/clock.dm
@@ -19,7 +19,7 @@
. += create_ui_notice("Clock Interval: [DisplayTimeText(COMP_CLOCK_DELAY)]", "orange", "clock")
/obj/item/circuit_component/clock/populate_ports()
- on = add_input_port("On", PORT_TYPE_NUMBER)
+ on = add_input_port("On", PORT_TYPE_BOOLEAN)
signal = add_output_port("Signal", PORT_TYPE_SIGNAL)
diff --git a/code/modules/wiremod/core/component_printer.dm b/code/modules/wiremod/core/component_printer.dm
index cda5a9adee1f..8f7ec81cfb45 100644
--- a/code/modules/wiremod/core/component_printer.dm
+++ b/code/modules/wiremod/core/component_printer.dm
@@ -213,23 +213,24 @@
return data
-/obj/machinery/component_printer/attackby(obj/item/weapon, mob/living/user, list/modifiers, list/attack_modifiers)
+/obj/machinery/component_printer/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
if (user.combat_mode)
- return ..()
+ return NONE
var/obj/item/integrated_circuit/circuit
- if(istype(weapon, /obj/item/integrated_circuit))
- circuit = weapon
- else if (istype(weapon, /obj/item/circuit_component/module))
- var/obj/item/circuit_component/module/module = weapon
+ if(istype(tool, /obj/item/integrated_circuit))
+ circuit = tool
+ else if (istype(tool, /obj/item/circuit_component/module))
+ var/obj/item/circuit_component/module/module = tool
circuit = module.internal_circuit
+
if (isnull(circuit))
- return ..()
+ return NONE
circuit.linked_component_printer = WEAKREF(src)
circuit.update_static_data_for_all_viewers()
balloon_alert(user, "successfully linked to the integrated circuit")
-
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/component_printer/crowbar_act(mob/living/user, obj/item/tool)
if(..())
@@ -447,14 +448,14 @@
created_atom.pixel_x = created_atom.base_pixel_x + rand(-5, 5)
created_atom.pixel_y = created_atom.base_pixel_y + rand(-5, 5)
-/obj/machinery/module_duplicator/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers)
+/obj/machinery/module_duplicator/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
var/list/data = list()
- if(istype(weapon, /obj/item/circuit_component/module))
- var/obj/item/circuit_component/module/module = weapon
+ if(istype(tool, /obj/item/circuit_component/module))
+ var/obj/item/circuit_component/module/module = tool
if(HAS_TRAIT(module, TRAIT_CIRCUIT_UNDUPABLE))
balloon_alert(user, "integrated circuit cannot be saved!")
- return ..()
+ return ITEM_INTERACT_BLOCKING
data["dupe_data"] = list()
module.save_data_to_list(data["dupe_data"])
@@ -462,11 +463,12 @@
data["name"] = module.display_name
data["desc"] = "A module that has been loaded in by [user]."
data["materials"] = list(SSmaterials.get_material(/datum/material/glass) = module.circuit_size * cost_per_component)
- else if(istype(weapon, /obj/item/integrated_circuit))
- var/obj/item/integrated_circuit/integrated_circuit = weapon
+ else if(istype(tool, /obj/item/integrated_circuit))
+ var/obj/item/integrated_circuit/integrated_circuit = tool
if(HAS_TRAIT(integrated_circuit, TRAIT_CIRCUIT_UNDUPABLE))
balloon_alert(user, "integrated circuit cannot be saved!")
- return ..()
+ return ITEM_INTERACT_BLOCKING
+
data["dupe_data"] = integrated_circuit.convert_to_json()
data["name"] = integrated_circuit.display_name
@@ -481,19 +483,20 @@
data["integrated_circuit"] = TRUE
if(!length(data))
- return ..()
+ return NONE
if(!data["name"])
balloon_alert(user, "it needs a name!")
- return ..()
+ return ITEM_INTERACT_BLOCKING
for(var/list/component_data as anything in scanned_designs)
if(component_data["name"] == data["name"])
balloon_alert(user, "name already exists!")
- return ..()
+ return ITEM_INTERACT_BLOCKING
flick("module-fab-scan", src)
addtimer(CALLBACK(src, PROC_REF(finish_module_scan), user, data), 1.4 SECONDS)
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/module_duplicator/proc/finish_module_scan(mob/user, data)
scanned_designs += list(data)
diff --git a/code/modules/wiremod/core/integrated_circuit.dm b/code/modules/wiremod/core/integrated_circuit.dm
index fb6ab656488d..5f6197e885f6 100644
--- a/code/modules/wiremod/core/integrated_circuit.dm
+++ b/code/modules/wiremod/core/integrated_circuit.dm
@@ -147,36 +147,40 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit)
SEND_SIGNAL(src, COMSIG_CIRCUIT_SET_LOCKED, new_value)
locked = new_value
-/obj/item/integrated_circuit/attackby(obj/item/I, mob/living/user, list/modifiers, list/attack_modifiers)
- . = ..()
- if(istype(I, /obj/item/circuit_component))
- add_component_manually(I, user)
- return
+/obj/item/integrated_circuit/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(istype(tool, /obj/item/circuit_component))
+ return add_component_manually(tool, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
- if(istype(I, /obj/item/stock_parts/power_store/cell))
+ if(istype(tool, /obj/item/stock_parts/power_store/cell))
if(cell)
balloon_alert(user, "there already is a cell inside!")
- return
- if(!user.transferItemToLoc(I, src))
- return
- set_cell(I)
- I.add_fingerprint(user)
- user.visible_message(span_notice("[user] inserts a power cell into [src]."), span_notice("You insert the power cell into [src]."))
- return
+ return ITEM_INTERACT_BLOCKING
- if(isidcard(I))
- balloon_alert(user, "owner id set for [I]")
- owner_id = WEAKREF(I)
- return
+ if(!user.transferItemToLoc(tool, src))
+ return ITEM_INTERACT_BLOCKING
- if(I.tool_behaviour == TOOL_SCREWDRIVER)
- if(!cell)
- return
- I.play_tool_sound(src)
- user.visible_message(span_notice("[user] unscrews the power cell from [src]."), span_notice("You unscrew the power cell from [src]."))
- cell.forceMove(drop_location())
- set_cell(null)
- return
+ set_cell(tool)
+ tool.add_fingerprint(user)
+ user.visible_message(span_notice("[user] inserts a power cell into [src]."), span_notice("You insert the power cell into [src]."))
+ return ITEM_INTERACT_SUCCESS
+
+ if(isidcard(tool))
+ balloon_alert(user, "owner id set for [tool]")
+ owner_id = WEAKREF(tool)
+ return ITEM_INTERACT_SUCCESS
+
+ return NONE
+
+/obj/item/integrated_circuit/screwdriver_act(mob/living/user, obj/item/tool)
+ if(!cell)
+ balloon_alert(user, "power cell missing!")
+ return ITEM_INTERACT_BLOCKING
+
+ tool.play_tool_sound(src)
+ user.visible_message(span_notice("[user] unscrews the power cell from [src]."), span_notice("You unscrew the power cell from [src]."))
+ cell.forceMove(drop_location())
+ set_cell(null)
+ return ITEM_INTERACT_SUCCESS
/**
* Registers an movable atom as a shell
@@ -292,7 +296,7 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit)
*/
/obj/item/integrated_circuit/proc/add_component_manually(obj/item/circuit_component/to_add, mob/living/user)
if (SEND_SIGNAL(src, COMSIG_CIRCUIT_ADD_COMPONENT_MANUALLY, to_add, user) & COMPONENT_CANCEL_ADD_COMPONENT)
- return
+ return FALSE
return add_component(to_add, user)
diff --git a/code/modules/wiremod/datatypes/boolean.dm b/code/modules/wiremod/datatypes/boolean.dm
new file mode 100644
index 000000000000..da446b532de9
--- /dev/null
+++ b/code/modules/wiremod/datatypes/boolean.dm
@@ -0,0 +1,10 @@
+/datum/circuit_datatype/boolean
+ datatype = PORT_TYPE_BOOLEAN
+ color = "bad" // This should be close enough to dark red.
+ datatype_flags = DATATYPE_FLAG_ALLOW_MANUAL_INPUT
+
+/datum/circuit_datatype/boolean/can_receive_from_datatype(datatype_to_check)
+ return TRUE
+
+/datum/circuit_datatype/boolean/convert_value(datum/port/port, value_to_convert, force)
+ return !!value_to_convert
diff --git a/code/modules/wiremod/datatypes/number.dm b/code/modules/wiremod/datatypes/number.dm
index fe964817d07a..69f11b8ae294 100644
--- a/code/modules/wiremod/datatypes/number.dm
+++ b/code/modules/wiremod/datatypes/number.dm
@@ -2,6 +2,7 @@
datatype = PORT_TYPE_NUMBER
color = "green"
datatype_flags = DATATYPE_FLAG_ALLOW_MANUAL_INPUT
+ can_receive_from = list(PORT_TYPE_BOOLEAN)
/datum/circuit_datatype/number/handle_manual_input(datum/port/input/port, mob/user, user_input)
return text2num(user_input)
diff --git a/code/modules/wiremod/datatypes/signal.dm b/code/modules/wiremod/datatypes/signal.dm
index 6a416e789e38..4f62f6bba614 100644
--- a/code/modules/wiremod/datatypes/signal.dm
+++ b/code/modules/wiremod/datatypes/signal.dm
@@ -7,6 +7,7 @@
PORT_TYPE_INSTANT_SIGNAL,
PORT_TYPE_RESPONSE_SIGNAL,
PORT_TYPE_SIGNAL,
+ PORT_TYPE_BOOLEAN,
)
/datum/circuit_datatype/signal/handle_manual_input(datum/port/input/port, mob/user, user_input)
diff --git a/code/modules/wiremod/shell/airlock.dm b/code/modules/wiremod/shell/airlock.dm
index f77e3175d809..b90508443a33 100644
--- a/code/modules/wiremod/shell/airlock.dm
+++ b/code/modules/wiremod/shell/airlock.dm
@@ -76,8 +76,8 @@
open = add_input_port("Open", PORT_TYPE_SIGNAL)
close = add_input_port("Close", PORT_TYPE_SIGNAL)
// States
- is_open = add_output_port("Is Open", PORT_TYPE_NUMBER)
- is_bolted = add_output_port("Is Bolted", PORT_TYPE_NUMBER)
+ is_open = add_output_port("Is Open", PORT_TYPE_BOOLEAN)
+ is_bolted = add_output_port("Is Bolted", PORT_TYPE_BOOLEAN)
// Output Signals
opened = add_output_port("Opened", PORT_TYPE_SIGNAL)
closed = add_output_port("Closed", PORT_TYPE_SIGNAL)
diff --git a/code/modules/wiremod/shell/brain_computer_interface.dm b/code/modules/wiremod/shell/brain_computer_interface.dm
index ab4a7d60fbb4..30f4910ec23d 100644
--- a/code/modules/wiremod/shell/brain_computer_interface.dm
+++ b/code/modules/wiremod/shell/brain_computer_interface.dm
@@ -74,7 +74,7 @@
message = add_input_port("Message", PORT_TYPE_STRING, trigger = null)
send_message_signal = add_input_port("Send Message", PORT_TYPE_SIGNAL)
- show_charge_meter = add_input_port("Show Charge Meter", PORT_TYPE_NUMBER, trigger = PROC_REF(update_charge_action))
+ show_charge_meter = add_input_port("Show Charge Meter", PORT_TYPE_BOOLEAN, trigger = PROC_REF(update_charge_action))
user_port = add_output_port("User", PORT_TYPE_USER)
@@ -332,27 +332,24 @@
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
-/obj/machinery/bci_implanter/attackby(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers)
- var/obj/item/organ/cyberimp/bci/new_bci = weapon
- if (istype(new_bci))
- if (!(locate(/obj/item/integrated_circuit) in new_bci))
- balloon_alert(user, "bci has no circuit!")
- return
-
- var/obj/item/organ/cyberimp/bci/previous_bci_to_implant = bci_to_implant
-
- user.transferItemToLoc(weapon, src)
- bci_to_implant = weapon
-
- if (isnull(previous_bci_to_implant))
- balloon_alert(user, "inserted bci")
- else
- balloon_alert(user, "swapped bci")
- user.put_in_hands(previous_bci_to_implant)
+/obj/machinery/bci_implanter/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if (!istype(tool, /obj/item/organ/cyberimp/bci))
+ return NONE
- return
+ var/obj/item/organ/cyberimp/bci/new_bci = tool
+ if (!(locate(/obj/item/integrated_circuit) in new_bci))
+ balloon_alert(user, "bci has no circuit!")
+ return ITEM_INTERACT_BLOCKING
- return ..()
+ var/obj/item/organ/cyberimp/bci/previous_bci_to_implant = bci_to_implant
+ user.transferItemToLoc(new_bci, src)
+ bci_to_implant = new_bci
+ if (isnull(previous_bci_to_implant))
+ balloon_alert(user, "inserted bci")
+ else
+ balloon_alert(user, "swapped bci")
+ user.put_in_hands(previous_bci_to_implant)
+ return ITEM_INTERACT_SUCCESS
/obj/machinery/bci_implanter/attackby_secondary(obj/item/weapon, mob/user, list/modifiers, list/attack_modifiers)
if (!occupant && default_deconstruction_screwdriver(user, icon_state, icon_state, weapon))
diff --git a/code/modules/wiremod/shell/dispenser.dm b/code/modules/wiremod/shell/dispenser.dm
index d31a5cb04c0a..4a257dda9023 100644
--- a/code/modules/wiremod/shell/dispenser.dm
+++ b/code/modules/wiremod/shell/dispenser.dm
@@ -60,27 +60,29 @@
new /obj/item/circuit_component/dispenser_bot()
), SHELL_CAPACITY_LARGE)
-/obj/structure/dispenser_bot/attackby(obj/item/item, mob/living/user, list/modifiers, list/attack_modifiers)
- if(user.combat_mode)
- return ..()
- if(istype(item, /obj/item/wrench) || istype(item, /obj/item/multitool) || istype(item, /obj/item/integrated_circuit))
- return ..()
- if(item.w_class > max_weight && !istype(item, /obj/item/storage/bag))
+/obj/structure/dispenser_bot/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if (user.combat_mode)
+ return NONE
+
+ if (tool.w_class > max_weight && !istype(tool, /obj/item/storage/bag))
balloon_alert(user, "item too big!")
- return FALSE
+ return ITEM_INTERACT_BLOCKING
+
if(length(stored_items) >= capacity)
balloon_alert(user, "at maximum capacity!")
- return FALSE
- if(istype(item, /obj/item/storage/bag))
- for(var/obj/item/bag_item in item.contents)
- if(length(stored_items) >= capacity)
- break
- if(bag_item.w_class > max_weight || istype(bag_item, /obj/item/storage/bag))
- continue
- add_item(user, bag_item)
- return TRUE
- add_item(user, item)
- return TRUE
+ return ITEM_INTERACT_BLOCKING
+
+ if(!istype(tool, /obj/item/storage/bag))
+ add_item(user, tool)
+ return ITEM_INTERACT_SUCCESS
+
+ for(var/obj/item/bag_item in tool.contents)
+ if(length(stored_items) >= capacity)
+ break
+ if(bag_item.w_class > max_weight || istype(bag_item, /obj/item/storage/bag))
+ continue
+ add_item(user, bag_item)
+ return ITEM_INTERACT_SUCCESS
/obj/structure/dispenser_bot/wrench_act(mob/living/user, obj/item/tool)
if(locked)
diff --git a/code/modules/wiremod/shell/implant.dm b/code/modules/wiremod/shell/implant.dm
index 3dab241a113c..2c719d6017ee 100644
--- a/code/modules/wiremod/shell/implant.dm
+++ b/code/modules/wiremod/shell/implant.dm
@@ -62,7 +62,7 @@
message = add_input_port("Message", PORT_TYPE_STRING, trigger = null)
send_message_signal = add_input_port("Send Message", PORT_TYPE_SIGNAL)
- show_charge_meter = add_input_port("Show Charge Meter", PORT_TYPE_NUMBER, trigger = PROC_REF(update_charge_action))
+ show_charge_meter = add_input_port("Show Charge Meter", PORT_TYPE_BOOLEAN, trigger = PROC_REF(update_charge_action))
user_port = add_output_port("User", PORT_TYPE_USER)
diff --git a/code/modules/wiremod/shell/module.dm b/code/modules/wiremod/shell/module.dm
index 57139cdfc1b8..a19a02f7fd9e 100644
--- a/code/modules/wiremod/shell/module.dm
+++ b/code/modules/wiremod/shell/module.dm
@@ -188,8 +188,8 @@
select_module = add_input_port("Select Module", PORT_TYPE_SIGNAL)
// States
wearer = add_output_port("Wearer", PORT_TYPE_USER)
- deployed = add_output_port("Deployed", PORT_TYPE_NUMBER)
- activated = add_output_port("Activated", PORT_TYPE_NUMBER)
+ deployed = add_output_port("Deployed", PORT_TYPE_BOOLEAN)
+ activated = add_output_port("Activated", PORT_TYPE_BOOLEAN)
selected_module = add_output_port("Selected Module", PORT_TYPE_STRING)
deployed_parts = add_output_port("Deployed Parts", PORT_TYPE_LIST(PORT_TYPE_STRING))
// Output Signals
diff --git a/html/changelogs/archive/2026-03.yml b/html/changelogs/archive/2026-03.yml
index 6811b8454f25..61ac1b06ee59 100644
--- a/html/changelogs/archive/2026-03.yml
+++ b/html/changelogs/archive/2026-03.yml
@@ -265,7 +265,41 @@
- bugfix: Fixes beebox and traitor bees not obeying the reagent blacklist
- balance: Maintenance pills (the achievement variety that only spawns in maint)
can now generate with xenomorph microbes and cyborg nanites
+2026-03-15:
+ JohnFulpWillard:
+ - bugfix: Chem scanning a maintenance pill removes the achievement value.
+ timothymtorres:
+ - balance: Sticky spider webs now drain stamina when mobs enter the turf
2026-03-16:
+ ArcaneMusic:
+ - bugfix: The cargo shuttle can no longer qdel your ghost mob.
+ Melbert:
+ - qol: Revenant abilities indicate if they are locked, and better indicate if they
+ are currently usable
+ - qol: Revenants are no longer alerted that they have no gravity (they always have
+ gravity)
+ - refactor: Refactored Revenant abilities, report any oddities with them.
+ - rscadd: Cannabis will calm down an angry monkey, eventually even turning them
+ docile (not pacifist!) temporarily
+ - rscadd: Nicotine will also calm down an angry monkey, albiet slower than Cannabis
+ and will never turn them entirely docile
+ - rscadd: Booze on the other hand will make a monkey even angrier (though only if
+ they are already upset with someone)
+ - bugfix: Pun Pun is Pun Pun again
+ - bugfix: Removes white pixel from airlock EA animation
+ - bugfix: Removes white pixel from firelock open animation
+ SmArtKar:
+ - refactor: Converted vehicles to use item_interaction/tool_acts
+ - refactor: Rewrote dispenser bots to use item_interaction
+ - refactor: Rewrote integrated circuits to use item_interaction and screwdriver_act
+ - refactor: Converted circuit printers/duplicators to item_interaction
+ - refactor: Rewrote BCI implanters to use item_interaction
+ - refactor: Converted bot construction to item_interaction
+ timothymtorres:
+ - rscadd: Spiders can now be tamed and ridden. They eat mouse, lizard, moth, fly,
+ and worm meat and can only be tamed when they are spiderlings or young.
+ - code_imp: Refactored tameable code to use TRAIT_TAMED instead of setting individual
+ variables on each mob.
FalloutFalcon:
- bugfix: Assignment of a faction ID was moved to the clan rather then discipline
- bugfix: Fixes a few NPCS not having AI controllers
@@ -288,8 +322,127 @@
- code_imp: Reimplemented permanent ghouling and embracing
minzeyes, XeonMations:
- rscadd: Reimplements Vicissitude
+2026-03-17:
+ Melbert:
+ - qol: If your pay gets docked by Central Command, it is noted in audit and transaction
+ log
+ - qol: If your pay gets docked by Central Command, you will always be notified,
+ regardless of bank alert preference
+ - admin: Dock Pay smite supports negatives better (for an easy way to give someone
+ money)
+ SmArtKar:
+ - refactor: Converted autosurgeons/robot bodyparts/dissection notes to item_interaction
+ - refactor: Converted crossbreeds/anomacores/RND machinery to item_interaction
+ - refactor: Converted tram objects to item_interaction/tool_act
+ kittysmooch:
+ - rscadd: 'Added new sprites for baseball bats, metal bats, the homerun bat, the
+ cricket bat, and the baseball itself!
+
+ :cl:'
+ lelandkemble:
+ - bugfix: No more infinite free grenade launchers for nukeops
+2026-03-18:
+ Absolucy:
+ - qol: Watcher Hatchling projectiles should no longer hit people buckled to you,
+ or anything you're buckled to, i.e a raptor mount.
+ ArcaneMusic:
+ - bugfix: The gas compressor once again can perform disk operations.
+ Ezel:
+ - qol: Shuttle engine access requirement changed from CE access to Engineering access.
+ - qol: Shuttle engine now comes in a flatpack box (it does not come with a multitool)
+ Generalcamo:
+ - rscadd: Added `balloon_alert_to_hearers`, which is similar to `balloon_alert_to_viewers`
+ but for audible things.
+ - qol: Existing instances of `balloon_alert_to_viewers` which covered sounds, now
+ uses `balloon_alert_to_hearers`.
+ Iajret:
+ - bugfix: Fixed runtimes from buckling basic mobs to operating tables
+ - bugfix: free_operation is now applied to animals when you buckle them to operating
+ tables
+ - bugfix: This in turn fixed vitals monitors being stuck always displaying basic
+ mob's health if you try to buckle one to operating table
+ Melbert:
+ - bugfix: Fix cargo ordering app (on pdas and modular computers) from being unable
+ to order some things
+ TealSeer:
+ - code_imp: added a unit test macro for running the same test multiple times
+ lelandkemble:
+ - code_imp: fixed a runtime when something other than a player breaks an airlock
+ - bugfix: Flux & Gravity anomaly cores work with assembly activation
+2026-03-19:
+ Absolucy:
+ - qol: Added screentips for scanning raptors with the RaptorDex PDA app and standalone
+ handheld RaptorDex.
+ - bugfix: Fixed the handheld RaptorDex not showing the "scanned" balloon alert when
+ scanning a raptor.
+ Melbert:
+ - rscadd: Intelligence vendors (from the event) have a 50% chance to deny users
+ who are not wealthy. When the vendors awake and start moving, they instead get
+ a 100% chance to deny anyone.
+2026-03-20:
+ Absolucy:
+ - balance: The Staff of Storms is now normal-sized, and fits in a backpack.
+ LemonInTheDark:
+ - rscadd: Added an option for rendering space parallax with old space sprites (the
+ ones from before we invented parallax), they're animated and I feel quite pretty.
+ - bugfix: Space parallax should hopefully behave a little more consistently now
+ - refactor: Rewrote a lot of how space parallax handled itself, please yell at me
+ if any bugs make themselves known
+ - rscadd: Reworks how sparks render. They're now a bit brighter, will fade out as
+ they move/if they hit something, will stack with each other less and also won't
+ start hang in the air on spawn.
+ - rscadd: Added a flickering effect to lighters, welding tools, flares, torches
+ and candles (since they're flames).
+ - bugfix: Overlay based lights (think flashlights) will no longer flash to double
+ intensity while being picked up.
+ - refactor: Reworked how some effects (explosion particles, sparks, some reagent
+ stuff) function, report any bugs!
+ Melbert:
+ - rscadd: Relics found from cracking boulders now have a (mostly) unique set of
+ mining and cultish related effects.
+ - rscadd: Relics found in gift boxes no longer need to be discovered first in science.
+ - rscdel: '"Add prosthetic limb" surgical operation has been reverted to be a bit
+ closer to how it used to work - you operate on the missing limb / limb stump,
+ rather than on the chest.'
+ - refactor: Missing limbs are now represented as limb stumps. In practice this should
+ change nothing (for now), as no features were rewritten to make use of these
+ besides surgery. Please report any oddities with missing limbs, however.
+ SyncIt21:
+ - bugfix: you can pry out/deconstruct floor tiles/reinforced tiles etc without destroying
+ any floor lights/sinks mounted on as long as there is plating underneath that
+ turf
+ levels0:
+ - bugfix: webbing production webs no longer harm their maker
+2026-03-21:
+ ArcaneMusic:
+ - bugfix: the BSA cannot be built half-into a wall.
+ - qol: building the DNA vault and BSA points to the tiles blocking it's construction.
+ Melbert:
+ - image: Generic surgical operations now have unique surgery radial icons
2026-03-23:
FalloutFalcon:
+ - spellcheck: removed extra space on spraying an atmos machine with spray can
+ LemonInTheDark:
+ - admin: Subsystem flags are now displayed as flags instead of raw numbers in vv
+ Melbert:
+ - qol: Audit log formats vendors without "the"
+ - rscadd: Adds Thanatorenasia, a 0 point neutral quirk that turns your body into
+ a "recovered crewmember" if you choose to DNR - meaning if revived, any ghost
+ could take control of it
+ - refactor: Refactored "ghostrol on revive" behavior a fair bit, report any oddities
+ with it / the spawners menu
+ SmArtKar:
+ - balance: Blue raptors had their HP increased from 220 to 300
+ - balance: Purple raptors now automatically start flying if the owner is about to
+ fall into a chasm.
+ - bugfix: Fixed 2 exploits which would render you immune to falling down a specific
+ chasm.
+ lelandkemble:
+ - bugfix: Monkey dust now properly removes anti-stun properties when its imbiber
+ is no longer a monkey
+ - spellcheck: Shoving someone into another person no longer sends the "you shoved"
+ message to the person who was shoved into
+ FalloutFalcon:
- rscadd: Fishing supplies can now be found in camping and hardware stores
- qol: You can now choose to report a bug upstream when making an issue report
- admin: All Second City verbs sorted into their own catagory
@@ -313,3 +466,40 @@
- rscadd: Melpominee
- code_imp: Moves Emotion to /mob
- code_imp: Proc to convert Emotion to a quality (i.e. Afraid -> fear)
+2026-03-24:
+ ArcaneMusic:
+ - bugfix: Cargo goodies are now visible in the ordering and request consoles.
+ Bugwasabi, vinylspiders:
+ - code_imp: Uses defines instead of magic numbers on carbon_stripping.dm
+ Iajret:
+ - bugfix: fixed vitals display not respecting APCs power when you attach it to a
+ wall
+ - rscadd: Vitals display can be printed in ancient lathes
+ Jacquerel:
+ - rscadd: Nonhuman mobs with hands can now pick up other mobs that are smaller than
+ them and can usually be picked up
+ JohnFulpWillard:
+ - qol: The Curator's barcode scanner now lets you check books out.
+ - bugfix: The library console can no longer borrow books for an infinite amount
+ of time.
+ Melbert:
+ - bugfix: The skeleton from being shocked matches your height
+ - bugfix: Drinking soup will loop until it's empty or you cancel it (as it used
+ to)
+ - rscadd: Some undergarmants will now use a generic replacement on lizard body shapes
+ that fit more appropriately
+ - refactor: Refactored how undergarmants generate their icons, report any oddities
+ with that
+ - code_imp: Cleaned up RTG code. Admins can now VV them easier. If you come across
+ a ruin on Lavaland or in space that uses them and see any odd behavior, report
+ it as an issue.
+ Y0SH1M4S73R:
+ - qol: Many circuit components have had ports that effectively act as boolean inputs
+ or outputs converted into a new boolean datatype.
+ cebutris:
+ - bugfix: Fixed an oversight with package wrapping
+ lelandkemble:
+ - bugfix: Friendly wisps, wizard gravity balls, & tesla periphery balls are no longer
+ disabled due to shuttle transit
+ - bugfix: Glasses that were in the vending machine now still count for the completion
+ of the Beach Bar virtual domain
diff --git a/icons/effects/old_parallax.dmi b/icons/effects/old_parallax.dmi
new file mode 100644
index 000000000000..cc58e7390e43
Binary files /dev/null and b/icons/effects/old_parallax.dmi differ
diff --git a/icons/hud/surgery_radial.dmi b/icons/hud/surgery_radial.dmi
new file mode 100644
index 000000000000..8aa49694e065
Binary files /dev/null and b/icons/hud/surgery_radial.dmi differ
diff --git a/icons/mob/actions/actions_revenant.dmi b/icons/mob/actions/actions_revenant.dmi
index d657e8a3daeb..3b75c4c1d30c 100644
Binary files a/icons/mob/actions/actions_revenant.dmi and b/icons/mob/actions/actions_revenant.dmi differ
diff --git a/icons/mob/clothing/digi_template.dmi b/icons/mob/clothing/digi_template_equpiment.dmi
similarity index 100%
rename from icons/mob/clothing/digi_template.dmi
rename to icons/mob/clothing/digi_template_equpiment.dmi
diff --git a/icons/mob/clothing/digi_template_underwear.dmi b/icons/mob/clothing/digi_template_underwear.dmi
new file mode 100644
index 000000000000..767a56476b60
Binary files /dev/null and b/icons/mob/clothing/digi_template_underwear.dmi differ
diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi
index 67e10ca1b74f..0949da25e15e 100644
Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ
diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi
index 7bf549f2bde7..29bb97b6b184 100644
Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ
diff --git a/icons/mob/inhands/weapons/melee_lefthand.dmi b/icons/mob/inhands/weapons/melee_lefthand.dmi
index 8ceaa871caf1..3f46e7ce78b0 100644
Binary files a/icons/mob/inhands/weapons/melee_lefthand.dmi and b/icons/mob/inhands/weapons/melee_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/melee_righthand.dmi b/icons/mob/inhands/weapons/melee_righthand.dmi
index 615aad431524..fb97f4e2b665 100644
Binary files a/icons/mob/inhands/weapons/melee_righthand.dmi and b/icons/mob/inhands/weapons/melee_righthand.dmi differ
diff --git a/icons/obj/doors/airlocks/station/overlays.dmi b/icons/obj/doors/airlocks/station/overlays.dmi
index 17e9005042e4..0d573582a688 100644
Binary files a/icons/obj/doors/airlocks/station/overlays.dmi and b/icons/obj/doors/airlocks/station/overlays.dmi differ
diff --git a/icons/obj/doors/doorfireglass.dmi b/icons/obj/doors/doorfireglass.dmi
index 83156ae4351e..250b5ca833a8 100644
Binary files a/icons/obj/doors/doorfireglass.dmi and b/icons/obj/doors/doorfireglass.dmi differ
diff --git a/icons/obj/toys/balls.dmi b/icons/obj/toys/balls.dmi
index 5f92c95cfe71..c3e439d00961 100644
Binary files a/icons/obj/toys/balls.dmi and b/icons/obj/toys/balls.dmi differ
diff --git a/icons/obj/weapons/bat.dmi b/icons/obj/weapons/bat.dmi
index aa7d9cf41491..e523d18fbfc2 100644
Binary files a/icons/obj/weapons/bat.dmi and b/icons/obj/weapons/bat.dmi differ
diff --git a/modular_darkpack/modules/ambience/code/music_subsystem.dm b/modular_darkpack/modules/ambience/code/music_subsystem.dm
index b3f446a72f81..bf67e93469b9 100644
--- a/modular_darkpack/modules/ambience/code/music_subsystem.dm
+++ b/modular_darkpack/modules/ambience/code/music_subsystem.dm
@@ -21,7 +21,7 @@
/// The subsystem used to play music to users every now and then, makes them real excited. copy-pasta from SSambience
SUBSYSTEM_DEF(music)
name = "Music"
- flags = SS_BACKGROUND|SS_NO_INIT
+ ss_flags = SS_BACKGROUND|SS_NO_INIT
priority = FIRE_PRIORITY_AMBIENCE
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 1 SECONDS
diff --git a/modular_darkpack/modules/cars/code/car_subsystem.dm b/modular_darkpack/modules/cars/code/car_subsystem.dm
index 4305343987c5..00e94e9ec1b9 100644
--- a/modular_darkpack/modules/cars/code/car_subsystem.dm
+++ b/modular_darkpack/modules/cars/code/car_subsystem.dm
@@ -1,6 +1,6 @@
PROCESSING_SUBSYSTEM_DEF(carpool)
name = "Car Pool"
- flags = SS_POST_FIRE_TIMING|SS_NO_INIT
+ ss_flags = SS_POST_FIRE_TIMING|SS_NO_INIT
priority = FIRE_PRIORITY_OBJ
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 5
diff --git a/modular_darkpack/modules/masquerade/code/subsystem/masquerade.dm b/modular_darkpack/modules/masquerade/code/subsystem/masquerade.dm
index 7c2b89c34c03..d82bd0d70707 100644
--- a/modular_darkpack/modules/masquerade/code/subsystem/masquerade.dm
+++ b/modular_darkpack/modules/masquerade/code/subsystem/masquerade.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(masquerade)
name = "Masquerade"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
var/masquerade_level = MASQUERADE_MAX_LEVEL
var/list/masquerade_breachers
diff --git a/modular_darkpack/modules/npc/code/human/npc_human_subsystem.dm b/modular_darkpack/modules/npc/code/human/npc_human_subsystem.dm
index 10887de97b85..e1c48eeaf754 100644
--- a/modular_darkpack/modules/npc/code/human/npc_human_subsystem.dm
+++ b/modular_darkpack/modules/npc/code/human/npc_human_subsystem.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(humannpcpool)
name = "Human NPC Pool"
- flags = SS_POST_FIRE_TIMING|SS_BACKGROUND
+ ss_flags = SS_POST_FIRE_TIMING|SS_BACKGROUND
priority = FIRE_PRIORITY_NPC
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
wait = 0.3 SECONDS
diff --git a/modular_darkpack/modules/paths/code/occult_research.dm b/modular_darkpack/modules/paths/code/occult_research.dm
index 4f77d2bd85f1..71ee8c648ec4 100644
--- a/modular_darkpack/modules/paths/code/occult_research.dm
+++ b/modular_darkpack/modules/paths/code/occult_research.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(occult_research)
name = "Occult Research"
- flags = SS_BACKGROUND|SS_NO_INIT
+ ss_flags = SS_BACKGROUND|SS_NO_INIT
wait = 60 SECONDS // How often to process research points
var/base_research_rate = 0.5 // Base points per tick
var/necromancy_bonus = 0.5
diff --git a/modular_darkpack/modules/phones/code/phone_subsystem.dm b/modular_darkpack/modules/phones/code/phone_subsystem.dm
index 0722a45b9962..9965aee04640 100644
--- a/modular_darkpack/modules/phones/code/phone_subsystem.dm
+++ b/modular_darkpack/modules/phones/code/phone_subsystem.dm
@@ -4,7 +4,7 @@
*/
SUBSYSTEM_DEF(phones)
name = "Phones"
- flags = SS_NO_FIRE|SS_NO_INIT
+ ss_flags = SS_NO_FIRE|SS_NO_INIT
// Seven digits, always start with 5
var/list/assigned_phone_numbers = list()
diff --git a/modular_darkpack/modules/storyteller_dice/code/roll_subsystem.dm b/modular_darkpack/modules/storyteller_dice/code/roll_subsystem.dm
index eb3ee9b451df..2c3f52003d5b 100644
--- a/modular_darkpack/modules/storyteller_dice/code/roll_subsystem.dm
+++ b/modular_darkpack/modules/storyteller_dice/code/roll_subsystem.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(roll)
name = "Dice Rolling"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
var/on_crit_extra_die_enabled = FALSE
var/on_crit_extra_success_enabled = FALSE
diff --git a/modular_darkpack/modules/vip_areas/code/bouncer_barrier_subsystem.dm b/modular_darkpack/modules/vip_areas/code/bouncer_barrier_subsystem.dm
index fee76f955de9..51a05cdc07ad 100644
--- a/modular_darkpack/modules/vip_areas/code/bouncer_barrier_subsystem.dm
+++ b/modular_darkpack/modules/vip_areas/code/bouncer_barrier_subsystem.dm
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(bouncer_barriers)
name = "Bouncer Barrier"
- flags = SS_NO_FIRE
+ ss_flags = SS_NO_FIRE
init_order = INIT_ORDER_BARRIER
var/barriers_enabled = TRUE
diff --git a/tgstation.dme b/tgstation.dme
index 5fa60003c0be..e46ce2f09e3e 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -352,6 +352,7 @@
#include "code\__DEFINES\dcs\signals\signals_leash.dm"
#include "code\__DEFINES\dcs\signals\signals_lift.dm"
#include "code\__DEFINES\dcs\signals\signals_light_eater.dm"
+#include "code\__DEFINES\dcs\signals\signals_light_intercept.dm"
#include "code\__DEFINES\dcs\signals\signals_lockable_storage.dm"
#include "code\__DEFINES\dcs\signals\signals_market.dm"
#include "code\__DEFINES\dcs\signals\signals_material_container.dm"
@@ -1787,7 +1788,6 @@
#include "code\datums\elements\decals\blood.dm"
#include "code\datums\elements\food\dunkable.dm"
#include "code\datums\elements\food\food_trash.dm"
-#include "code\datums\elements\food\foodlike_drink.dm"
#include "code\datums\elements\food\fried_item.dm"
#include "code\datums\elements\food\grilled_item.dm"
#include "code\datums\elements\food\love_food_buff.dm"
@@ -2021,6 +2021,7 @@
#include "code\datums\quirks\neutral_quirks\pineapple_hater.dm"
#include "code\datums\quirks\neutral_quirks\pineapple_liker.dm"
#include "code\datums\quirks\neutral_quirks\shifty_eyes.dm"
+#include "code\datums\quirks\neutral_quirks\thanatorenasia.dm"
#include "code\datums\quirks\neutral_quirks\transhumanist.dm"
#include "code\datums\quirks\neutral_quirks\vegetarian.dm"
#include "code\datums\quirks\positive_quirks\alcohol_tolerance.dm"
@@ -3632,6 +3633,7 @@
#include "code\modules\antagonists\revenant\haunted_item.dm"
#include "code\modules\antagonists\revenant\revenant_antag.dm"
#include "code\modules\antagonists\revenant\revenant_blight.dm"
+#include "code\modules\antagonists\revenant\revenant_skill.dm"
#include "code\modules\antagonists\revolution\enemy_of_the_state.dm"
#include "code\modules\antagonists\revolution\revolution.dm"
#include "code\modules\antagonists\revolution\revolution_handler.dm"
@@ -4894,6 +4896,7 @@
#include "code\modules\library\skill_learning\job_skillchips\research_director.dm"
#include "code\modules\library\skill_learning\job_skillchips\roboticist.dm"
#include "code\modules\library\skill_learning\job_skillchips\station_engineer.dm"
+#include "code\modules\lighting\light_middleman.dm"
#include "code\modules\lighting\lighting_area.dm"
#include "code\modules\lighting\lighting_atom.dm"
#include "code\modules\lighting\lighting_corner.dm"
@@ -6502,6 +6505,7 @@
#include "code\modules\surgery\bodyparts\helpers.dm"
#include "code\modules\surgery\bodyparts\parts.dm"
#include "code\modules\surgery\bodyparts\robot_bodyparts.dm"
+#include "code\modules\surgery\bodyparts\stumps.dm"
#include "code\modules\surgery\bodyparts\worn_feature_offset.dm"
#include "code\modules\surgery\bodyparts\wounds.dm"
#include "code\modules\surgery\bodyparts\species_parts\android_parts.dm"
@@ -6885,6 +6889,7 @@
#include "code\modules\wiremod\core\variable.dm"
#include "code\modules\wiremod\datatypes\any.dm"
#include "code\modules\wiremod\datatypes\basic.dm"
+#include "code\modules\wiremod\datatypes\boolean.dm"
#include "code\modules\wiremod\datatypes\datum.dm"
#include "code\modules\wiremod\datatypes\entity.dm"
#include "code\modules\wiremod\datatypes\number.dm"
diff --git a/tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx b/tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx
index 2d97b55d8aad..a83315c8cd66 100644
--- a/tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx
+++ b/tgui/packages/tgui/interfaces/Cargo/CargoCatalog.tsx
@@ -147,7 +147,6 @@ function CatalogTabs(props: CatalogTabsProps & Props) {
setActiveSupplyName(supply.name);
setSearchText('');
}}
- style={supply.name === "Goodies" ? {display: 'none'} : undefined}
>
{supply.name}
diff --git a/tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.jsx b/tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.jsx
index 5fe91ad383e9..0595b9650582 100644
--- a/tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.jsx
+++ b/tgui/packages/tgui/interfaces/IntegratedCircuit/FundamentalTypes.jsx
@@ -129,6 +129,17 @@ export const FUNDAMENTAL_DATA_TYPES = {
);
},
+ boolean: (props) => {
+ const { name, value, setValue, color } = props;
+ return (
+
+ {name}
+
+ setValue(!value)} />
+
+
+ );
+ },
};
export const DATATYPE_DISPLAY_HANDLERS = {
diff --git a/tgui/packages/tgui/interfaces/LibraryConsole/screens/Checkout.tsx b/tgui/packages/tgui/interfaces/LibraryConsole/screens/Checkout.tsx
index c0e3aa30c14b..526ade6f7595 100644
--- a/tgui/packages/tgui/interfaces/LibraryConsole/screens/Checkout.tsx
+++ b/tgui/packages/tgui/interfaces/LibraryConsole/screens/Checkout.tsx
@@ -65,6 +65,7 @@ export function Checkout(props) {
function CheckoutModal(props) {
const { act, data } = useBackend();
+ const { checkout_title } = data;
const inventory = data.inventory
.map((book, i) => ({
@@ -77,7 +78,6 @@ function CheckoutModal(props) {
const { checkoutBookState } = useLibraryContext();
const [checkoutBook, setCheckoutBook] = checkoutBookState;
- const [bookName, setBookName] = useState('Insert Book name...');
const [checkoutee, setCheckoutee] = useState('Recipient');
const [checkoutPeriod, setCheckoutPeriod] = useState(5);
@@ -91,9 +91,15 @@ function CheckoutModal(props) {
book.title)}
- onSelected={(e) => setBookName(e)}
+ onSelected={(e) => {
+ act('set_checkout', {
+ book_name: e,
+ });
+ }}
/>
@@ -110,7 +116,7 @@ function CheckoutModal(props) {
value={checkoutPeriod}
unit=" Minutes"
minValue={1}
- maxValue={1440}
+ maxValue={120}
step={1}
stepPixelSize={10}
onChange={(value) => setCheckoutPeriod(value)}
@@ -128,7 +134,6 @@ function CheckoutModal(props) {
onClick={() => {
setCheckoutBook(false);
act('checkout', {
- book_name: bookName,
loaned_to: checkoutee,
checkout_time: checkoutPeriod,
});
diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/darkpack_height.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/darkpack_height.tsx
index 170b76fa83a9..def14e845419 100644
--- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/darkpack_height.tsx
+++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/character_preferences/darkpack_height.tsx
@@ -1,5 +1,5 @@
// THIS IS A DARKPACK UI FILE
-import { FeatureNumeric, FeatureValueProps, FeatureNumericData, FeatureNumberInput } from '../base';
+import { type FeatureNumeric, type FeatureValueProps, type FeatureNumericData, FeatureNumberInput } from '../base';
import { Stack, Box } from 'tgui-core/components';
type HeightServerData = FeatureNumericData & {
diff --git a/tgui/packages/tgui/interfaces/Telephone/ScreenIRC.tsx b/tgui/packages/tgui/interfaces/Telephone/ScreenIRC.tsx
index 64d5aa69d701..4b522a462200 100644
--- a/tgui/packages/tgui/interfaces/Telephone/ScreenIRC.tsx
+++ b/tgui/packages/tgui/interfaces/Telephone/ScreenIRC.tsx
@@ -40,8 +40,7 @@ export const ScreenViewingChannel = (props: {
{viewing_channel ? (
- <>
- {viewing_channel.messages.map((message) => (
+ viewing_channel.messages.map((message) => (
Story by {message.author} - [{message.time_stamp}]
@@ -53,8 +52,7 @@ export const ScreenViewingChannel = (props: {
) : null}
- ))}
- >
+ ))
) : (
'ERROR: Channel invalid.'
)}
diff --git a/tgui/packages/tgui/interfaces/Telephone/ScreenPhone.tsx b/tgui/packages/tgui/interfaces/Telephone/ScreenPhone.tsx
index 4be2a857e512..751784da2316 100644
--- a/tgui/packages/tgui/interfaces/Telephone/ScreenPhone.tsx
+++ b/tgui/packages/tgui/interfaces/Telephone/ScreenPhone.tsx
@@ -27,7 +27,7 @@ export const ScreenPhone = (props: {
}
act('terminal_sound');
if (digit === '_') {
- setEnteredNumber(enteredNumber + ' ');
+ setEnteredNumber(`${enteredNumber} `);
} else {
setEnteredNumber(enteredNumber + digit);
}