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); }