")
+/// Makes a fieldset with a name in the middle top part. Can apply additional classes
+#define fieldset_block(title, content, classes) ("")
+/// Makes a horizontal line with text in the middle
+#define separator_hr(str) ("
" + str + "
")
/// Emboldens runechat messages
#define RUNECHAT_BOLD(str) "+[str]+"
+/// Helper which creates a chat message which may have a tooltip in some contexts, but not others.
+#define conditional_tooltip(normal_text, tooltip_text, condition) ((condition) ? (span_tooltip(tooltip_text, normal_text)) : (normal_text))
diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm
index 60cd3a7b018b7..439d803fb942d 100644
--- a/code/__DEFINES/combat.dm
+++ b/code/__DEFINES/combat.dm
@@ -76,15 +76,12 @@
#define CANUNCONSCIOUS (1<<2)
/// If set, this mob can be grabbed or pushed when bumped into
#define CANPUSH (1<<3)
-/// Mob godmode. Prevents most statuses and damage from being taken, but is more often than not a crapshoot. Use with caution.
-#define GODMODE (1<<4)
DEFINE_BITFIELD(status_flags, list(
"CAN STUN" = CANSTUN,
"CAN KNOCKDOWN" = CANKNOCKDOWN,
"CAN UNCONSCIOUS" = CANUNCONSCIOUS,
"CAN PUSH" = CANPUSH,
- "GOD MODE" = GODMODE,
))
//Health Defines
diff --git a/code/__DEFINES/crafting.dm b/code/__DEFINES/crafting.dm
index 54dc479aa7306..cb7930e9d1fb6 100644
--- a/code/__DEFINES/crafting.dm
+++ b/code/__DEFINES/crafting.dm
@@ -28,6 +28,10 @@
#define CRAFT_CHECK_DENSITY (1<<5)
/// If the created atom will gain custom mat datums
#define CRAFT_APPLIES_MATS (1<<6)
+/// Crafting passes reagents of components to the finished product
+#define CRAFT_TRANSFERS_REAGENTS (1<<7)
+/// Crafting clears all reagents present in the finished product
+#define CRAFT_CLEARS_REAGENTS (1<<8)
//food/drink crafting defines
//When adding new defines, please make sure to also add them to the encompassing list
diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm
index 3282c9387a1e5..2e42957aa3a08 100644
--- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm
+++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_main.dm
@@ -9,6 +9,8 @@
#define COMSIG_ATOM_AFTER_SUCCESSFUL_INITIALIZED_ON "atom_init_success_on"
///from base of atom/examine(): (/mob, list/examine_text)
#define COMSIG_ATOM_EXAMINE "atom_examine"
+///from base of atom/examine_tags(): (/mob, list/examine_tags)
+#define COMSIG_ATOM_EXAMINE_TAGS "atom_examine_tags"
///from base of atom/get_examine_name(): (/mob, list/overrides)
#define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name"
//Positions for overrides list
diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm
index 24d7cd0b1701a..36a2ca2c80584 100644
--- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm
+++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movable.dm
@@ -15,9 +15,6 @@
///from base of atom/movable/Bump(): (/atom)
#define COMSIG_MOVABLE_BUMP "movable_bump"
#define COMPONENT_INTERCEPT_BUMPED (1<<0)
-///from base of atom/movable/newtonian_move(): (inertia_direction, start_delay)
-#define COMSIG_MOVABLE_NEWTONIAN_MOVE "movable_newtonian_move"
- #define COMPONENT_MOVABLE_NEWTONIAN_BLOCK (1<<0)
///from datum/component/drift/apply_initial_visuals(): ()
#define COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT "movable_drift_visual_attempt"
#define DRIFT_VISUAL_FAILED (1<<0)
@@ -119,6 +116,9 @@
/// From base of area/Exited(): (area/left, direction)
#define COMSIG_MOVABLE_EXITED_AREA "movable_exited_area"
+///from base of /datum/component/splat/splat: (hit_atom)
+#define COMSIG_MOVABLE_SPLAT "movable_splat"
+
///from base of /atom/movable/point_at: (atom/A, obj/effect/temp_visual/point/point)
#define COMSIG_MOVABLE_POINTED "movable_pointed"
diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm
index 6448be3fecb74..46e179ee567ba 100644
--- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm
+++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm
@@ -89,7 +89,6 @@
/// Sent from [atom/proc/item_interaction], when this atom is used as a tool and an event occurs
#define COMSIG_ITEM_TOOL_ACTED "tool_item_acted"
-/// This is sent via item interaction (IE, item clicking on atom) right before the item's inserted into the atom's storage
-/// Args: (obj/item/inserting, mob/living/user)
-#define COMSIG_ATOM_STORAGE_ITEM_INTERACT_INSERT "atom_storage_item_interact_insert"
- #define BLOCK_STORAGE_INSERT (1<<0)
+/// from /obj/projectile/energy/fisher/on_hit() or /obj/item/gun/energy/recharge/fisher when striking a target
+#define COMSIG_ATOM_SABOTEUR_ACT "hit_by_saboteur"
+ #define COMSIG_SABOTEUR_SUCCESS 1
diff --git a/code/__DEFINES/dcs/signals/signals_bitrunning.dm b/code/__DEFINES/dcs/signals/signals_bitrunning.dm
index 23461a90a1108..ac3095d6f5af8 100644
--- a/code/__DEFINES/dcs/signals/signals_bitrunning.dm
+++ b/code/__DEFINES/dcs/signals/signals_bitrunning.dm
@@ -53,3 +53,6 @@
/// from /obj/effect/mob_spawn/ghost_role/human/virtual_domain/proc/artificial_spawn() : (mob/living/runner)
#define COMSIG_BITRUNNER_SPAWNED "bitrunner_spawned"
+
+/// from /obj/effect/landmark/bitrunning/mob_segment/proc/spawn_mobs() : (list/mob/living)
+#define COMSIG_BITRUNNING_MOB_SEGMENT_SPAWNED "bitrunner_mob_segment_spawned"
diff --git a/code/__DEFINES/dcs/signals/signals_fish.dm b/code/__DEFINES/dcs/signals/signals_fish.dm
index 8af3b8dfca79c..9b614f924549f 100644
--- a/code/__DEFINES/dcs/signals/signals_fish.dm
+++ b/code/__DEFINES/dcs/signals/signals_fish.dm
@@ -8,6 +8,13 @@
///The item won't be inserted into the aquarium, but will early return attackby anyway.
#define COMSIG_CANNOT_INSERT_IN_AQUARIUM (1<<1)
+///Updates the appearance of a newly generated aquarium content visual:(visual)
+#define COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE "aquarium_content_apply_appearance"
+///Updates the base position of an aquarium content visual:(aquarium, visual)
+#define AQUARIUM_CONTENT_RANDOMIZE_POSITION "aquarium_content_randomize_position"
+///Updates the animation of an aquarium content visual:(aquarium, visual)
+#define COMSIG_AQUARIUM_CONTENT_DO_ANIMATION "aquarium_content_do_animation"
+
// Fish signals
#define COMSIG_FISH_STATUS_CHANGED "fish_status_changed"
#define COMSIG_FISH_STIRRED "fish_stirred"
@@ -15,19 +22,32 @@
#define COMSIG_FISH_LIFE "fish_life"
///From /datum/fish_trait/eat_fish: (predator)
#define COMSIG_FISH_EATEN_BY_OTHER_FISH "fish_eaten_by_other_fish"
+///From /obj/item/fish/generate_reagents_to_add, which returns a holder when the fish is eaten or composted for example: (list/reagents)
+#define COMSIG_GENERATE_REAGENTS_TO_ADD "generate_reagents_to_add"
///From /obj/item/fish/feed: (fed_reagents, fed_reagent_type)
#define COMSIG_FISH_FED "fish_on_fed"
-///from /obj/item/fish/pet_fish
-#define COMSIG_FISH_PETTED "fish_petted"
///From /obj/item/fish/update_size_and_weight: (new_size, new_weight)
#define COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT "fish_update_size_and_weight"
///From /obj/item/fish/update_fish_force: (weight_rank, bonus_malus)
#define COMSIG_FISH_FORCE_UPDATED "fish_force_updated"
+///From /obj/item/fish/interact_with_atom_secondary, sent to the target: (fish)
+#define COMSIG_FISH_RELEASED_INTO "fish_released_into"
+
+///From /datum/fishing_challenge/New: (datum/fishing_challenge/challenge)
+#define COMSIG_MOB_BEGIN_FISHING "mob_begin_fishing"
+///From /datum/fishing_challenge/start_minigame_phase: (datum/fishing_challenge/challenge)
+#define COMSIG_MOB_BEGIN_FISHING_MINIGAME "mob_begin_fishing_minigame"
+///From /datum/fishing_challenge/completed: (datum/fishing_challenge/challenge, win)
+#define COMSIG_MOB_COMPLETE_FISHING "mob_complete_fishing"
+
+/// Rolling a reward path for a fishing challenge
+#define COMSIG_FISHING_CHALLENGE_ROLL_REWARD "fishing_roll_reward"
+/// Adjusting the difficulty of a rishing challenge, often based on the reward path
+#define COMSIG_FISHING_CHALLENGE_GET_DIFFICULTY "fishing_get_difficulty"
/// Fishing challenge completed
-#define COMSIG_FISHING_CHALLENGE_COMPLETED "fishing_completed"
/// Sent to the fisherman when the reward is dispensed: (reward)
-#define COMSIG_FISH_SOURCE_REWARD_DISPENSED "mob_fish_source_reward_dispensed"
+#define COMSIG_FISH_SOURCE_REWARD_DISPENSED "fish_source_reward_dispensed"
/// Called when you try to use fishing rod on anything
#define COMSIG_PRE_FISHING "pre_fishing"
diff --git a/code/__DEFINES/dcs/signals/signals_global_object.dm b/code/__DEFINES/dcs/signals/signals_global_object.dm
index bed06ff176c1f..d100f47a3c97c 100644
--- a/code/__DEFINES/dcs/signals/signals_global_object.dm
+++ b/code/__DEFINES/dcs/signals/signals_global_object.dm
@@ -1,9 +1,9 @@
/// signals from globally accessible objects
-///from SSJob whenever SetupOccupations() is called, all occupations are set
+///from SSJob whenever setup_occupations() is called, all occupations are set
#define COMSIG_OCCUPATIONS_SETUP "occupations_setup"
-///from SSJob when DivideOccupations is called
+///from SSJob when divide_occupations() is called
#define COMSIG_OCCUPATIONS_DIVIDED "occupations_divided"
///from SSsun when the sun changes position : (azimuth)
diff --git a/code/__DEFINES/dcs/signals/signals_hud.dm b/code/__DEFINES/dcs/signals/signals_hud.dm
index 2d5d3eaa59cc3..b141f7d8f576b 100644
--- a/code/__DEFINES/dcs/signals/signals_hud.dm
+++ b/code/__DEFINES/dcs/signals/signals_hud.dm
@@ -1,5 +1,7 @@
/// Sent from /datum/hud/proc/on_eye_change(): (atom/old_eye, atom/new_eye)
#define COMSIG_HUD_EYE_CHANGED "hud_eye_changed"
+/// Sent from /datum/hud/proc/eye_z_changed() : (new_z)
+#define COMSIG_HUD_Z_CHANGED "hud_z_changed"
/// Sent from /datum/hud/proc/eye_z_changed() : (old_offset, new_offset)
#define COMSIG_HUD_OFFSET_CHANGED "hud_offset_changed"
/// Sent from /atom/movable/screen/lobby/button/collapse/proc/collapse_buttons() : ()
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm
index 16f7e00e78a19..1c6fcbffbda3d 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm
@@ -11,3 +11,6 @@
#define COMSIG_BOT_RESET "bot_reset"
///Sent off /mob/living/basic/bot/proc/set_mode_flags() : (new_flags)
#define COMSIG_BOT_MODE_FLAGS_SET "bot_mode_flags_set"
+
+///Signal sent off of ai/movement/proc/start_moving_towards
+#define COMSIG_MOB_AI_MOVEMENT_STARTED "mob_ai_movement_started"
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
index 56d5ddf452471..b95ffba607fd3 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_carbon.dm
@@ -118,7 +118,7 @@
///Applied preferences to a human
#define COMSIG_HUMAN_PREFS_APPLIED "human_prefs_applied"
-///Whenever EquipRanked is called, called after job is set
+///Whenever equip_rank is called, called after job is set
#define COMSIG_JOB_RECEIVED "job_received"
///from /mob/living/carbon/human/proc/set_coretemperature(): (oldvalue, newvalue)
#define COMSIG_HUMAN_CORETEMP_CHANGE "human_coretemp_change"
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
index 85e6a62d46c18..4a558c5fa7e03 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm
@@ -52,6 +52,8 @@
#define MOVE_ARG_NEW_LOC 1
/// The argument of move_args which dictates our movement direction
#define MOVE_ARG_DIRECTION 2
+/// From base of /client/Move(): (new_loc, direction)
+#define COMSIG_MOB_CLIENT_MOVE_NOGRAV "mob_client_move_nograv"
/// From base of /client/Move(): (direction, old_dir)
#define COMSIG_MOB_CLIENT_MOVED "mob_client_moved"
/// From base of /client/proc/change_view() (mob/source, new_size)
@@ -140,6 +142,11 @@
#define SPEECH_SAYMODE 10
#define SPEECH_MODS 11
+///from /datum/component/speechmod/handle_speech(): ()
+#define COMSIG_TRY_MODIFY_SPEECH "try_modify_speech"
+ ///Return value if we prevent speech from being modified
+ #define PREVENT_MODIFY_SPEECH 1
+
///from /mob/say_dead(): (mob/speaker, message)
#define COMSIG_MOB_DEADSAY "mob_deadsay"
#define MOB_DEADSAY_SIGNAL_INTERCEPT (1<<0)
@@ -170,8 +177,8 @@
///Called on user, from base of /datum/strippable_item/try_(un)equip() (atom/target, obj/item/equipping?)
#define COMSIG_TRY_STRIP "try_strip"
#define COMPONENT_CANT_STRIP (1<<0)
-///From /datum/component/creamed/Initialize()
-#define COMSIG_MOB_CREAMED "mob_creamed"
+///From /datum/component/face_decal/splat/Initialize()
+#define COMSIG_MOB_HIT_BY_SPLAT "hit_by_splat"
///From /obj/item/gun/proc/check_botched()
#define COMSIG_MOB_CLUMSY_SHOOT_FOOT "mob_clumsy_shoot_foot"
///from /obj/item/hand_item/slapper/attack_atom(): (source=obj/structure/table/slammed_table, mob/living/slammer)
@@ -248,5 +255,9 @@
/// from /mob/proc/key_down(): (key, client/client, full_key)
#define COMSIG_MOB_KEYDOWN "mob_key_down"
+/// from /mob/Process_Spacemove(movement_dir, continuous_move): (movement_dir, continuous_move, atom/backup)
+#define COMSIG_MOB_ATTEMPT_HALT_SPACEMOVE "mob_attempt_halt_spacemove"
+ #define COMPONENT_PREVENT_SPACEMOVE_HALT (1<<0)
+
/// from /mob/update_incapacitated(): (old_incap, new_incap)
#define COMSIG_MOB_INCAPACITATE_CHANGED "mob_incapacitated"
diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm
index 47f5b7485991b..aee6f2df79bb7 100644
--- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm
+++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm
@@ -8,3 +8,12 @@
#define COMSIG_BORG_HUG_HANDLED 1
///called from /mob/living/silicon/attack_hand proc
#define COMSIG_MOB_PAT_BORG "mob_pat_borg"
+///called when someone is inquiring about an AI's linked core
+#define COMSIG_SILICON_AI_CORE_STATUS "AI_core_status"
+ #define COMPONENT_CORE_ALL_GOOD (1<<0)
+ #define COMPONENT_CORE_DISCONNECTED (1<<1)
+///called when an AI (malf or perhaps combat upgraded or some other circumstance that has them inhabit
+///an APC) enters an APC
+#define COMSIG_SILICON_AI_OCCUPY_APC "AI_occupy_apc"
+///called when an AI vacates an APC
+#define COMSIG_SILICON_AI_VACATE_APC "AI_vacate_apc"
diff --git a/code/__DEFINES/dcs/signals/signals_mod.dm b/code/__DEFINES/dcs/signals/signals_mod.dm
index d3439cf857291..8cabf7537ab99 100644
--- a/code/__DEFINES/dcs/signals/signals_mod.dm
+++ b/code/__DEFINES/dcs/signals/signals_mod.dm
@@ -39,3 +39,5 @@
#define COMSIG_MOD_WEARER_SET "mod_wearer_set"
/// Called when the MODsuit wearer is unset.
#define COMSIG_MOD_WEARER_UNSET "mod_wearer_unset"
+/// Sent by the tether module when it triggers its snapping function
+#define COMSIG_MOD_TETHER_SNAP "mod_tether_snap"
diff --git a/code/__DEFINES/dcs/signals/signals_movetype.dm b/code/__DEFINES/dcs/signals/signals_movetype.dm
index bc8b296b47531..da584ba022f4a 100644
--- a/code/__DEFINES/dcs/signals/signals_movetype.dm
+++ b/code/__DEFINES/dcs/signals/signals_movetype.dm
@@ -1,6 +1,3 @@
-// /datum/element/movetype_handler signals
-/// Called when the floating anim has to be temporarily stopped and restarted later: (timer)
-#define COMSIG_PAUSE_FLOATING_ANIM "pause_floating_anim"
/// From base of datum/element/movetype_handler/on_movement_type_trait_gain: (flag, old_movement_type)
#define COMSIG_MOVETYPE_FLAG_ENABLED "movetype_flag_enabled"
/// From base of datum/element/movetype_handler/on_movement_type_trait_loss: (flag, old_movement_type)
diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm
index 797256017d456..a9cc41b7d8d8d 100644
--- a/code/__DEFINES/dcs/signals/signals_object.dm
+++ b/code/__DEFINES/dcs/signals/signals_object.dm
@@ -194,6 +194,8 @@
#define COMSIG_TOOL_IN_USE "tool_in_use"
///from base of [/obj/item/proc/tool_start_check]: (mob/living/user)
#define COMSIG_TOOL_START_USE "tool_start_use"
+/// From /obj/item/multitool/remove_buffer(): (buffer)
+#define COMSIG_MULTITOOL_REMOVE_BUFFER "multitool_remove_buffer"
///from [/obj/item/proc/disableEmbedding]:
#define COMSIG_ITEM_DISABLE_EMBED "item_disable_embed"
///from [/obj/effect/mine/proc/triggermine]:
@@ -415,10 +417,8 @@
#define COMSIG_PROJECTILE_ON_SPAWN_DROP "projectile_on_spawn_drop"
///sent to the projectile when spawning the item (shrapnel) that may be embedded: (new_item)
#define COMSIG_PROJECTILE_ON_SPAWN_EMBEDDED "projectile_on_spawn_embedded"
-
-/// from /obj/projectile/energy/fisher/on_hit() or /obj/item/gun/energy/recharge/fisher when striking a target
-#define COMSIG_HIT_BY_SABOTEUR "hit_by_saboteur"
- #define COMSIG_SABOTEUR_SUCCESS (1<<0)
+///sent to the projectile when successfully embedding into something
+#define COMSIG_PROJECTILE_ON_EMBEDDED "projectile_on_embedded"
// /obj/vehicle/sealed/car/vim signals
diff --git a/code/__DEFINES/dcs/signals/signals_plane_master_group.dm b/code/__DEFINES/dcs/signals/signals_plane_master_group.dm
new file mode 100644
index 0000000000000..d27adb5f8c957
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_plane_master_group.dm
@@ -0,0 +1,2 @@
+/// from /datum/plane_master_group/proc/set_hud(): (datum/hud/new_hud)
+#define COMSIG_GROUP_HUD_CHANGED "group_hud_changed"
diff --git a/code/__DEFINES/dcs/signals/signals_screentips.dm b/code/__DEFINES/dcs/signals/signals_screentips.dm
index 8f7326ee2ee79..31a851c048395 100644
--- a/code/__DEFINES/dcs/signals/signals_screentips.dm
+++ b/code/__DEFINES/dcs/signals/signals_screentips.dm
@@ -21,3 +21,15 @@
/// Tells the contextual screentips system that the list context was mutated.
#define CONTEXTUAL_SCREENTIP_SET (1 << 0)
+
+
+/// A user screentip name override.
+/// These are used for mobs that may override the names of atoms they hover over.
+/// Examples include prosopagnosia (sees human names as Unknown regardless of what they are).
+/// Called on /mob with a mutable screentip name list, the item being used, and the atom hovered over.
+/// A screentip name override list is a list used for returning a string value from the signal. Only the first value matters.
+/// If you mutate the list in this signal, you must return SCREENTIP_NAME_SET.
+#define COMSIG_MOB_REQUESTING_SCREENTIP_NAME_FROM_USER "mob_requesting_screentip_name_from_user"
+
+/// Tells the screentips system that the list names was mutated.
+#define SCREENTIP_NAME_SET (1 << 0)
diff --git a/code/__DEFINES/dcs/signals/signals_spell.dm b/code/__DEFINES/dcs/signals/signals_spell.dm
index 8d02affded85a..08074116be2c3 100644
--- a/code/__DEFINES/dcs/signals/signals_spell.dm
+++ b/code/__DEFINES/dcs/signals/signals_spell.dm
@@ -68,6 +68,9 @@
#define COMSIG_SPELL_TOUCH_HAND_HIT "spell_touch_hand_cast"
// Jaunt Spells
+/// Sent from datum/action/cooldown/spell/jaunt/before_cast, before the mob enters jaunting as a pre-check: (datum/action/cooldown/spell/spell)
+#define COMSIG_MOB_PRE_JAUNT "spell_mob_pre_jaunt"
+ #define COMPONENT_BLOCK_JAUNT (1<<0)
/// Sent from datum/action/cooldown/spell/jaunt/enter_jaunt, to the mob jaunting: (obj/effect/dummy/phased_mob/jaunt, datum/action/cooldown/spell/spell)
#define COMSIG_MOB_ENTER_JAUNT "spell_mob_enter_jaunt"
/// Set from /obj/effect/dummy/phased_mob after the mob is ejected from its contents: (obj/effect/dummy/phased_mob/jaunt, mob/living/unjaunter)
diff --git a/code/__DEFINES/fish.dm b/code/__DEFINES/fish.dm
index d6bcc2ec796e5..43e0e8587f9aa 100644
--- a/code/__DEFINES/fish.dm
+++ b/code/__DEFINES/fish.dm
@@ -6,6 +6,11 @@
// Baseline fishing difficulty levels
#define FISHING_DEFAULT_DIFFICULTY 15
#define FISHING_EASY_DIFFICULTY 10
+/**
+ * The minimum value of the difficulty of the minigame (unless it reaches 0 than it's auto-win)
+ * Any lower than this and the fish will be way too lethargic for the minigame to be engaging in the slightest.
+ */
+#define FISHING_MINIMUM_DIFFICULTY 6
/// Difficulty modifier when bait is fish's favorite
#define FAV_BAIT_DIFFICULTY_MOD -5
@@ -67,9 +72,11 @@
#define FISHING_MINIGAME_RULE_FLIP (1 << 5)
///Skip the biting phase and go straight to the minigame, avoiding the penalty for having slow reflexes.
#define FISHING_MINIGAME_AUTOREEL (1 << 6)
+///The fish will fade in and out at intervals
+#define FISHING_MINIGAME_RULE_CAMO (1 << 7)
///all the effects that are active and will last for a few seconds before triggering a cooldown
-#define FISHING_MINIGAME_ACTIVE_EFFECTS (FISHING_MINIGAME_RULE_ANTIGRAV|FISHING_MINIGAME_RULE_FLIP)
+#define FISHING_MINIGAME_ACTIVE_EFFECTS (FISHING_MINIGAME_RULE_ANTIGRAV|FISHING_MINIGAME_RULE_FLIP|FISHING_MINIGAME_RULE_CAMO)
/// The default additive value for fishing hook catch weight modifiers.
#define FISHING_DEFAULT_HOOK_BONUS_ADDITIVE 0
@@ -134,12 +141,27 @@
#define FISH_WEIGHT_SLOWDOWN_EXPONENT 0.54
///Used to calculate the force of the fish by comparing (1 + log(weight/this_define)) and the w_class of the item.
#define FISH_WEIGHT_FORCE_DIVISOR 250
+///The multiplier used in the FISH_WEIGHT_BITE_DIVISOR define
+#define FISH_WEIGHT_GRIND_TO_BITE_MULT 0.4
+///Used to calculate how many bites a fish can take and therefore the amount of reagents it has.
+#define FISH_WEIGHT_BITE_DIVISOR (FISH_GRIND_RESULTS_WEIGHT_DIVISOR * FISH_WEIGHT_GRIND_TO_BITE_MULT)
///The breeding timeout for newly instantiated fish is multiplied by this.
#define NEW_FISH_BREEDING_TIMEOUT_MULT 2
///The last feeding timestamp of newly instantiated fish is multiplied by this: ergo, they spawn 50% hungry.
#define NEW_FISH_LAST_FEEDING_MULT 0.5
+//IF YOU ADD ANY NEW FLAG, ADD IT TO THE RESPECTIVE BITFIELD in _globalvars/bitfields.dm TOO!
+
+///This fish is shown in the catalog and on the wiki (this only matters as an initial, compile-time value)
+#define FISH_FLAG_SHOW_IN_CATALOG (1<<0)
+///This fish has a flopping animation done through matrices
+#define FISH_DO_FLOP_ANIM (1<<1)
+///This fish has been petted in the last 30 seconds
+#define FISH_FLAG_PETTED (1<<2)
+///This fish can be scanned to complete fish scanning experiments
+#define FISH_FLAG_EXPERIMENT_SCANNABLE (1<<3)
+
#define MIN_AQUARIUM_TEMP T0C
#define MAX_AQUARIUM_TEMP (T0C + 100)
#define DEFAULT_AQUARIUM_TEMP (T0C + 24)
@@ -156,8 +178,8 @@
#define AQUARIUM_FLUID_SALTWATER "Saltwater"
#define AQUARIUM_FLUID_SULPHWATEVER "Sulfuric Water"
#define AQUARIUM_FLUID_AIR "Air"
-#define AQUARIUM_FLUID_ANADROMOUS "Adaptive to both Freshwater and Saltwater"
-#define AQUARIUM_FLUID_ANY_WATER "Adaptive to all kind of water"
+#define AQUARIUM_FLUID_ANADROMOUS "Anadromous"
+#define AQUARIUM_FLUID_ANY_WATER "Any Fluid"
///Fluff. The name of the aquarium company shown in the fish catalog
#define AQUARIUM_COMPANY "Aquatech Ltd."
@@ -182,3 +204,44 @@
//Minigame defines
/// The height of the minigame slider. Not in pixels, but minigame units.
#define FISHING_MINIGAME_AREA 1000
+
+///The fish needs to be cooked for at least this long so that it can be safely eaten
+#define FISH_SAFE_COOKING_DURATION 30 SECONDS
+
+///Defines for fish properties from the collect_fish_properties proc
+#define FISH_PROPERTIES_FAV_BAIT "fav_bait"
+#define FISH_PROPERTIES_BAD_BAIT "bad_bait"
+#define FISH_PROPERTIES_TRAITS "fish_traits"
+#define FISH_PROPERTIES_BEAUTY_SCORE "beauty_score"
+#define FISH_PROPERTIES_EVOLUTIONS "evolutions"
+
+///Define for favorite and disliked baits that aren't just item typepaths.
+#define FISH_BAIT_TYPE "Type"
+#define FISH_BAIT_FOODTYPE "Foodtype"
+#define FISH_BAIT_REAGENT "Reagent"
+#define FISH_BAIT_VALUE "Value"
+#define FISH_BAIT_AMOUNT "Amount"
+
+
+///We multiply the weight of fish inside the loot table by this value if we are goofy enough to fish without a bait.
+#define FISH_WEIGHT_MULT_WITHOUT_BAIT 0.15
+
+/**
+ * A macro to ensure the wikimedia filenames of fish icons are unique, especially since there're a couple fish that have
+ * quite ambiguous names/icon_states like "checkered" or "pike"
+ */
+#define FISH_AUTOWIKI_FILENAME(fish) SANITIZE_FILENAME("[initial(fish.icon_state)]_wiki_fish")
+
+///The list keys for the autowiki for fish sources
+#define FISH_SOURCE_AUTOWIKI_NAME "name"
+#define FISH_SOURCE_AUTOWIKI_ICON "icon"
+#define FISH_SOURCE_AUTOWIKI_WEIGHT "weight"
+#define FISH_SOURCE_AUTOWIKI_WEIGHT_SUFFIX "weight_suffix"
+#define FISH_SOURCE_AUTOWIKI_NOTES "notes"
+
+///Special value for the name key that always comes first when the data is sorted, regardless of weight.
+#define FISH_SOURCE_AUTOWIKI_DUD "Nothing"
+///Special value for the name key that always comes last
+#define FISH_SOURCE_AUTOWIKI_OTHER "Other Stuff"
+///The filename for the icon for "other stuff" which we don't articulate about on the autowiki
+#define FISH_SOURCE_AUTOWIKI_QUESTIONMARK "questionmark"
diff --git a/code/__DEFINES/food.dm b/code/__DEFINES/food.dm
index c9b7cb43cf0d9..b7e5b38482d16 100644
--- a/code/__DEFINES/food.dm
+++ b/code/__DEFINES/food.dm
@@ -170,12 +170,21 @@ GLOBAL_LIST_INIT(food_buffs, list(
#define FOOD_IN_CONTAINER (1<<0)
/// Finger food can be eaten while walking / running around
#define FOOD_FINGER_FOOD (1<<1)
+/// Examining this edible won't show infos on food types, bites and remote tasting etc.
+#define FOOD_NO_EXAMINE (1<<2)
+/// This food item doesn't track bitecounts, use responsibly.
+#define FOOD_NO_BITECOUNT (1<<3)
DEFINE_BITFIELD(food_flags, list(
"FOOD_FINGER_FOOD" = FOOD_FINGER_FOOD,
"FOOD_IN_CONTAINER" = FOOD_IN_CONTAINER,
+ "FOOD_NO_EXAMINE" = FOOD_NO_EXAMINE,
+ "FOOD_NO_BITECOUNT" = FOOD_NO_BITECOUNT,
))
+///Define for return value of the after_eat callback that will call OnConsume if it hasn't already.
+#define FOOD_AFTER_EAT_CONSUME_ANYWAY 2
+
#define STOP_SERVING_BREAKFAST (15 MINUTES)
#define FOOD_MEAT_HUMAN 50
diff --git a/code/__DEFINES/footsteps.dm b/code/__DEFINES/footsteps.dm
index a8a7ad975af2e..cffe920215335 100644
--- a/code/__DEFINES/footsteps.dm
+++ b/code/__DEFINES/footsteps.dm
@@ -84,10 +84,10 @@ GLOBAL_LIST_INIT(footstep, list(
'sound/effects/footstep/grass3.ogg',
'sound/effects/footstep/grass4.ogg'), 75, 0),
FOOTSTEP_WATER = list(list(
- 'sound/effects/footstep/water1.ogg',
- 'sound/effects/footstep/water2.ogg',
- 'sound/effects/footstep/water3.ogg',
- 'sound/effects/footstep/water4.ogg'), 100, 1),
+ 'sound/effects/footstep/water/water1.ogg',
+ 'sound/effects/footstep/water/water2.ogg',
+ 'sound/effects/footstep/water/water3.ogg',
+ 'sound/effects/footstep/water/water4.ogg'), 100, 1),
FOOTSTEP_LAVA = list(list(
'sound/effects/footstep/lava1.ogg',
'sound/effects/footstep/lava2.ogg',
@@ -134,10 +134,10 @@ GLOBAL_LIST_INIT(barefootstep, list(
'sound/effects/footstep/grass3.ogg',
'sound/effects/footstep/grass4.ogg'), 75, 0),
FOOTSTEP_WATER = list(list(
- 'sound/effects/footstep/water1.ogg',
- 'sound/effects/footstep/water2.ogg',
- 'sound/effects/footstep/water3.ogg',
- 'sound/effects/footstep/water4.ogg'), 100, 1),
+ 'sound/effects/footstep/water/water1.ogg',
+ 'sound/effects/footstep/water/water2.ogg',
+ 'sound/effects/footstep/water/water3.ogg',
+ 'sound/effects/footstep/water/water4.ogg'), 100, 1),
FOOTSTEP_LAVA = list(list(
'sound/effects/footstep/lava1.ogg',
'sound/effects/footstep/lava2.ogg',
@@ -178,10 +178,10 @@ GLOBAL_LIST_INIT(clawfootstep, list(
'sound/effects/footstep/grass3.ogg',
'sound/effects/footstep/grass4.ogg'), 75, 0),
FOOTSTEP_WATER = list(list(
- 'sound/effects/footstep/water1.ogg',
- 'sound/effects/footstep/water2.ogg',
- 'sound/effects/footstep/water3.ogg',
- 'sound/effects/footstep/water4.ogg'), 100, 1),
+ 'sound/effects/footstep/water/water1.ogg',
+ 'sound/effects/footstep/water/water2.ogg',
+ 'sound/effects/footstep/water/water3.ogg',
+ 'sound/effects/footstep/water/water4.ogg'), 100, 1),
FOOTSTEP_LAVA = list(list(
'sound/effects/footstep/lava1.ogg',
'sound/effects/footstep/lava2.ogg',
@@ -196,10 +196,10 @@ GLOBAL_LIST_INIT(heavyfootstep, list(
'sound/effects/footstep/heavy1.ogg',
'sound/effects/footstep/heavy2.ogg'), 100, 2),
FOOTSTEP_WATER = list(list(
- 'sound/effects/footstep/water1.ogg',
- 'sound/effects/footstep/water2.ogg',
- 'sound/effects/footstep/water3.ogg',
- 'sound/effects/footstep/water4.ogg'), 100, 2),
+ 'sound/effects/footstep/water/water1.ogg',
+ 'sound/effects/footstep/water/water2.ogg',
+ 'sound/effects/footstep/water/water3.ogg',
+ 'sound/effects/footstep/water/water4.ogg'), 100, 2),
FOOTSTEP_LAVA = list(list(
'sound/effects/footstep/lava1.ogg',
'sound/effects/footstep/lava2.ogg',
diff --git a/code/__DEFINES/hud.dm b/code/__DEFINES/hud.dm
index e762b406d1eb4..3c46589002475 100644
--- a/code/__DEFINES/hud.dm
+++ b/code/__DEFINES/hud.dm
@@ -77,7 +77,7 @@
#define ui_building "EAST-4:22,SOUTH:21"
#define ui_language_menu "EAST-4:6,SOUTH:21"
#define ui_navigate_menu "EAST-4:22,SOUTH:5"
-#define ui_floor_menu "EAST-4:14,SOUTH:37"
+#define ui_floor_changer "EAST-3:24, SOUTH+1:3"
//Upper left (action buttons)
#define ui_action_palette "WEST+0:23,NORTH-1:5"
@@ -110,6 +110,9 @@
#define ui_living_pull "EAST-1:28,CENTER-3:15"
#define ui_living_healthdoll "EAST-1:28,CENTER-1:15"
+//Humans
+#define ui_human_floor_changer "EAST-4:22, SOUTH+1:7"
+
//Drones
#define ui_drone_drop "CENTER+1:18,SOUTH:5"
#define ui_drone_pull "CENTER+1.5:2,SOUTH:5"
@@ -132,7 +135,7 @@
#define ui_borg_alerts "CENTER+4:21,SOUTH:5"
#define ui_borg_language_menu "CENTER+4:19,SOUTH+1:6"
#define ui_borg_navigate_menu "CENTER+4:19,SOUTH+1:6"
-#define ui_borg_floor_menu "CENTER+4:-13,SOUTH+1:6"
+#define ui_borg_floor_changer "EAST-1:28,SOUTH+1:39"
//Aliens
#define ui_alien_health "EAST,CENTER-1:15"
@@ -141,7 +144,6 @@
#define ui_alien_storage_r "CENTER+1:18,SOUTH:5"
#define ui_alien_language_menu "EAST-4:20,SOUTH:5"
#define ui_alien_navigate_menu "EAST-4:20,SOUTH:5"
-#define ui_alien_floor_menu "EAST-4:-12,SOUTH:5"
//AI
#define ui_ai_core "BOTTOM:6,RIGHT-4"
@@ -150,7 +152,6 @@
#define ui_ai_state_laws "BOTTOM:6,RIGHT-1"
#define ui_ai_mod_int "BOTTOM:6,RIGHT"
#define ui_ai_language_menu "BOTTOM+1:8,RIGHT-1:30"
-#define ui_ai_floor_menu "BOTTOM+1:8,RIGHT-1:14"
#define ui_ai_crew_monitor "BOTTOM:6,CENTER-1"
#define ui_ai_crew_manifest "BOTTOM:6,CENTER"
@@ -192,8 +193,8 @@
#define ui_ghost_teleport "SOUTH:6,CENTER:24"
#define ui_ghost_pai "SOUTH: 6, CENTER+1:24"
#define ui_ghost_minigames "SOUTH: 6, CENTER+2:24"
-#define ui_ghost_language_menu "SOUTH: 22, CENTER+3:8"
-#define ui_ghost_floor_menu "SOUTH: 6, CENTER+3:8"
+#define ui_ghost_language_menu "SOUTH: 22, CENTER+3:22"
+#define ui_ghost_floor_changer "SOUTH: 6, CENTER+3:23"
//Blobbernauts
#define ui_blobbernaut_overmind_health "EAST-1:28,CENTER+0:19"
diff --git a/code/__DEFINES/inventory.dm b/code/__DEFINES/inventory.dm
index c58f12bf5c957..b06bf36fdb8fa 100644
--- a/code/__DEFINES/inventory.dm
+++ b/code/__DEFINES/inventory.dm
@@ -110,6 +110,8 @@ DEFINE_BITFIELD(no_equip_flags, list(
#define HIDEMUTWINGS (1<<13)
///hides belts and riggings
#define HIDEBELT (1<<14)
+///hides antennae
+#define HIDEANTENNAE (1<<15)
//bitflags for clothing coverage - also used for limbs
#define HEAD (1<<0)
diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm
index 8846672efe1b7..572b6f1197e82 100644
--- a/code/__DEFINES/is_helpers.dm
+++ b/code/__DEFINES/is_helpers.dm
@@ -253,7 +253,7 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list(
#define ismecha(A) (istype(A, /obj/vehicle/sealed/mecha))
-#define ismopable(A) (A && (A.layer <= FLOOR_CLEAN_LAYER)) //If something can be cleaned by floor-cleaning devices such as mops or clean bots
+#define ismopable(A) (A && ((A.plane == FLOOR_PLANE) ? (A.layer <= FLOOR_CLEAN_LAYER) : (A.layer <= GAME_CLEAN_LAYER))) //If something can be cleaned by floor-cleaning devices such as mops or clean bots
#define isorgan(A) (istype(A, /obj/item/organ))
@@ -279,6 +279,8 @@ GLOBAL_LIST_INIT(turfs_pass_meteor, typecacheof(list(
#define is_reagent_container(O) (istype(O, /obj/item/reagent_containers))
+#define isapc(A) (istype(A, /obj/machinery/power/apc))
+
//Assemblies
#define isassembly(O) (istype(O, /obj/item/assembly))
@@ -328,4 +330,4 @@ GLOBAL_LIST_INIT(book_types, typecacheof(list(
#define is_unassigned_job(job_type) (istype(job_type, /datum/job/unassigned))
#define isprojectilespell(thing) (istype(thing, /datum/action/cooldown/spell/pointed/projectile))
-#define is_multi_tile_object(atom) (atom.bound_width > world.icon_size || atom.bound_height > world.icon_size)
+#define is_multi_tile_object(atom) (atom.bound_width > ICON_SIZE_X || atom.bound_height > ICON_SIZE_Y)
diff --git a/code/__DEFINES/jobs.dm b/code/__DEFINES/jobs.dm
index 2c3b151855cef..953e7648009b8 100644
--- a/code/__DEFINES/jobs.dm
+++ b/code/__DEFINES/jobs.dm
@@ -93,6 +93,7 @@
#define JOB_LAWYER "Lawyer"
#define JOB_CHAPLAIN "Chaplain"
#define JOB_PSYCHOLOGIST "Psychologist"
+#define JOB_PUN_PUN "Pun Pun"
//ERTs
#define JOB_ERT_DEATHSQUAD "Death Commando"
#define JOB_ERT_COMMANDER "Emergency Response Team Commander"
@@ -136,31 +137,32 @@
#define JOB_DISPLAY_ORDER_LAWYER 12
#define JOB_DISPLAY_ORDER_CHAPLAIN 13
#define JOB_DISPLAY_ORDER_PSYCHOLOGIST 14
-#define JOB_DISPLAY_ORDER_AI 15
-#define JOB_DISPLAY_ORDER_CYBORG 16
-#define JOB_DISPLAY_ORDER_CHIEF_ENGINEER 17
-#define JOB_DISPLAY_ORDER_STATION_ENGINEER 18
-#define JOB_DISPLAY_ORDER_ATMOSPHERIC_TECHNICIAN 19
-#define JOB_DISPLAY_ORDER_QUARTERMASTER 20
-#define JOB_DISPLAY_ORDER_CARGO_TECHNICIAN 21
-#define JOB_DISPLAY_ORDER_SHAFT_MINER 22
-#define JOB_DISPLAY_ORDER_BITRUNNER 23
-#define JOB_DISPLAY_ORDER_CARGO_GORILLA 24
-#define JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER 25
-#define JOB_DISPLAY_ORDER_MEDICAL_DOCTOR 26
-#define JOB_DISPLAY_ORDER_PARAMEDIC 27
-#define JOB_DISPLAY_ORDER_CHEMIST 28
-#define JOB_DISPLAY_ORDER_CORONER 29
-#define JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR 30
-#define JOB_DISPLAY_ORDER_SCIENTIST 31
-#define JOB_DISPLAY_ORDER_ROBOTICIST 32
-#define JOB_DISPLAY_ORDER_GENETICIST 33
-#define JOB_DISPLAY_ORDER_HEAD_OF_SECURITY 34
-#define JOB_DISPLAY_ORDER_VETERAN_ADVISOR 35
-#define JOB_DISPLAY_ORDER_WARDEN 36
-#define JOB_DISPLAY_ORDER_DETECTIVE 37
-#define JOB_DISPLAY_ORDER_SECURITY_OFFICER 38
-#define JOB_DISPLAY_ORDER_PRISONER 39
+#define JOB_DISPLAY_ORDER_PUN_PUN 15
+#define JOB_DISPLAY_ORDER_AI 16
+#define JOB_DISPLAY_ORDER_CYBORG 17
+#define JOB_DISPLAY_ORDER_CHIEF_ENGINEER 18
+#define JOB_DISPLAY_ORDER_STATION_ENGINEER 19
+#define JOB_DISPLAY_ORDER_ATMOSPHERIC_TECHNICIAN 20
+#define JOB_DISPLAY_ORDER_QUARTERMASTER 21
+#define JOB_DISPLAY_ORDER_CARGO_TECHNICIAN 22
+#define JOB_DISPLAY_ORDER_SHAFT_MINER 23
+#define JOB_DISPLAY_ORDER_BITRUNNER 24
+#define JOB_DISPLAY_ORDER_CARGO_GORILLA 25
+#define JOB_DISPLAY_ORDER_CHIEF_MEDICAL_OFFICER 26
+#define JOB_DISPLAY_ORDER_MEDICAL_DOCTOR 27
+#define JOB_DISPLAY_ORDER_PARAMEDIC 28
+#define JOB_DISPLAY_ORDER_CHEMIST 29
+#define JOB_DISPLAY_ORDER_CORONER 30
+#define JOB_DISPLAY_ORDER_RESEARCH_DIRECTOR 31
+#define JOB_DISPLAY_ORDER_SCIENTIST 32
+#define JOB_DISPLAY_ORDER_ROBOTICIST 33
+#define JOB_DISPLAY_ORDER_GENETICIST 34
+#define JOB_DISPLAY_ORDER_HEAD_OF_SECURITY 35
+#define JOB_DISPLAY_ORDER_VETERAN_ADVISOR 36
+#define JOB_DISPLAY_ORDER_WARDEN 37
+#define JOB_DISPLAY_ORDER_DETECTIVE 38
+#define JOB_DISPLAY_ORDER_SECURITY_OFFICER 39
+#define JOB_DISPLAY_ORDER_PRISONER 40
#define DEPARTMENT_UNASSIGNED "No Department"
@@ -203,7 +205,7 @@ DEFINE_BITFIELD(departments_bitflags, list(
#define JOB_ANNOUNCE_ARRIVAL (1<<0)
/// Whether the mob is added to the crew manifest.
#define JOB_CREW_MANIFEST (1<<1)
-/// Whether the mob is equipped through SSjob.EquipRank() on spawn.
+/// Whether the mob is equipped through SSjob.equip_rank() on spawn.
#define JOB_EQUIP_RANK (1<<2)
/// Whether the job is considered a regular crew member of the station. Equipment such as AI and cyborgs not included.
#define JOB_CREW_MEMBER (1<<3)
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index d5c17a877a4b6..8005787676aec 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -32,6 +32,7 @@
#define DEFAULT_PLANE 0 //Marks out the default plane, even if we don't use it
+#define WEATHER_PLANE 1
#define AREA_PLANE 2
#define MASSIVE_OBJ_PLANE 3
#define GHOST_PLANE 4
@@ -65,6 +66,8 @@
///Things that should render ignoring lighting
#define ABOVE_LIGHTING_PLANE 17
+#define WEATHER_GLOW_PLANE 18
+
///---------------- MISC -----------------------
///Pipecrawling images
@@ -148,6 +151,11 @@
#define ABOVE_OPEN_TURF_LAYER (12 + TOPDOWN_LAYER)
///catwalk overlay of /turf/open/floor/plating/catwalk_floor
#define CATWALK_LAYER (13 + TOPDOWN_LAYER)
+#define LOWER_RUNE_LAYER (14 + TOPDOWN_LAYER)
+#define RUNE_LAYER (15 + TOPDOWN_LAYER)
+/// [GAME_CLEAN_LAYER] but for floors.
+/// Basically any layer below this (numerically) is "on" a floor for the purposes of washing
+#define FLOOR_CLEAN_LAYER (20 + TOPDOWN_LAYER)
//WALL_PLANE layers
#define BELOW_CLOSED_TURF_LAYER 2.053
@@ -166,12 +174,10 @@
#define PLUMBING_PIPE_VISIBILE_LAYER 2.495//layer = initial(layer) + ducting_layer / 3333 in atmospherics/handle_layer() to determine order of duct overlap
#define BOT_PATH_LAYER 2.497
#define LOW_OBJ_LAYER 2.5
-#define LOW_SIGIL_LAYER 2.52
-#define SIGIL_LAYER 2.53
#define HIGH_PIPE_LAYER 2.54
// Anything above this layer is not "on" a turf for the purposes of washing
// I hate this life of ours
-#define FLOOR_CLEAN_LAYER 2.55
+#define GAME_CLEAN_LAYER 2.55
#define TRAM_STRUCTURE_LAYER 2.57
#define TRAM_FLOOR_LAYER 2.58
#define TRAM_WALL_LAYER 2.59
diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm
index 9d2cf451c62e1..8dd8db3fd06c6 100644
--- a/code/__DEFINES/machines.dm
+++ b/code/__DEFINES/machines.dm
@@ -135,6 +135,15 @@
/// Max length of a status line in the status display
#define MAX_STATUS_LINE_LENGTH 40
+///Define for automated system arrival announcement
+#define AUTO_ANNOUNCE_ARRIVAL "ARRIVAL"
+///Define for automated system announcement when a head of staff arrives
+#define AUTO_ANNOUNCE_NEWHEAD "NEWHEAD"
+///Define for automated system announcement for when the arrival shuttle is broken
+#define AUTO_ANNOUNCE_ARRIVALS_BROKEN "ARRIVALS_BROKEN"
+///Define for automated system announcement for researched nodes
+#define AUTO_ANNOUNCE_NODE "NODE"
+
/// Blank Status Display
#define SD_BLANK 0
/// Shows the emergency shuttle timer
diff --git a/code/__DEFINES/maps.dm b/code/__DEFINES/maps.dm
index 3c87195e99076..33147916f4e38 100644
--- a/code/__DEFINES/maps.dm
+++ b/code/__DEFINES/maps.dm
@@ -221,7 +221,7 @@ Always compile, always use that verb, and always make sure that it works for wha
#define CLUSTER_CHECK_ALL 30 //!Don't let anything cluster, like, at all
/// Checks the job changes in the map config for the passed change key.
-#define CHECK_MAP_JOB_CHANGE(job, change) SSmapping.config.job_changes?[job]?[change]
+#define CHECK_MAP_JOB_CHANGE(job, change) SSmapping.current_map.job_changes?[job]?[change]
///Identifiers for away mission spawnpoints
#define AWAYSTART_BEACH "AWAYSTART_BEACH"
diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm
index 1939ca94ec455..a7a95817b4405 100644
--- a/code/__DEFINES/maths.dm
+++ b/code/__DEFINES/maths.dm
@@ -188,21 +188,21 @@
var/pixel_x = 0
var/pixel_y = 0
for(var/i in 1 to increments)
- pixel_x += sin(angle)+16*sin(angle)*2
- pixel_y += cos(angle)+16*cos(angle)*2
+ pixel_x += sin(angle)+(ICON_SIZE_X/2)*sin(angle)*2
+ pixel_y += cos(angle)+(ICON_SIZE_Y/2)*cos(angle)*2
var/new_x = starting.x
var/new_y = starting.y
- while(pixel_x > 16)
- pixel_x -= 32
+ while(pixel_x > (ICON_SIZE_X/2))
+ pixel_x -= ICON_SIZE_X
new_x++
- while(pixel_x < -16)
- pixel_x += 32
+ while(pixel_x < -(ICON_SIZE_X/2))
+ pixel_x += ICON_SIZE_X
new_x--
- while(pixel_y > 16)
- pixel_y -= 32
+ while(pixel_y > (ICON_SIZE_Y/2))
+ pixel_y -= ICON_SIZE_Y
new_y++
- while(pixel_y < -16)
- pixel_y += 32
+ while(pixel_y < -(ICON_SIZE_Y/2))
+ pixel_y += ICON_SIZE_Y
new_y--
new_x = clamp(new_x, 1, world.maxx)
new_y = clamp(new_y, 1, world.maxy)
diff --git a/code/__DEFINES/memory_defines.dm b/code/__DEFINES/memory_defines.dm
index 2b07ab6270d57..f6c537f9e8187 100644
--- a/code/__DEFINES/memory_defines.dm
+++ b/code/__DEFINES/memory_defines.dm
@@ -1,7 +1,7 @@
///name of the file that has all the memory strings
#define MEMORY_FILE "memories.json"
///name of the file that has all the saved engravings
-#define ENGRAVING_SAVE_FILE "data/engravings/[SSmapping.config.map_name]_engravings.json"
+#define ENGRAVING_SAVE_FILE "data/engravings/[SSmapping.current_map.map_name]_engravings.json"
///name of the file that has all the prisoner tattoos
#define PRISONER_TATTOO_SAVE_FILE "data/engravings/prisoner_tattoos.json"
///Current version of the engraving persistence json
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 0de4d7e6ab763..f0368ac8d92df 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -44,6 +44,10 @@
#define VENTCRAWLER_NUDE 1
#define VENTCRAWLER_ALWAYS 2
+// Flags for the mob_flags var on /mob
+/// May override the names used in screentips of OTHER OBJECTS hovered over.
+#define MOB_HAS_SCREENTIPS_NAME_OVERRIDE (1 << 0)
+
//Mob bio-types flags
///The mob is organic, can heal from medical sutures.
#define MOB_ORGANIC (1 << 0)
@@ -995,3 +999,7 @@ GLOBAL_LIST_INIT(layers_to_offset, list(
#define BUTT_SPRITE_PLASMA "plasma"
#define BUTT_SPRITE_FUZZY "fuzzy"
#define BUTT_SPRITE_SLIME "slime"
+
+/// Distance which you can see someone's ID card
+/// Short enough that you can inspect over tables (bartender checking age)
+#define ID_EXAMINE_DISTANCE 3
diff --git a/code/__DEFINES/movement.dm b/code/__DEFINES/movement.dm
index be3546ea102d1..9706819610f5e 100644
--- a/code/__DEFINES/movement.dm
+++ b/code/__DEFINES/movement.dm
@@ -2,21 +2,21 @@
#define MIN_GLIDE_SIZE 1
/// The maximum for glide_size to be clamped to.
/// This shouldn't be higher than the icon size, and generally you shouldn't be changing this, but it's here just in case.
-#define MAX_GLIDE_SIZE 32
+#define MAX_GLIDE_SIZE ICON_SIZE_ALL
/// Compensating for time dilation
GLOBAL_VAR_INIT(glide_size_multiplier, 1.0)
///Broken down, here's what this does:
-/// divides the world icon_size (32) by delay divided by ticklag to get the number of pixels something should be moving each tick.
+/// divides the world icon_size by delay divided by ticklag to get the number of pixels something should be moving each tick.
/// The division result is given a min value of 1 to prevent obscenely slow glide sizes from being set
/// Then that's multiplied by the global glide size multiplier. 1.25 by default feels pretty close to spot on. This is just to try to get byond to behave.
/// The whole result is then clamped to within the range above.
/// Not very readable but it works
-#define DELAY_TO_GLIDE_SIZE(delay) (clamp(((world.icon_size / max((delay) / world.tick_lag, 1)) * GLOB.glide_size_multiplier), MIN_GLIDE_SIZE, MAX_GLIDE_SIZE))
+#define DELAY_TO_GLIDE_SIZE(delay) (clamp(((ICON_SIZE_ALL / max((delay) / world.tick_lag, 1)) * GLOB.glide_size_multiplier), MIN_GLIDE_SIZE, MAX_GLIDE_SIZE))
///Similar to DELAY_TO_GLIDE_SIZE, except without the clamping, and it supports piping in an unrelated scalar
-#define MOVEMENT_ADJUSTED_GLIDE_SIZE(delay, movement_disparity) (world.icon_size / ((delay) / world.tick_lag) * movement_disparity * GLOB.glide_size_multiplier)
+#define MOVEMENT_ADJUSTED_GLIDE_SIZE(delay, movement_disparity) (ICON_SIZE_ALL / ((delay) / world.tick_lag) * movement_disparity * GLOB.glide_size_multiplier)
//Movement loop priority. Only one loop can run at a time, this dictates that
// Higher numbers beat lower numbers
@@ -134,3 +134,19 @@ GLOBAL_VAR_INIT(glide_size_multiplier, 1.0)
#define MOVELOOP_FAILURE 0
#define MOVELOOP_SUCCESS 1
#define MOVELOOP_NOT_READY 2
+
+#define NEWTONS *1
+
+#define DEFAULT_INERTIA_SPEED 5
+/// Maximum inertia that an object can hold. Used to prevent objects from getting to stupid speeds.
+#define INERTIA_FORCE_CAP 25 NEWTONS
+/// How much inertia is deducted when a mob has newtonian spacemove capabilities and is not moving in the same direction
+#define INERTIA_FORCE_SPACEMOVE_REDUCTION 0.75 NEWTONS
+/// How much inertia we must have to not be able to instantly stop after having something to grab
+#define INERTIA_FORCE_SPACEMOVE_GRAB 1.5 NEWTONS
+/// How much inertia is required for the impacted object to be thrown at the wall
+#define INERTIA_FORCE_THROW_FLOOR 10 NEWTONS
+/// How much inertia is required past the floor to add 1 strength
+#define INERTIA_FORCE_PER_THROW_FORCE 5 NEWTONS
+// Results in maximum speed of 1 tile per tick, capped at about 2/3rds of maximum force
+#define INERTIA_SPEED_COEF 0.375
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index d6b62da97d470..5fb9d9447bbc0 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -80,6 +80,7 @@
//Job preferences levels
+#define JP_ANY 0
#define JP_LOW 1
#define JP_MEDIUM 2
#define JP_HIGH 3
diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm
index 168e4f9d85dfe..080725b16162c 100644
--- a/code/__DEFINES/sound.dm
+++ b/code/__DEFINES/sound.dm
@@ -188,3 +188,6 @@ GLOBAL_LIST_INIT(announcer_keys, list(
#define SFX_DEFAULT_FISH_SLAP "default_fish_slap"
#define SFX_ALT_FISH_SLAP "alt_fish_slap"
#define SFX_FISH_PICKUP "fish_pickup"
+#define SFX_CAT_MEOW "cat_meow"
+#define SFX_CAT_PURR "cat_purr"
+#define SFX_LIQUID_POUR "liquid_pour"
diff --git a/code/__DEFINES/span.dm b/code/__DEFINES/span.dm
index 259a13e1e9afe..9b3c2612afa34 100644
--- a/code/__DEFINES/span.dm
+++ b/code/__DEFINES/span.dm
@@ -14,8 +14,8 @@
#define span_alien(str) ("" + str + "")
#define span_announce(str) ("" + str + "")
#define span_announcement_header(str) ("" + str + "")
-#define span_average(str) ("" + str + "")
+#define span_bad(str) ("" + str + "")
#define span_big(str) ("" + str + "")
#define span_bigicon(str) ("" + str + "")
#define span_binarysay(str) ("" + str + "")
@@ -48,9 +48,12 @@
#define span_drone(str) ("" + str + "")
#define span_engradio(str) ("" + str + "")
#define span_extremelybig(str) ("" + str + "")
+#define span_emote(str) ("" + str + "")
#define span_enteradio(str) ("" + str + "")
+#define span_game(str) ("" + str + "")
#define span_game_say(str) ("" + str + "")
#define span_ghostalert(str) ("" + str + "")
+#define span_good(str) ("" + str + "")
#define span_green(str) ("" + str + "")
#define span_greenannounce(str) ("" + str + "")
#define span_greenteamradio(str) ("" + str + "")
@@ -70,6 +73,7 @@
#define span_info(str) ("" + str + "")
#define span_infoplain(str) ("" + str + "")
#define span_interface(str) ("" + str + "")
+#define span_italics(str) ("" + str + "")
#define span_linkify(str) ("" + str + "")
#define span_looc(str) ("" + str + "")
#define span_major_announcement_text(str) ("" + str + "")
@@ -117,19 +121,22 @@
#define span_secradio(str) ("" + str + "")
#define span_servradio(str) ("" + str + "")
#define span_singing(str) ("" + str + "")
+#define span_slightly_larger(str) ("" + str + "")
#define span_slime(str) ("" + str + "")
#define span_small(str) ("" + str + "")
+#define span_smalldanger(str) ("" + str + "")
#define span_smallnotice(str) ("" + str + "")
#define span_smallnoticeital(str) ("" + str + "")
#define span_soapbox(str) ("" + str + "")
+#define span_spiderbreacher(str) ("" + str + "")
#define span_spiderbroodmother(str) ("" + str + "")
#define span_spiderscout(str) ("" + str + "")
-#define span_spiderbreacher(str) ("" + str + "")
#define span_subheader_announcement_text(str) ("" + str + "")
#define span_suicide(str) ("" + str + "")
#define span_suppradio(str) ("" + str + "")
#define span_syndradio(str) ("" + str + "")
#define span_tape_recorder(str) ("" + str + "")
+#define span_tinydanger(str) ("" + str + "")
#define span_tinynotice(str) ("" + str + "")
#define span_tinynoticeital(str) ("" + str + "")
#define span_unconscious(str) ("" + str + "")
diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm
index ead7764d60523..121cf5a072d17 100644
--- a/code/__DEFINES/status_effects.dm
+++ b/code/__DEFINES/status_effects.dm
@@ -37,7 +37,7 @@
/// Does this by inverting the passed in flags and seeing if we're still incapacitated
#define INCAPACITATED_IGNORING(mob, flags) (mob.incapacitated & ~(flags))
-/// Maxamounts of fire stacks a mob can get
+/// Max amounts of fire stacks a mob can get
#define MAX_FIRE_STACKS 20
/// If a mob has a higher threshold than this, the icon shown will be increased to the big fire icon.
#define MOB_BIG_FIRE_STACK_THRESHOLD 3
diff --git a/code/__DEFINES/surgery.dm b/code/__DEFINES/surgery.dm
index feddc24c6f858..237e956ca7fae 100644
--- a/code/__DEFINES/surgery.dm
+++ b/code/__DEFINES/surgery.dm
@@ -28,6 +28,8 @@
#define ORGAN_VIRGIN (1<<10)
/// ALWAYS show this when scanned by advanced scanners, even if it is totally healthy
#define ORGAN_PROMINENT (1<<11)
+/// An organ that is ostensibly dangerous when inside a body
+#define ORGAN_HAZARDOUS (1<<12)
/// Helper to figure out if a limb is organic
#define IS_ORGANIC_LIMB(limb) (limb.bodytype & BODYTYPE_ORGANIC)
diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm
index 4766b3dfe661e..42f2d5fc31fee 100644
--- a/code/__DEFINES/tgs.dm
+++ b/code/__DEFINES/tgs.dm
@@ -1,7 +1,7 @@
// tgstation-server DMAPI
// The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in IETF RFC 2119.
-#define TGS_DMAPI_VERSION "7.2.1"
+#define TGS_DMAPI_VERSION "7.3.0"
// All functions and datums outside this document are subject to change with any version and should not be relied on.
diff --git a/code/__DEFINES/tools.dm b/code/__DEFINES/tools.dm
index 2348bd49f194b..794c56691a6da 100644
--- a/code/__DEFINES/tools.dm
+++ b/code/__DEFINES/tools.dm
@@ -39,3 +39,16 @@
/// Combination flag for any item interaction that blocks the rest of the attack chain
#define ITEM_INTERACT_ANY_BLOCKER (ITEM_INTERACT_SUCCESS | ITEM_INTERACT_BLOCKING)
+
+/// How many seconds between each fuel depletion tick ("use" proc)
+#define TOOL_FUEL_BURN_INTERVAL 5
+
+///This is a number I got by quickly searching up the temperature to melt iron/glass, though not really realistic.
+///This is used for places where lighters should not be hot enough to be used as a welding tool on.
+#define HIGH_TEMPERATURE_REQUIRED 1500
+
+/**
+ * A helper for checking if an item interaction should be skipped.
+ * This is only used explicitly because some interactions may not want to ever be skipped.
+ */
+#define SHOULD_SKIP_INTERACTION(target, item, user) (HAS_TRAIT(target, TRAIT_COMBAT_MODE_SKIP_INTERACTION) && user.combat_mode)
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index 11b1e0d2b3127..4fdf598f65977 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -26,6 +26,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_RESTRAINED "restrained"
/// Apply this to make a mob not dense, and remove it when you want it to no longer make them undense, other sources of undesity will still apply. Always define a unique source when adding a new instance of this!
#define TRAIT_UNDENSE "undense"
+/// Makes the mob immune to damage and several other ailments.
+#define TRAIT_GODMODE "godmode"
/// Expands our FOV by 30 degrees if restricted
#define TRAIT_EXPANDED_FOV "expanded_fov"
/// Doesn't miss attacks
@@ -112,6 +114,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_STABLELIVER "stable_liver"
#define TRAIT_VATGROWN "vatgrown"
#define TRAIT_RESISTHEAT "resist_heat"
+/// Trait for when you can no longer gain body heat
+#define TRAIT_HYPOTHERMIC "body_hypothermic"
///For when you've gotten a power from a dna vault
#define TRAIT_USED_DNA_VAULT "used_dna_vault"
/// For when you want to be able to touch hot things, but still want fire to be an issue.
@@ -219,22 +223,30 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_NO_STAGGER "no_stagger"
/// Getting hit by thrown movables won't push you away
#define TRAIT_NO_THROW_HITPUSH "no_throw_hitpush"
+/// This mob likes to eat fish. Raw, uncut fish.
+#define TRAIT_FISH_EATER "fish_eater"
///Added to mob or mind, changes the icons of the fish shown in the minigame UI depending on the possible reward.
#define TRAIT_REVEAL_FISH "reveal_fish"
///This trait gets you a list of fishes that can be caught when examining a fishing spot.
#define TRAIT_EXAMINE_FISHING_SPOT "examine_fishing_spot"
///lobstrosities and carps will prioritize/flee from those that have this trait (given by the skill-locked hat)
#define TRAIT_SCARY_FISHERMAN "scary_fisherman"
+/// Atoms with this trait can be right-clicked with a fish to release them, presumably back in the fishing spot they were caught from.
+#define TRAIT_CATCH_AND_RELEASE "catch_and_release"
///This trait lets you get the size and weight of the fish by examining them
#define TRAIT_EXAMINE_FISH "examine_fish"
///This trait lets you roughly know if the fish is dead, starving, drowning or sick by examining them
#define TRAIT_EXAMINE_DEEPER_FISH "examine_deeper_fish"
///Trait given to turfs or objects that can be fished from
#define TRAIT_FISHING_SPOT "fishing_spot"
+///This trait prevents the fishing spot from being linked to the fish-porter when a multitool is being used.
+#define TRAIT_UNLINKABLE_FISHING_SPOT "unlinkable_fishing_spot"
///Trait given to mobs that can fish without a rod
#define TRAIT_PROFOUND_FISHER "profound_fisher"
/// If an atom has this trait, then you can toss a bottle with a message in it.
#define TRAIT_MESSAGE_IN_A_BOTTLE_LOCATION "message_in_a_bottle_location"
+/// Stops other objects of the same type from being inserted inside the same aquarium it's in.
+#define TRAIT_UNIQUE_AQUARIUM_CONTENT "unique_aquarium_content"
/// This trait lets you evaluate someone's fitness level against your own
#define TRAIT_EXAMINE_FITNESS "reveal_power_level"
/// These mobs have particularly hygienic tongues
@@ -656,7 +668,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_ASHSTORM_IMMUNE "ashstorm_immune"
#define TRAIT_SNOWSTORM_IMMUNE "snowstorm_immune"
#define TRAIT_RADSTORM_IMMUNE "radstorm_immune"
-#define TRAIT_VOIDSTORM_IMMUNE "voidstorm_immune"
#define TRAIT_WEATHER_IMMUNE "weather_immune" //Immune to ALL weather effects.
/// Cannot be grabbed by goliath tentacles
@@ -738,6 +749,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_T_RAY_VISIBLE "t-ray-visible"
/// If this item's been fried
#define TRAIT_FOOD_FRIED "food_fried"
+/// If this item's been bbq grilled
+#define TRAIT_FOOD_BBQ_GRILLED "food_bbq_grilled"
/// This is a silver slime created item
#define TRAIT_FOOD_SILVER "food_silver"
/// If this item's been made by a chef instead of being map-spawned or admin-spawned or such
@@ -748,6 +761,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_NEEDS_TWO_HANDS "needstwohands"
/// Can't be catched when thrown
#define TRAIT_UNCATCHABLE "uncatchable"
+/// You won't catch duds while fishing with this rod.
+#define TRAIT_ROD_REMOVE_FISHING_DUD "rod_remove_fishing_dud"
/// Stuff that can go inside fish cases
#define TRAIT_FISH_CASE_COMPATIBILE "fish_case_compatibile"
/// If the item can be used as a bit.
@@ -761,7 +776,14 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Baits with this trait will ignore bait preferences and related fish traits.
#define TRAIT_OMNI_BAIT "omni_bait"
/// The bait won't be consumed when used
-#define TRAIT_BAIT_UNCONSUMABLE "bait_unconsumabe"
+#define TRAIT_BAIT_UNCONSUMABLE "bait_unconsumable"
+/// This bait ignores environmental conditions for fishing (like low light for nocturnal fish)
+#define TRAIT_BAIT_IGNORE_ENVIRONMENT "bait_ignore_environment"
+/**
+ * This bait won't apply TRAIT_ROD_REMOVE_FISHING_DUD to the rod it's attached on,
+ * instead, it'll allow the fishing dud to be there unless there's at least one fish that likes the bait
+ */
+#define TRAIT_BAIT_ALLOW_FISHING_DUD "bait_dont_affect_fishing_dud"
/// Plants that were mutated as a result of passive instability, not a mutation threshold.
#define TRAIT_PLANT_WILDMUTATE "wildmutation"
/// If you hit an APC with exposed internals with this item it will try to shock you
@@ -790,6 +812,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_HAUNTED "haunted"
/// An item that, if it has contents, will ignore its contents when scanning for contraband.
#define TRAIT_CONTRABAND_BLOCKER "contraband_blocker"
+/// For edible items that cannot be composted inside hydro trays
+#define TRAIT_UNCOMPOSTABLE "uncompostable"
//quirk traits
#define TRAIT_ALCOHOL_TOLERANCE "alcohol_tolerance"
@@ -826,6 +850,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_THROWINGARM "throwing_arm"
#define TRAIT_SETTLER "settler"
#define TRAIT_STRONG_STOMACH "strong_stomach"
+#define TRAIT_VEGETARIAN "trait_vegetarian"
/// This mob always lands on their feet when they fall, for better or for worse.
#define TRAIT_CATLIKE_GRACE "catlike_grace"
@@ -950,6 +975,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_FISH_AMPHIBIOUS "fish_amphibious"
///Trait needed for the lubefish evolution
#define TRAIT_FISH_FED_LUBE "fish_fed_lube"
+#define TRAIT_FISH_WELL_COOKED "fish_well_cooked"
#define TRAIT_FISH_NO_HUNGER "fish_no_hunger"
///It comes from a fish case. Relevant for bounties so far.
#define TRAIT_FISH_FROM_CASE "fish_from_case"
@@ -959,6 +985,12 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_FISH_RECESSIVE "fish_recessive"
///This fish comes equipped with a stinger (increased damage and potentially venomous if also toxic)
#define TRAIT_FISH_STINGER "fish_stinger"
+///This fish is currently on cooldown and cannot splash ink unto people's faces
+#define TRAIT_FISH_INK_ON_COOLDOWN "fish_ink_on_cooldown"
+///This fish requires two hands to carry even if smaller than FISH_SIZE_TWO_HANDS_REQUIRED, as long as it's bulky-sized.
+#define TRAIT_FISH_SHOULD_TWOHANDED "fish_should_twohanded"
+///This fish won't be killed when cooked.
+#define TRAIT_FISH_SURVIVE_COOKING "fish_survive_cooking"
/// Trait given to angelic constructs to let them purge cult runes
#define TRAIT_ANGELIC "angelic"
@@ -1106,11 +1138,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// this object has been frozen
#define TRAIT_FROZEN "frozen"
-/// Currently fishing
-#define TRAIT_GONE_FISHING "fishing"
-/// Currently fishing, and it's the active minigame phase
-#define TRAIT_ACTIVELY_FISHING "actively_fishing"
-
/// Makes a character be better/worse at tackling depending on their wing's status
#define TRAIT_TACKLING_WINGED_ATTACKER "tacking_winged_attacker"
@@ -1253,6 +1280,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
///Trait given to a turf that should not be allowed to be terraformed, such as turfs holding ore vents.
#define TRAIT_NO_TERRAFORM "no_terraform"
+///Trait that prevents mobs from stopping by grabbing objects
+#define TRAIT_NOGRAV_ALWAYS_DRIFT "nograv_always_drift"
+
///Mobs with these trait do not get italicized/quiet speech when speaking in low pressure
#define TRAIT_SPEECH_BOOSTER "speech_booster"
@@ -1262,4 +1292,20 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
///Trait which allows mobs to parry mining mob projectiles
#define TRAIT_MINING_PARRYING "mining_parrying"
+/**
+ *
+ * This trait is used in some interactions very high in the interaction chain to allow
+ * certain atoms to be skipped by said interactions if the user is in combat mode.
+ *
+ * Its primarily use case is for stuff like storage and tables, to allow things like emags to be bagged
+ * (because in some contexts you might want to be emagging a bag, and in others you might want to be storing it.)
+ *
+ * This is only checked by certain items explicitly so you can't just add the trait and expect it to work.
+ * (This may be changed later but I chose to do it this way to avoid messing up interactions which require combat mode)
+ */
+#define TRAIT_COMBAT_MODE_SKIP_INTERACTION "combat_mode_skip_interaction"
+
+///A "fake" effect that should not be subject to normal effect removal methods (like the effect remover component)
+#define TRAIT_ILLUSORY_EFFECT "illusory_effect"
+
// END TRAIT DEFINES
diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm
index ecdbdd15e3591..beb2b98944bc0 100644
--- a/code/__DEFINES/traits/sources.dm
+++ b/code/__DEFINES/traits/sources.dm
@@ -196,6 +196,9 @@
/// Trait given by a fulton extraction pack
#define FULTON_PACK_TRAIT "fulton-pack"
+/// Trait from mob/living/update_transform()
+#define UPDATE_TRANSFORM_TRAIT "update_transform"
+
/// Trait granted by the berserker hood.
#define BERSERK_TRAIT "berserk_trait"
/// Trait granted by [/obj/item/rod_of_asclepius]
@@ -296,5 +299,11 @@
/// Trait when a drink was renamed by a shaker
#define SHAKER_LABEL_TRAIT "shaker_trait"
+/// Trait given by a jetpack
+#define JETPACK_TRAIT "jetpack_trait"
+
/// Trait added by style component
#define STYLE_TRAIT "style"
+
+/// Trait from an engraving
+#define ENGRAVED_TRAIT "engraved"
diff --git a/code/__HELPERS/atoms.dm b/code/__HELPERS/atoms.dm
index 7106ec81be1ba..d54b29b3f4ac9 100644
--- a/code/__HELPERS/atoms.dm
+++ b/code/__HELPERS/atoms.dm
@@ -63,6 +63,8 @@
var/turf/target_turf = get_turf(target)
if(get_dist(source, target) > length)
return FALSE
+ if(current == target_turf)
+ return TRUE
var/steps = 1
if(current == target_turf)//they are on the same turf, source can see the target
return TRUE
@@ -83,9 +85,9 @@
return get_dir(start, end) & (rand() * (dx+dy) < dy ? 3 : 12)
/**
- * Finds the distance between two atoms, in pixels
- * centered = FALSE counts from turf edge to edge
- * centered = TRUE counts from turf center to turf center
+ * Finds the distance between two atoms, in pixels \
+ * centered = FALSE counts from turf edge to edge \
+ * centered = TRUE counts from turf center to turf center \
* of course mathematically this is just adding world.icon_size on again
**/
/proc/get_pixel_distance(atom/start, atom/end, centered = TRUE)
@@ -93,7 +95,7 @@
return 0
. = bounds_dist(start, end) + sqrt((((start.pixel_x + end.pixel_x) ** 2) + ((start.pixel_y + end.pixel_y) ** 2)))
if(centered)
- . += world.icon_size
+ . += ICON_SIZE_ALL
/**
* Check if there is already a wall item on the turf loc
@@ -334,6 +336,6 @@ rough example of the "cone" made by the 3 dirs checked
var/icon_width = icon_dimensions["width"]
var/icon_height = icon_dimensions["height"]
return list(
- "x" = icon_width > world.icon_size && pixel_x != 0 ? (icon_width - world.icon_size) * 0.5 : 0,
- "y" = icon_height > world.icon_size && pixel_y != 0 ? (icon_height - world.icon_size) * 0.5 : 0,
+ "x" = icon_width > ICON_SIZE_X && pixel_x != 0 ? (icon_width - ICON_SIZE_X) * 0.5 : 0,
+ "y" = icon_height > ICON_SIZE_Y && pixel_y != 0 ? (icon_height - ICON_SIZE_Y) * 0.5 : 0,
)
diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm
index f9957f4c7393e..76651964e24e0 100644
--- a/code/__HELPERS/cmp.dm
+++ b/code/__HELPERS/cmp.dm
@@ -195,3 +195,20 @@
/proc/cmp_deathmatch_mods(datum/deathmatch_modifier/a, datum/deathmatch_modifier/b)
return sorttext(b.name, a.name)
+
+/**
+ * Orders fish types following this order (freshwater -> saltwater -> anadromous -> sulphuric water -> any water -> air)
+ * If both share the same required fluid type, they'll be ordered by name instead.
+ */
+/proc/cmp_fish_fluid(obj/item/fish/a, obj/item/fish/b)
+ var/static/list/fluids_priority = list(
+ AQUARIUM_FLUID_FRESHWATER,
+ AQUARIUM_FLUID_SALTWATER,
+ AQUARIUM_FLUID_ANADROMOUS,
+ AQUARIUM_FLUID_SULPHWATEVER,
+ AQUARIUM_FLUID_ANY_WATER,
+ AQUARIUM_FLUID_AIR,
+ )
+ var/position_a = fluids_priority.Find(initial(a.required_fluid_type))
+ var/position_b = fluids_priority.Find(initial(b.required_fluid_type))
+ return cmp_numeric_asc(position_a, position_b) || cmp_text_asc(initial(b.name), initial(a.name))
diff --git a/code/__HELPERS/dynamic_human_icon_gen.dm b/code/__HELPERS/dynamic_human_icon_gen.dm
index df8f4716bb918..f1f3be038a86b 100644
--- a/code/__HELPERS/dynamic_human_icon_gen.dm
+++ b/code/__HELPERS/dynamic_human_icon_gen.dm
@@ -57,6 +57,7 @@ GLOBAL_LIST_EMPTY(dynamic_human_appearances)
/proc/set_dynamic_human_appearance(list/arguments)
var/atom/target = arguments[1] //1st argument is the target
var/dynamic_appearance = get_dynamic_human_appearance(arglist(arguments.Copy(2))) //the rest of the arguments starting from 2 matter to the proc
- target.icon = 'icons/blanks/32x32.dmi'
- target.icon_state = "nothing"
+ target.icon = 'icons/mob/human/human.dmi'
+ target.icon_state = ""
+ target.appearance_flags |= KEEP_TOGETHER
target.copy_overlays(dynamic_appearance, cut_old = TRUE)
diff --git a/code/__HELPERS/filters.dm b/code/__HELPERS/filters.dm
index cd44409ddb239..14233a2807636 100644
--- a/code/__HELPERS/filters.dm
+++ b/code/__HELPERS/filters.dm
@@ -23,7 +23,7 @@ GLOBAL_LIST_INIT(master_filter_info, list(
)
),
// Not implemented, but if this isn't uncommented some windows will just error
- // Needs either a proper matrix editor, or just a hook to our existing one
+ // Needs either a proper matrix editor, or just a hook to our existing one
// Issue is filterrific assumes variables will have the same value type if they share the same name, which this violates
// Gotta refactor this sometime
"color" = list(
@@ -169,7 +169,7 @@ GLOBAL_LIST_INIT(master_filter_info, list(
if(!isnull(space))
.["space"] = space
-/proc/displacement_map_filter(icon, render_source, x, y, size = 32)
+/proc/displacement_map_filter(icon, render_source, x, y, size = ICON_SIZE_ALL)
. = list("type" = "displace")
if(!isnull(icon))
.["icon"] = icon
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index ce48e593980b5..3eb89831957b8 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -181,7 +181,7 @@
//First we spawn a dude.
var/mob/living/carbon/human/new_character = new//The mob being spawned.
- SSjob.SendToLateJoin(new_character)
+ SSjob.send_to_late_join(new_character)
ghost_player.client.prefs.safe_transfer_prefs_to(new_character)
new_character.dna.update_dna_identity()
@@ -235,7 +235,7 @@
if(!SSticker.IsRoundInProgress() || QDELETED(character))
return
var/area/player_area = get_area(character)
- deadchat_broadcast(" has arrived at the station at [player_area.name].", "[character.real_name] ([rank])", follow_target = character, message_type=DEADCHAT_ARRIVALRATTLE)
+ deadchat_broadcast(span_game(" has arrived at the station at [span_name(player_area.name)]."), span_game("[span_name(character.real_name)] ([rank])"), follow_target = character, message_type=DEADCHAT_ARRIVALRATTLE)
if(!character.mind)
return
if(!GLOB.announcement_systems.len)
@@ -243,8 +243,16 @@
if(!(character.mind.assigned_role.job_flags & JOB_ANNOUNCE_ARRIVAL))
return
- var/obj/machinery/announcement_system/announcer = pick(GLOB.announcement_systems)
- announcer.announce("ARRIVAL", character.real_name, rank, list()) //make the list empty to make it announce it in common
+ var/obj/machinery/announcement_system/announcer
+ var/list/available_machines = list()
+ for(var/obj/machinery/announcement_system/announce as anything in GLOB.announcement_systems)
+ if(announce.arrival_toggle)
+ available_machines += announce
+ break
+ if(!length(available_machines))
+ return
+ announcer = pick(available_machines)
+ announcer.announce(AUTO_ANNOUNCE_ARRIVAL, character.real_name, rank, list()) //make the list empty to make it announce it in common
///Check if the turf pressure allows specialized equipment to work
/proc/lavaland_equipment_pressure_check(turf/turf_to_check)
diff --git a/code/__HELPERS/hallucinations.dm b/code/__HELPERS/hallucinations.dm
index edd65ee926abf..d3d4a2630ed05 100644
--- a/code/__HELPERS/hallucinations.dm
+++ b/code/__HELPERS/hallucinations.dm
@@ -86,6 +86,30 @@ GLOBAL_LIST_EMPTY(all_ongoing_hallucinations)
if(length(optional_messages))
to_chat(nearby_living, pick(optional_messages))
+/**
+ * Emits a hallucinating pulse around the passed atom.
+ * Affects everyone in the passed radius except for those with TRAIT_MADNESS_IMMUNE. This affects blind players.
+ *
+ * center - required, the center of the pulse
+ * radius - the radius around that the pulse reaches
+ * hallucination_duration - how much hallucination is added by the pulse. reduced based on distance to the center.
+ * hallucination_max_duration - a cap on how much hallucination can be added
+ * optional_messages - optional list of messages passed. Those affected by pulses will be given one of the messages in said list.
+ */
+/proc/hallucination_pulse(atom/center, radius = 7, hallucination_duration = 50 SECONDS, hallucination_max_duration, list/optional_messages)
+ for(var/mob/living/nearby_living in range(center, radius))
+ if(HAS_MIND_TRAIT(nearby_living, TRAIT_MADNESS_IMMUNE))
+ continue
+
+ if(nearby_living.mob_biotypes & NO_HALLUCINATION_BIOTYPES)
+ continue
+
+ // Everyone else gets hallucinations.
+ var/dist = sqrt(1 / max(1, get_dist(nearby_living, center)))
+ nearby_living.adjust_hallucinations_up_to(hallucination_duration * dist, hallucination_max_duration)
+ if(length(optional_messages))
+ to_chat(nearby_living, pick(optional_messages))
+
/// Global weighted list of all hallucinations that can show up randomly.
GLOBAL_LIST_INIT(random_hallucination_weighted_list, generate_hallucination_weighted_list())
@@ -226,7 +250,7 @@ ADMIN_VERB(debug_hallucination_weighted_list_per_type, R_DEBUG, "Show Hallucinat
if(!custom_icon_state)
return
- var/custom_name = tgui_input_text(user, "What name should it show up as? (Can be empty)", "Custom Delusion: Name")
+ var/custom_name = tgui_input_text(user, "What name should it show up as? (Can be empty)", "Custom Delusion: Name", max_length = MAX_NAME_LEN)
delusion_args += list(
custom_icon_file = custom_icon_file,
diff --git a/code/__HELPERS/hearted.dm b/code/__HELPERS/hearted.dm
index adae298516ec6..d8f7832cbc06b 100644
--- a/code/__HELPERS/hearted.dm
+++ b/code/__HELPERS/hearted.dm
@@ -45,11 +45,11 @@
var/heart_nominee
switch(attempt)
if(1)
- heart_nominee = tgui_input_text(src, "What was their name? Just a first or last name may be enough.", "<3?")
+ heart_nominee = tgui_input_text(src, "What was their name? Just a first or last name may be enough.", "<3?", max_length = MAX_NAME_LEN)
if(2)
- heart_nominee = tgui_input_text(src, "Try again, what was their name? Just a first or last name may be enough.", "<3?")
+ heart_nominee = tgui_input_text(src, "Try again, what was their name? Just a first or last name may be enough.", "<3?", max_length = MAX_NAME_LEN)
if(3)
- heart_nominee = tgui_input_text(src, "One more try, what was their name? Just a first or last name may be enough.", "<3?")
+ heart_nominee = tgui_input_text(src, "One more try, what was their name? Just a first or last name may be enough.", "<3?", max_length = MAX_NAME_LEN)
if(!heart_nominee)
return
diff --git a/code/__HELPERS/honkerblast.dm b/code/__HELPERS/honkerblast.dm
index c0712f420f2d7..f49a5ca4aca29 100644
--- a/code/__HELPERS/honkerblast.dm
+++ b/code/__HELPERS/honkerblast.dm
@@ -5,7 +5,7 @@
var/list/properly_honked = list()
var/list/severely_honked = list()
- playsound(origin_turf, 'sound/items/airhorn.ogg', 100, TRUE)
+ playsound(origin_turf, 'sound/items/airhorn/airhorn.ogg', 100, TRUE)
for(var/mob/living/carbon/victim as anything in hearers(max(light_range, medium_range, heavy_range), origin_turf))
if(!victim.can_hear())
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index 127ae5387e1b0..ec238d2e495b0 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -402,7 +402,7 @@ world
/// appearance system (overlays/underlays, etc.) is not available.
///
/// Only the first argument is required.
-/proc/getFlatIcon(image/appearance, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE)
+/proc/getFlatIcon(image/appearance, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE, parentcolor)
// Loop through the underlays, then overlays, sorting them into the layers list
#define PROCESS_OVERLAYS_OR_UNDERLAYS(flat, process, base_layer) \
for (var/i in 1 to process.len) { \
@@ -466,25 +466,24 @@ world
var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have
- //Try to remove/optimize this section ASAP, CPU hog.
- //Determines if there's directionals.
- if(render_icon && curdir != SOUTH)
- if (
- !length(icon_states(icon(curicon, curstate, NORTH))) \
- && !length(icon_states(icon(curicon, curstate, EAST))) \
- && !length(icon_states(icon(curicon, curstate, WEST))) \
- )
- base_icon_dir = SOUTH
+ if(render_icon)
+ //Try to remove/optimize this section if you can, it's a CPU hog.
+ //Determines if there're directionals.
+ if (curdir != SOUTH)
+ // icon states either have 1, 4 or 8 dirs. We only have to check
+ // one of NORTH, EAST or WEST to know that this isn't a 1-dir icon_state since they just have SOUTH.
+ if(!length(icon_states(icon(curicon, curstate, NORTH))))
+ base_icon_dir = SOUTH
+
+ var/list/icon_dimensions = get_icon_dimensions(curicon)
+ var/icon_width = icon_dimensions["width"]
+ var/icon_height = icon_dimensions["height"]
+ if(icon_width != 32 || icon_height != 32)
+ flat.Scale(icon_width, icon_height)
if(!base_icon_dir)
base_icon_dir = curdir
- // Expand our canvas to fit if we're too big
- if(render_icon)
- var/icon/active_icon = icon(curicon)
- if(active_icon.Width() != 32 || active_icon.Height() != 32)
- flat.Scale(active_icon.Width(), active_icon.Height())
-
var/curblend = appearance.blend_mode || defblend
if(appearance.overlays.len || appearance.underlays.len)
@@ -514,6 +513,20 @@ world
var/addY1 = 0
var/addY2 = 0
+ if(appearance.color)
+ if(islist(appearance.color))
+ flat.MapColors(arglist(appearance.color))
+ else
+ flat.Blend(appearance.color, ICON_MULTIPLY)
+
+ if(parentcolor && !(appearance.appearance_flags & RESET_COLOR))
+ if(islist(parentcolor))
+ flat.MapColors(arglist(parentcolor))
+ else
+ flat.Blend(parentcolor, ICON_MULTIPLY)
+
+ var/next_parentcolor = appearance.color || parentcolor
+
for(var/image/layer_image as anything in layers)
if(layer_image.alpha == 0)
continue
@@ -521,8 +534,14 @@ world
if(layer_image == copy) // 'layer_image' is an /image based on the object being flattened.
curblend = BLEND_OVERLAY
add = icon(layer_image.icon, layer_image.icon_state, base_icon_dir)
+ if(appearance.color)
+ if(islist(appearance.color))
+ add.MapColors(arglist(appearance.color))
+ else
+ add.Blend(appearance.color, ICON_MULTIPLY)
else // 'I' is an appearance object.
- add = getFlatIcon(image(layer_image), curdir, curicon, curstate, curblend, FALSE, no_anim)
+ add = getFlatIcon(image(layer_image), curdir, curicon, curstate, curblend, FALSE, no_anim, next_parentcolor)
+
if(!add)
continue
@@ -554,11 +573,6 @@ world
// Blend the overlay into the flattened icon
flat.Blend(add, blendMode2iconMode(curblend), layer_image.pixel_x + 2 - flatX1, layer_image.pixel_y + 2 - flatY1)
- if(appearance.color)
- if(islist(appearance.color))
- flat.MapColors(arglist(appearance.color))
- else
- flat.Blend(appearance.color, ICON_MULTIPLY)
if(appearance.alpha < 255)
flat.Blend(rgb(255, 255, 255, appearance.alpha), ICON_MULTIPLY)
@@ -1024,7 +1038,7 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
var/icon/target_icon = target
var/icon_base64 = icon2base64(target_icon)
- if (target_icon.Height() > world.icon_size || target_icon.Width() > world.icon_size)
+ if (target_icon.Height() > ICON_SIZE_Y || target_icon.Width() > ICON_SIZE_X)
var/icon_md5 = md5(icon_base64)
icon_base64 = bicon_cache[icon_md5]
if (!icon_base64) // Doesn't exist yet, make it.
@@ -1078,14 +1092,14 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects)
var/top_part_filter = filter(type="alpha",icon=icon('icons/effects/alphacolors.dmi',"white"),y=0)
filters += top_part_filter
var/filter_index = length(filters)
- animate(filters[filter_index],y=-32,time=time)
+ animate(filters[filter_index],y=-ICON_SIZE_Y,time=time)
//Appearing part
var/obj/effect/overlay/appearing_part = new
appearing_part.appearance = result_appearance
appearing_part.appearance_flags |= KEEP_TOGETHER | KEEP_APART
appearing_part.vis_flags = VIS_INHERIT_ID
appearing_part.filters = filter(type="alpha",icon=icon('icons/effects/alphacolors.dmi',"white"),y=0,flags=MASK_INVERSE)
- animate(appearing_part.filters[1],y=-32,time=time)
+ animate(appearing_part.filters[1],y=-ICON_SIZE_Y,time=time)
transformation_objects += appearing_part
//Transform effect thing
if(transform_appearance)
@@ -1132,19 +1146,19 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects)
if(!x_dimension || !y_dimension)
return
- if((x_dimension == world.icon_size) && (y_dimension == world.icon_size))
+ if((x_dimension == ICON_SIZE_X) && (y_dimension == ICON_SIZE_Y))
return image_to_center
//Offset the image so that its bottom left corner is shifted this many pixels
//This makes it infinitely easier to draw larger inhands/images larger than world.iconsize
//but still use them in game
- var/x_offset = -((x_dimension / world.icon_size) - 1) * (world.icon_size * 0.5)
- var/y_offset = -((y_dimension / world.icon_size) - 1) * (world.icon_size * 0.5)
+ var/x_offset = -((x_dimension / ICON_SIZE_X) - 1) * (ICON_SIZE_X * 0.5)
+ var/y_offset = -((y_dimension / ICON_SIZE_Y) - 1) * (ICON_SIZE_Y * 0.5)
- //Correct values under world.icon_size
- if(x_dimension < world.icon_size)
+ //Correct values under icon_size
+ if(x_dimension < ICON_SIZE_X)
x_offset *= -1
- if(y_dimension < world.icon_size)
+ if(y_dimension < ICON_SIZE_Y)
y_offset *= -1
image_to_center.pixel_x = x_offset
@@ -1202,7 +1216,7 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects)
*/
/proc/get_size_in_tiles(obj/target)
var/icon/size_check = icon(target.icon, target.icon_state)
- var/size = size_check.Width() / world.icon_size
+ var/size = size_check.Width() / ICON_SIZE_X
return size
@@ -1215,11 +1229,11 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects)
var/size = get_size_in_tiles(src)
if(dir in list(NORTH, SOUTH))
- bound_width = size * world.icon_size
- bound_height = world.icon_size
+ bound_width = size * ICON_SIZE_X
+ bound_height = ICON_SIZE_Y
else
- bound_width = world.icon_size
- bound_height = size * world.icon_size
+ bound_width = ICON_SIZE_X
+ bound_height = size * ICON_SIZE_Y
/// Returns a list containing the width and height of an icon file
/proc/get_icon_dimensions(icon_path)
@@ -1247,15 +1261,15 @@ GLOBAL_LIST_EMPTY(transformation_animation_objects)
var/width = icon_dimensions["width"]
var/height = icon_dimensions["height"]
- if(width > world.icon_size)
- alert_overlay.pixel_x = -(world.icon_size / 2) * ((width - world.icon_size) / world.icon_size)
- if(height > world.icon_size)
- alert_overlay.pixel_y = -(world.icon_size / 2) * ((height - world.icon_size) / world.icon_size)
- if(width > world.icon_size || height > world.icon_size)
+ if(width > ICON_SIZE_X)
+ alert_overlay.pixel_x = -(ICON_SIZE_X / 2) * ((width - ICON_SIZE_X) / ICON_SIZE_X)
+ if(height > ICON_SIZE_Y)
+ alert_overlay.pixel_y = -(ICON_SIZE_Y / 2) * ((height - ICON_SIZE_Y) / ICON_SIZE_Y)
+ if(width > ICON_SIZE_X || height > ICON_SIZE_Y)
if(width >= height)
- scale = world.icon_size / width
+ scale = ICON_SIZE_X / width
else
- scale = world.icon_size / height
+ scale = ICON_SIZE_Y / height
alert_overlay.transform = alert_overlay.transform.Scale(scale)
return alert_overlay
diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm
index 5a55fd46fd296..7e6db6fb0209b 100644
--- a/code/__HELPERS/maths.dm
+++ b/code/__HELPERS/maths.dm
@@ -2,8 +2,8 @@
/proc/get_angle(atom/movable/start, atom/movable/end)//For beams.
if(!start || !end)
return 0
- var/dy =(32 * end.y + end.pixel_y) - (32 * start.y + start.pixel_y)
- var/dx =(32 * end.x + end.pixel_x) - (32 * start.x + start.pixel_x)
+ var/dy =(ICON_SIZE_Y * end.y + end.pixel_y) - (ICON_SIZE_Y * start.y + start.pixel_y)
+ var/dx =(ICON_SIZE_X * end.x + end.pixel_x) - (ICON_SIZE_X * start.x + start.pixel_x)
return delta_to_angle(dx, dy)
/// Calculate the angle produced by a pair of x and y deltas
@@ -18,8 +18,8 @@
/// Angle between two arbitrary points and horizontal line same as [/proc/get_angle]
/proc/get_angle_raw(start_x, start_y, start_pixel_x, start_pixel_y, end_x, end_y, end_pixel_x, end_pixel_y)
- var/dy = (32 * end_y + end_pixel_y) - (32 * start_y + start_pixel_y)
- var/dx = (32 * end_x + end_pixel_x) - (32 * start_x + start_pixel_x)
+ var/dy = (ICON_SIZE_Y * end_y + end_pixel_y) - (ICON_SIZE_Y * start_y + start_pixel_y)
+ var/dx = (ICON_SIZE_X * end_x + end_pixel_x) - (ICON_SIZE_X * start_x + start_pixel_x)
if(!dy)
return (dx >= 0) ? 90 : 270
. = arctan(dx/dy)
@@ -241,3 +241,7 @@
/// Useful for providing an additive modifier to a value that is used as a divisor, such as `/obj/projectile/var/speed`
/proc/reciprocal_add(x, y)
return 1/((1/x)+y)
+
+/// 180s an angle
+/proc/reverse_angle(angle)
+ return (angle + 180) % 360
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 7c8e84e226d23..867f6f9e7649d 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -208,7 +208,7 @@ GLOBAL_LIST_INIT(skin_tone_names, list(
var/atom/target_loc = target?.loc
var/drifting = FALSE
- if(GLOB.move_manager.processing_on(user, SSspacedrift))
+ if(GLOB.move_manager.processing_on(user, SSnewtonian_movement))
drifting = TRUE
var/holding = user.get_active_held_item()
@@ -237,7 +237,7 @@ GLOBAL_LIST_INIT(skin_tone_names, list(
if(!QDELETED(progbar))
progbar.update(world.time - starttime)
- if(drifting && !GLOB.move_manager.processing_on(user, SSspacedrift))
+ if(drifting && !GLOB.move_manager.processing_on(user, SSnewtonian_movement))
drifting = FALSE
user_loc = user.loc
diff --git a/code/__HELPERS/mouse_control.dm b/code/__HELPERS/mouse_control.dm
index 0c99e53e7a0cd..14588b41cb701 100644
--- a/code/__HELPERS/mouse_control.dm
+++ b/code/__HELPERS/mouse_control.dm
@@ -11,8 +11,8 @@
var/x = (text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32)
var/y = (text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32)
var/list/screenview = getviewsize(client.view)
- var/screenviewX = screenview[1] * world.icon_size
- var/screenviewY = screenview[2] * world.icon_size
+ var/screenviewX = screenview[1] * ICON_SIZE_X
+ var/screenviewY = screenview[2] * ICON_SIZE_Y
var/ox = round(screenviewX/2) - client.pixel_x //"origin" x
var/oy = round(screenviewY/2) - client.pixel_y //"origin" y
var/angle = SIMPLIFY_DEGREES(ATAN2(y - oy, x - ox))
diff --git a/code/__HELPERS/movement.dm b/code/__HELPERS/movement.dm
new file mode 100644
index 0000000000000..e820b3dfff125
--- /dev/null
+++ b/code/__HELPERS/movement.dm
@@ -0,0 +1,2 @@
+/// Converts w_class into newtons from throwing it, in (0.6 ~ 2.2) range
+#define WEIGHT_TO_NEWTONS(w_class, arguments...) 0.2 NEWTONS + w_class * 0.4 NEWTONS
diff --git a/code/__HELPERS/priority_announce.dm b/code/__HELPERS/priority_announce.dm
index c1ca3cc638b26..96155ea5588a1 100644
--- a/code/__HELPERS/priority_announce.dm
+++ b/code/__HELPERS/priority_announce.dm
@@ -142,7 +142,7 @@
else
finalized_announcement = CHAT_ALERT_DEFAULT_SPAN(jointext(minor_announcement_strings, ""))
- var/custom_sound = sound_override || (alert ? 'sound/misc/notice1.ogg' : 'sound/misc/notice2.ogg')
+ var/custom_sound = sound_override || (alert ? 'sound/announcer/notice/notice1.ogg' : 'sound/announcer/notice/notice2.ogg')
dispatch_announcement_to_players(finalized_announcement, players, custom_sound, should_play_sound)
/// Sends an announcement about the level changing to players. Uses the passed in datum and the subsystem's previous security level to generate the message.
@@ -186,7 +186,7 @@
/// Proc that just dispatches the announcement to our applicable audience. Only the announcement is a mandatory arg.
/proc/dispatch_announcement_to_players(announcement, list/players = GLOB.player_list, sound_override = null, should_play_sound = TRUE)
- var/sound_to_play = !isnull(sound_override) ? sound_override : 'sound/misc/notice2.ogg'
+ var/sound_to_play = !isnull(sound_override) ? sound_override : 'sound/announcer/notice/notice2.ogg'
for(var/mob/target in players)
if(isnewplayer(target) || !target.can_hear())
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 71e80014bb54a..72af6cf3ac181 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -571,7 +571,7 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
/datum/controller/subsystem/ticker/proc/medal_report()
if(GLOB.commendations.len)
var/list/parts = list()
- parts += "Medal Commendations:"
+ parts += span_header("Medal Commendations:")
for (var/com in GLOB.commendations)
parts += com
return "
[parts.Join(" ")]
"
@@ -660,7 +660,7 @@ GLOBAL_LIST_INIT(achievements_unlocked, list())
var/datum/action/report/R = new
C.player_details.player_actions += R
R.Grant(C.mob)
- to_chat(C,"Show roundend report again")
+ to_chat(C,span_infoplain("Show roundend report again"))
/datum/action/report
name = "Show roundend report"
diff --git a/code/__HELPERS/screen_objs.dm b/code/__HELPERS/screen_objs.dm
index cb8520225ab8c..00f6bd415704f 100644
--- a/code/__HELPERS/screen_objs.dm
+++ b/code/__HELPERS/screen_objs.dm
@@ -13,11 +13,11 @@
if(findtext(screen_loc, "EAST")) // If you're starting from the east, we start from the east too
x += view_size[1]
if(findtext(screen_loc, "WEST")) // HHHHHHHHHHHHHHHHHHHHHH WEST is technically a 1 tile offset from the start. Shoot me please
- x += world.icon_size
+ x += ICON_SIZE_X
if(findtext(screen_loc, "NORTH"))
y += view_size[2]
if(findtext(screen_loc, "SOUTH"))
- y += world.icon_size
+ y += ICON_SIZE_Y
var/list/x_and_y = splittext(screen_loc, ",")
@@ -36,8 +36,8 @@
x_coord = text2num(cut_relative_direction(x_coord))
y_coord = text2num(cut_relative_direction(y_coord))
- x += x_coord * world.icon_size
- y += y_coord * world.icon_size
+ x += x_coord * ICON_SIZE_X
+ y += y_coord * ICON_SIZE_Y
if(length(x_pack) > 1)
x += text2num(x_pack[2])
@@ -51,14 +51,14 @@
/proc/offset_to_screen_loc(x_offset, y_offset, view = null)
if(view)
var/list/view_bounds = view_to_pixels(view)
- x_offset = clamp(x_offset, world.icon_size, view_bounds[1])
- y_offset = clamp(y_offset, world.icon_size, view_bounds[2])
+ x_offset = clamp(x_offset, ICON_SIZE_X, view_bounds[1])
+ y_offset = clamp(y_offset, ICON_SIZE_Y, view_bounds[2])
// Round with no argument is floor, so we get the non pixel offset here
- var/x = round(x_offset / world.icon_size)
- var/pixel_x = x_offset % world.icon_size
- var/y = round(y_offset / world.icon_size)
- var/pixel_y = y_offset % world.icon_size
+ var/x = round(x_offset / ICON_SIZE_X)
+ var/pixel_x = x_offset % ICON_SIZE_X
+ var/y = round(y_offset / ICON_SIZE_Y)
+ var/pixel_y = y_offset % ICON_SIZE_Y
var/list/generated_loc = list()
generated_loc += "[x]"
@@ -88,9 +88,9 @@
// Bias to the right, down, left, and then finally up
if(base_x + target_offset < view_size[1])
return offset_to_screen_loc(base_x + target_offset, base_y, view)
- if(base_y - target_offset > world.icon_size)
+ if(base_y - target_offset > ICON_SIZE_Y)
return offset_to_screen_loc(base_x, base_y - target_offset, view)
- if(base_x - target_offset > world.icon_size)
+ if(base_x - target_offset > ICON_SIZE_X)
return offset_to_screen_loc(base_x - target_offset, base_y, view)
if(base_y + target_offset < view_size[2])
return offset_to_screen_loc(base_x, base_y + target_offset, view)
@@ -104,12 +104,12 @@
/// Returns a screen_loc format for a tiling screen objects from start and end positions. Start should be bottom left corner, and end top right corner.
/proc/spanning_screen_loc(start_px, start_py, end_px, end_py)
- var/starting_tile_x = round(start_px / 32)
- start_px -= starting_tile_x * 32
- var/starting_tile_y = round(start_py/ 32)
- start_py -= starting_tile_y * 32
- var/ending_tile_x = round(end_px / 32)
- end_px -= ending_tile_x * 32
- var/ending_tile_y = round(end_py / 32)
- end_py -= ending_tile_y * 32
+ var/starting_tile_x = round(start_px / ICON_SIZE_X)
+ start_px -= starting_tile_x * ICON_SIZE_X
+ var/starting_tile_y = round(start_py/ ICON_SIZE_Y)
+ start_py -= starting_tile_y * ICON_SIZE_Y
+ var/ending_tile_x = round(end_px / ICON_SIZE_X)
+ end_px -= ending_tile_x * ICON_SIZE_X
+ var/ending_tile_y = round(end_py / ICON_SIZE_Y)
+ end_py -= ending_tile_y * ICON_SIZE_Y
return "[starting_tile_x]:[start_px],[starting_tile_y]:[start_py] to [ending_tile_x]:[end_px],[ending_tile_y]:[end_py]"
diff --git a/code/__HELPERS/spatial_info.dm b/code/__HELPERS/spatial_info.dm
index 529532f50cf4d..a2c47e87c0a10 100644
--- a/code/__HELPERS/spatial_info.dm
+++ b/code/__HELPERS/spatial_info.dm
@@ -211,37 +211,49 @@
for(var/obj/item/radio/radio as anything in radios)
. |= get_hearers_in_LOS(radio.canhear_range, radio, FALSE)
+//Used when converting pixels to tiles to make them accurate
+#define OFFSET_X (0.5 / ICON_SIZE_X)
+#define OFFSET_Y (0.5 / ICON_SIZE_Y)
+
///Calculate if two atoms are in sight, returns TRUE or FALSE
/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5)
- var/turf/T
+ var/turf/current_turf
if(X1 == X2)
if(Y1 == Y2)
return TRUE //Light cannot be blocked on same tile
else
- var/s = SIGN(Y2-Y1)
- Y1+=s
+ var/sign = SIGN(Y2-Y1)
+ Y1 += sign
while(Y1 != Y2)
- T=locate(X1,Y1,Z)
- if(IS_OPAQUE_TURF(T))
+ current_turf = locate(X1, Y1, Z)
+ if(IS_OPAQUE_TURF(current_turf))
return FALSE
- Y1+=s
+ Y1 += sign
else
- var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1))
- var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles
+ //This looks scary but we're just calculating a linear function (y = mx + b)
+
+ //m = y/x
+ var/m = (ICON_SIZE_Y*(Y2-Y1) + (PY2-PY1)) / (ICON_SIZE_X*(X2-X1) + (PX2-PX1))//In pixels
+
+ //b = y - mx
+ var/b = (Y1 + PY1/ICON_SIZE_Y - OFFSET_Y) - m*(X1 + PX1/ICON_SIZE_X - OFFSET_X)//In tiles
+
var/signX = SIGN(X2-X1)
var/signY = SIGN(Y2-Y1)
- if(X1= mx+b
+ Y1 += signY //Line exits tile vertically
else
- X1+=signX //Line exits tile horizontally
- T=locate(X1,Y1,Z)
- if(IS_OPAQUE_TURF(T))
+ X1 += signX //Line exits tile horizontally
+ current_turf = locate(X1, Y1, Z)
+ if(IS_OPAQUE_TURF(current_turf))
return FALSE
return TRUE
+#undef OFFSET_X
+#undef OFFSET_Y
/proc/is_in_sight(atom/first_atom, atom/second_atom)
var/turf/first_turf = get_turf(first_atom)
diff --git a/code/__HELPERS/turfs.dm b/code/__HELPERS/turfs.dm
index c4867ba999373..c779c4b681b0f 100644
--- a/code/__HELPERS/turfs.dm
+++ b/code/__HELPERS/turfs.dm
@@ -237,9 +237,9 @@ Turf and target are separate in case you want to teleport some distance from a t
var/list/icon_dimensions = get_icon_dimensions(checked_atom.icon)
var/checked_atom_icon_height = icon_dimensions["height"]
var/checked_atom_icon_width = icon_dimensions["width"]
- if(checked_atom_icon_height != world.icon_size || checked_atom_icon_width != world.icon_size)
- pixel_x_offset += ((checked_atom_icon_width / world.icon_size) - 1) * (world.icon_size * 0.5)
- pixel_y_offset += ((checked_atom_icon_height / world.icon_size) - 1) * (world.icon_size * 0.5)
+ if(checked_atom_icon_height != ICON_SIZE_Y || checked_atom_icon_width != ICON_SIZE_X)
+ pixel_x_offset += ((checked_atom_icon_width / ICON_SIZE_X) - 1) * (ICON_SIZE_X * 0.5)
+ pixel_y_offset += ((checked_atom_icon_height / ICON_SIZE_Y) - 1) * (ICON_SIZE_Y * 0.5)
return list(pixel_x_offset, pixel_y_offset)
@@ -248,8 +248,8 @@ Turf and target are separate in case you want to teleport some distance from a t
**/
/proc/pixel_offset_turf(turf/offset_from, list/offsets)
//DY and DX
- var/rough_x = round(round(offsets[1], world.icon_size) / world.icon_size)
- var/rough_y = round(round(offsets[2], world.icon_size) / world.icon_size)
+ var/rough_x = round(round(offsets[1], ICON_SIZE_X) / ICON_SIZE_X)
+ var/rough_y = round(round(offsets[2], ICON_SIZE_Y) / ICON_SIZE_Y)
var/final_x = clamp(offset_from.x + rough_x, 1, world.maxx)
var/final_y = clamp(offset_from.y + rough_y, 1, world.maxy)
@@ -275,8 +275,8 @@ Turf and target are separate in case you want to teleport some distance from a t
click_turf_y = origin.y + text2num(click_turf_y[1]) - round(actual_view[2] / 2) - 1
var/turf/click_turf = locate(clamp(click_turf_x, 1, world.maxx), clamp(click_turf_y, 1, world.maxy), click_turf_z)
- LAZYSET(modifiers, ICON_X, "[(click_turf_px - click_turf.pixel_x) + ((click_turf_x - click_turf.x) * world.icon_size)]")
- LAZYSET(modifiers, ICON_Y, "[(click_turf_py - click_turf.pixel_y) + ((click_turf_y - click_turf.y) * world.icon_size)]")
+ LAZYSET(modifiers, ICON_X, "[(click_turf_px - click_turf.pixel_x) + ((click_turf_x - click_turf.x) * ICON_SIZE_X)]")
+ LAZYSET(modifiers, ICON_Y, "[(click_turf_py - click_turf.pixel_y) + ((click_turf_y - click_turf.y) * ICON_SIZE_Y)]")
return click_turf
///Almost identical to the params_to_turf(), but unused (remove?)
diff --git a/code/__HELPERS/view.dm b/code/__HELPERS/view.dm
index 30e8bc8f9f973..139bdedc425ff 100644
--- a/code/__HELPERS/view.dm
+++ b/code/__HELPERS/view.dm
@@ -16,8 +16,8 @@
if(!view)
return list(0, 0)
var/list/view_info = getviewsize(view)
- view_info[1] *= world.icon_size
- view_info[2] *= world.icon_size
+ view_info[1] *= ICON_SIZE_X
+ view_info[2] *= ICON_SIZE_Y
return view_info
/**
diff --git a/code/__byond_version_compat.dm b/code/__byond_version_compat.dm
index 0f19332934d09..6680e655551f5 100644
--- a/code/__byond_version_compat.dm
+++ b/code/__byond_version_compat.dm
@@ -9,6 +9,11 @@
#error You need version 515.1627 or higher
#endif
+// Unable to compile this version thanks to mutable appearance changes
+#if (DM_VERSION == 515 && DM_BUILD == 1643)
+#error This version of BYOND cannot compile this project. Visit www.byond.com/download/build to download an older version or update (if possible).
+#endif
+
// Keep savefile compatibilty at minimum supported level
/savefile/byond_version = MIN_COMPILER_VERSION
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index 8db59bccc3532..3e3f2bfe8d93a 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -65,6 +65,7 @@ DEFINE_BITFIELD(area_flags, list(
"VALID_TERRITORY" = VALID_TERRITORY,
"XENOBIOLOGY_COMPATIBLE" = XENOBIOLOGY_COMPATIBLE,
"NO_BOH" = NO_BOH,
+ "UNLIMITED_FISHING" = UNLIMITED_FISHING,
))
DEFINE_BITFIELD(turf_flags, list(
@@ -253,6 +254,10 @@ DEFINE_BITFIELD(mob_biotypes, list(
"MOB_UNDEAD" = MOB_UNDEAD,
))
+DEFINE_BITFIELD(mob_flags, list(
+ "MOB_HAS_SCREENTIPS_NAME_OVERRIDE" = MOB_HAS_SCREENTIPS_NAME_OVERRIDE,
+))
+
DEFINE_BITFIELD(mob_respiration_type, list(
"RESPIRATION_OXYGEN" = RESPIRATION_OXYGEN,
"RESPIRATION_N2" = RESPIRATION_N2,
@@ -560,6 +565,13 @@ DEFINE_BITFIELD(gun_flags, list(
"TURRET_INCOMPATIBLE" = TURRET_INCOMPATIBLE,
))
+DEFINE_BITFIELD(fish_flags, list(
+ "FISH_FLAG_SHOW_IN_CATALOG" = FISH_FLAG_SHOW_IN_CATALOG,
+ "FISH_DO_FLOP_ANIM" = FISH_DO_FLOP_ANIM,
+ "FISH_FLAG_PETTED" = FISH_FLAG_PETTED,
+ "FISH_FLAG_EXPERIMENT_SCANNABLE" = FISH_FLAG_EXPERIMENT_SCANNABLE,
+))
+
DEFINE_BITFIELD(bot_mode_flags, list(
"POWER_ON" = BOT_MODE_ON,
"AUTO_PATROL" = BOT_MODE_AUTOPATROL,
diff --git a/code/_globalvars/lists/achievements.dm b/code/_globalvars/lists/achievements.dm
index 283931f99847c..c788f070ad3b7 100644
--- a/code/_globalvars/lists/achievements.dm
+++ b/code/_globalvars/lists/achievements.dm
@@ -3,7 +3,7 @@ GLOBAL_LIST_EMPTY(commendations)
GLOBAL_LIST_INIT(achievement_categories, list("Bosses", "Jobs", "Skills", "Misc", "Mafia", "Scores"))
///A list of sounds that can be played when unlocking an achievement, set in the preferences.
GLOBAL_LIST_INIT(achievement_sounds, list(
- CHEEVO_SOUND_PING = sound('sound/effects/glockenspiel_ping.ogg', volume = 70),
- CHEEVO_SOUND_JINGLE = sound('sound/effects/beeps_jingle.ogg', volume = 70),
- CHEEVO_SOUND_TADA = sound('sound/effects/tada_fanfare.ogg', volume = 30),
+ CHEEVO_SOUND_PING = sound('sound/effects/achievement/glockenspiel_ping.ogg', volume = 70),
+ CHEEVO_SOUND_JINGLE = sound('sound/effects/achievement/beeps_jingle.ogg', volume = 70),
+ CHEEVO_SOUND_TADA = sound('sound/effects/achievement/tada_fanfare.ogg', volume = 30),
))
diff --git a/code/_globalvars/lists/ambience.dm b/code/_globalvars/lists/ambience.dm
index 12a389cf081f8..24d765b71c183 100644
--- a/code/_globalvars/lists/ambience.dm
+++ b/code/_globalvars/lists/ambience.dm
@@ -1,167 +1,167 @@
GLOBAL_LIST_INIT(generic_ambience,list(
- 'sound/ambience/ambigen1.ogg',
- 'sound/ambience/ambigen2.ogg',
- 'sound/ambience/ambigen3.ogg',
- 'sound/ambience/ambigen4.ogg',
- 'sound/ambience/ambigen5.ogg',
- 'sound/ambience/ambigen6.ogg',
- 'sound/ambience/ambigen7.ogg',
- 'sound/ambience/ambigen8.ogg',
- 'sound/ambience/ambigen9.ogg',
- 'sound/ambience/ambigen10.ogg',
- 'sound/ambience/ambigen11.ogg',
- 'sound/ambience/ambigen13.ogg',
- 'sound/ambience/ambigen14.ogg',
+ 'sound/ambience/general/ambigen1.ogg',
+ 'sound/ambience/general/ambigen2.ogg',
+ 'sound/ambience/general/ambigen3.ogg',
+ 'sound/ambience/general/ambigen4.ogg',
+ 'sound/ambience/general/ambigen5.ogg',
+ 'sound/ambience/general/ambigen6.ogg',
+ 'sound/ambience/general/ambigen7.ogg',
+ 'sound/ambience/general/ambigen8.ogg',
+ 'sound/ambience/general/ambigen9.ogg',
+ 'sound/ambience/general/ambigen10.ogg',
+ 'sound/ambience/general/ambigen11.ogg',
+ 'sound/ambience/general/ambigen13.ogg',
+ 'sound/ambience/general/ambigen14.ogg',
))
GLOBAL_LIST_INIT(holy_ambience,list(
- 'sound/ambience/ambicha1.ogg',
- 'sound/ambience/ambicha2.ogg',
- 'sound/ambience/ambicha3.ogg',
- 'sound/ambience/ambicha4.ogg',
- 'sound/ambience/ambiholy.ogg',
- 'sound/ambience/ambiholy2.ogg',
- 'sound/ambience/ambiholy3.ogg',
+ 'sound/ambience/holy/ambicha1.ogg',
+ 'sound/ambience/holy/ambicha2.ogg',
+ 'sound/ambience/holy/ambicha3.ogg',
+ 'sound/ambience/holy/ambicha4.ogg',
+ 'sound/ambience/holy/ambiholy.ogg',
+ 'sound/ambience/holy/ambiholy2.ogg',
+ 'sound/ambience/holy/ambiholy3.ogg',
))
GLOBAL_LIST_INIT(danger_ambience,list(
- 'sound/ambience/ambidanger.ogg',
- 'sound/ambience/ambidanger2.ogg',
+ 'sound/ambience/misc/ambidanger.ogg',
+ 'sound/ambience/misc/ambidanger2.ogg',
))
GLOBAL_LIST_INIT(ruins_ambience,list(
- 'sound/ambience/ambicave.ogg',
- 'sound/ambience/ambidanger.ogg',
- 'sound/ambience/ambidanger2.ogg',
- 'sound/ambience/ambimaint1.ogg',
- 'sound/ambience/ambimine.ogg',
- 'sound/ambience/ambimystery.ogg',
- 'sound/ambience/ambiruin.ogg',
- 'sound/ambience/ambiruin2.ogg',
- 'sound/ambience/ambiruin3.ogg',
- 'sound/ambience/ambiruin4.ogg',
- 'sound/ambience/ambiruin5.ogg',
- 'sound/ambience/ambiruin6.ogg',
- 'sound/ambience/ambiruin7.ogg',
- 'sound/ambience/ambitech3.ogg',
+ 'sound/ambience/lavaland/ambicave.ogg',
+ 'sound/ambience/misc/ambidanger.ogg',
+ 'sound/ambience/misc/ambidanger2.ogg',
+ 'sound/ambience/maintenance/ambimaint1.ogg',
+ 'sound/ambience/ruin/ambimine.ogg',
+ 'sound/ambience/misc/ambimystery.ogg',
+ 'sound/ambience/ruin/ambiruin.ogg',
+ 'sound/ambience/ruin/ambiruin2.ogg',
+ 'sound/ambience/ruin/ambiruin3.ogg',
+ 'sound/ambience/ruin/ambiruin4.ogg',
+ 'sound/ambience/ruin/ambiruin5.ogg',
+ 'sound/ambience/ruin/ambiruin6.ogg',
+ 'sound/ambience/ruin/ambiruin7.ogg',
+ 'sound/ambience/engineering/ambitech3.ogg',
))
GLOBAL_LIST_INIT(engi_ambience,list(
- 'sound/ambience/ambiatmos.ogg',
- 'sound/ambience/ambiatmos2.ogg',
- 'sound/ambience/ambisin1.ogg',
- 'sound/ambience/ambisin2.ogg',
- 'sound/ambience/ambisin3.ogg',
- 'sound/ambience/ambisin4.ogg',
- 'sound/ambience/ambitech.ogg',
- 'sound/ambience/ambitech2.ogg',
- 'sound/ambience/ambitech3.ogg',
+ 'sound/ambience/engineering/ambiatmos.ogg',
+ 'sound/ambience/engineering/ambiatmos2.ogg',
+ 'sound/ambience/engineering/ambisin1.ogg',
+ 'sound/ambience/engineering/ambisin2.ogg',
+ 'sound/ambience/engineering/ambisin3.ogg',
+ 'sound/ambience/engineering/ambisin4.ogg',
+ 'sound/ambience/engineering/ambitech.ogg',
+ 'sound/ambience/engineering/ambitech2.ogg',
+ 'sound/ambience/engineering/ambitech3.ogg',
))
GLOBAL_LIST_INIT(mining_ambience, list(
- 'sound/ambience/ambicave.ogg',
- 'sound/ambience/ambidanger.ogg',
- 'sound/ambience/ambidanger2.ogg',
- 'sound/ambience/ambilava1.ogg',
- 'sound/ambience/ambilava2.ogg',
- 'sound/ambience/ambilava3.ogg',
- 'sound/ambience/ambimaint1.ogg',
- 'sound/ambience/ambimine.ogg',
- 'sound/ambience/ambiruin.ogg',
- 'sound/ambience/ambiruin2.ogg',
- 'sound/ambience/ambiruin3.ogg',
- 'sound/ambience/ambiruin4.ogg',
- 'sound/ambience/ambiruin5.ogg',
- 'sound/ambience/ambiruin6.ogg',
- 'sound/ambience/ambiruin7.ogg',
+ 'sound/ambience/lavaland/ambicave.ogg',
+ 'sound/ambience/misc/ambidanger.ogg',
+ 'sound/ambience/misc/ambidanger2.ogg',
+ 'sound/ambience/lavaland/ambilava1.ogg',
+ 'sound/ambience/lavaland/ambilava2.ogg',
+ 'sound/ambience/lavaland/ambilava3.ogg',
+ 'sound/ambience/maintenance/ambimaint1.ogg',
+ 'sound/ambience/ruin/ambimine.ogg',
+ 'sound/ambience/ruin/ambiruin.ogg',
+ 'sound/ambience/ruin/ambiruin2.ogg',
+ 'sound/ambience/ruin/ambiruin3.ogg',
+ 'sound/ambience/ruin/ambiruin4.ogg',
+ 'sound/ambience/ruin/ambiruin5.ogg',
+ 'sound/ambience/ruin/ambiruin6.ogg',
+ 'sound/ambience/ruin/ambiruin7.ogg',
))
GLOBAL_LIST_INIT(icemoon_ambience,list(
- 'sound/ambience/ambiicetheme.ogg',
- 'sound/ambience/ambiicemelody1.ogg',
- 'sound/ambience/ambiicemelody2.ogg',
- 'sound/ambience/ambiicemelody3.ogg',
- 'sound/ambience/ambiicemelody4.ogg',
- 'sound/ambience/ambiicesting1.ogg',
- 'sound/ambience/ambiicesting2.ogg',
- 'sound/ambience/ambiicesting3.ogg',
- 'sound/ambience/ambiicesting4.ogg',
- 'sound/ambience/ambiicesting5.ogg',
+ 'sound/ambience/icemoon/ambiicetheme.ogg',
+ 'sound/ambience/icemoon/ambiicemelody1.ogg',
+ 'sound/ambience/icemoon/ambiicemelody2.ogg',
+ 'sound/ambience/icemoon/ambiicemelody3.ogg',
+ 'sound/ambience/icemoon/ambiicemelody4.ogg',
+ 'sound/ambience/icemoon/ambiicesting1.ogg',
+ 'sound/ambience/icemoon/ambiicesting2.ogg',
+ 'sound/ambience/icemoon/ambiicesting3.ogg',
+ 'sound/ambience/icemoon/ambiicesting4.ogg',
+ 'sound/ambience/icemoon/ambiicesting5.ogg',
))
GLOBAL_LIST_INIT(medical_ambience,list(
- 'sound/ambience/ambinice.ogg',
+ 'sound/ambience/medical/ambinice.ogg',
))
GLOBAL_LIST_INIT(virology_ambience,list(
- 'sound/ambience/ambiviro.ogg',
- 'sound/ambience/ambiviro1.ogg',
- 'sound/ambience/ambiviro2.ogg',
+ 'sound/ambience/medical/ambiviro.ogg',
+ 'sound/ambience/medical/ambiviro1.ogg',
+ 'sound/ambience/medical/ambiviro2.ogg',
))
GLOBAL_LIST_INIT(spooky_ambience,list(
- 'sound/ambience/ambimo1.ogg',
- 'sound/ambience/ambimo2.ogg',
- 'sound/ambience/ambimystery.ogg',
- 'sound/ambience/ambiodd.ogg',
- 'sound/ambience/ambiruin6.ogg',
- 'sound/ambience/ambiruin7.ogg',
+ 'sound/ambience/medical/ambimo1.ogg',
+ 'sound/ambience/medical/ambimo2.ogg',
+ 'sound/ambience/misc/ambimystery.ogg',
+ 'sound/ambience/misc/ambiodd.ogg',
+ 'sound/ambience/ruin/ambiruin6.ogg',
+ 'sound/ambience/ruin/ambiruin7.ogg',
))
GLOBAL_LIST_INIT(space_ambience,list(
- 'sound/ambience/ambiatmos.ogg',
- 'sound/ambience/ambispace.ogg',
- 'sound/ambience/ambispace2.ogg',
- 'sound/ambience/ambispace3.ogg',
- 'sound/ambience/ambispace4.ogg',
- 'sound/ambience/ambispace5.ogg',
- 'sound/ambience/ambispace6.ogg',
- 'sound/ambience/title2.ogg',
+ 'sound/ambience/engineering/ambiatmos.ogg',
+ 'sound/ambience/space/ambispace.ogg',
+ 'sound/ambience/space/ambispace2.ogg',
+ 'sound/ambience/space/ambispace3.ogg',
+ 'sound/ambience/space/ambispace4.ogg',
+ 'sound/ambience/space/ambispace5.ogg',
+ 'sound/ambience/space/ambispace6.ogg',
+ 'sound/music/lobby_music/title2.ogg',
))
GLOBAL_LIST_INIT(maint_ambience,list(
- 'sound/ambience/ambimaint1.ogg',
- 'sound/ambience/ambimaint2.ogg',
- 'sound/ambience/ambimaint3.ogg',
- 'sound/ambience/ambimaint4.ogg',
- 'sound/ambience/ambimaint5.ogg',
- 'sound/ambience/ambimaint6.ogg',
- 'sound/ambience/ambimaint7.ogg',
- 'sound/ambience/ambimaint8.ogg',
- 'sound/ambience/ambimaint9.ogg',
- 'sound/ambience/ambimaint10.ogg',
- 'sound/ambience/ambimaint11.ogg',
- 'sound/ambience/ambimaint12.ogg',
- 'sound/ambience/ambitech2.ogg',
- 'sound/voice/lowHiss1.ogg',
- 'sound/voice/lowHiss2.ogg',
- 'sound/voice/lowHiss3.ogg',
- 'sound/voice/lowHiss4.ogg',
- 'sound/ambience/maintambience.ogg',
+ 'sound/ambience/maintenance/ambimaint1.ogg',
+ 'sound/ambience/maintenance/ambimaint2.ogg',
+ 'sound/ambience/maintenance/ambimaint3.ogg',
+ 'sound/ambience/maintenance/ambimaint4.ogg',
+ 'sound/ambience/maintenance/ambimaint5.ogg',
+ 'sound/ambience/maintenance/ambimaint6.ogg',
+ 'sound/ambience/maintenance/ambimaint7.ogg',
+ 'sound/ambience/maintenance/ambimaint8.ogg',
+ 'sound/ambience/maintenance/ambimaint9.ogg',
+ 'sound/ambience/maintenance/ambimaint10.ogg',
+ 'sound/ambience/maintenance/ambimaint11.ogg',
+ 'sound/ambience/maintenance/ambimaint12.ogg',
+ 'sound/ambience/engineering/ambitech2.ogg',
+ 'sound/mobs/non-humanoids/hiss/lowHiss1.ogg',
+ 'sound/mobs/non-humanoids/hiss/lowHiss2.ogg',
+ 'sound/mobs/non-humanoids/hiss/lowHiss3.ogg',
+ 'sound/mobs/non-humanoids/hiss/lowHiss4.ogg',
+ 'sound/ambience/maintenance/maintambience.ogg',
))
GLOBAL_LIST_INIT(away_ambience,list(
- 'sound/ambience/ambiatmos.ogg',
- 'sound/ambience/ambiatmos2.ogg',
- 'sound/ambience/ambidanger.ogg',
- 'sound/ambience/ambidanger2.ogg',
- 'sound/ambience/ambimaint.ogg',
- 'sound/ambience/ambiodd.ogg',
- 'sound/ambience/ambiruin.ogg',
- 'sound/ambience/ambiruin2.ogg',
- 'sound/ambience/ambiruin3.ogg',
- 'sound/ambience/ambiruin4.ogg',
- 'sound/ambience/ambiruin5.ogg',
- 'sound/ambience/ambiruin6.ogg',
- 'sound/ambience/ambiruin7.ogg',
- 'sound/ambience/ambitech.ogg',
- 'sound/ambience/ambitech2.ogg',
+ 'sound/ambience/engineering/ambiatmos.ogg',
+ 'sound/ambience/engineering/ambiatmos2.ogg',
+ 'sound/ambience/misc/ambidanger.ogg',
+ 'sound/ambience/misc/ambidanger2.ogg',
+ 'sound/ambience/maintenance/ambimaint.ogg',
+ 'sound/ambience/misc/ambiodd.ogg',
+ 'sound/ambience/ruin/ambiruin.ogg',
+ 'sound/ambience/ruin/ambiruin2.ogg',
+ 'sound/ambience/ruin/ambiruin3.ogg',
+ 'sound/ambience/ruin/ambiruin4.ogg',
+ 'sound/ambience/ruin/ambiruin5.ogg',
+ 'sound/ambience/ruin/ambiruin6.ogg',
+ 'sound/ambience/ruin/ambiruin7.ogg',
+ 'sound/ambience/engineering/ambitech.ogg',
+ 'sound/ambience/engineering/ambitech2.ogg',
))
GLOBAL_LIST_INIT(reebe_ambience,list(
- 'sound/ambience/ambireebe1.ogg',
- 'sound/ambience/ambireebe2.ogg',
- 'sound/ambience/ambireebe3.ogg',
+ 'sound/ambience/misc/ambireebe1.ogg',
+ 'sound/ambience/misc/ambireebe2.ogg',
+ 'sound/ambience/misc/ambireebe3.ogg',
))
GLOBAL_LIST_INIT(creepy_ambience,list(
@@ -169,25 +169,25 @@ GLOBAL_LIST_INIT(creepy_ambience,list(
'sound/effects/ghost2.ogg',
'sound/effects/heart_beat.ogg',
'sound/effects/screech.ogg',
- 'sound/hallucinations/behind_you1.ogg',
- 'sound/hallucinations/behind_you2.ogg',
- 'sound/hallucinations/far_noise.ogg',
- 'sound/hallucinations/growl1.ogg',
- 'sound/hallucinations/growl2.ogg',
- 'sound/hallucinations/growl3.ogg',
- 'sound/hallucinations/i_see_you1.ogg',
- 'sound/hallucinations/i_see_you2.ogg',
- 'sound/hallucinations/im_here1.ogg',
- 'sound/hallucinations/im_here2.ogg',
- 'sound/hallucinations/look_up1.ogg',
- 'sound/hallucinations/look_up2.ogg',
- 'sound/hallucinations/over_here1.ogg',
- 'sound/hallucinations/over_here2.ogg',
- 'sound/hallucinations/over_here3.ogg',
- 'sound/hallucinations/turn_around1.ogg',
- 'sound/hallucinations/turn_around2.ogg',
- 'sound/hallucinations/veryfar_noise.ogg',
- 'sound/hallucinations/wail.ogg',
+ 'sound/effects/hallucinations/behind_you1.ogg',
+ 'sound/effects/hallucinations/behind_you2.ogg',
+ 'sound/effects/hallucinations/far_noise.ogg',
+ 'sound/effects/hallucinations/growl1.ogg',
+ 'sound/effects/hallucinations/growl2.ogg',
+ 'sound/effects/hallucinations/growl3.ogg',
+ 'sound/effects/hallucinations/i_see_you1.ogg',
+ 'sound/effects/hallucinations/i_see_you2.ogg',
+ 'sound/effects/hallucinations/im_here1.ogg',
+ 'sound/effects/hallucinations/im_here2.ogg',
+ 'sound/effects/hallucinations/look_up1.ogg',
+ 'sound/effects/hallucinations/look_up2.ogg',
+ 'sound/effects/hallucinations/over_here1.ogg',
+ 'sound/effects/hallucinations/over_here2.ogg',
+ 'sound/effects/hallucinations/over_here3.ogg',
+ 'sound/effects/hallucinations/turn_around1.ogg',
+ 'sound/effects/hallucinations/turn_around2.ogg',
+ 'sound/effects/hallucinations/veryfar_noise.ogg',
+ 'sound/effects/hallucinations/wail.ogg',
))
GLOBAL_LIST_INIT(ambience_assoc,list(
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index 82cc3f1cf10b3..123deaa2518e5 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -134,22 +134,22 @@ GLOBAL_LIST_EMPTY(female_clothing_icons)
GLOBAL_LIST_INIT(scarySounds, list(
'sound/effects/footstep/clownstep1.ogg',
'sound/effects/footstep/clownstep2.ogg',
- 'sound/effects/glassbr1.ogg',
- 'sound/effects/glassbr2.ogg',
- 'sound/effects/glassbr3.ogg',
- 'sound/items/welder.ogg',
- 'sound/items/welder2.ogg',
- 'sound/machines/airlock.ogg',
- 'sound/voice/hiss1.ogg',
- 'sound/voice/hiss2.ogg',
- 'sound/voice/hiss3.ogg',
- 'sound/voice/hiss4.ogg',
- 'sound/voice/hiss5.ogg',
- 'sound/voice/hiss6.ogg',
- 'sound/weapons/armbomb.ogg',
- 'sound/weapons/taser.ogg',
- 'sound/weapons/thudswoosh.ogg',
- 'sound/weapons/shove.ogg',
+ 'sound/effects/glass/glassbr1.ogg',
+ 'sound/effects/glass/glassbr2.ogg',
+ 'sound/effects/glass/glassbr3.ogg',
+ 'sound/items/tools/welder.ogg',
+ 'sound/items/tools/welder2.ogg',
+ 'sound/machines/airlock/airlock.ogg',
+ 'sound/mobs/non-humanoids/hiss/hiss1.ogg',
+ 'sound/mobs/non-humanoids/hiss/hiss2.ogg',
+ 'sound/mobs/non-humanoids/hiss/hiss3.ogg',
+ 'sound/mobs/non-humanoids/hiss/hiss4.ogg',
+ 'sound/mobs/non-humanoids/hiss/hiss5.ogg',
+ 'sound/mobs/non-humanoids/hiss/hiss6.ogg',
+ 'sound/items/weapons/armbomb.ogg',
+ 'sound/items/weapons/taser.ogg',
+ 'sound/items/weapons/thudswoosh.ogg',
+ 'sound/items/weapons/shove.ogg',
))
diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm
index 8a3697eaa4b56..dad143c4279db 100644
--- a/code/_globalvars/traits/_traits.dm
+++ b/code/_globalvars/traits/_traits.dm
@@ -10,6 +10,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_AI_PAUSED" = TRAIT_AI_PAUSED,
"TRAIT_BANNED_FROM_CARGO_SHUTTLE" = TRAIT_BANNED_FROM_CARGO_SHUTTLE,
"TRAIT_BEING_SHOCKED" = TRAIT_BEING_SHOCKED,
+ "TRAIT_CATCH_AND_RELEASE" = TRAIT_CATCH_AND_RELEASE,
"TRAIT_COMMISSIONED" = TRAIT_COMMISSIONED,
"TRAIT_CLIMBABLE" = TRAIT_CLIMBABLE,
"TRAIT_CURRENTLY_CLEANING" = TRAIT_CURRENTLY_CLEANING,
@@ -17,6 +18,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_DO_NOT_SPLASH" = TRAIT_DO_NOT_SPLASH,
"TRAIT_DRIED" = TRAIT_DRIED,
"TRAIT_DRYABLE" = TRAIT_DRYABLE,
+ "TRAIT_FISHING_SPOT" = TRAIT_FISHING_SPOT,
"TRAIT_FOOD_CHEF_MADE" = TRAIT_FOOD_CHEF_MADE,
"TRAIT_FOOD_FRIED" = TRAIT_FOOD_FRIED,
"TRAIT_QUALITY_FOOD_INGREDIENT" = TRAIT_QUALITY_FOOD_INGREDIENT,
@@ -29,6 +31,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_SPINNING" = TRAIT_SPINNING,
"TRAIT_STICKERED" = TRAIT_STICKERED,
"TRAIT_UNHITTABLE_BY_PROJECTILES" = TRAIT_UNHITTABLE_BY_PROJECTILES,
+ "TRAIT_UNLINKABLE_FISHING_SPOT" = TRAIT_UNLINKABLE_FISHING_SPOT,
),
/atom/movable = list(
"TRAIT_ACTIVE_STORAGE" = TRAIT_ACTIVE_STORAGE,
@@ -37,6 +40,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_BLOCKING_EXPLOSIVES" = TRAIT_BLOCKING_EXPLOSIVES,
"TRAIT_BOULDER_BREAKER" = TRAIT_BOULDER_BREAKER,
"TRAIT_CASTABLE_LOC" = TRAIT_CASTABLE_LOC,
+ "TRAIT_CHASM_STOPPER" = TRAIT_CHASM_STOPPER,
+ "TRAIT_COMBAT_MODE_SKIP_INTERACTION" = TRAIT_COMBAT_MODE_SKIP_INTERACTION,
"TRAIT_DEL_ON_SPACE_DUMP" = TRAIT_DEL_ON_SPACE_DUMP,
"TRAIT_FISH_CASE_COMPATIBILE" = TRAIT_FISH_CASE_COMPATIBILE,
"TRAIT_FROZEN" = TRAIT_FROZEN,
@@ -50,28 +55,26 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_MOVE_FLYING" = TRAIT_MOVE_FLYING,
"TRAIT_MOVE_GROUND" = TRAIT_MOVE_GROUND,
"TRAIT_MOVE_PHASING" = TRAIT_MOVE_PHASING,
- "TRAIT_MOVE_VENTCRAWLING" = TRAIT_MOVE_VENTCRAWLING,
"TRAIT_MOVE_UPSIDE_DOWN" = TRAIT_MOVE_UPSIDE_DOWN,
+ "TRAIT_MOVE_VENTCRAWLING" = TRAIT_MOVE_VENTCRAWLING,
+ "TRAIT_NOT_ENGRAVABLE" = TRAIT_NOT_ENGRAVABLE,
"TRAIT_NO_FLOATING_ANIM" = TRAIT_NO_FLOATING_ANIM,
"TRAIT_NO_MANIFEST_CONTENTS_ERROR" = TRAIT_NO_MANIFEST_CONTENTS_ERROR,
"TRAIT_NO_MISSING_ITEM_ERROR" = TRAIT_NO_MISSING_ITEM_ERROR,
"TRAIT_NO_THROW_HITPUSH" = TRAIT_NO_THROW_HITPUSH,
- "TRAIT_NOT_ENGRAVABLE" = TRAIT_NOT_ENGRAVABLE,
- "TRAIT_SPELLS_TRANSFER_TO_LOC" = TRAIT_SPELLS_TRANSFER_TO_LOC,
"TRAIT_ODD_CUSTOMIZABLE_FOOD_INGREDIENT" = TRAIT_ODD_CUSTOMIZABLE_FOOD_INGREDIENT,
"TRAIT_ON_HIT_EFFECT" = TRAIT_ON_HIT_EFFECT,
"TRAIT_RUNECHAT_HIDDEN" = TRAIT_RUNECHAT_HIDDEN,
"TRAIT_SCARY_FISHERMAN" = TRAIT_SCARY_FISHERMAN,
"TRAIT_SECLUDED_LOCATION" = TRAIT_SECLUDED_LOCATION,
"TRAIT_SNOWSTORM_IMMUNE" = TRAIT_SNOWSTORM_IMMUNE,
+ "TRAIT_SPELLS_TRANSFER_TO_LOC" = TRAIT_SPELLS_TRANSFER_TO_LOC,
"TRAIT_TELEKINESIS_CONTROLLED" = TRAIT_TELEKINESIS_CONTROLLED,
"TRAIT_UNDERFLOOR" = TRAIT_UNDERFLOOR,
"TRAIT_UNIQUE_IMMERSE" = TRAIT_UNIQUE_IMMERSE,
- "TRAIT_VOIDSTORM_IMMUNE" = TRAIT_VOIDSTORM_IMMUNE,
- "TRAIT_WAS_RENAMED" = TRAIT_WAS_RENAMED,
"TRAIT_WADDLING" = TRAIT_WADDLING,
+ "TRAIT_WAS_RENAMED" = TRAIT_WAS_RENAMED,
"TRAIT_WEATHER_IMMUNE" = TRAIT_WEATHER_IMMUNE,
- "TRAIT_CHASM_STOPPER" = TRAIT_CHASM_STOPPER,
),
/datum/controller/subsystem/economy = list(
"TRAIT_MARKET_CRASHING" = TRAIT_MARKET_CRASHING,
@@ -123,7 +126,6 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_ABDUCTOR_TRAINING" = TRAIT_ABDUCTOR_TRAINING,
"TRAIT_ACT_AS_CULTIST" = TRAIT_ACT_AS_CULTIST,
"TRAIT_ACT_AS_HERETIC" = TRAIT_ACT_AS_HERETIC,
- "TRAIT_ACTIVELY_FISHING" = TRAIT_ACTIVELY_FISHING,
"TRAIT_ADAMANTINE_EXTRACT_ARMOR" = TRAIT_ADAMANTINE_EXTRACT_ARMOR,
"TRAIT_ADVANCEDTOOLUSER" = TRAIT_ADVANCEDTOOLUSER,
"TRAIT_AGENDER" = TRAIT_AGENDER,
@@ -238,6 +240,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_FEARLESS" = TRAIT_FEARLESS,
"TRAIT_FENCE_CLIMBER" = TRAIT_FENCE_CLIMBER,
"TRAIT_FINGERPRINT_PASSTHROUGH" = TRAIT_FINGERPRINT_PASSTHROUGH,
+ "TRAIT_FISH_EATER" = TRAIT_FISH_EATER,
"TRAIT_FIST_MINING" = TRAIT_FIST_MINING,
"TRAIT_FIXED_MUTANT_COLORS" = TRAIT_FIXED_MUTANT_COLORS,
"TRAIT_FLESH_DESIRE" = TRAIT_FLESH_DESIRE,
@@ -257,7 +260,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_GARLIC_BREATH" = TRAIT_GARLIC_BREATH,
"TRAIT_GENELESS" = TRAIT_GENELESS,
"TRAIT_GIANT" = TRAIT_GIANT,
- "TRAIT_GONE_FISHING" = TRAIT_GONE_FISHING,
+ "TRAIT_GODMODE" = TRAIT_GODMODE,
"TRAIT_GOOD_HEARING" = TRAIT_GOOD_HEARING,
"TRAIT_GRABWEAKNESS" = TRAIT_GRABWEAKNESS,
"TRAIT_GREENTEXT_CURSED" = TRAIT_GREENTEXT_CURSED,
@@ -282,6 +285,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_HOT_SPRING_CURSED" = TRAIT_HOT_SPRING_CURSED,
"TRAIT_HULK" = TRAIT_HULK,
"TRAIT_HUSK" = TRAIT_HUSK,
+ "TRAIT_HYPOTHERMIC" = TRAIT_HYPOTHERMIC,
"TRAIT_ID_APPRAISER" = TRAIT_ID_APPRAISER,
"TRAIT_IGNORE_ELEVATION" = TRAIT_IGNORE_ELEVATION,
"TRAIT_IGNORESLOWDOWN" = TRAIT_IGNORESLOWDOWN,
@@ -522,6 +526,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_USER_SCOPED" = TRAIT_USER_SCOPED,
"TRAIT_USES_SKINTONES" = TRAIT_USES_SKINTONES,
"TRAIT_VATGROWN" = TRAIT_VATGROWN,
+ "TRAIT_VEGETARIAN" = TRAIT_VEGETARIAN,
"TRAIT_VENTCRAWLER_ALWAYS" = TRAIT_VENTCRAWLER_ALWAYS,
"TRAIT_VENTCRAWLER_NUDE" = TRAIT_VENTCRAWLER_NUDE,
"TRAIT_VIRUSIMMUNE" = TRAIT_VIRUSIMMUNE,
@@ -539,11 +544,15 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_XENO_IMMUNE" = TRAIT_XENO_IMMUNE,
"TRAIT_XRAY_HEARING" = TRAIT_XRAY_HEARING,
"TRAIT_XRAY_VISION" = TRAIT_XRAY_VISION,
+ "TRAIT_NOGRAV_ALWAYS_DRIFT" = TRAIT_NOGRAV_ALWAYS_DRIFT,
"TRAIT_SPEECH_BOOSTER" = TRAIT_SPEECH_BOOSTER,
"TRAIT_MINING_PARRYING" = TRAIT_MINING_PARRYING,
+ "TRAIT_ILLUSORY_EFFECT" = TRAIT_ILLUSORY_EFFECT,
),
/obj/item = list(
"TRAIT_APC_SHOCKING" = TRAIT_APC_SHOCKING,
+ "TRAIT_BAIT_ALLOW_FISHING_DUD" = TRAIT_BAIT_ALLOW_FISHING_DUD,
+ "TRAIT_BAIT_IGNORE_ENVIRONMENT" = TRAIT_BAIT_IGNORE_ENVIRONMENT,
"TRAIT_BAIT_UNCONSUMABLE" = TRAIT_BAIT_UNCONSUMABLE,
"TRAIT_BAKEABLE" = TRAIT_BAKEABLE,
"TRAIT_BASIC_QUALITY_BAIT" = TRAIT_BASIC_QUALITY_BAIT,
@@ -553,6 +562,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_CUSTOM_TAP_SOUND" = TRAIT_CUSTOM_TAP_SOUND,
"TRAIT_DANGEROUS_OBJECT" = TRAIT_DANGEROUS_OBJECT,
"TRAIT_FISHING_BAIT" = TRAIT_FISHING_BAIT,
+ "TRAIT_FOOD_BBQ_GRILLED" = TRAIT_FOOD_BBQ_GRILLED,
"TRAIT_GERM_SENSITIVE" = TRAIT_GERM_SENSITIVE,
"TRAIT_GOOD_QUALITY_BAIT" = TRAIT_GOOD_QUALITY_BAIT,
"TRAIT_GREAT_QUALITY_BAIT" = TRAIT_GREAT_QUALITY_BAIT,
@@ -573,6 +583,8 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_T_RAY_VISIBLE" = TRAIT_T_RAY_VISIBLE,
"TRAIT_TRANSFORM_ACTIVE" = TRAIT_TRANSFORM_ACTIVE,
"TRAIT_UNCATCHABLE" = TRAIT_UNCATCHABLE,
+ "TRAIT_UNCOMPOSTABLE" = TRAIT_UNCOMPOSTABLE,
+ "TRAIT_UNIQUE_AQUARIUM_CONTENT" = TRAIT_UNIQUE_AQUARIUM_CONTENT,
"TRAIT_WIELDED" = TRAIT_WIELDED,
),
/obj/item/ammo_casing = list(
@@ -604,16 +616,23 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_FISH_FED_LUBE" = TRAIT_FISH_FED_LUBE,
"TRAIT_FISH_FLOPPING" = TRAIT_FISH_FLOPPING,
"TRAIT_FISH_FROM_CASE" = TRAIT_FISH_FROM_CASE,
+ "TRAIT_FISH_INK_ON_COOLDOWN" = TRAIT_FISH_INK_ON_COOLDOWN,
"TRAIT_FISH_NO_HUNGER" = TRAIT_FISH_NO_HUNGER,
"TRAIT_FISH_NO_MATING" = TRAIT_FISH_NO_MATING,
"TRAIT_FISH_RECESSIVE" = TRAIT_FISH_RECESSIVE,
"TRAIT_FISH_SELF_REPRODUCE" = TRAIT_FISH_SELF_REPRODUCE,
+ "TRAIT_FISH_SHOULD_TWOHANDED" = TRAIT_FISH_SHOULD_TWOHANDED,
"TRAIT_FISH_STASIS" = TRAIT_FISH_STASIS,
"TRAIT_FISH_STINGER" = TRAIT_FISH_STINGER,
+ "TRAIT_FISH_SURVIVE_COOKING" = TRAIT_FISH_SURVIVE_COOKING,
"TRAIT_FISH_TOXIN_IMMUNE" = TRAIT_FISH_TOXIN_IMMUNE,
"TRAIT_RESIST_EMULSIFY" = TRAIT_RESIST_EMULSIFY,
+ "TRAIT_FISH_WELL_COOKED" = TRAIT_FISH_WELL_COOKED,
"TRAIT_YUCKY_FISH" = TRAIT_YUCKY_FISH,
),
+ /obj/item/fishing_rod = list(
+ "TRAIT_ROD_REMOVE_FISHING_DUD" = TRAIT_ROD_REMOVE_FISHING_DUD,
+ ),
/obj/item/integrated_circuit = list(
"TRAIT_CIRCUIT_UI_OPEN" = TRAIT_CIRCUIT_UI_OPEN,
"TRAIT_CIRCUIT_UNDUPABLE" = TRAIT_CIRCUIT_UNDUPABLE,
@@ -661,7 +680,6 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_CONTAINMENT_FIELD" = TRAIT_CONTAINMENT_FIELD,
"TRAIT_ELEVATED_TURF" = TRAIT_ELEVATED_TURF,
"TRAIT_FIREDOOR_STOP" = TRAIT_FIREDOOR_STOP,
- "TRAIT_FISHING_SPOT" = TRAIT_FISHING_SPOT,
"TRAIT_HYPERSPACE_STOPPED" = TRAIT_HYPERSPACE_STOPPED,
"TRAIT_IMMERSE_STOPPED" = TRAIT_IMMERSE_STOPPED,
"TRAIT_LAVA_STOPPED" = TRAIT_LAVA_STOPPED,
diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm
index afb8709109832..335e35a7111ec 100644
--- a/code/_globalvars/traits/admin_tooling.dm
+++ b/code/_globalvars/traits/admin_tooling.dm
@@ -4,8 +4,9 @@
GLOBAL_LIST_INIT(admin_visible_traits, list(
/atom = list(
- "TRAIT_UNHITTABLE_BY_PROJECTILES" = TRAIT_UNHITTABLE_BY_PROJECTILES,
+ "TRAIT_CATCH_AND_RELEASE" = TRAIT_CATCH_AND_RELEASE,
"TRAIT_KEEP_TOGETHER" = TRAIT_KEEP_TOGETHER,
+ "TRAIT_UNHITTABLE_BY_PROJECTILES" = TRAIT_UNHITTABLE_BY_PROJECTILES,
),
/atom/movable = list(
"TRAIT_ASHSTORM_IMMUNE" = TRAIT_ASHSTORM_IMMUNE,
@@ -20,7 +21,6 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_RUNECHAT_HIDDEN" = TRAIT_RUNECHAT_HIDDEN,
"TRAIT_SCARY_FISHERMAN" = TRAIT_SCARY_FISHERMAN,
"TRAIT_SNOWSTORM_IMMUNE" = TRAIT_SNOWSTORM_IMMUNE,
- "TRAIT_VOIDSTORM_IMMUNE" = TRAIT_VOIDSTORM_IMMUNE,
"TRAIT_WEATHER_IMMUNE" = TRAIT_WEATHER_IMMUNE,
),
/mob = list(
@@ -102,6 +102,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_FAT" = TRAIT_FAT,
"TRAIT_FEARLESS" = TRAIT_FEARLESS,
"TRAIT_FENCE_CLIMBER" = TRAIT_FENCE_CLIMBER,
+ "TRAIT_FISH_EATER" = TRAIT_FISH_EATER,
"TRAIT_FIST_MINING" = TRAIT_FIST_MINING,
"TRAIT_FIXED_MUTANT_COLORS" = TRAIT_FIXED_MUTANT_COLORS,
"TRAIT_FLESH_DESIRE" = TRAIT_FLESH_DESIRE,
@@ -118,6 +119,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_GARLIC_BREATH" = TRAIT_GARLIC_BREATH,
"TRAIT_GENELESS" = TRAIT_GENELESS,
"TRAIT_GIANT" = TRAIT_GIANT,
+ "TRAIT_GODMODE" = TRAIT_GODMODE,
"TRAIT_GOOD_HEARING" = TRAIT_GOOD_HEARING,
"TRAIT_GRABWEAKNESS" = TRAIT_GRABWEAKNESS,
"TRAIT_GREENTEXT_CURSED" = TRAIT_GREENTEXT_CURSED,
@@ -300,6 +302,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_UNSTABLE" = TRAIT_UNSTABLE,
"TRAIT_USED_DNA_VAULT" = TRAIT_USED_DNA_VAULT,
"TRAIT_USES_SKINTONES" = TRAIT_USES_SKINTONES,
+ "TRAIT_VEGETARIAN" = TRAIT_VEGETARIAN,
"TRAIT_VENTCRAWLER_ALWAYS" = TRAIT_VENTCRAWLER_ALWAYS,
"TRAIT_VENTCRAWLER_NUDE" = TRAIT_VENTCRAWLER_NUDE,
"TRAIT_VIRUSIMMUNE" = TRAIT_VIRUSIMMUNE,
@@ -339,16 +342,22 @@ GLOBAL_LIST_INIT(admin_visible_traits, list(
"TRAIT_FISH_ELECTROGENESIS" = TRAIT_FISH_ELECTROGENESIS,
"TRAIT_FISH_FED_LUBE" = TRAIT_FISH_FED_LUBE,
"TRAIT_FISH_FROM_CASE" = TRAIT_FISH_FROM_CASE,
+ "TRAIT_FISH_INK_ON_COOLDOWN" = TRAIT_FISH_INK_ON_COOLDOWN,
"TRAIT_FISH_NO_HUNGER" = TRAIT_FISH_NO_HUNGER,
"TRAIT_FISH_NO_MATING" = TRAIT_FISH_NO_MATING,
"TRAIT_FISH_RECESSIVE" = TRAIT_FISH_RECESSIVE,
"TRAIT_FISH_SELF_REPRODUCE" = TRAIT_FISH_SELF_REPRODUCE,
+ "TRAIT_FISH_SHOULD_TWOHANDED" = TRAIT_FISH_SHOULD_TWOHANDED,
"TRAIT_FISH_STASIS" = TRAIT_FISH_STASIS,
"TRAIT_FISH_STINGER" = TRAIT_FISH_STINGER,
+ "TRAIT_FISH_SURVIVE_COOKING" = TRAIT_FISH_SURVIVE_COOKING,
"TRAIT_FISH_TOXIN_IMMUNE" = TRAIT_FISH_TOXIN_IMMUNE,
"TRAIT_RESIST_EMULSIFY" = TRAIT_RESIST_EMULSIFY,
"TRAIT_YUCKY_FISH" = TRAIT_YUCKY_FISH,
),
+ /obj/item/fishing_rod = list(
+ "TRAIT_ROD_REMOVE_FISHING_DUD" = TRAIT_ROD_REMOVE_FISHING_DUD,
+ ),
/obj/item/organ/internal/liver = list(
"TRAIT_BALLMER_SCIENTIST" = TRAIT_BALLMER_SCIENTIST,
"TRAIT_COMEDY_METABOLISM" = TRAIT_COMEDY_METABOLISM,
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 0c441ba928ee4..ff9a1fc54eb1b 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -400,15 +400,15 @@
mouse_opacity = MOUSE_OPACITY_OPAQUE
screen_loc = "CENTER"
-#define MAX_SAFE_BYOND_ICON_SCALE_TILES (MAX_SAFE_BYOND_ICON_SCALE_PX / world.icon_size)
-#define MAX_SAFE_BYOND_ICON_SCALE_PX (33 * 32) //Not using world.icon_size on purpose.
+#define MAX_SAFE_BYOND_ICON_SCALE_TILES (MAX_SAFE_BYOND_ICON_SCALE_PX / ICON_SIZE_ALL)
+#define MAX_SAFE_BYOND_ICON_SCALE_PX (33 * 32) //Not using world.icon_size on purpose. //Ok well I trust you
/atom/movable/screen/click_catcher/proc/UpdateGreed(view_size_x = 15, view_size_y = 15)
var/icon/newicon = icon('icons/hud/screen_gen.dmi', "catcher")
var/ox = min(MAX_SAFE_BYOND_ICON_SCALE_TILES, view_size_x)
var/oy = min(MAX_SAFE_BYOND_ICON_SCALE_TILES, view_size_y)
- var/px = view_size_x * world.icon_size
- var/py = view_size_y * world.icon_size
+ var/px = view_size_x * ICON_SIZE_X
+ var/py = view_size_y * ICON_SIZE_Y
var/sx = min(MAX_SAFE_BYOND_ICON_SCALE_PX, px)
var/sy = min(MAX_SAFE_BYOND_ICON_SCALE_PX, py)
newicon.Scale(sx, sy)
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index d477195a603ab..5e4ee1e849dbb 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -435,7 +435,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
if(!QDELETED(rube) && !QDELETED(offerer))
offerer.visible_message(span_danger("[offerer] pulls away from [rube]'s slap at the last second, dodging the high-five entirely!"), span_nicegreen("[rube] fails to make contact with your hand, making an utter fool of [rube.p_them()]self!"), span_hear("You hear a disappointing sound of flesh not hitting flesh!"), ignored_mobs=rube)
to_chat(rube, span_userdanger("[uppertext("NO! [offerer] PULLS [offerer.p_their()] HAND AWAY FROM YOURS! YOU'RE TOO SLOW!")]"))
- playsound(offerer, 'sound/weapons/thudswoosh.ogg', 100, TRUE, 1)
+ playsound(offerer, 'sound/items/weapons/thudswoosh.ogg', 100, TRUE, 1)
rube.Knockdown(1 SECONDS)
offerer.add_mood_event("high_five", /datum/mood_event/down_low)
rube.add_mood_event("high_five", /datum/mood_event/too_slow)
diff --git a/code/_onclick/hud/alien.dm b/code/_onclick/hud/alien.dm
index 476140acb1edd..b9a0e3bf655f4 100644
--- a/code/_onclick/hud/alien.dm
+++ b/code/_onclick/hud/alien.dm
@@ -59,14 +59,15 @@
H.leap_icon.screen_loc = ui_alien_storage_r
static_inventory += H.leap_icon
+ floor_change = new /atom/movable/screen/floor_changer(null, src)
+ floor_change.icon = ui_style
+ floor_change.screen_loc = ui_above_intent
+ static_inventory += floor_change
+
using = new/atom/movable/screen/language_menu(null, src)
using.screen_loc = ui_alien_language_menu
static_inventory += using
- using = new /atom/movable/screen/floor_menu(null, src)
- using.screen_loc = ui_alien_floor_menu
- static_inventory += using
-
using = new /atom/movable/screen/navigate(null, src)
using.screen_loc = ui_alien_navigate_menu
static_inventory += using
diff --git a/code/_onclick/hud/alien_larva.dm b/code/_onclick/hud/alien_larva.dm
index 77d135ce2c663..bb2b9fcb14aee 100644
--- a/code/_onclick/hud/alien_larva.dm
+++ b/code/_onclick/hud/alien_larva.dm
@@ -10,6 +10,11 @@
action_intent.screen_loc = ui_combat_toggle
static_inventory += action_intent
+ floor_change = new /atom/movable/screen/floor_changer(null, src)
+ floor_change.icon = ui_style
+ floor_change.screen_loc = ui_above_intent
+ static_inventory += floor_change
+
healths = new /atom/movable/screen/healths/alien(null, src)
infodisplay += healths
@@ -32,10 +37,6 @@
using.screen_loc = ui_alien_language_menu
static_inventory += using
- using = new /atom/movable/screen/floor_menu(null, src)
- using.screen_loc = ui_alien_floor_menu
- static_inventory += using
-
using = new /atom/movable/screen/navigate(null, src)
using.screen_loc = ui_alien_navigate_menu
static_inventory += using
diff --git a/code/_onclick/hud/credits.dm b/code/_onclick/hud/credits.dm
index c4650437c6396..2ce3923c88740 100644
--- a/code/_onclick/hud/credits.dm
+++ b/code/_onclick/hud/credits.dm
@@ -1,6 +1,6 @@
#define CREDIT_ROLL_SPEED 125
#define CREDIT_SPAWN_SPEED 10
-#define CREDIT_ANIMATE_HEIGHT (14 * world.icon_size)
+#define CREDIT_ANIMATE_HEIGHT (14 * ICON_SIZE_Y)
#define CREDIT_EASE_DURATION 22
#define CREDITS_PATH "[global.config.directory]/contributors.dmi"
@@ -45,9 +45,9 @@
parent = P
icon_state = credited
maptext = MAPTEXT_PIXELLARI(credited)
- maptext_x = world.icon_size + 8
- maptext_y = (world.icon_size / 2) - 4
- maptext_width = world.icon_size * 3
+ maptext_x = ICON_SIZE_X + 8
+ maptext_y = (ICON_SIZE_Y / 2) - 4
+ maptext_width = ICON_SIZE_X * 3
var/matrix/M = matrix(transform)
M.Translate(0, CREDIT_ANIMATE_HEIGHT)
animate(src, transform = M, time = CREDIT_ROLL_SPEED)
diff --git a/code/_onclick/hud/generic_dextrous.dm b/code/_onclick/hud/generic_dextrous.dm
index aac5a2b75ccaa..4048fd91b16f6 100644
--- a/code/_onclick/hud/generic_dextrous.dm
+++ b/code/_onclick/hud/generic_dextrous.dm
@@ -33,6 +33,10 @@
action_intent.screen_loc = ui_combat_toggle
static_inventory += action_intent
+ floor_change = new /atom/movable/screen/floor_changer(null, src)
+ floor_change.icon = 'icons/hud/screen_midnight.dmi'
+ static_inventory += floor_change
+
zone_select = new /atom/movable/screen/zone_sel(null, src)
zone_select.icon = ui_style
diff --git a/code/_onclick/hud/ghost.dm b/code/_onclick/hud/ghost.dm
index e20c1ede2f663..9f90076a3ac71 100644
--- a/code/_onclick/hud/ghost.dm
+++ b/code/_onclick/hud/ghost.dm
@@ -91,10 +91,10 @@
using.icon = ui_style
static_inventory += using
- using = new /atom/movable/screen/floor_menu(null, src)
- using.screen_loc = ui_ghost_floor_menu
- using.icon = ui_style
- static_inventory += using
+ floor_change = new /atom/movable/screen/floor_changer/vertical(null, src)
+ floor_change.icon = ui_style
+ floor_change.screen_loc = ui_ghost_floor_changer
+ static_inventory += floor_change
/datum/hud/ghost/show_hud(version = 0, mob/viewmob)
// don't show this HUD if observing; show the HUD of the observee
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 73497bf418ff1..2f15e52ff31f6 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -41,6 +41,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
var/atom/movable/screen/rest_icon
var/atom/movable/screen/throw_icon
var/atom/movable/screen/module_store_icon
+ var/atom/movable/screen/floor_change
var/list/static_inventory = list() //the screen objects which are static
var/list/toggleable_inventory = list() //the screen objects which can be hidden
@@ -197,6 +198,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
SIGNAL_HANDLER
update_parallax_pref() // If your eye changes z level, so should your parallax prefs
var/turf/eye_turf = get_turf(eye)
+ SEND_SIGNAL(src, COMSIG_HUD_Z_CHANGED, eye_turf.z)
var/new_offset = GET_TURF_PLANE_OFFSET(eye_turf)
if(current_plane_offset == new_offset)
return
@@ -228,6 +230,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
zone_select = null
pull_icon = null
rest_icon = null
+ floor_change = null
hand_slots.Cut()
QDEL_LIST(toggleable_inventory)
@@ -419,6 +422,11 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
return
update_robot_modules_display()
+/datum/hud/new_player/show_hud(version = 0, mob/viewmob)
+ . = ..()
+ if(.)
+ show_station_trait_buttons()
+
/datum/hud/proc/hidden_inventory_update()
return
@@ -541,7 +549,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
if(!our_client)
position_action(button, button.linked_action.default_button_position)
return
- button.screen_loc = get_valid_screen_location(relative_to.screen_loc, world.icon_size, our_client.view_size.getView()) // Asks for a location adjacent to our button that won't overflow the map
+ button.screen_loc = get_valid_screen_location(relative_to.screen_loc, ICON_SIZE_ALL, our_client.view_size.getView()) // Asks for a location adjacent to our button that won't overflow the map
button.location = relative_to.location
@@ -705,14 +713,14 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
// We're primarially concerned about width here, if someone makes us 1x2000 I wish them a swift and watery death
var/furthest_screen_loc = ButtonNumberToScreenCoords(column_max - 1)
var/list/offsets = screen_loc_to_offset(furthest_screen_loc, owner_view)
- if(offsets[1] > world.icon_size && offsets[1] < view_size[1] && offsets[2] > world.icon_size && offsets[2] < view_size[2]) // We're all good
+ if(offsets[1] > ICON_SIZE_X && offsets[1] < view_size[1] && offsets[2] > ICON_SIZE_Y && offsets[2] < view_size[2]) // We're all good
return
for(column_max in column_max - 1 to 1 step -1) // Yes I could do this by unwrapping ButtonNumberToScreenCoords, but I don't feel like it
var/tested_screen_loc = ButtonNumberToScreenCoords(column_max)
offsets = screen_loc_to_offset(tested_screen_loc, owner_view)
// We've found a valid max length, pack it in
- if(offsets[1] > world.icon_size && offsets[1] < view_size[1] && offsets[2] > world.icon_size && offsets[2] < view_size[2])
+ if(offsets[1] > ICON_SIZE_X && offsets[1] < view_size[1] && offsets[2] > ICON_SIZE_Y && offsets[2] < view_size[2])
break
// Use our newly resized column max
refresh_actions()
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index 5834a3973555c..0ab0f022ca22e 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -70,15 +70,16 @@
using.icon = ui_style
static_inventory += using
- using = new /atom/movable/screen/floor_menu(null, src)
- using.icon = ui_style
- static_inventory += using
-
action_intent = new /atom/movable/screen/combattoggle/flashy(null, src)
action_intent.icon = ui_style
action_intent.screen_loc = ui_combat_toggle
static_inventory += action_intent
+ floor_change = new /atom/movable/screen/floor_changer(null, src)
+ floor_change.icon = ui_style
+ floor_change.screen_loc = ui_human_floor_changer
+ static_inventory += floor_change
+
using = new /atom/movable/screen/mov_intent(null, src)
using.icon = ui_style
diff --git a/code/_onclick/hud/living.dm b/code/_onclick/hud/living.dm
index 70084b1ecd9c6..d70d2f7d55d39 100644
--- a/code/_onclick/hud/living.dm
+++ b/code/_onclick/hud/living.dm
@@ -15,6 +15,10 @@
action_intent.screen_loc = ui_combat_toggle
static_inventory += action_intent
+ floor_change = new /atom/movable/screen/floor_changer(null, src)
+ floor_change.icon = 'icons/hud/screen_midnight.dmi'
+ static_inventory += floor_change
+
combo_display = new /atom/movable/screen/combo(null, src)
infodisplay += combo_display
diff --git a/code/_onclick/hud/map_popups.dm b/code/_onclick/hud/map_popups.dm
index f0277d187ff4d..bf524b6c8d906 100644
--- a/code/_onclick/hud/map_popups.dm
+++ b/code/_onclick/hud/map_popups.dm
@@ -105,8 +105,8 @@
if(!popup_name)
return
clear_map("[popup_name]_map")
- var/x_value = world.icon_size * tilesize * width
- var/y_value = world.icon_size * tilesize * height
+ var/x_value = ICON_SIZE_X * tilesize * width
+ var/y_value = ICON_SIZE_Y * tilesize * height
var/map_name = create_popup(popup_name, title, x_value, y_value)
var/atom/movable/screen/background/background = new
diff --git a/code/_onclick/hud/movable_screen_objects.dm b/code/_onclick/hud/movable_screen_objects.dm
index 2910a9f0cc829..cac1be97ae688 100644
--- a/code/_onclick/hud/movable_screen_objects.dm
+++ b/code/_onclick/hud/movable_screen_objects.dm
@@ -37,8 +37,8 @@
var/client/our_client = usr.client
var/list/offset = screen_loc_to_offset(LAZYACCESS(modifiers, SCREEN_LOC))
if(snap2grid) //Discard Pixel Values
- offset[1] = FLOOR(offset[1], world.icon_size) // drops any pixel offset
- offset[2] = FLOOR(offset[2], world.icon_size) // drops any pixel offset
+ offset[1] = FLOOR(offset[1], ICON_SIZE_X) // drops any pixel offset
+ offset[2] = FLOOR(offset[2], ICON_SIZE_Y) // drops any pixel offset
else //Normalise Pixel Values (So the object drops at the center of the mouse, not 16 pixels off)
offset[1] += x_off
offset[2] += y_off
diff --git a/code/_onclick/hud/new_player.dm b/code/_onclick/hud/new_player.dm
index 371341aec0bf5..1c794b5566d2d 100644
--- a/code/_onclick/hud/new_player.dm
+++ b/code/_onclick/hud/new_player.dm
@@ -6,6 +6,7 @@
/datum/hud/new_player
///Whether the menu is currently on the client's screen or not
var/menu_hud_status = TRUE
+ var/list/shown_station_trait_buttons
/datum/hud/new_player/New(mob/owner)
. = ..()
@@ -26,31 +27,58 @@
if (!lobbyscreen.always_shown)
lobbyscreen.RegisterSignal(src, COMSIG_HUD_LOBBY_COLLAPSED, TYPE_PROC_REF(/atom/movable/screen/lobby, collapse_button))
lobbyscreen.RegisterSignal(src, COMSIG_HUD_LOBBY_EXPANDED, TYPE_PROC_REF(/atom/movable/screen/lobby, expand_button))
- if (istype(lobbyscreen, /atom/movable/screen/lobby/button))
- var/atom/movable/screen/lobby/button/lobby_button = lobbyscreen
- lobby_button.owner = REF(owner)
- add_station_trait_buttons()
-/// Display buttons for relevant station traits
-/datum/hud/new_player/proc/add_station_trait_buttons()
+/// Load and then display the buttons for relevant station traits
+/datum/hud/new_player/proc/show_station_trait_buttons()
if (!mymob?.client || mymob.client.interviewee || !length(GLOB.lobby_station_traits))
return
- var/buttons_created = 0
- var/y_offset = 397
- var/y_button_offset = 27
for (var/datum/station_trait/trait as anything in GLOB.lobby_station_traits)
- if (!trait.can_display_lobby_button(mymob.client))
+ if (QDELETED(trait) || !trait.can_display_lobby_button(mymob.client))
+ remove_station_trait_button(trait)
+ continue
+ if(LAZYACCESS(shown_station_trait_buttons, trait))
continue
var/atom/movable/screen/lobby/button/sign_up/sign_up_button = new(our_hud = src)
- sign_up_button.SlowInit()
- sign_up_button.owner = REF(mymob)
- sign_up_button.screen_loc = offset_to_screen_loc(233, y_offset, mymob.client.view)
- y_offset += y_button_offset
- static_inventory += sign_up_button
trait.setup_lobby_button(sign_up_button)
- buttons_created++
- if (buttons_created >= MAX_STATION_TRAIT_BUTTONS_VERTICAL)
- return
+ static_inventory |= sign_up_button
+ LAZYSET(shown_station_trait_buttons, trait, sign_up_button)
+ RegisterSignal(trait, COMSIG_QDELETING, PROC_REF(remove_station_trait_button))
+
+ place_station_trait_buttons()
+
+/// Display the buttosn for relevant station traits.
+/datum/hud/new_player/proc/place_station_trait_buttons()
+ if(hud_version != HUD_STYLE_STANDARD || !mymob?.client)
+ return
+
+ var/y_offset = 397
+ var/x_offset = 233
+ var/y_button_offset = 27
+ var/x_button_offset = -27
+ var/iteration = 0
+ for(var/trait in shown_station_trait_buttons)
+ var/atom/movable/screen/lobby/button/sign_up/sign_up_button = shown_station_trait_buttons[trait]
+ iteration++
+ sign_up_button.screen_loc = offset_to_screen_loc(x_offset, y_offset, mymob.client.view)
+ mymob.client.screen |= sign_up_button
+ if (iteration >= MAX_STATION_TRAIT_BUTTONS_VERTICAL)
+ iteration = 0
+ y_offset = 397
+ x_offset += x_button_offset
+ else
+ y_offset += y_button_offset
+
+/// Remove a station trait button, then re-order the rest.
+/datum/hud/new_player/proc/remove_station_trait_button(datum/station_trait/trait)
+ SIGNAL_HANDLER
+ var/atom/movable/screen/lobby/button/sign_up/button = LAZYACCESS(shown_station_trait_buttons, trait)
+ if(!button)
+ return
+ LAZYREMOVE(shown_station_trait_buttons, trait)
+ UnregisterSignal(trait, COMSIG_QDELETING)
+ static_inventory -= button
+ qdel(button)
+ place_station_trait_buttons()
/atom/movable/screen/lobby
plane = SPLASHSCREEN_PLANE
@@ -94,11 +122,9 @@
var/enabled = TRUE
///Is the button currently being hovered over with the mouse?
var/highlighted = FALSE
- /// The ref of the mob that owns this button. Only the owner can click on it.
- var/owner
/atom/movable/screen/lobby/button/Click(location, control, params)
- if(owner != REF(usr))
+ if(usr != get_mob())
return
if(!usr.client || usr.client.interviewee)
@@ -113,7 +139,7 @@
return TRUE
/atom/movable/screen/lobby/button/MouseEntered(location,control,params)
- if(owner != REF(usr))
+ if(usr != get_mob())
return
if(!usr.client || usr.client.interviewee)
@@ -124,7 +150,7 @@
update_appearance(UPDATE_ICON)
/atom/movable/screen/lobby/button/MouseExited()
- if(owner != REF(usr))
+ if(usr != get_mob())
return
if(!usr.client || usr.client.interviewee)
diff --git a/code/_onclick/hud/parallax/parallax.dm b/code/_onclick/hud/parallax/parallax.dm
index 135c3f0caef6e..ee266cd21e314 100644
--- a/code/_onclick/hud/parallax/parallax.dm
+++ b/code/_onclick/hud/parallax/parallax.dm
@@ -191,7 +191,7 @@
if(!offset_x && !offset_y && !force)
return
- var/glide_rate = round(world.icon_size / screenmob.glide_size * world.tick_lag, world.tick_lag)
+ var/glide_rate = round(ICON_SIZE_ALL / screenmob.glide_size * world.tick_lag, world.tick_lag)
C.previous_turf = posobj
var/largest_change = max(abs(offset_x), abs(offset_y))
@@ -297,8 +297,8 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
/atom/movable/screen/parallax_layer/proc/update_o(view)
if (!view)
view = world.view
-
- var/static/parallax_scaler = world.icon_size / 480
+ var/static/pixel_grid_size = ICON_SIZE_ALL * 15
+ var/static/parallax_scaler = ICON_SIZE_ALL / pixel_grid_size
// Turn the view size into a grid of correctly scaled overlays
var/list/viewscales = getviewsize(view)
@@ -311,8 +311,8 @@ INITIALIZE_IMMEDIATE(/atom/movable/screen/parallax_layer)
if(x == 0 && y == 0)
continue
var/mutable_appearance/texture_overlay = mutable_appearance(icon, icon_state)
- texture_overlay.pixel_w += 480 * x
- texture_overlay.pixel_z += 480 * y
+ 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)
diff --git a/code/_onclick/hud/picture_in_picture.dm b/code/_onclick/hud/picture_in_picture.dm
index b6ac49446fc80..f2cf8f4b21081 100644
--- a/code/_onclick/hud/picture_in_picture.dm
+++ b/code/_onclick/hud/picture_in_picture.dm
@@ -56,7 +56,7 @@
move_tab.icon_state = "move"
move_tab.plane = HUD_PLANE
var/matrix/M = matrix()
- M.Translate(0, (height + 0.25) * world.icon_size)
+ M.Translate(0, (height + 0.25) * ICON_SIZE_Y)
move_tab.transform = M
add_overlay(move_tab)
@@ -69,7 +69,7 @@
MA.plane = HUD_PLANE
button_x.appearance = MA
M = matrix()
- M.Translate((max(4, width) - 0.75) * world.icon_size, (height + 0.25) * world.icon_size)
+ M.Translate((max(4, width) - 0.75) * ICON_SIZE_X, (height + 0.25) * ICON_SIZE_Y)
button_x.transform = M
vis_contents += button_x
@@ -82,7 +82,7 @@
MA.plane = HUD_PLANE
button_expand.appearance = MA
M = matrix()
- M.Translate(world.icon_size, (height + 0.25) * world.icon_size)
+ M.Translate(ICON_SIZE_X, (height + 0.25) * ICON_SIZE_Y)
button_expand.transform = M
vis_contents += button_expand
@@ -95,7 +95,7 @@
MA.plane = HUD_PLANE
button_shrink.appearance = MA
M = matrix()
- M.Translate(2 * world.icon_size, (height + 0.25) * world.icon_size)
+ M.Translate(2 * ICON_SIZE_X, (height + 0.25) * ICON_SIZE_Y)
button_shrink.transform = M
vis_contents += button_shrink
@@ -103,7 +103,7 @@
if((width > 0) && (height > 0))
var/matrix/M = matrix()
M.Scale(width + 0.5, height + 0.5)
- M.Translate((width-1)/2 * world.icon_size, (height-1)/2 * world.icon_size)
+ M.Translate((width-1)/2 * ICON_SIZE_X, (height-1)/2 * ICON_SIZE_Y)
standard_background.transform = M
add_overlay(standard_background)
@@ -115,7 +115,7 @@
src.width = width
src.height = height
- y_off = -height * world.icon_size - 16
+ y_off = (-height * ICON_SIZE_Y) - (ICON_SIZE_Y / 2)
cut_overlays()
add_background()
diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm
index 3bd370120b3a4..ab95d3bb392eb 100644
--- a/code/_onclick/hud/radial.dm
+++ b/code/_onclick/hud/radial.dm
@@ -388,8 +388,8 @@ GLOBAL_LIST_EMPTY(radial_menus)
if (user_space)
var/turf/user_turf = get_turf(user)
var/turf/anchor_turf = get_turf(anchor)
- offset_x = (anchor_turf.x - user_turf.x) * world.icon_size + anchor.pixel_x - user.pixel_x
- offset_y = (anchor_turf.y - user_turf.y) * world.icon_size + anchor.pixel_y - user.pixel_y
+ offset_x = (anchor_turf.x - user_turf.x) * ICON_SIZE_X + anchor.pixel_x - user.pixel_x
+ offset_y = (anchor_turf.y - user_turf.y) * ICON_SIZE_Y + anchor.pixel_y - user.pixel_y
menu.show_to(user, offset_x, offset_y)
menu.wait(user, anchor, require_near)
var/answer = menu.selected_choice
diff --git a/code/_onclick/hud/rendering/plane_master_group.dm b/code/_onclick/hud/rendering/plane_master_group.dm
index 23096cc0e9ccd..4bed46f983f4a 100644
--- a/code/_onclick/hud/rendering/plane_master_group.dm
+++ b/code/_onclick/hud/rendering/plane_master_group.dm
@@ -24,10 +24,23 @@
build_plane_masters(0, SSmapping.max_plane_offset)
/datum/plane_master_group/Destroy()
- orphan_hud()
+ set_hud(null)
QDEL_LIST_ASSOC_VAL(plane_masters)
return ..()
+/datum/plane_master_group/proc/set_hud(datum/hud/new_hud)
+ if(new_hud == our_hud)
+ return
+ if(our_hud)
+ our_hud.master_groups -= key
+ hide_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)
+
/// 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)
if(viewing_hud.master_groups[key])
@@ -42,18 +55,11 @@
relay_loc = "1,1"
rebuild_plane_masters()
- our_hud = viewing_hud
+ set_hud(viewing_hud)
our_hud.master_groups[key] = src
show_hud()
build_planes_offset(our_hud, active_offset)
-/// Hide the plane master from its current hud, fully clear it out
-/datum/plane_master_group/proc/orphan_hud()
- if(our_hud)
- our_hud.master_groups -= key
- hide_hud()
- our_hud = null
-
/// Well, refresh our group, mostly useful for plane specific updates
/datum/plane_master_group/proc/refresh_hud()
hide_hud()
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 c96361348f0de..acfa5ee274ca2 100644
--- a/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
+++ b/code/_onclick/hud/rendering/plane_masters/plane_master_subtypes.dm
@@ -243,6 +243,18 @@
documentation = "Holds the areas themselves, which ends up meaning it holds any overlays/effects we apply to areas. NOT snow or rad storms, those go on above lighting"
plane = AREA_PLANE
+/atom/movable/screen/plane_master/weather
+ name = "Weather"
+ documentation = "Holds the main tiling 32x32 sprites of weather. We mask against walls that are on the edge of weather effects."
+ plane = WEATHER_PLANE
+ start_hidden = TRUE
+
+/atom/movable/screen/plane_master/weather/set_home(datum/plane_master_group/home)
+ . = ..()
+ if(!.)
+ return
+ home.AddComponent(/datum/component/hide_weather_planes, src)
+
/atom/movable/screen/plane_master/massive_obj
name = "Massive object"
documentation = "Huge objects need to render above everything else on the game plane, otherwise they'd well, get clipped and look not that huge. This does that."
@@ -285,6 +297,18 @@
documentation = "Anything on the game plane that needs a space to draw on that will be above the lighting plane.\
Mostly little alerts and effects, also sometimes contains things that are meant to look as if they glow."
+/atom/movable/screen/plane_master/weather_glow
+ name = "Weather Glow"
+ documentation = "Holds the glowing parts of the main tiling 32x32 sprites of weather."
+ plane = WEATHER_GLOW_PLANE
+ start_hidden = TRUE
+
+/atom/movable/screen/plane_master/weather_glow/set_home(datum/plane_master_group/home)
+ . = ..()
+ if(!.)
+ return
+ home.AddComponent(/datum/component/hide_weather_planes, src)
+
/**
* Handles emissive overlays and emissive blockers.
*/
diff --git a/code/_onclick/hud/rendering/render_plate.dm b/code/_onclick/hud/rendering/render_plate.dm
index e4cdc41ca1cfb..66339c837d050 100644
--- a/code/_onclick/hud/rendering/render_plate.dm
+++ b/code/_onclick/hud/rendering/render_plate.dm
@@ -345,7 +345,7 @@
if(!.)
return
- RegisterSignal(mymob, COMSIG_MOB_SIGHT_CHANGE, PROC_REF(handle_sight))
+ RegisterSignal(mymob, COMSIG_MOB_SIGHT_CHANGE, PROC_REF(handle_sight), override = TRUE)
handle_sight(mymob, mymob.sight, NONE)
/atom/movable/screen/plane_master/rendering_plate/light_mask/hide_from(mob/oldmob)
diff --git a/code/_onclick/hud/robot.dm b/code/_onclick/hud/robot.dm
index e2e534443691a..2d82bb0c1e6e8 100644
--- a/code/_onclick/hud/robot.dm
+++ b/code/_onclick/hud/robot.dm
@@ -87,11 +87,6 @@
using.screen_loc = ui_borg_navigate_menu
static_inventory += using
-// Z-level floor change
- using = new /atom/movable/screen/floor_menu(null, src)
- using.screen_loc = ui_borg_floor_menu
- static_inventory += using
-
//Radio
using = new /atom/movable/screen/robot/radio(null, src)
using.screen_loc = ui_borg_radio
@@ -149,6 +144,11 @@
action_intent.screen_loc = ui_combat_toggle
static_inventory += action_intent
+ floor_change = new /atom/movable/screen/floor_changer(null, src)
+ floor_change.icon = ui_style
+ floor_change.screen_loc = ui_borg_floor_changer
+ static_inventory += floor_change
+
//Health
healths = new /atom/movable/screen/healths/robot(null, src)
infodisplay += healths
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index cb06e00d116ef..31390c62cbb91 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -160,33 +160,6 @@
/atom/movable/screen/language_menu/Click()
usr.get_language_holder().open_language_menu(usr)
-/atom/movable/screen/floor_menu
- name = "change floor"
- icon = 'icons/hud/screen_midnight.dmi'
- icon_state = "floor_change"
- screen_loc = ui_floor_menu
-
-/atom/movable/screen/floor_menu/Initialize(mapload)
- . = ..()
- register_context()
-
-/atom/movable/screen/floor_menu/add_context(atom/source, list/context, obj/item/held_item, mob/user)
- . = ..()
-
- context[SCREENTIP_CONTEXT_LMB] = "Go up a floor"
- context[SCREENTIP_CONTEXT_RMB] = "Go down a floor"
- return CONTEXTUAL_SCREENTIP_SET
-
-/atom/movable/screen/floor_menu/Click(location,control,params)
- var/list/modifiers = params2list(params)
-
- if(LAZYACCESS(modifiers, RIGHT_CLICK) || LAZYACCESS(modifiers, ALT_CLICK))
- usr.down()
- return
-
- usr.up()
- return
-
/atom/movable/screen/inventory
/// The identifier for the slot. It has nothing to do with ID cards.
var/slot_id
@@ -380,6 +353,34 @@
icon = 'icons/hud/screen_cyborg.dmi'
screen_loc = ui_borg_intents
+/atom/movable/screen/floor_changer
+ name = "change floor"
+ icon = 'icons/hud/screen_midnight.dmi'
+ icon_state = "floor_change"
+ screen_loc = ui_floor_changer
+ var/vertical = FALSE
+
+/atom/movable/screen/floor_changer/Click(location,control,params)
+ var/list/modifiers = params2list(params)
+
+ var/mouse_position
+
+ if(vertical)
+ mouse_position = text2num(LAZYACCESS(modifiers, ICON_Y))
+ else
+ mouse_position = text2num(LAZYACCESS(modifiers, ICON_X))
+
+ if(mouse_position > 16)
+ usr.up()
+ return
+
+ usr.down()
+ return
+
+/atom/movable/screen/floor_changer/vertical
+ icon_state = "floor_change_v"
+ vertical = TRUE
+
/atom/movable/screen/spacesuit
name = "Space suit cell status"
icon_state = "spacesuit_0"
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 5af9b7e016156..e7c4ef3e06790 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -214,7 +214,7 @@
return FALSE
if(!force && !HAS_TRAIT(src, TRAIT_CUSTOM_TAP_SOUND))
- playsound(src, 'sound/weapons/tap.ogg', get_clamped_volume(), TRUE, -1)
+ playsound(src, 'sound/items/weapons/tap.ogg', get_clamped_volume(), TRUE, -1)
else if(hitsound)
playsound(src, hitsound, get_clamped_volume(), TRUE, extrarange = stealthy_audio ? SILENCED_SOUND_EXTRARANGE : -1, falloff_distance = 0)
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index 964459aea68d4..e1ed3284441f6 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -313,13 +313,13 @@
/datum/config_entry/string/banappeals
/datum/config_entry/string/wikiurl
- default = "http://www.tgstation13.org/wiki"
+ default = "http://tgstation13.org/wiki"
/datum/config_entry/string/forumurl
default = "http://tgstation13.org/phpBB/index.php"
/datum/config_entry/string/rulesurl
- default = "http://www.tgstation13.org/wiki/Rules"
+ default = "http://tgstation13.org/wiki/Rules"
/datum/config_entry/string/githuburl
default = "https://www.github.com/tgstation/tgstation"
@@ -738,3 +738,27 @@
/datum/config_entry/number/upload_limit_admin
default = 5242880
min_val = 0
+
+/// The minimum number of tallies a map vote entry can have.
+/datum/config_entry/number/map_vote_minimum_tallies
+ default = 1
+ min_val = 0
+ max_val = 50
+
+/// The flat amount all maps get by default
+/datum/config_entry/number/map_vote_flat_bonus
+ default = 5
+ min_val = 0
+ max_val = INFINITY
+
+/// The maximum number of tallies a map vote entry can have.
+/datum/config_entry/number/map_vote_maximum_tallies
+ default = 200
+ min_val = 0
+ max_val = INFINITY
+
+/// The number of tallies that are carried over between rounds.
+/datum/config_entry/number/map_vote_tally_carryover_percentage
+ default = 100
+ min_val = 0
+ max_val = 100
diff --git a/code/controllers/subsystem/ai_controllers.dm b/code/controllers/subsystem/ai_controllers.dm
index 30d3c4986f2a2..49c571a9a0763 100644
--- a/code/controllers/subsystem/ai_controllers.dm
+++ b/code/controllers/subsystem/ai_controllers.dm
@@ -23,14 +23,12 @@ SUBSYSTEM_DEF(ai_controllers)
/datum/controller/subsystem/ai_controllers/fire(resumed)
var/timer = TICK_USAGE_REAL
for(var/datum/ai_controller/ai_controller as anything in GLOB.ai_controllers_by_status[planning_status])
- if(!COOLDOWN_FINISHED(ai_controller, failed_planning_cooldown))
- continue
-
- if(!ai_controller.able_to_plan())
+ if(!ai_controller.able_to_plan)
continue
ai_controller.SelectBehaviors(wait * 0.1)
- if(!LAZYLEN(ai_controller.current_behaviors)) //Still no plan
- COOLDOWN_START(ai_controller, failed_planning_cooldown, AI_FAILED_PLANNING_COOLDOWN)
+
+ if(!length(ai_controller.current_behaviors)) //Still no plan
+ ai_controller.planning_failed()
our_cost = MC_AVERAGE(our_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm
index 4557e153db190..bbc21becc0f3b 100644
--- a/code/controllers/subsystem/air.dm
+++ b/code/controllers/subsystem/air.dm
@@ -488,29 +488,26 @@ SUBSYSTEM_DEF(air)
T.excited = FALSE
///Adds a turf to active processing, handles duplicates. Call this with blockchanges == TRUE if you want to nuke the assoc excited group
-/datum/controller/subsystem/air/proc/add_to_active(turf/open/T, blockchanges = FALSE)
- if(istype(T) && T.air)
- T.significant_share_ticker = 0
- if(blockchanges && T.excited_group) //This is used almost exclusivly for shuttles, so the excited group doesn't stay behind
- T.excited_group.garbage_collect() //Nuke it
- if(T.excited) //Don't keep doing it if there's no point
+/datum/controller/subsystem/air/proc/add_to_active(turf/open/activate, blockchanges = FALSE)
+ if(istype(activate) && activate.air)
+ activate.significant_share_ticker = 0
+ if(blockchanges && activate.excited_group) //This is used almost exclusivly for shuttles, so the excited group doesn't stay behind
+ activate.excited_group.garbage_collect() //Nuke it
+ if(activate.excited) //Don't keep doing it if there's no point
return
#ifdef VISUALIZE_ACTIVE_TURFS
- T.add_atom_colour(COLOR_VIBRANT_LIME, TEMPORARY_COLOUR_PRIORITY)
+ activate.add_atom_colour(COLOR_VIBRANT_LIME, TEMPORARY_COLOUR_PRIORITY)
#endif
- T.excited = TRUE
- active_turfs += T
- if(currentpart == SSAIR_ACTIVETURFS)
- currentrun += T
- else if(T.flags_1 & INITIALIZED_1)
- for(var/turf/S in T.atmos_adjacent_turfs)
- add_to_active(S, TRUE)
+ activate.excited = TRUE
+ active_turfs += activate
+ else if(activate.flags_1 & INITIALIZED_1)
+ for(var/turf/neighbor as anything in activate.atmos_adjacent_turfs)
+ add_to_active(neighbor, TRUE)
else if(map_loading)
if(queued_for_activation)
- queued_for_activation[T] = T
- return
+ queued_for_activation[activate] = activate
else
- T.requires_activation = TRUE
+ activate.requires_activation = TRUE
/datum/controller/subsystem/air/StartLoadingMap()
LAZYINITLIST(queued_for_activation)
@@ -560,8 +557,8 @@ SUBSYSTEM_DEF(air)
if(enemy_tile.current_cycle == -INFINITE)
continue
// .air instead of .return_air() because we can guarantee that the proc won't do anything
- if(potential_diff.air.compare(enemy_tile.air))
- //testing("Active turf found. Return value of compare(): [T.air.compare(enemy_tile.air)]")
+ if(potential_diff.air.compare(enemy_tile.air, MOLES))
+ //testing("Active turf found. Return value of compare(): [T.air.compare(enemy_tile.air, MOLES)]")
if(!potential_diff.excited)
potential_diff.excited = TRUE
SSair.active_turfs += potential_diff
diff --git a/code/controllers/subsystem/ambience.dm b/code/controllers/subsystem/ambience.dm
index 7258b0b16e948..87f088a41ea13 100644
--- a/code/controllers/subsystem/ambience.dm
+++ b/code/controllers/subsystem/ambience.dm
@@ -70,16 +70,16 @@ SUBSYSTEM_DEF(ambience)
///A list of rare sound effects to fuck with players. No, it does not contain actual minecraft sounds anymore.
var/static/list/minecraft_cave_noises = list(
- 'sound/machines/airlock.ogg',
+ 'sound/machines/airlock/airlock.ogg',
'sound/effects/snap.ogg',
'sound/effects/footstep/clownstep1.ogg',
'sound/effects/footstep/clownstep2.ogg',
- 'sound/items/welder.ogg',
- 'sound/items/welder2.ogg',
- 'sound/items/crowbar.ogg',
+ 'sound/items/tools/welder.ogg',
+ 'sound/items/tools/welder2.ogg',
+ 'sound/items/tools/crowbar.ogg',
'sound/items/deconstruct.ogg',
- 'sound/ambience/source_holehit3.ogg',
- 'sound/ambience/cavesound3.ogg',
+ 'sound/ambience/misc/source_holehit3.ogg',
+ 'sound/ambience//misc/cavesound3.ogg',
)
/area/station/maintenance/play_ambience(mob/M, sound/override_sound, volume)
diff --git a/code/controllers/subsystem/bitrunning.dm b/code/controllers/subsystem/bitrunning.dm
index 78afb101945b0..63c2561f0f496 100644
--- a/code/controllers/subsystem/bitrunning.dm
+++ b/code/controllers/subsystem/bitrunning.dm
@@ -25,7 +25,7 @@ SUBSYSTEM_DEF(bitrunning)
var/can_view_reward = domain.difficulty < (scanner_tier + 1) && domain.cost <= points + 3
UNTYPED_LIST_ADD(levels, list(
- "announce_ghosts"= domain.announce_to_ghosts,
+ "announce_ghosts" = domain.announce_to_ghosts,
"cost" = domain.cost,
"desc" = can_view ? domain.desc : "Limited scanning capabilities. Cannot infer domain details.",
"difficulty" = domain.difficulty,
diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm
index 18ba37eb13785..83c666de64ac4 100644
--- a/code/controllers/subsystem/blackbox.dm
+++ b/code/controllers/subsystem/blackbox.dm
@@ -357,7 +357,7 @@ Versioning
"z_coord" = L.z,
"last_words" = L.last_words,
"suicide" = did_they_suicide,
- "map" = SSmapping.config.map_name,
+ "map" = SSmapping.current_map.map_name,
"internet_address" = world.internet_address || "0",
"port" = "[world.port]",
"round_id" = GLOB.round_id,
diff --git a/code/controllers/subsystem/dynamic/dynamic.dm b/code/controllers/subsystem/dynamic/dynamic.dm
index 56fcaac27fcb2..27db0ad9ea8af 100644
--- a/code/controllers/subsystem/dynamic/dynamic.dm
+++ b/code/controllers/subsystem/dynamic/dynamic.dm
@@ -532,7 +532,7 @@ SUBSYSTEM_DEF(dynamic)
var/security = 0 // BANDASTATION EDIT - Force players to play Sec
- SSjob.DivideOccupations(pure = TRUE, allow_all = TRUE)
+ SSjob.divide_occupations(pure = TRUE, allow_all = TRUE)
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/player = i
if(player.ready == PLAYER_READY_TO_PLAY && player.mind && player.check_preferences())
@@ -551,7 +551,7 @@ SUBSYSTEM_DEF(dynamic)
if(player.mind?.assigned_role?.departments_list?.Find(/datum/job_department/security))
security++
// BANDASTATION EDIT END
- SSjob.ResetOccupations()
+ SSjob.reset_occupations()
log_dynamic("Listing [roundstart_rules.len] round start rulesets, and [candidates.len] players ready.")
if (candidates.len <= 0)
log_dynamic("[candidates.len] candidates.")
@@ -1038,7 +1038,7 @@ SUBSYSTEM_DEF(dynamic)
var/list/reopened_jobs = list()
for(var/mob/living/quitter in GLOB.suicided_mob_list)
- var/datum/job/job = SSjob.GetJob(quitter.job)
+ var/datum/job/job = SSjob.get_job(quitter.job)
if(!job || !(job.job_flags & JOB_REOPEN_ON_ROUNDSTART_LOSS))
continue
if(!include_command && job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
diff --git a/code/controllers/subsystem/dynamic/dynamic_rulesets.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets.dm
index c77cce50d0157..0d3242a4d17b2 100644
--- a/code/controllers/subsystem/dynamic/dynamic_rulesets.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets.dm
@@ -283,7 +283,7 @@
if(length(exclusive_roles))
var/exclusive_candidate = FALSE
for(var/role in exclusive_roles)
- var/datum/job/job = SSjob.GetJob(role)
+ var/datum/job/job = SSjob.get_job(role)
if((role in candidate_client.prefs.job_preferences) && SSjob.check_job_eligibility(candidate_player, job, "Dynamic Roundstart TC", add_job_to_log = TRUE) == JOB_AVAILABLE)
exclusive_candidate = TRUE
diff --git a/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
index 3476702e741ed..4d182febb07e5 100644
--- a/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_midround.dm
@@ -413,7 +413,7 @@
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/finish_setup(mob/new_character, index)
- new_character.mind.set_assigned_role(SSjob.GetJobType(/datum/job/nuclear_operative))
+ new_character.mind.set_assigned_role(SSjob.get_job_type(/datum/job/nuclear_operative))
new_character.mind.special_role = ROLE_NUCLEAR_OPERATIVE
if(index == 1)
var/datum/antagonist/nukeop/leader/leader_antag_datum = new()
@@ -572,12 +572,12 @@
var/mob/living/carbon/human/new_nightmare = new (find_maintenance_spawn(atmos_sensitive = TRUE, require_darkness = TRUE))
player_mind.transfer_to(new_nightmare)
- player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/nightmare))
+ player_mind.set_assigned_role(SSjob.get_job_type(/datum/job/nightmare))
player_mind.special_role = ROLE_NIGHTMARE
player_mind.add_antag_datum(/datum/antagonist/nightmare)
new_nightmare.set_species(/datum/species/shadow/nightmare)
- playsound(new_nightmare, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
+ playsound(new_nightmare, 'sound/effects/magic/ethereal_exit.ogg', 50, TRUE, -1)
message_admins("[ADMIN_LOOKUPFLW(new_nightmare)] has been made into a Nightmare by the midround ruleset.")
log_dynamic("[key_name(new_nightmare)] was spawned as a Nightmare by the midround ruleset.")
return new_nightmare
@@ -618,7 +618,7 @@
player_mind.transfer_to(S)
player_mind.add_antag_datum(/datum/antagonist/space_dragon)
- playsound(S, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
+ playsound(S, 'sound/effects/magic/ethereal_exit.ogg', 50, TRUE, -1)
message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a Space Dragon by the midround ruleset.")
log_dynamic("[key_name(S)] was spawned as a Space Dragon by the midround ruleset.")
priority_announce("A large organic energy flux has been recorded near of [station_name()], please stand-by.", "Lifesign Alert")
@@ -930,7 +930,7 @@
new_datum.original_ref = WEAKREF(clone_victim.mind)
new_datum.setup_clone()
- playsound(clone, 'sound/weapons/zapbang.ogg', 30, TRUE)
+ playsound(clone, 'sound/items/weapons/zapbang.ogg', 30, TRUE)
new /obj/item/storage/toolbox/mechanical(clone.loc) //so they dont get stuck in maints
message_admins("[ADMIN_LOOKUPFLW(clone)] has been made into a Paradox Clone by the midround ruleset.")
@@ -991,11 +991,11 @@
var/mob/living/carbon/human/voidwalker = new (space_turf)
player_mind.transfer_to(voidwalker)
- player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/voidwalker))
+ player_mind.set_assigned_role(SSjob.get_job_type(/datum/job/voidwalker))
player_mind.special_role = antag_flag
player_mind.add_antag_datum(antag_datum)
- playsound(voidwalker, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
+ playsound(voidwalker, 'sound/effects/magic/ethereal_exit.ogg', 50, TRUE, -1)
message_admins("[ADMIN_LOOKUPFLW(voidwalker)] has been made into a Voidwalker by the midround ruleset.")
log_dynamic("[key_name(voidwalker)] was spawned as a Voidwalker by the midround ruleset.")
return voidwalker
diff --git a/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm b/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm
index 999cd156b18d8..79eedc0adb8d7 100644
--- a/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm
+++ b/code/controllers/subsystem/dynamic/dynamic_rulesets_roundstart.dm
@@ -63,7 +63,7 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE)
flags = HIGH_IMPACT_RULESET
/datum/dynamic_ruleset/roundstart/malf_ai/ready(forced)
- var/datum/job/ai_job = SSjob.GetJobType(/datum/job/ai)
+ var/datum/job/ai_job = SSjob.get_job_type(/datum/job/ai)
// If we're not forced, we're going to make sure we can actually have an AI in this shift,
if(!forced && min(ai_job.total_positions - ai_job.current_positions, ai_job.spawn_positions) <= 0)
@@ -75,7 +75,7 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE)
/datum/dynamic_ruleset/roundstart/malf_ai/pre_execute(population)
. = ..()
- var/datum/job/ai_job = SSjob.GetJobType(/datum/job/ai)
+ var/datum/job/ai_job = SSjob.get_job_type(/datum/job/ai)
// Maybe a bit too pedantic, but there should never be more malf AIs than there are available positions, spawn positions or antag cap allocations.
var/num_malf = min(get_antag_cap(population), min(ai_job.total_positions - ai_job.current_positions, ai_job.spawn_positions))
for (var/i in 1 to num_malf)
@@ -296,7 +296,7 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE)
var/mob/M = pick_n_take(candidates)
if (M)
assigned += M.mind
- M.mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_wizard))
+ M.mind.set_assigned_role(SSjob.get_job_type(/datum/job/space_wizard))
M.mind.special_role = ROLE_WIZARD
return TRUE
@@ -429,7 +429,7 @@ GLOBAL_VAR_INIT(revolutionary_win, FALSE)
break
var/mob/M = pick_n_take(candidates)
assigned += M.mind
- M.mind.set_assigned_role(SSjob.GetJobType(job_type))
+ M.mind.set_assigned_role(SSjob.get_job_type(job_type))
M.mind.special_role = required_role
return TRUE
diff --git a/code/controllers/subsystem/explosions.dm b/code/controllers/subsystem/explosions.dm
index 7bb597ba30e12..20194e66626ca 100644
--- a/code/controllers/subsystem/explosions.dm
+++ b/code/controllers/subsystem/explosions.dm
@@ -352,7 +352,7 @@ ADMIN_VERB(check_bomb_impacts, R_DEBUG, "Check Bomb Impact", "See what the effec
who_did_it = "\[Projectile firer: [ADMIN_LOOKUPFLW(fired_projectile.firer)]\]"
who_did_it_game_log = "\[Projectile firer: [key_name(fired_projectile.firer)]\]"
else
- who_did_it = "\[Projectile firer: [ADMIN_LOOKUPFLW(fired_projectile.firer.fingerprintslast)]\]"
+ who_did_it = "\[Projectile firer: [ADMIN_LOOKUPFLW(fired_projectile.firer?.fingerprintslast)]\]"
who_did_it_game_log = "\[Projectile firer: [key_name(fired_projectile.firer.fingerprintslast)]\]"
// Otherwise if the explosion cause is an atom, try get the fingerprints.
else if(istype(explosion_cause))
@@ -521,7 +521,7 @@ ADMIN_VERB(check_bomb_impacts, R_DEBUG, "Check Bomb Impact", "See what the effec
* - [creaking_sound][/sound]: The sound that plays when the station creaks during the explosion.
* - [hull_creaking_sound][/sound]: The sound that plays when the station creaks after the explosion.
*/
-/datum/controller/subsystem/explosions/proc/shake_the_room(turf/epicenter, near_distance, far_distance, quake_factor, echo_factor, creaking, sound/near_sound = sound(get_sfx(SFX_EXPLOSION)), sound/far_sound = sound('sound/effects/explosionfar.ogg'), sound/echo_sound = sound('sound/effects/explosion_distant.ogg'), sound/creaking_sound = sound(get_sfx(SFX_EXPLOSION_CREAKING)), hull_creaking_sound = sound(get_sfx(SFX_HULL_CREAKING)))
+/datum/controller/subsystem/explosions/proc/shake_the_room(turf/epicenter, near_distance, far_distance, quake_factor, echo_factor, creaking, sound/near_sound = sound(get_sfx(SFX_EXPLOSION)), sound/far_sound = sound('sound/effects/explosion/explosionfar.ogg'), sound/echo_sound = sound('sound/effects/explosion/explosion_distant.ogg'), sound/creaking_sound = sound(get_sfx(SFX_EXPLOSION_CREAKING)), hull_creaking_sound = sound(get_sfx(SFX_HULL_CREAKING)))
var/frequency = get_rand_frequency()
var/blast_z = epicenter.z
if(isnull(creaking)) // Autoset creaking.
diff --git a/code/controllers/subsystem/id_access.dm b/code/controllers/subsystem/id_access.dm
index b80e5718128bd..93b823f18b595 100644
--- a/code/controllers/subsystem/id_access.dm
+++ b/code/controllers/subsystem/id_access.dm
@@ -409,6 +409,12 @@ SUBSYSTEM_DEF(id_access)
if(trim.assignment)
id_card.assignment = trim.assignment
+ var/datum/job/trim_job = trim.find_job()
+ if (!isnull(id_card.registered_account))
+ var/datum/job/old_job = id_card.registered_account.account_job
+ id_card.registered_account.account_job = trim_job
+ id_card.registered_account.update_account_job_lists(trim_job, old_job)
+
id_card.update_label()
id_card.update_icon()
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index b1629237dc6bf..08c1df969003f 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -22,7 +22,7 @@ SUBSYSTEM_DEF(job)
var/list/unassigned = list() //Players who need jobs
var/initial_players_to_assign = 0 //used for checking against population caps
- // Whether to run DivideOccupations pure so that there are no side-effects from calling it other than
+ // Whether to run divide_occupations pure so that there are no side-effects from calling it other than
// a player's assigned_role being set to some value.
var/run_divide_occupation_pure = FALSE
@@ -31,7 +31,7 @@ SUBSYSTEM_DEF(job)
var/overflow_role = /datum/job/assistant
- var/list/level_order = list(JP_HIGH,JP_MEDIUM,JP_LOW)
+ var/list/level_order = list(JP_HIGH, JP_MEDIUM, JP_LOW)
/// Lazylist of mob:occupation_string pairs.
var/list/dynamic_forced_occupations
@@ -88,7 +88,7 @@ SUBSYSTEM_DEF(job)
setup_job_lists()
job_config_datum_singletons = generate_config_singletons() // we set this up here regardless in case someone wants to use the verb to generate the config file.
if(!length(all_occupations))
- SetupOccupations()
+ setup_occupations()
if(CONFIG_GET(flag/load_jobs_from_txt))
load_jobs_from_config()
set_overflow_role(CONFIG_GET(string/overflow_job)) // this must always go after load_jobs_from_config() due to how the legacy systems operate, this always takes precedent.
@@ -108,9 +108,9 @@ SUBSYSTEM_DEF(job)
return overflow_jobs
/datum/controller/subsystem/job/proc/set_overflow_role(new_overflow_role)
- var/datum/job/new_overflow = ispath(new_overflow_role) ? GetJobType(new_overflow_role) : GetJob(new_overflow_role)
+ var/datum/job/new_overflow = ispath(new_overflow_role) ? get_job_type(new_overflow_role) : get_job(new_overflow_role)
if(!new_overflow)
- JobDebug("Failed to set new overflow role: [new_overflow_role]")
+ job_debug("SET_OVRFLW: Failed to set new overflow role: [new_overflow_role]")
CRASH("set_overflow_role failed | new_overflow_role: [isnull(new_overflow_role) ? "null" : new_overflow_role]")
var/cap = CONFIG_GET(number/overflow_cap)
@@ -121,17 +121,16 @@ SUBSYSTEM_DEF(job)
if(new_overflow.type == overflow_role)
return
- var/datum/job/old_overflow = GetJobType(overflow_role)
+ var/datum/job/old_overflow = get_job_type(overflow_role)
old_overflow.allow_bureaucratic_error = initial(old_overflow.allow_bureaucratic_error)
old_overflow.spawn_positions = initial(old_overflow.spawn_positions)
old_overflow.total_positions = initial(old_overflow.total_positions)
if(!(initial(old_overflow.job_flags) & JOB_CANNOT_OPEN_SLOTS))
old_overflow.job_flags &= ~JOB_CANNOT_OPEN_SLOTS
overflow_role = new_overflow.type
- JobDebug("Overflow role set to : [new_overflow.type]")
+ job_debug("SET_OVRFLW: Overflow role set to: [new_overflow.type]")
-
-/datum/controller/subsystem/job/proc/SetupOccupations()
+/datum/controller/subsystem/job/proc/setup_occupations()
name_occupations = list()
type_occupations = list()
@@ -205,20 +204,20 @@ SUBSYSTEM_DEF(job)
return TRUE
-/datum/controller/subsystem/job/proc/GetJob(rank)
+/datum/controller/subsystem/job/proc/get_job(rank)
if(!length(all_occupations))
- SetupOccupations()
+ setup_occupations()
return name_occupations[rank]
-/datum/controller/subsystem/job/proc/GetJobType(jobtype)
+/datum/controller/subsystem/job/proc/get_job_type(jobtype)
RETURN_TYPE(/datum/job)
if(!length(all_occupations))
- SetupOccupations()
+ setup_occupations()
return type_occupations[jobtype]
/datum/controller/subsystem/job/proc/get_department_type(department_type)
if(!length(all_occupations))
- SetupOccupations()
+ setup_occupations()
return joinable_departments_by_type[department_type]
/**
@@ -230,89 +229,92 @@ SUBSYSTEM_DEF(job)
* * latejoin - Set to TRUE if this is a latejoin role assignment.
* * do_eligibility_checks - Set to TRUE to conduct all job eligibility tests and reject on failure. Set to FALSE if job eligibility has been tested elsewhere and they can be safely skipped.
*/
-/datum/controller/subsystem/job/proc/AssignRole(mob/dead/new_player/player, datum/job/job, latejoin = FALSE, do_eligibility_checks = TRUE)
- JobDebug("Running AR, Player: [player], Job: [isnull(job) ? "null" : job], LateJoin: [latejoin]")
+/datum/controller/subsystem/job/proc/assign_role(mob/dead/new_player/player, datum/job/job, latejoin = FALSE, do_eligibility_checks = TRUE)
+ job_debug("AR: Running, Player: [player], Job: [isnull(job) ? "null" : job], LateJoin: [latejoin]")
if(!player?.mind || !job)
- JobDebug("AR has failed, player has no mind or job is null, Player: [player], Rank: [isnull(job) ? "null" : job.type]")
+ job_debug("AR: Failed, player has no mind or job is null. Player: [player], Rank: [isnull(job) ? "null" : job.type]")
return FALSE
if(do_eligibility_checks && (check_job_eligibility(player, job, "AR", add_job_to_log = TRUE) != JOB_AVAILABLE))
return FALSE
- JobDebug("Player: [player] is now Rank: [job.title], JCP:[job.current_positions], JPL:[latejoin ? job.total_positions : job.spawn_positions]")
+ job_debug("AR: Role now set and assigned - [player] is [job.title], JCP:[job.current_positions], JPL:[latejoin ? job.total_positions : job.spawn_positions]")
player.mind.set_assigned_role(job)
unassigned -= player
job.current_positions++
return TRUE
-/datum/controller/subsystem/job/proc/FindOccupationCandidates(datum/job/job, level)
- JobDebug("Running FOC, Job: [job], Level: [job_priority_level_to_string(level)]")
+/datum/controller/subsystem/job/proc/find_occupation_candidates(datum/job/job, level = 0)
+ job_debug("FOC: Now running, Job: [job], Level: [job_priority_level_to_string(level)]")
var/list/candidates = list()
for(var/mob/dead/new_player/player in unassigned)
if(!player)
- JobDebug("FOC player no longer exists.")
+ job_debug("FOC: Player no longer exists.")
continue
+
if(!player.client)
- JobDebug("FOC player client no longer exists, Player: [player]")
+ job_debug("FOC: Player client no longer exists, Player: [player]")
continue
+
// Initial screening check. Does the player even have the job enabled, if they do - Is it at the correct priority level?
var/player_job_level = player.client?.prefs.job_preferences[job.title]
if(isnull(player_job_level))
- JobDebug("FOC player job not enabled, Player: [player]")
+ job_debug("FOC: Player job not enabled, Player: [player]")
continue
- else if(player_job_level != level)
- JobDebug("FOC player job enabled at wrong level, Player: [player], TheirLevel: [job_priority_level_to_string(player_job_level)], ReqLevel: [job_priority_level_to_string(level)]")
+
+ if(level && (player_job_level != level))
+ job_debug("FOC: Player job enabled at wrong level, Player: [player], TheirLevel: [job_priority_level_to_string(player_job_level)], ReqLevel: [job_priority_level_to_string(level)]")
continue
- // This check handles its own output to JobDebug.
+ // This check handles its own output to job_debug.
if(check_job_eligibility(player, job, "FOC", add_job_to_log = FALSE) != JOB_AVAILABLE)
continue
// They have the job enabled, at this priority level, with no restrictions applying to them.
- JobDebug("FOC pass, Player: [player], Level: [job_priority_level_to_string(level)]")
+ job_debug("FOC: Player eligible, Player: [player], Level: [job_priority_level_to_string(level)]")
candidates += player
return candidates
-/datum/controller/subsystem/job/proc/GiveRandomJob(mob/dead/new_player/player)
- JobDebug("GRJ Giving random job, Player: [player]")
+/datum/controller/subsystem/job/proc/give_random_job(mob/dead/new_player/player)
+ job_debug("GRJ: Giving random job, Player: [player]")
. = FALSE
for(var/datum/job/job as anything in shuffle(joinable_occupations))
if(QDELETED(player))
- JobDebug("GRJ player is deleted, aborting")
+ job_debug("GRJ: Player is deleted, aborting")
break
if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1)
- JobDebug("GRJ job lacks spawn positions to be eligible, Player: [player], Job: [job]")
+ job_debug("GRJ: Job lacks spawn positions to be eligible, Player: [player], Job: [job]")
continue
- if(istype(job, GetJobType(overflow_role))) // We don't want to give him assistant, that's boring!
- JobDebug("GRJ skipping overflow role, Player: [player], Job: [job]")
+ if(istype(job, get_job_type(overflow_role))) // We don't want to give him assistant, that's boring!
+ job_debug("GRJ: Skipping overflow role, Player: [player], Job: [job]")
continue
if(job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) //If you want a command position, select it!
- JobDebug("GRJ skipping command role, Player: [player], Job: [job]")
+ job_debug("GRJ: Skipping command role, Player: [player], Job: [job]")
continue
- // This check handles its own output to JobDebug.
+ // This check handles its own output to job_debug.
if(check_job_eligibility(player, job, "GRJ", add_job_to_log = TRUE) != JOB_AVAILABLE)
continue
- if(AssignRole(player, job, do_eligibility_checks = FALSE))
- JobDebug("GRJ Random job given, Player: [player], Job: [job]")
+ if(assign_role(player, job, do_eligibility_checks = FALSE))
+ job_debug("GRJ: Random job given, Player: [player], Job: [job]")
return TRUE
- JobDebug("GRJ Player eligible but AssignRole failed, Player: [player], Job: [job]")
+ job_debug("GRJ: Player eligible but assign_role failed, Player: [player], Job: [job]")
-/datum/controller/subsystem/job/proc/ResetOccupations()
- JobDebug("Occupations reset.")
+/datum/controller/subsystem/job/proc/reset_occupations()
+ job_debug("RO: Occupations reset.")
for(var/mob/dead/new_player/player as anything in GLOB.new_player_list)
if(!player?.mind)
continue
- player.mind.set_assigned_role(GetJobType(/datum/job/unassigned))
+ player.mind.set_assigned_role(get_job_type(/datum/job/unassigned))
player.mind.special_role = null
- SetupOccupations()
+ setup_occupations()
unassigned = list()
if(CONFIG_GET(flag/load_jobs_from_txt))
// Any errors with the configs has already been said, we don't need to repeat them here.
@@ -321,12 +323,11 @@ SUBSYSTEM_DEF(job)
return
-/**
- * Will try to select a head, ignoring ALL non-head preferences for every level until.
- *
- * Basically tries to ensure there is at least one head in every shift if anyone has that job preference enabled at all.
+/*
+ * Forces a random Head of Staff role to be assigned to a random eligible player.
+ * Returns TRUE if a player was selected and assigned the role. FALSE otherwise.
*/
-/datum/controller/subsystem/job/proc/FillHeadPosition()
+/datum/controller/subsystem/job/proc/force_one_head_assignment()
var/datum/job_department/command_department = get_department_type(/datum/job_department/command)
if(!command_department)
return FALSE
@@ -334,60 +335,73 @@ SUBSYSTEM_DEF(job)
for(var/datum/job/job as anything in command_department.department_jobs)
if((job.current_positions >= job.total_positions) && job.total_positions != -1)
continue
- var/list/candidates = FindOccupationCandidates(job, level)
+ var/list/candidates = find_occupation_candidates(job, level)
if(!candidates.len)
continue
var/mob/dead/new_player/candidate = pick(candidates)
- // Eligibility checks done as part of FindOccupationCandidates.
- if(AssignRole(candidate, job, do_eligibility_checks = FALSE))
+ // Eligibility checks done as part of find_occupation_candidates.
+ if(assign_role(candidate, job, do_eligibility_checks = FALSE))
return TRUE
return FALSE
/**
* Attempts to fill out all possible head positions for players with that job at a a given job priority level.
+ * Returns the number of Head positions assigned.
*
* Arguments:
- * * level - One of the JP_LOW, JP_MEDIUM or JP_HIGH defines. Attempts to find candidates with head jobs at this priority only.
+ * * level - One of the JP_LOW, JP_MEDIUM, JP_HIGH or JP_ANY defines. Attempts to find candidates with head jobs at that priority only.
*/
-/datum/controller/subsystem/job/proc/CheckHeadPositions(level)
+/datum/controller/subsystem/job/proc/fill_all_head_positions_at_priority(level)
+ . = 0
var/datum/job_department/command_department = get_department_type(/datum/job_department/command)
+
if(!command_department)
- return
+ return .
+
for(var/datum/job/job as anything in command_department.department_jobs)
if((job.current_positions >= job.total_positions) && job.total_positions != -1)
continue
- var/list/candidates = FindOccupationCandidates(job, level)
+
+ var/list/candidates = find_occupation_candidates(job, level)
if(!candidates.len)
continue
+
var/mob/dead/new_player/candidate = pick(candidates)
- // Eligibility checks done as part of FindOccupationCandidates
- AssignRole(candidate, job, do_eligibility_checks = FALSE)
+
+ // Eligibility checks done as part of find_occupation_candidates() above.
+ if(!assign_role(candidate, job, do_eligibility_checks = FALSE))
+ continue
+
+ .++
+
+ if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1)
+ job_debug("JOBS: Command Job is now full, Job: [job], Positions: [job.current_positions], Limit: [job.spawn_positions]")
/// Attempts to fill out all available AI positions.
/datum/controller/subsystem/job/proc/fill_ai_positions()
- var/datum/job/ai_job = GetJob(JOB_AI)
+ var/datum/job/ai_job = get_job(JOB_AI)
if(!ai_job)
return
// In byond for(in to) loops, the iteration is inclusive so we need to stop at ai_job.total_positions - 1
for(var/i in ai_job.current_positions to ai_job.total_positions - 1)
for(var/level in level_order)
var/list/candidates = list()
- candidates = FindOccupationCandidates(ai_job, level)
+ candidates = find_occupation_candidates(ai_job, level)
if(candidates.len)
var/mob/dead/new_player/candidate = pick(candidates)
- // Eligibility checks done as part of FindOccupationCandidates
- if(AssignRole(candidate, GetJobType(/datum/job/ai), do_eligibility_checks = FALSE))
+ // Eligibility checks done as part of find_occupation_candidates
+ if(assign_role(candidate, get_job_type(/datum/job/ai), do_eligibility_checks = FALSE))
break
-/** Proc DivideOccupations
+/** Proc divide_occupations
* fills var "assigned_role" for all ready players.
* This proc must not have any side effect besides of modifying "assigned_role".
**/
-/datum/controller/subsystem/job/proc/DivideOccupations(pure = FALSE, allow_all = FALSE)
+/datum/controller/subsystem/job/proc/divide_occupations(pure = FALSE, allow_all = FALSE)
//Setup new player list and get the jobs list
- JobDebug("Running DO, allow_all = [allow_all], pure = [pure]")
+ job_debug("DO: Running, allow_all = [allow_all], pure = [pure]")
run_divide_occupation_pure = pure
SEND_SIGNAL(src, COMSIG_OCCUPATIONS_DIVIDED, pure, allow_all)
@@ -397,162 +411,175 @@ SUBSYSTEM_DEF(job)
if(player.ready == PLAYER_READY_TO_PLAY && player.check_preferences() && player.mind && is_unassigned_job(player.mind.assigned_role))
unassigned += player
- initial_players_to_assign = unassigned.len
+ initial_players_to_assign = length(unassigned)
- JobDebug("DO, Len: [unassigned.len]")
+ job_debug("DO: Player count to assign roles to: [initial_players_to_assign]")
//Scale number of open security officer slots to population
setup_officer_positions()
//Jobs will have fewer access permissions if the number of players exceeds the threshold defined in game_options.txt
- var/mat = CONFIG_GET(number/minimal_access_threshold)
- if(mat)
- if(mat > unassigned.len)
+ var/min_access_threshold = CONFIG_GET(number/minimal_access_threshold)
+ if(min_access_threshold)
+ if(min_access_threshold > initial_players_to_assign)
CONFIG_SET(flag/jobs_have_minimal_access, FALSE)
else
CONFIG_SET(flag/jobs_have_minimal_access, TRUE)
- //Shuffle players and jobs
- unassigned = shuffle(unassigned)
+ //Shuffle player list.
+ shuffle_inplace(unassigned)
- HandleFeedbackGathering()
+ handle_feedback_gathering()
- // Dynamic has picked a ruleset that requires enforcing some jobs before others.
- JobDebug("DO, Assigning Priority Positions: [length(dynamic_forced_occupations)]")
+ // Assign any priority positions before all other standard job selections.
+ job_debug("DO: Assigning priority positions")
assign_priority_positions()
+ job_debug("DO: Priority assignment complete")
+
+ // The overflow role has limitless slots, plus having the Overflow box ticked in prefs should (with one exception) set the priority to JP_HIGH.
+ // So everyone with overflow enabled will get that job. Thus we can assign it immediately to all players that have it enabled.
+ job_debug("DO: Assigning early overflow roles")
+ assign_all_overflow_positions()
+ job_debug("DO: Early overflow roles assigned.")
+
+ // At this point we can assume the following:
+ // From assign_priority_positions()
+ // 1. If possible, any necessary job roles to allow Dynamic rulesets to execute (such as an AI for malf AI) are satisfied.
+ // 2. All Head of Staff roles with any player pref set to JP_HIGH are filled out.
+ // 3. If any player not selected by the above has any Head of Staff preference enabled at any JP_ level, there is at least one Head of Staff.
+ //
+ // From assign_all_overflow_positions()
+ // 4. Anyone with the overflow role enabled has been given the overflow role.
+
+ // Copy the joinable occupation list and filter out ineligible occupations due to above job assignments.
+ var/list/available_occupations = joinable_occupations.Copy()
+ var/datum/job_department/command_department = get_department_type(/datum/job_department/command)
- //People who wants to be the overflow role, sure, go on.
- JobDebug("DO, Running Overflow Check 1")
- var/datum/job/overflow_datum = GetJobType(overflow_role)
- var/list/overflow_candidates = FindOccupationCandidates(overflow_datum, JP_LOW)
- JobDebug("AC1, Candidates: [overflow_candidates.len]")
- for(var/mob/dead/new_player/player in overflow_candidates)
- JobDebug("AC1 pass, Player: [player]")
- // Eligibility checks done as part of FindOccupationCandidates
- AssignRole(player, GetJobType(overflow_role), do_eligibility_checks = FALSE)
- overflow_candidates -= player
- JobDebug("DO, AC1 end")
-
- //Select one head
- JobDebug("DO, Running Head Check")
- FillHeadPosition()
- JobDebug("DO, Head Check end")
-
- // Fill out any remaining AI positions.
- JobDebug("DO, Running AI Check")
- fill_ai_positions()
- JobDebug("DO, AI Check end")
+ for(var/datum/job/job in available_occupations)
+ // Make sure the job isn't filled. If it is, remove it from the list so it doesn't get checked.
+ if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1)
+ job_debug("DO: Job is now filled, Job: [job], Current: [job.current_positions], Limit: [job.spawn_positions]")
+ available_occupations -= job
+ continue
- //Other jobs are now checked
- JobDebug("DO, Running standard job assignment")
- // New job giving system by Donkie
- // This will cause lots of more loops, but since it's only done once it shouldn't really matter much at all.
- // Hopefully this will add more randomness and fairness to job giving.
+ // Command jobs are handled via fill_all_head_positions_at_priority(...)
+ // Remove these jobs from the list of available occupations to prevent multiple players being assigned to the same
+ // limited role without constantly having to iterate over the available_occupations list and re-check them.
+ if(job in command_department?.department_jobs)
+ available_occupations -= job
+
+ job_debug("DO: Running standard job assignment")
- // Loop through all levels from high to low
- var/list/shuffledoccupations = shuffle(joinable_occupations)
for(var/level in level_order)
- //Check the head jobs first each level
- CheckHeadPositions(level)
+ job_debug("JOBS: Filling in head roles, Level: [job_priority_level_to_string(level)]")
+ // Fill the head jobs first each level
+ fill_all_head_positions_at_priority(level)
// Loop through all unassigned players
for(var/mob/dead/new_player/player in unassigned)
if(!allow_all)
- if(PopcapReached())
- RejectPlayer(player)
-
- // Loop through all jobs
- for(var/datum/job/job in shuffledoccupations) // SHUFFLE ME BABY
- if(!job)
- JobDebug("FOC invalid/null job in occupations, Player: [player], Job: [job]")
- shuffledoccupations -= job
- continue
+ if(popcap_reached())
+ job_debug("JOBS: Popcap reached, trying to reject player: [player]")
+ try_reject_player(player)
- // Make sure the job isn't filled. If it is, remove it from the list so it doesn't get checked again.
- if((job.current_positions >= job.spawn_positions) && job.spawn_positions != -1)
- JobDebug("FOC job filled and not overflow, Player: [player], Job: [job], Current: [job.current_positions], Limit: [job.spawn_positions]")
- shuffledoccupations -= job
- continue
+ job_debug("JOBS: Finding a job for player: [player], at job priority pref: [job_priority_level_to_string(level)]")
+ // Loop through all jobs and build a list of jobs this player could be eligible for.
+ var/list/possible_jobs = list()
+ for(var/datum/job/job in available_occupations)
// Filter any job that doesn't fit the current level.
var/player_job_level = player.client?.prefs.job_preferences[job.title]
if(isnull(player_job_level))
- JobDebug("FOC player job not enabled, Player: [player]")
+ job_debug("JOBS: Job not enabled, Job: [job]")
continue
- else if(player_job_level != level)
- JobDebug("FOC player job enabled but at different level, Player: [player], TheirLevel: [job_priority_level_to_string(player_job_level)], ReqLevel: [job_priority_level_to_string(level)]")
+ if(player_job_level != level)
+ job_debug("JOBS: Job enabled at different priority pref, Job: [job], TheirLevel: [job_priority_level_to_string(player_job_level)], ReqLevel: [job_priority_level_to_string(level)]")
continue
- if(check_job_eligibility(player, job, "DO", add_job_to_log = TRUE) != JOB_AVAILABLE)
+ if(check_job_eligibility(player, job, "JOBS", add_job_to_log = TRUE) != JOB_AVAILABLE)
continue
- JobDebug("DO pass, Player: [player], Level:[level], Job:[job.title]")
- AssignRole(player, job, do_eligibility_checks = FALSE)
- unassigned -= player
- break
+ possible_jobs += job
+
+ // If there are no possible jobs for them at this priority, skip them.
+ if(!length(possible_jobs))
+ job_debug("JOBS: Player not eligible for any available jobs at this priority level: [player]")
+ continue
+
+ // Otherwise, pick one of those jobs at random.
+ var/datum/job/picked_job = pick(possible_jobs)
- JobDebug("DO, Ending standard job assignment")
+ job_debug("JOBS: Now assigning role to player: [player], Job:[picked_job.title]")
+ assign_role(player, picked_job, do_eligibility_checks = FALSE)
+ if((picked_job.current_positions >= picked_job.spawn_positions) && picked_job.spawn_positions != -1)
+ job_debug("JOBS: Job is now full, Job: [picked_job], Positions: [picked_job.current_positions], Limit: [picked_job.spawn_positions]")
+ available_occupations -= picked_job
- JobDebug("DO, Handle unassigned.")
- // Hand out random jobs to the people who didn't get any in the last check
- // Also makes sure that they got their preference correct
+ job_debug("DO: Ending standard job assignment")
+
+ job_debug("DO: Handle unassigned")
+ // For any players that didn't get a job, fall back on their pref setting for what to do.
for(var/mob/dead/new_player/player in unassigned)
- HandleUnassigned(player, allow_all)
- JobDebug("DO, Ending handle unassigned.")
+ handle_unassigned(player, allow_all)
+ job_debug("DO: Ending handle unassigned")
- JobDebug("DO, Handle unrejectable unassigned")
+ job_debug("DO: Handle unrejectable unassigned")
//Mop up people who can't leave.
for(var/mob/dead/new_player/player in unassigned) //Players that wanted to back out but couldn't because they're antags (can you feel the edge case?)
- if(!GiveRandomJob(player))
- if(!AssignRole(player, GetJobType(overflow_role))) //If everything is already filled, make them an assistant
- JobDebug("DO, Forced antagonist could not be assigned any random job or the overflow role. DivideOccupations failed.")
- JobDebug("---------------------------------------------------")
+ if(!give_random_job(player))
+ if(!assign_role(player, get_job_type(overflow_role))) //If everything is already filled, make them an assistant
+ job_debug("DO: Forced antagonist could not be assigned any random job or the overflow role. divide_occupations failed.")
+ job_debug("---------------------------------------------------")
run_divide_occupation_pure = FALSE
return FALSE //Living on the edge, the forced antagonist couldn't be assigned to overflow role (bans, client age) - just reroll
- JobDebug("DO, Ending handle unrejectable unassigned")
+ job_debug("DO: Ending handle unrejectable unassigned")
- JobDebug("All divide occupations tasks completed.")
- JobDebug("---------------------------------------------------")
+ job_debug("All divide occupations tasks completed.")
+ job_debug("---------------------------------------------------")
run_divide_occupation_pure = FALSE
return TRUE
//We couldn't find a job from prefs for this guy.
-/datum/controller/subsystem/job/proc/HandleUnassigned(mob/dead/new_player/player, allow_all = FALSE)
+/datum/controller/subsystem/job/proc/handle_unassigned(mob/dead/new_player/player, allow_all = FALSE)
var/jobless_role = player.client.prefs.read_preference(/datum/preference/choiced/jobless_role)
if(!allow_all)
- if(PopcapReached())
- RejectPlayer(player)
+ if(popcap_reached())
+ job_debug("HU: Popcap reached, trying to reject player: [player]")
+ try_reject_player(player)
return
switch (jobless_role)
if (BEOVERFLOW)
- var/datum/job/overflow_role_datum = GetJobType(overflow_role)
+ var/datum/job/overflow_role_datum = get_job_type(overflow_role)
if(check_job_eligibility(player, overflow_role_datum, debug_prefix = "HU", add_job_to_log = TRUE) != JOB_AVAILABLE)
- RejectPlayer(player)
+ job_debug("HU: Player cannot be overflow, trying to reject: [player]")
+ try_reject_player(player)
return
- if(!AssignRole(player, overflow_role_datum, do_eligibility_checks = FALSE))
- RejectPlayer(player)
+ if(!assign_role(player, overflow_role_datum, do_eligibility_checks = FALSE))
+ job_debug("HU: Player could not be assigned overflow role, trying to reject: [player]")
+ try_reject_player(player)
return
if (BERANDOMJOB)
- if(!GiveRandomJob(player))
- RejectPlayer(player)
+ if(!give_random_job(player))
+ job_debug("HU: Player cannot be given a random job, trying to reject: [player]")
+ try_reject_player(player)
return
if (RETURNTOLOBBY)
- RejectPlayer(player)
+ job_debug("HU: Player unable to be assigned job, return to lobby enabled: [player]")
+ try_reject_player(player)
return
else //Something gone wrong if we got here.
- var/message = "HU: [player] fell through handling unassigned"
- JobDebug(message)
- log_game(message)
- message_admins(message)
- RejectPlayer(player)
+ job_debug("HU: [player] has an invalid jobless_role var: [jobless_role]")
+ log_game("[player] has an invalid jobless_role var: [jobless_role]")
+ message_admins("[player] has an invalid jobless_role, this shouldn't happen.")
+ try_reject_player(player)
//Gives the player the stuff he should have with his rank
-/datum/controller/subsystem/job/proc/EquipRank(mob/living/equipping, datum/job/job, client/player_client)
+/datum/controller/subsystem/job/proc/equip_rank(mob/living/equipping, datum/job/job, client/player_client)
equipping.job = job.title
SEND_SIGNAL(equipping, COMSIG_JOB_RECEIVED, job)
@@ -572,7 +599,7 @@ SUBSYSTEM_DEF(job)
/datum/controller/subsystem/job/proc/handle_auto_deadmin_roles(client/C, rank)
if(!C?.holder)
return TRUE
- var/datum/job/job = GetJob(rank)
+ var/datum/job/job = get_job(rank)
var/timegate_expired = FALSE
// allow only forcing deadminning in the first X seconds of the round if auto_deadmin_timegate is set in config
@@ -590,7 +617,7 @@ SUBSYSTEM_DEF(job)
return C.holder.auto_deadmin()
/datum/controller/subsystem/job/proc/setup_officer_positions()
- var/datum/job/J = SSjob.GetJob(JOB_SECURITY_OFFICER)
+ var/datum/job/J = SSjob.get_job(JOB_SECURITY_OFFICER)
if(!J)
CRASH("setup_officer_positions(): Security officer job is missing")
@@ -598,7 +625,7 @@ SUBSYSTEM_DEF(job)
if(ssc > 0)
if(J.spawn_positions > 0)
var/officer_positions = min(12, max(J.spawn_positions, round(unassigned.len / ssc))) //Scale between configured minimum and 12 officers
- JobDebug("Setting open security officer positions to [officer_positions]")
+ job_debug("SOP: Setting open security officer positions to [officer_positions]")
J.total_positions = officer_positions
J.spawn_positions = officer_positions
@@ -614,7 +641,7 @@ SUBSYSTEM_DEF(job)
else //We ran out of spare locker spawns!
break
-/datum/controller/subsystem/job/proc/HandleFeedbackGathering()
+/datum/controller/subsystem/job/proc/handle_feedback_gathering()
for(var/datum/job/job as anything in joinable_occupations)
var/high = 0 //high
var/medium = 0 //medium
@@ -653,7 +680,7 @@ SUBSYSTEM_DEF(job)
SSblackbox.record_feedback("nested tally", "job_preferences", young, list("[job.title]", "young"))
SSblackbox.record_feedback("nested tally", "job_preferences", newbie, list("[job.title]", "newbie"))
-/datum/controller/subsystem/job/proc/PopcapReached()
+/datum/controller/subsystem/job/proc/popcap_reached()
var/hpc = CONFIG_GET(number/hard_popcap)
var/epc = CONFIG_GET(number/extreme_popcap)
if(hpc || epc)
@@ -662,15 +689,15 @@ SUBSYSTEM_DEF(job)
return 1
return 0
-/datum/controller/subsystem/job/proc/RejectPlayer(mob/dead/new_player/player)
+/datum/controller/subsystem/job/proc/try_reject_player(mob/dead/new_player/player)
if(player.mind && player.mind.special_role)
- return
- if(PopcapReached())
- JobDebug("Popcap overflow Check observer located, Player: [player]")
- JobDebug("Player rejected :[player]")
+ job_debug("RJCT: Player unable to be rejected due to special_role, Player: [player], SpecialRole: [player.mind.special_role]")
+ return FALSE
+
+ job_debug("RJCT: Player rejected, Player: [player]")
unassigned -= player
if(!run_divide_occupation_pure)
- to_chat(player, "You have failed to qualify for any job you desired.")
+ to_chat(player, span_infoplain("You have failed to qualify for any job you desired."))
player.ready = PLAYER_NOT_READY
@@ -679,10 +706,10 @@ SUBSYSTEM_DEF(job)
var/oldjobs = SSjob.all_occupations
sleep(2 SECONDS)
for (var/datum/job/job as anything in oldjobs)
- INVOKE_ASYNC(src, PROC_REF(RecoverJob), job)
+ INVOKE_ASYNC(src, PROC_REF(recover_job), job)
-/datum/controller/subsystem/job/proc/RecoverJob(datum/job/J)
- var/datum/job/newjob = GetJob(J.title)
+/datum/controller/subsystem/job/proc/recover_job(datum/job/J)
+ var/datum/job/newjob = get_job(J.title)
if (!istype(newjob))
return
newjob.total_positions = J.total_positions
@@ -699,7 +726,7 @@ SUBSYSTEM_DEF(job)
if(buckle && isliving(joining_mob))
buckle_mob(joining_mob, FALSE, FALSE)
-/datum/controller/subsystem/job/proc/SendToLateJoin(mob/M, buckle = TRUE)
+/datum/controller/subsystem/job/proc/send_to_late_join(mob/M, buckle = TRUE)
var/atom/destination
if(M.mind && !is_unassigned_job(M.mind.assigned_role) && length(GLOB.jobspawn_overrides[M.mind.assigned_role.title])) //We're doing something special today.
destination = pick(GLOB.jobspawn_overrides[M.mind.assigned_role.title])
@@ -734,19 +761,6 @@ SUBSYSTEM_DEF(job)
stack_trace("Unable to find last resort spawn point.")
return GET_ERROR_ROOM
-///Lands specified mob at a random spot in the hallways
-/datum/controller/subsystem/job/proc/DropLandAtRandomHallwayPoint(mob/living/living_mob)
- var/turf/spawn_turf = get_safe_random_station_turf(typesof(/area/station/hallway))
-
- if(!spawn_turf)
- SendToLateJoin(living_mob)
- else
- podspawn(list(
- "target" = spawn_turf,
- "path" = /obj/structure/closet/supplypod/centcompod,
- "spawn" = living_mob
- ))
-
/// Returns a list of minds of all heads of staff who are alive
/datum/controller/subsystem/job/proc/get_living_heads()
. = list()
@@ -781,7 +795,7 @@ SUBSYSTEM_DEF(job)
if(sec.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_SECURITY)
. += sec
-/datum/controller/subsystem/job/proc/JobDebug(message)
+/datum/controller/subsystem/job/proc/job_debug(message)
log_job_debug(message)
/// Builds various lists of jobs based on station, centcom and additional jobs with icons associated with them.
@@ -846,12 +860,47 @@ SUBSYSTEM_DEF(job)
safe_code_timer_id = null
safe_code_request_loc = null
-/// Blindly assigns the required roles to every player in the dynamic_forced_occupations list.
+/// Assigns roles that are considered high priority, either due to dynamic needing to force a specific role for a specific ruleset
+/// or making sure roles critical to round progression exist where possible every shift.
/datum/controller/subsystem/job/proc/assign_priority_positions()
+ job_debug("APP: Assigning Dynamic ruleset forced occupations: [length(dynamic_forced_occupations)]")
for(var/mob/new_player in dynamic_forced_occupations)
- // Eligibility checks already carried out as part of the dynamic ruleset trim_candidates proc.area
- // However no guarantee of game state between then and now, so don't skip eligibility checks on AssignRole.
- AssignRole(new_player, GetJob(dynamic_forced_occupations[new_player]))
+ // Eligibility checks already carried out as part of the dynamic ruleset trim_candidates proc.
+ // However no guarantee of game state between then and now, so don't skip eligibility checks on assign_role.
+ assign_role(new_player, get_job(dynamic_forced_occupations[new_player]))
+
+ // Get JP_HIGH department Heads of Staff in place. Indirectly useful for the Revolution ruleset to have as many Heads as possible.
+ job_debug("APP: Assigning all JP_HIGH head of staff roles.")
+ var/head_count = fill_all_head_positions_at_priority(JP_HIGH)
+
+ // If nobody has JP_HIGH on a Head role, try to force at least one Head of Staff so every shift has the best chance
+ // of having at least one leadership role.
+ if(head_count == 0)
+ force_one_head_assignment()
+
+ // Fill out all AI positions.
+ job_debug("APP: Filling all AI positions")
+ fill_ai_positions()
+
+/datum/controller/subsystem/job/proc/assign_all_overflow_positions()
+ job_debug("OVRFLW: Assigning all overflow roles.")
+ job_debug("OVRFLW: This shift's overflow role: [overflow_role]")
+ var/datum/job/overflow_datum = get_job_type(overflow_role)
+
+ // When the Overflow role changes for any reason, this allows players to set otherwise invalid job priority pref states.
+ // So if Assistant is the "usual" Overflow but it gets changed to Clown for a shift, players can set the Assistant role's priorities
+ // to JP_MEDIUM and JP_LOW. When the "usual" Overflow role comes back, it returns to an On option in the prefs menu but still
+ // keeps its old JP_MEDIUM or JP_LOW value in the background.
+
+ // Due to this prefs quirk, we actually don't want to find JP_HIGH candidates as it may exclude people with abnormal pref states that
+ // appear normal from the UI. By passing in JP_ANY, it will return all players that have the overflow job pref (which should be a toggle)
+ // set to any level.
+ var/list/overflow_candidates = find_occupation_candidates(overflow_datum, JP_ANY)
+ for(var/mob/dead/new_player/player in overflow_candidates)
+ // Eligibility checks done as part of find_occupation_candidates, so skip them.
+ assign_role(player, get_job_type(overflow_role), do_eligibility_checks = FALSE)
+ job_debug("OVRFLW: Assigned overflow to player: [player]")
+ job_debug("OVRFLW: All overflow roles assigned.")
/// Takes a job priority #define such as JP_LOW and gets its string representation for logging.
/datum/controller/subsystem/job/proc/job_priority_level_to_string(priority)
@@ -869,40 +918,40 @@ SUBSYSTEM_DEF(job)
* Arguments:
* * player - The player to check for job eligibility.
* * possible_job - The job to check for eligibility against.
- * * debug_prefix - Logging prefix for the JobDebug log entries. For example, GRJ during GiveRandomJob or DO during DivideOccupations.
+ * * debug_prefix - Logging prefix for the job_debug log entries. For example, GRJ during give_random_job or DO during divide_occupations.
* * add_job_to_log - If TRUE, appends the job type to the log entry. If FALSE, does not. Set to FALSE when check is part of iterating over players for a specific job, set to TRUE when check is part of iterating over jobs for a specific player and you don't want extra log entry spam.
*/
/datum/controller/subsystem/job/proc/check_job_eligibility(mob/dead/new_player/player, datum/job/possible_job, debug_prefix = "", add_job_to_log = FALSE)
if(!player.mind)
- JobDebug("[debug_prefix] player has no mind, Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
+ job_debug("[debug_prefix]: Player has no mind, Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
return JOB_UNAVAILABLE_GENERIC
if(possible_job.title in player.mind.restricted_roles)
- JobDebug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_ANTAG_INCOMPAT, possible_job.title)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
+ job_debug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_ANTAG_INCOMPAT, possible_job.title)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
return JOB_UNAVAILABLE_ANTAG_INCOMPAT
if(!possible_job.player_old_enough(player.client))
- JobDebug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_ACCOUNTAGE, possible_job.title)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
+ job_debug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_ACCOUNTAGE, possible_job.title)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
return JOB_UNAVAILABLE_ACCOUNTAGE
var/required_playtime_remaining = possible_job.required_playtime_remaining(player.client)
if(required_playtime_remaining)
- JobDebug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_PLAYTIME, possible_job.title)], Player: [player], MissingTime: [required_playtime_remaining][add_job_to_log ? ", Job: [possible_job]" : ""]")
+ job_debug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_PLAYTIME, possible_job.title)], Player: [player], MissingTime: [required_playtime_remaining][add_job_to_log ? ", Job: [possible_job]" : ""]")
return JOB_UNAVAILABLE_PLAYTIME
// Run the banned check last since it should be the rarest check to fail and can access the database.
if(is_banned_from(player.ckey, possible_job.title))
- JobDebug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_BANNED, possible_job.title)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
+ job_debug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_BANNED, possible_job.title)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
return JOB_UNAVAILABLE_BANNED
// Check for character age
if(possible_job.required_character_age > player.client.prefs.read_preference(/datum/preference/numeric/age) && possible_job.required_character_age != null)
- JobDebug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_AGE)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
+ job_debug("[debug_prefix] Error: [get_job_unavailable_error_message(JOB_UNAVAILABLE_AGE)], Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
return JOB_UNAVAILABLE_AGE
// Need to recheck the player exists after is_banned_from since it can query the DB which may sleep.
if(QDELETED(player))
- JobDebug("[debug_prefix] player is qdeleted, Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
+ job_debug("[debug_prefix]: Player is qdeleted, Player: [player][add_job_to_log ? ", Job: [possible_job]" : ""]")
return JOB_UNAVAILABLE_GENERIC
return JOB_AVAILABLE
diff --git a/code/controllers/subsystem/library.dm b/code/controllers/subsystem/library.dm
index a657e442748a4..bfe77f70f02dd 100644
--- a/code/controllers/subsystem/library.dm
+++ b/code/controllers/subsystem/library.dm
@@ -26,14 +26,14 @@ SUBSYSTEM_DEF(library)
/datum/controller/subsystem/library/proc/load_shelves()
var/list/datum/callback/load_callbacks = list()
-
+
for(var/obj/structure/bookcase/case_to_load as anything in shelves_to_load)
if(!case_to_load)
stack_trace("A null bookcase somehow ended up in SSlibrary's shelves_to_load list. Did something harddel?")
continue
load_callbacks += CALLBACK(case_to_load, TYPE_PROC_REF(/obj/structure/bookcase, load_shelf))
shelves_to_load = null
-
+
//Load all of the shelves asyncronously at the same time, blocking until the last one is finished.
callback_select(load_callbacks, savereturns = FALSE)
@@ -59,6 +59,6 @@ SUBSYSTEM_DEF(library)
/datum/controller/subsystem/library/proc/prepare_library_areas()
library_areas = typesof(/area/station/service/library) - /area/station/service/library/abandoned
- var/list/additional_areas = SSmapping.config.library_areas
+ var/list/additional_areas = SSmapping.current_map.library_areas
if(additional_areas)
library_areas += additional_areas
diff --git a/code/controllers/subsystem/map_vote.dm b/code/controllers/subsystem/map_vote.dm
new file mode 100644
index 0000000000000..7d0be38f92072
--- /dev/null
+++ b/code/controllers/subsystem/map_vote.dm
@@ -0,0 +1,160 @@
+#define MAP_VOTE_CACHE_LOCATION "data/map_vote_cache.json"
+
+SUBSYSTEM_DEF(map_vote)
+ name = "Map Vote"
+ flags = SS_NO_FIRE
+
+ /// Has an admin specifically set a map.
+ var/admin_override = FALSE
+
+ /// Have we already done a vote.
+ var/already_voted = FALSE
+
+ /// The map that has been chosen for next round.
+ var/datum/map_config/next_map_config
+
+ /// Stores the current map vote cache, so that players can look at the current tally.
+ var/list/map_vote_cache
+
+ /// Stores the previous map vote cache, used when a map vote is reverted.
+ var/list/previous_cache
+
+ /// Stores a formatted html string of the tally counts
+ var/tally_printout = span_red("Loading...")
+
+/datum/controller/subsystem/map_vote/Initialize()
+ if(rustg_file_exists(MAP_VOTE_CACHE_LOCATION))
+ map_vote_cache = json_decode(file2text(MAP_VOTE_CACHE_LOCATION))
+ var/carryover = CONFIG_GET(number/map_vote_tally_carryover_percentage)
+ for(var/map_id in map_vote_cache)
+ map_vote_cache[map_id] = round(map_vote_cache[map_id] * (carryover / 100))
+ sanitize_cache()
+ else
+ map_vote_cache = list()
+ update_tally_printout()
+ return SS_INIT_SUCCESS
+
+/datum/controller/subsystem/map_vote/proc/write_cache()
+ rustg_file_write(json_encode(map_vote_cache), MAP_VOTE_CACHE_LOCATION)
+
+/datum/controller/subsystem/map_vote/proc/sanitize_cache()
+ var/max = CONFIG_GET(number/map_vote_maximum_tallies)
+ for(var/map_id in map_vote_cache)
+ if(!(map_id in config.maplist))
+ map_vote_cache -= map_id
+ var/count = map_vote_cache[map_id]
+ if(count > max)
+ map_vote_cache[map_id] = max
+
+/datum/controller/subsystem/map_vote/proc/send_map_vote_notice(...)
+ var/static/last_message_at
+ if(last_message_at == world.time)
+ message_admins("Call to send_map_vote_notice twice in one game tick. Yell at someone to condense messages.")
+ last_message_at = world.time
+
+ var/list/messages = args.Copy()
+ to_chat(world, span_purple(examine_block("Map Vote\n\n[messages.Join("\n")]")))
+
+/datum/controller/subsystem/map_vote/proc/finalize_map_vote(datum/vote/map_vote/map_vote)
+ if(already_voted)
+ message_admins("Attempted to finalize a map vote after a map vote has already been finalized.")
+ return
+ already_voted = TRUE
+
+ var/flat = CONFIG_GET(number/map_vote_flat_bonus)
+ previous_cache = map_vote_cache.Copy()
+ for(var/map_id in map_vote.choices)
+ var/datum/map_config/map = config.maplist[map_id]
+ map_vote_cache[map_id] += (map_vote.choices[map_id] * map.voteweight) + flat
+ sanitize_cache()
+ write_cache()
+ update_tally_printout()
+
+ if(admin_override)
+ send_map_vote_notice("Admin Override is in effect. Map will not be changed.", "Tallies are recorded and saved.")
+ return
+
+ var/list/valid_maps = filter_cache_to_valid_maps()
+ if(!length(valid_maps))
+ send_map_vote_notice("No valid maps.")
+ return
+
+ var/winner = pick_weight(filter_cache_to_valid_maps())
+ set_next_map(config.maplist[winner])
+ send_map_vote_notice("Map Selected - [span_bold(next_map_config.map_name)]")
+
+ // do not reset tallies if only one map is even possible
+ if(length(valid_maps) > 1)
+ map_vote_cache[winner] = CONFIG_GET(number/map_vote_minimum_tallies)
+ write_cache()
+ update_tally_printout()
+
+/// Returns a list of all map options that are invalid for the current population.
+/datum/controller/subsystem/map_vote/proc/get_valid_map_vote_choices()
+ var/list/valid_maps = list()
+
+ // Fill in our default choices with all of the maps in our map config, if they are votable and not blocked.
+ var/list/maps = shuffle(global.config.maplist)
+ for(var/map in maps)
+ var/datum/map_config/possible_config = config.maplist[map]
+ if(!possible_config.votable || (possible_config.map_name in SSpersistence.blocked_maps))
+ continue
+ valid_maps += possible_config.map_name
+
+ var/filter_threshold = 0
+ if(SSticker.HasRoundStarted())
+ filter_threshold = get_active_player_count(alive_check = FALSE, afk_check = TRUE, human_check = FALSE)
+ else
+ filter_threshold = length(GLOB.clients)
+
+ for(var/map in valid_maps)
+ var/datum/map_config/possible_config = config.maplist[map]
+ if(possible_config.config_min_users > 0 && filter_threshold < possible_config.config_min_users)
+ valid_maps -= map
+
+ else if(possible_config.config_max_users > 0 && filter_threshold > possible_config.config_max_users)
+ valid_maps -= map
+
+ return valid_maps
+
+/datum/controller/subsystem/map_vote/proc/filter_cache_to_valid_maps()
+ var/connected_players = length(GLOB.player_list)
+ var/list/valid_maps = list()
+ for(var/map_id in map_vote_cache)
+ var/datum/map_config/map = config.maplist[map_id]
+ if(!map.votable)
+ continue
+ if(map.config_min_users > 0 && (connected_players < map.config_min_users))
+ continue
+ if(map.config_max_users > 0 && (connected_players > map.config_max_users))
+ continue
+ valid_maps[map_id] = map_vote_cache[map_id]
+ return valid_maps
+
+/datum/controller/subsystem/map_vote/proc/set_next_map(datum/map_config/change_to)
+ if(!change_to.MakeNextMap())
+ message_admins("Failed to set new map with next_map.json for [change_to.map_name]!")
+ return FALSE
+
+ next_map_config = change_to
+ return TRUE
+
+/datum/controller/subsystem/map_vote/proc/revert_next_map()
+ if(!next_map_config)
+ return
+ if(previous_cache)
+ map_vote_cache = previous_cache
+ previous_cache = null
+
+ already_voted = FALSE
+ admin_override = FALSE
+ send_map_vote_notice("Next map reverted. Voting re-enabled.")
+
+#undef MAP_VOTE_CACHE_LOCATION
+
+/datum/controller/subsystem/map_vote/proc/update_tally_printout()
+ var/list/data = list()
+ for(var/map_id in map_vote_cache)
+ var/datum/map_config/map = config.maplist[map_id]
+ data += "[map.map_name] - [map_vote_cache[map_id]]"
+ tally_printout = examine_block("Current Tallies\n\n[data.Join("\n")]")
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index 8dce11f137746..bfdd32a6cd824 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -6,15 +6,8 @@ SUBSYSTEM_DEF(mapping)
var/list/nuke_tiles = list()
var/list/nuke_threats = list()
- var/datum/map_config/config
- var/datum/map_config/next_map_config
-
- /// Has the map for the next round been voted for already?
- var/map_voted = FALSE
- /// Has the map for the next round been deliberately chosen by an admin?
- var/map_force_chosen = FALSE
- /// Has the map vote been rocked?
- var/map_vote_rocked = FALSE
+ /// The current map config the server loaded at round start.
+ var/datum/map_config/current_map
var/list/map_templates = list()
@@ -95,20 +88,20 @@ SUBSYSTEM_DEF(mapping)
/datum/controller/subsystem/mapping/PreInit()
..()
#ifdef FORCE_MAP
- config = load_map_config(FORCE_MAP, FORCE_MAP_DIRECTORY)
+ current_map = load_map_config(FORCE_MAP, FORCE_MAP_DIRECTORY)
#else
- config = load_map_config(error_if_missing = FALSE)
+ current_map = load_map_config(error_if_missing = FALSE)
#endif
/datum/controller/subsystem/mapping/Initialize()
if(initialized)
return SS_INIT_SUCCESS
- if(config.defaulted)
- var/old_config = config
- config = global.config.defaultmap
- if(!config || config.defaulted)
- to_chat(world, span_boldannounce("Unable to load next or default map config, defaulting to MetaStation."))
- config = old_config
+ if(current_map.defaulted)
+ var/datum/map_config/old_config = current_map
+ current_map = config.defaultmap
+ if(!current_map || current_map.defaulted)
+ to_chat(world, span_boldannounce("Unable to load next or default map config, defaulting to [old_config.map_name]."))
+ current_map = old_config
plane_offset_to_true = list()
true_to_offset_planes = list()
plane_to_offset = list()
@@ -132,11 +125,11 @@ SUBSYSTEM_DEF(mapping)
#ifndef LOWMEMORYMODE
// Create space ruin levels
- while (space_levels_so_far < config.space_ruin_levels)
+ while (space_levels_so_far < current_map.space_ruin_levels)
add_new_zlevel("Ruin Area [space_levels_so_far+1]", ZTRAITS_SPACE)
++space_levels_so_far
// Create empty space levels
- while (space_levels_so_far < config.space_empty_levels + config.space_ruin_levels)
+ while (space_levels_so_far < current_map.space_empty_levels + current_map.space_ruin_levels)
empty_space = add_new_zlevel("Empty Area [space_levels_so_far+1]", list(ZTRAIT_LINKAGE = CROSSLINKED))
++space_levels_so_far
@@ -144,7 +137,7 @@ SUBSYSTEM_DEF(mapping)
if(CONFIG_GET(flag/roundstart_away))
createRandomZlevel(prob(CONFIG_GET(number/config_gateway_chance)))
- else if (SSmapping.config.load_all_away_missions) // we're likely in a local testing environment, so punch it.
+ else if (SSmapping.current_map.load_all_away_missions) // we're likely in a local testing environment, so punch it.
load_all_away_missions()
loading_ruins = TRUE
@@ -363,9 +356,7 @@ Used by the AI doomsday and the self-destruct nuke.
holodeck_templates = SSmapping.holodeck_templates
areas_in_z = SSmapping.areas_in_z
- config = SSmapping.config
- next_map_config = SSmapping.next_map_config
-
+ current_map = SSmapping.current_map
clearing_reserved_turfs = SSmapping.clearing_reserved_turfs
z_list = SSmapping.z_list
@@ -395,13 +386,23 @@ Used by the AI doomsday and the self-destruct nuke.
if (!length(traits)) // null or empty - default
for (var/i in 1 to total_z)
- traits += list(default_traits)
+ traits += list(default_traits.Copy())
else if (total_z != traits.len) // mismatch
INIT_ANNOUNCE("WARNING: [traits.len] trait sets specified for [total_z] z-levels in [path]!")
if (total_z < traits.len) // ignore extra traits
traits.Cut(total_z + 1)
while (total_z > traits.len) // fall back to defaults on extra levels
- traits += list(default_traits)
+ traits += list(default_traits.Copy())
+
+ if(total_z > 1) // it's a multi z map
+ for(var/z in 1 to total_z)
+ if(z == 1) // bottom z-level
+ traits[z]["Up"] = TRUE
+ else if(z == total_z) // top z-level
+ traits[z]["Down"] = TRUE
+ else
+ traits[z]["Down"] = TRUE
+ traits[z]["Up"] = TRUE
// preload the relevant space_level datums
var/start_z = world.maxz + 1
@@ -439,22 +440,22 @@ Used by the AI doomsday and the self-destruct nuke.
// load the station
station_start = world.maxz + 1
- INIT_ANNOUNCE("Loading [config.map_name]...")
- LoadGroup(FailedZs, "Station", config.map_path, config.map_file, config.traits, ZTRAITS_STATION)
+ INIT_ANNOUNCE("Loading [current_map.map_name]...")
+ LoadGroup(FailedZs, "Station", current_map.map_path, current_map.map_file, current_map.traits, ZTRAITS_STATION)
if(SSdbcore.Connect())
var/datum/db_query/query_round_map_name = SSdbcore.NewQuery({"
UPDATE [format_table_name("round")] SET map_name = :map_name WHERE id = :round_id
- "}, list("map_name" = config.map_name, "round_id" = GLOB.round_id))
+ "}, list("map_name" = current_map.map_name, "round_id" = GLOB.round_id))
query_round_map_name.Execute()
qdel(query_round_map_name)
#ifndef LOWMEMORYMODE
- if(config.minetype == "lavaland")
+ if(current_map.minetype == "lavaland")
LoadGroup(FailedZs, "Lavaland", "map_files/Mining", "Lavaland.dmm", default_traits = ZTRAITS_LAVALAND)
- else if (!isnull(config.minetype) && config.minetype != "none")
- INIT_ANNOUNCE("WARNING: An unknown minetype '[config.minetype]' was set! This is being ignored! Update the maploader code!")
+ else if (!isnull(current_map.minetype) && current_map.minetype != "none")
+ INIT_ANNOUNCE("WARNING: An unknown minetype '[current_map.minetype]' was set! This is being ignored! Update the maploader code!")
#endif
if(LAZYLEN(FailedZs)) //but seriously, unless the server's filesystem is messed up this will never happen
@@ -467,10 +468,8 @@ Used by the AI doomsday and the self-destruct nuke.
#undef INIT_ANNOUNCE
// Custom maps are removed after station loading so the map files does not persist for no reason.
- if(config.map_path == CUSTOM_MAP_PATH)
- fdel("_maps/custom/[config.map_file]")
- // And as the file is now removed set the next map to default.
- next_map_config = load_default_map_config()
+ if(current_map.map_path == CUSTOM_MAP_PATH)
+ fdel("_maps/custom/[current_map.map_file]")
/**
* Global list of AREA TYPES that are associated with the station.
@@ -502,88 +501,6 @@ GLOBAL_LIST_EMPTY(the_station_areas)
for(var/area/A as anything in GLOB.areas)
A.RunTerrainPopulation()
-/datum/controller/subsystem/mapping/proc/maprotate()
- if(map_voted || SSmapping.next_map_config) //If voted or set by other means.
- return
-
- var/players = GLOB.clients.len
- var/list/mapvotes = list()
- //count votes
- var/pmv = CONFIG_GET(flag/preference_map_voting)
- if(pmv)
- for (var/client/c in GLOB.clients)
- var/vote = c.prefs.read_preference(/datum/preference/choiced/preferred_map)
- if (!vote)
- if (global.config.defaultmap)
- mapvotes[global.config.defaultmap.map_name] += 1
- continue
- mapvotes[vote] += 1
- else
- for(var/M in global.config.maplist)
- mapvotes[M] = 1
-
- //filter votes
- for (var/map in mapvotes)
- if (!map)
- mapvotes.Remove(map)
- continue
- if (!(map in global.config.maplist))
- mapvotes.Remove(map)
- continue
- if(map in SSpersistence.blocked_maps)
- mapvotes.Remove(map)
- continue
- var/datum/map_config/VM = global.config.maplist[map]
- if (!VM)
- mapvotes.Remove(map)
- continue
- if (VM.voteweight <= 0)
- mapvotes.Remove(map)
- continue
- if (VM.config_min_users > 0 && players < VM.config_min_users)
- mapvotes.Remove(map)
- continue
- if (VM.config_max_users > 0 && players > VM.config_max_users)
- mapvotes.Remove(map)
- continue
-
- if(pmv)
- mapvotes[map] = mapvotes[map]*VM.voteweight
-
- var/pickedmap = pick_weight(mapvotes)
- if (!pickedmap)
- return
- var/datum/map_config/VM = global.config.maplist[pickedmap]
- message_admins("Randomly rotating map to [VM.map_name]")
- . = changemap(VM)
- if (. && VM.map_name != config.map_name)
- to_chat(world, span_boldannounce("Map rotation has chosen [VM.map_name] for next round!"))
-
-/datum/controller/subsystem/mapping/proc/mapvote()
- if(map_voted || SSmapping.next_map_config) //If voted or set by other means.
- return
- if(SSvote.current_vote) //Theres already a vote running, default to rotation.
- maprotate()
- return
- SSvote.initiate_vote(/datum/vote/map_vote, "automatic map rotation", forced = TRUE)
-
-/datum/controller/subsystem/mapping/proc/changemap(datum/map_config/change_to)
- if(!change_to.MakeNextMap())
- next_map_config = load_default_map_config()
- message_admins("Failed to set new map with next_map.json for [change_to.map_name]! Using default as backup!")
- return
-
- var/filter_threshold = get_active_player_count(alive_check = FALSE, afk_check = TRUE, human_check = FALSE)
- if (change_to.config_min_users > 0 && filter_threshold != 0 && filter_threshold < change_to.config_min_users)
- message_admins("[change_to.map_name] was chosen for the next map, despite there being less current players than its set minimum population range!")
- log_game("[change_to.map_name] was chosen for the next map, despite there being less current players than its set minimum population range!")
- if (change_to.config_max_users > 0 && filter_threshold > change_to.config_max_users)
- message_admins("[change_to.map_name] was chosen for the next map, despite there being more current players than its set maximum population range!")
- log_game("[change_to.map_name] was chosen for the next map, despite there being more current players than its set maximum population range!")
-
- next_map_config = change_to
- return TRUE
-
/datum/controller/subsystem/mapping/proc/preloadTemplates(path = "_maps/templates/") //see master controller setup
var/list/filelist = flist(path)
for(var/map in filelist)
@@ -598,10 +515,10 @@ GLOBAL_LIST_EMPTY(the_station_areas)
/datum/controller/subsystem/mapping/proc/preloadRuinTemplates()
// Still supporting bans by filename
var/list/banned = generateMapList("spaceruinblacklist.txt")
- if(config.minetype == "lavaland")
+ if(current_map.minetype == "lavaland")
banned += generateMapList("lavaruinblacklist.txt")
- else if(config.blacklist_file)
- banned += generateMapList(config.blacklist_file)
+ else if(current_map.blacklist_file)
+ banned += generateMapList(current_map.blacklist_file)
for(var/item in sort_list(subtypesof(/datum/map_template/ruin), GLOBAL_PROC_REF(cmp_ruincost_priority)))
var/datum/map_template/ruin/ruin_type = item
@@ -961,7 +878,7 @@ ADMIN_VERB(load_away_mission, R_FUN, "Load Away Mission", "Load a specific away
/// Returns true if the map we're playing on is on a planet
/datum/controller/subsystem/mapping/proc/is_planetary()
- return config.planetary
+ return current_map.planetary
/// For debug purposes, will add every single away mission present in a given directory.
/// You can optionally pass in a string directory to load from instead of the default.
diff --git a/code/controllers/subsystem/movement/movement.dm b/code/controllers/subsystem/movement/movement.dm
index 425c67a0c474f..d6043d596bb0e 100644
--- a/code/controllers/subsystem/movement/movement.dm
+++ b/code/controllers/subsystem/movement/movement.dm
@@ -66,7 +66,7 @@ SUBSYSTEM_DEF(movement)
return // Still work to be done
var/bucket_time = bucket_info[MOVEMENT_BUCKET_TIME]
smash_bucket(1, bucket_time) // We assume we're the first bucket in the queue right now
- visual_delay = MC_AVERAGE_FAST(visual_delay, max((world.time - canonical_time) / wait, 1))
+ visual_delay = MC_AVERAGE_FAST(visual_delay, max((world.time - canonical_time) / TICKS2DS(wait), 1))
/// Removes a bucket from our system. You only need to pass in the time, but if you pass in the index of the list you save us some work
/datum/controller/subsystem/movement/proc/smash_bucket(index, bucket_time)
diff --git a/code/controllers/subsystem/movement/movement_types.dm b/code/controllers/subsystem/movement/movement_types.dm
index ec0136bc8c178..58b1c58b0bca1 100644
--- a/code/controllers/subsystem/movement/movement_types.dm
+++ b/code/controllers/subsystem/movement/movement_types.dm
@@ -869,3 +869,95 @@
var/atom/old_loc = moving.loc
holder.current_pipe = holder.current_pipe.transfer(holder)
return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE
+
+
+/**
+ * Helper proc for the smooth_move datum
+ *
+ * Returns TRUE if the loop sucessfully started, or FALSE if it failed
+ *
+ * Arguments:
+ * moving - The atom we want to move
+ * angle - Angle at which we want to move
+ * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1
+ * timeout - Time in deci-seconds until the moveloop self expires. Defaults to INFINITY
+ * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem
+ * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY
+ * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
+ *
+**/
+
+/datum/move_manager/proc/smooth_move(moving, angle, delay, timeout, subsystem, priority, flags, datum/extra_info)
+ return add_to_loop(moving, subsystem, /datum/move_loop/smooth_move, priority, flags, extra_info, delay, timeout, angle)
+
+/datum/move_loop/smooth_move
+ /// Angle at which we move. 0 is north because byond.
+ var/angle = 0
+ /// When this gets bigger than 1, we move a turf
+ var/x_ticker = 0
+ var/y_ticker = 0
+ /// The rate at which we move, between 0 and 1. Cached to cut down on trig
+ var/x_rate = 0
+ var/y_rate = 1
+ /// Sign for our movement
+ var/x_sign = 1
+ var/y_sign = 1
+ /// Actual move delay, as delay will be modified by move() depending on what direction we move in
+ var/saved_delay
+
+/datum/move_loop/smooth_move/setup(delay, timeout, angle)
+ . = ..()
+ if(!.)
+ return FALSE
+ set_angle(angle)
+ saved_delay = delay
+
+/datum/move_loop/smooth_move/set_delay(new_delay)
+ new_delay = round(new_delay, world.tick_lag)
+ . = ..()
+ saved_delay = delay
+
+/datum/move_loop/smooth_move/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, atom/chasing, home = FALSE)
+ if(..() && angle == src.angle)
+ return TRUE
+ return FALSE
+
+/datum/move_loop/smooth_move/move()
+ var/atom/old_loc = moving.loc
+ // Defaulting to 2 because if one rate is 0 the other is guaranteed to be 1, so maxing out at 1 to_move
+ var/x_to_move = x_rate > 0 ? (1 - x_ticker) / x_rate : 2
+ var/y_to_move = y_rate > 0 ? (1 - y_ticker) / y_rate : 2
+ var/move_dist = min(x_to_move, y_to_move)
+ x_ticker += x_rate * move_dist
+ y_ticker += y_rate * move_dist
+
+ // Per Bresenham's, if we are closer to the next tile's center move diagonally. Checked by seeing if we pass into the next tile after moving another half a tile
+ var/move_x = (x_ticker + x_rate * 0.5) > 1
+ var/move_y = (y_ticker + y_rate * 0.5) > 1
+ if (move_x)
+ x_ticker = 0
+ if (move_y)
+ y_ticker = 0
+
+ var/turf/next_turf = locate(moving.x + (move_x ? x_sign : 0), moving.y + (move_y ? y_sign : 0), moving.z)
+ moving.Move(next_turf, get_dir(moving, next_turf), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
+
+ if (old_loc == moving?.loc)
+ return MOVELOOP_FAILURE
+
+ delay = saved_delay
+ if (move_x && move_y)
+ delay *= 1.4
+
+ return MOVELOOP_SUCCESS
+
+/datum/move_loop/smooth_move/proc/set_angle(new_angle)
+ angle = new_angle
+ x_rate = sin(angle)
+ y_rate = cos(angle)
+ x_sign = SIGN(x_rate)
+ y_sign = SIGN(y_rate)
+ x_rate = abs(x_rate)
+ y_rate = abs(y_rate)
+ x_ticker = 0
+ y_ticker = 0
diff --git a/code/controllers/subsystem/movement/newtonian_movement.dm b/code/controllers/subsystem/movement/newtonian_movement.dm
new file mode 100644
index 0000000000000..aeb03a576dae0
--- /dev/null
+++ b/code/controllers/subsystem/movement/newtonian_movement.dm
@@ -0,0 +1,31 @@
+/// 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
+ runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+
+ var/stat_tag = "P" //Used for logging
+ var/list/processing = list()
+ var/list/currentrun = list()
+
+/datum/controller/subsystem/movement/newtonian_movement/stat_entry(msg)
+ msg = "[stat_tag]:[length(processing)]"
+ return ..()
+
+/datum/controller/subsystem/movement/newtonian_movement/fire(resumed = FALSE)
+ . = ..()
+ if (!resumed)
+ currentrun = processing.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/current_run = currentrun
+
+ while(current_run.len)
+ var/datum/thing = current_run[current_run.len]
+ current_run.len--
+ if(QDELETED(thing))
+ processing -= thing
+ else if(thing.process(TICKS2DS(wait) * 0.1) == PROCESS_KILL)
+ // fully stop so that a future START_PROCESSING will work
+ STOP_PROCESSING(src, thing)
+ if (MC_TICK_CHECK)
+ return
diff --git a/code/controllers/subsystem/movement/spacedrift.dm b/code/controllers/subsystem/movement/spacedrift.dm
deleted file mode 100644
index 4002b5eb555f2..0000000000000
--- a/code/controllers/subsystem/movement/spacedrift.dm
+++ /dev/null
@@ -1,5 +0,0 @@
-MOVEMENT_SUBSYSTEM_DEF(spacedrift)
- name = "Space Drift"
- priority = FIRE_PRIORITY_SPACEDRIFT
- flags = SS_NO_INIT|SS_TICKER
- runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
diff --git a/code/controllers/subsystem/nightshift.dm b/code/controllers/subsystem/nightshift.dm
index b8df42742e43c..170f12696147f 100644
--- a/code/controllers/subsystem/nightshift.dm
+++ b/code/controllers/subsystem/nightshift.dm
@@ -26,7 +26,7 @@ SUBSYSTEM_DEF(nightshift)
/datum/controller/subsystem/nightshift/proc/announce(message)
priority_announce(
text = message,
- sound = 'sound/misc/notice2.ogg',
+ sound = 'sound/announcer/notice/notice2.ogg',
sender_override = "Automated Lighting System Announcement",
color_override = "grey",
)
diff --git a/code/controllers/subsystem/persistence/_persistence.dm b/code/controllers/subsystem/persistence/_persistence.dm
index 6438015d11d38..d38d7fa372e25 100644
--- a/code/controllers/subsystem/persistence/_persistence.dm
+++ b/code/controllers/subsystem/persistence/_persistence.dm
@@ -111,7 +111,7 @@ SUBSYSTEM_DEF(persistence)
for(var/map in config.maplist)
var/datum/map_config/VM = config.maplist[map]
var/run = 0
- if(VM.map_name == SSmapping.config.map_name)
+ if(VM.map_name == SSmapping.current_map.map_name)
run++
for(var/name in SSpersistence.saved_maps)
if(VM.map_name == name)
@@ -128,7 +128,7 @@ SUBSYSTEM_DEF(persistence)
saved_maps += mapstosave
for(var/i = mapstosave; i > 1; i--)
saved_maps[i] = saved_maps[i-1]
- saved_maps[1] = SSmapping.config.map_name
+ saved_maps[1] = SSmapping.current_map.map_name
var/json_file = file(FILE_RECENT_MAPS)
var/list/file_data = list()
file_data["data"] = saved_maps
diff --git a/code/controllers/subsystem/persistence/engravings.dm b/code/controllers/subsystem/persistence/engravings.dm
index f47fc7fbba124..ad00c7909d723 100644
--- a/code/controllers/subsystem/persistence/engravings.dm
+++ b/code/controllers/subsystem/persistence/engravings.dm
@@ -14,7 +14,7 @@
saved_engravings = json["entries"]
if(!saved_engravings.len)
- log_world("Failed to load engraved messages on map [SSmapping.config.map_name]")
+ log_world("Failed to load engraved messages on map [SSmapping.current_map.map_name]")
return
var/list/viable_turfs = get_area_turfs(/area/station/maintenance, subtypes = TRUE) + get_area_turfs(/area/station/security/prison, subtypes = TRUE)
@@ -27,7 +27,7 @@
var/successfully_loaded_engravings = 0
- for(var/iteration in 1 to rand(MIN_PERSISTENT_ENGRAVINGS, MAX_PERSISTENT_ENGRAVINGS))
+ for(var/iteration in 1 to min(rand(MIN_PERSISTENT_ENGRAVINGS, MAX_PERSISTENT_ENGRAVINGS), saved_engravings.len))
var/engraving = pick_n_take(saved_engravings)
if(!islist(engraving))
stack_trace("something's wrong with the engraving data! one of the saved engravings wasn't a list!")
@@ -42,7 +42,7 @@
successfully_loaded_engravings++
turfs_to_pick_from -= engraved_wall
- log_world("Loaded [successfully_loaded_engravings] engraved messages on map [SSmapping.config.map_name]")
+ log_world("Loaded [successfully_loaded_engravings] engraved messages on map [SSmapping.current_map.map_name]")
///Saves all new engravings in the world.
/datum/controller/subsystem/persistence/proc/save_wall_engravings()
diff --git a/code/controllers/subsystem/polling.dm b/code/controllers/subsystem/polling.dm
index 6cdcfbfd45949..b237edd3870b6 100644
--- a/code/controllers/subsystem/polling.dm
+++ b/code/controllers/subsystem/polling.dm
@@ -36,7 +36,7 @@ SUBSYSTEM_DEF(polling)
* * chat_text_border_icon: Object or path to make an icon of to decorate the chat announcement.
* * announce_chosen: Whether we should announce the chosen candidates in chat. This is ignored unless amount_to_pick is greater than 0.
*
- * Returns a list of all mobs who signed up for the poll.
+ * Returns a list of all mobs who signed up for the poll, OR, in the case that amount_to_pick is equal to 1 the singular mob/null if no available candidates.
*/
/datum/controller/subsystem/polling/proc/poll_candidates(
question,
@@ -155,7 +155,7 @@ SUBSYSTEM_DEF(polling)
act_never = "[custom_link_style_start]\[Never For This Round\]"
if(!duplicate_message_check(alert_poll)) //Only notify people once. They'll notice if there are multiple and we don't want to spam people.
- SEND_SOUND(candidate_mob, 'sound/misc/notice2.ogg')
+ SEND_SOUND(candidate_mob, 'sound/announcer/notice/notice2.ogg')
var/surrounding_icon
if(chat_text_border_icon)
var/image/surrounding_image
@@ -175,9 +175,11 @@ SUBSYSTEM_DEF(polling)
UNTIL(new_poll.finished)
if(!(amount_to_pick > 0))
return new_poll.signed_up
- if(length(new_poll.signed_up) < amount_to_pick)
- return new_poll.signed_up
for(var/pick in 1 to amount_to_pick)
+ // There may be less people signed up than amount_to_pick
+ // pick_n_take returns the default return value of null if passed an empty list, so just break in that case rather than adding null to the list.
+ if(!length(new_poll.signed_up))
+ break
new_poll.chosen_candidates += pick_n_take(new_poll.signed_up)
if(announce_chosen)
new_poll.announce_chosen(group)
diff --git a/code/controllers/subsystem/processing/ai_idle_behaviors.dm b/code/controllers/subsystem/processing/ai_idle_behaviors.dm
index cda3d354882f4..8875d971ad87c 100644
--- a/code/controllers/subsystem/processing/ai_idle_behaviors.dm
+++ b/code/controllers/subsystem/processing/ai_idle_behaviors.dm
@@ -1,6 +1,17 @@
PROCESSING_SUBSYSTEM_DEF(idle_ai_behaviors)
- name = "idle_ai_behaviors"
- flags = SS_NO_INIT | SS_BACKGROUND
+ name = "AI Idle Behaviors"
+ flags = SS_BACKGROUND
wait = 1.5 SECONDS
priority = FIRE_PRIORITY_IDLE_NPC
init_order = INIT_ORDER_AI_IDLE_CONTROLLERS //must execute only after ai behaviors are initialized
+ ///List of all the idle ai behaviors
+ var/list/idle_behaviors = list()
+
+/datum/controller/subsystem/processing/idle_ai_behaviors/Initialize()
+ setup_idle_behaviors()
+ return SS_INIT_SUCCESS
+
+/datum/controller/subsystem/processing/idle_ai_behaviors/proc/setup_idle_behaviors()
+ for(var/behavior_type in subtypesof(/datum/idle_behavior))
+ var/datum/idle_behavior/behavior = new behavior_type
+ idle_behaviors[behavior_type] = behavior
diff --git a/code/controllers/subsystem/processing/fishing.dm b/code/controllers/subsystem/processing/fishing.dm
index ca54141de82a8..0e8c126fe9330 100644
--- a/code/controllers/subsystem/processing/fishing.dm
+++ b/code/controllers/subsystem/processing/fishing.dm
@@ -1,4 +1,61 @@
/// subsystem for the fishing minigame processing.
PROCESSING_SUBSYSTEM_DEF(fishing)
name = "Fishing"
+ flags = SS_BACKGROUND|SS_POST_FIRE_TIMING
wait = 0.05 SECONDS // If you raise it to 0.1 SECONDS, you better also modify [datum/fish_movement/move_fish()]
+ ///Cached fish properties so we don't have to initalize fish every time
+ var/list/fish_properties
+ ///A cache of fish that can be caught by each type of fishing lure
+ var/list/lure_catchables
+
+/datum/controller/subsystem/processing/fishing/Initialize()
+ ///init the properties
+ fish_properties = list()
+ for(var/fish_type in subtypesof(/obj/item/fish))
+ var/obj/item/fish/fish = new fish_type(null, FALSE)
+ var/list/properties = list()
+ fish_properties[fish_type] = properties
+ properties[FISH_PROPERTIES_FAV_BAIT] = fish.favorite_bait.Copy()
+ properties[FISH_PROPERTIES_BAD_BAIT] = fish.disliked_bait.Copy()
+ properties[FISH_PROPERTIES_TRAITS] = fish.fish_traits.Copy()
+
+ var/list/evo_types = fish.evolution_types?.Copy()
+ properties[FISH_PROPERTIES_EVOLUTIONS] = evo_types
+ for(var/type in evo_types)
+ LAZYADD(GLOB.fishes_by_fish_evolution[type], fish_type)
+
+ var/beauty_score = "???"
+ switch(fish.beauty)
+ if(-INFINITY to FISH_BEAUTY_DISGUSTING)
+ beauty_score = "OH HELL NAW!"
+ if(FISH_BEAUTY_DISGUSTING to FISH_BEAUTY_UGLY)
+ beauty_score = "☆☆☆☆☆"
+ if(FISH_BEAUTY_UGLY to FISH_BEAUTY_BAD)
+ beauty_score = "★☆☆☆☆"
+ if(FISH_BEAUTY_BAD to FISH_BEAUTY_NULL)
+ beauty_score = "★★☆☆☆"
+ if(FISH_BEAUTY_NULL to FISH_BEAUTY_GENERIC)
+ beauty_score = "★★★☆☆"
+ if(FISH_BEAUTY_GENERIC to FISH_BEAUTY_GOOD)
+ beauty_score = "★★★★☆"
+ if(FISH_BEAUTY_GOOD to FISH_BEAUTY_GREAT)
+ beauty_score = "★★★★★"
+ if(FISH_BEAUTY_GREAT to INFINITY)
+ beauty_score = "★★★★★★"
+
+ properties[FISH_PROPERTIES_BEAUTY_SCORE] = beauty_score
+
+ qdel(fish)
+
+ ///init the list of things lures can catch
+ lure_catchables = list()
+ var/list/fish_types = subtypesof(/obj/item/fish)
+ for(var/lure_type in typesof(/obj/item/fishing_lure))
+ var/obj/item/fishing_lure/lure = new lure_type
+ lure_catchables[lure_type] = list()
+ for(var/obj/item/fish/fish_type as anything in fish_types)
+ if(lure.is_catchable_fish(fish_type, fish_properties[fish_type]))
+ lure_catchables[lure_type] += fish_type
+ qdel(lure)
+
+ return SS_INIT_SUCCESS
diff --git a/code/controllers/subsystem/processing/manufacturing.dm b/code/controllers/subsystem/processing/manufacturing.dm
new file mode 100644
index 0000000000000..8bc9c6af5d57b
--- /dev/null
+++ b/code/controllers/subsystem/processing/manufacturing.dm
@@ -0,0 +1,4 @@
+PROCESSING_SUBSYSTEM_DEF(manufacturing)
+ name = "Manufacturing Processing"
+ wait = 1 SECONDS
+ stat_tag = "MN"
diff --git a/code/controllers/subsystem/processing/station.dm b/code/controllers/subsystem/processing/station.dm
index 883ab37456d2c..c58840cfa7ad1 100644
--- a/code/controllers/subsystem/processing/station.dm
+++ b/code/controllers/subsystem/processing/station.dm
@@ -164,6 +164,8 @@ PROCESSING_SUBSYSTEM_DEF(station)
///Creates a given trait of a specific type, while also removing any blacklisted ones from the future pool.
/datum/controller/subsystem/processing/station/proc/setup_trait(datum/station_trait/trait_type)
+ if(locate(trait_type) in station_traits)
+ return
var/datum/station_trait/trait_instance = new trait_type()
station_traits += trait_instance
log_game("Station Trait: [trait_instance.name] chosen for this round.")
@@ -179,5 +181,4 @@ PROCESSING_SUBSYSTEM_DEF(station)
var/datum/hud/new_player/observer_hud = player.hud_used
if (!istype(observer_hud))
continue
- observer_hud.add_station_trait_buttons()
- observer_hud.show_hud(observer_hud.hud_version)
+ observer_hud.show_station_trait_buttons()
diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm
index 11a055d292ce2..0ad0a78589221 100644
--- a/code/controllers/subsystem/shuttle.dm
+++ b/code/controllers/subsystem/shuttle.dm
@@ -140,6 +140,9 @@ SUBSYSTEM_DEF(shuttle)
/// Did the supermatter start a cascade event?
var/supermatter_cascade = FALSE
+ /// List of express consoles that are waiting for pack initialization
+ var/list/obj/machinery/computer/cargo/express/express_consoles = list()
+
/datum/controller/subsystem/shuttle/Initialize()
order_number = rand(1, 9000)
@@ -172,6 +175,9 @@ SUBSYSTEM_DEF(shuttle)
supply_packs[pack.id] = pack
+ for (var/obj/machinery/computer/cargo/express/console as anything in express_consoles)
+ console.packin_up(TRUE)
+
setup_shuttles(stationary_docking_ports)
has_purchase_shuttle_access = init_has_purchase_shuttle_access()
@@ -275,7 +281,7 @@ SUBSYSTEM_DEF(shuttle)
priority_announce(
text = "Emergency shuttle uplink interference detected, shuttle call disabled while the system reinitializes. Estimated restore in [DisplayTimeText(lockout_timer, round_seconds_to = 60)].",
title = "Uplink Interference",
- sound = 'sound/misc/announce_dig.ogg',
+ sound = 'sound/announcer/announcement/announce_dig.ogg',
sender_override = "Emergency Shuttle Uplink Alert",
color_override = "grey",
)
@@ -289,7 +295,7 @@ SUBSYSTEM_DEF(shuttle)
priority_announce(
text= "Emergency shuttle uplink services are now back online.",
title = "Uplink Restored",
- sound = 'sound/misc/announce_dig.ogg',
+ sound = 'sound/announcer/announcement/announce_dig.ogg',
sender_override = "Emergency Shuttle Uplink Alert",
color_override = "green",
)
@@ -523,7 +529,7 @@ SUBSYSTEM_DEF(shuttle)
priority_announce(
text = "Departure has been postponed indefinitely pending conflict resolution.",
title = "Hostile Environment Detected",
- sound = 'sound/misc/notice1.ogg',
+ sound = 'sound/announcer/notice/notice1.ogg',
sender_override = "Emergency Shuttle Uplink Alert",
color_override = "grey",
)
@@ -533,7 +539,7 @@ SUBSYSTEM_DEF(shuttle)
priority_announce(
text = "You have [DisplayTimeText(emergency_dock_time)] to board the emergency shuttle.",
title = "Hostile Environment Resolved",
- sound = 'sound/misc/announce_dig.ogg',
+ sound = 'sound/announcer/announcement/announce_dig.ogg',
sender_override = "Emergency Shuttle Uplink Alert",
color_override = "green",
)
diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm
index 020598a573b67..cf158586ce497 100644
--- a/code/controllers/subsystem/statpanel.dm
+++ b/code/controllers/subsystem/statpanel.dm
@@ -22,9 +22,9 @@ SUBSYSTEM_DEF(statpanels)
/datum/controller/subsystem/statpanels/fire(resumed = FALSE)
if (!resumed)
num_fires++
- var/datum/map_config/cached = SSmapping.next_map_config
+ var/datum/map_config/cached = SSmap_vote.next_map_config
global_data = list(
- "Map: [SSmapping.config?.map_name || "Loading..."]",
+ "Map: [SSmapping.current_map?.map_name || "Loading..."]",
cached ? "Next Map: [cached.map_name]" : null,
"Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]",
"Server Time: [time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss")]",
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
index fc0375f4f0b6a..da403db9e4559 100644
--- a/code/controllers/subsystem/throwing.dm
+++ b/code/controllers/subsystem/throwing.dm
@@ -202,6 +202,11 @@ SUBSYSTEM_DEF(throwing)
if(!thrownthing)
return
thrownthing.throwing = null
+ var/drift_force = speed
+ if (isitem(thrownthing))
+ var/obj/item/thrownitem = thrownthing
+ drift_force *= WEIGHT_TO_NEWTONS(thrownitem.w_class)
+
if (!hit)
for (var/atom/movable/obstacle as anything in get_turf(thrownthing)) //looking for our target on the turf we land on.
if (obstacle == target)
@@ -214,9 +219,9 @@ SUBSYSTEM_DEF(throwing)
thrownthing.throw_impact(get_turf(thrownthing), src) // we haven't hit something yet and we still must, let's hit the ground.
if(QDELETED(thrownthing)) //throw_impact can delete things, such as glasses smashing
return //deletion should already be handled by on_thrownthing_qdel()
- thrownthing.newtonian_move(init_dir)
+ thrownthing.newtonian_move(delta_to_angle(dist_x, dist_y), drift_force = drift_force)
else
- thrownthing.newtonian_move(init_dir)
+ thrownthing.newtonian_move(delta_to_angle(dist_x, dist_y), drift_force = drift_force)
if(target)
thrownthing.throw_impact(target, src)
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index ff76464bee56a..bcd33f04c98f0 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -93,12 +93,12 @@ SUBSYSTEM_DEF(ticker)
switch(L.len)
if(3) //rare+MAP+sound.ogg or MAP+rare.sound.ogg -- Rare Map-specific sounds
if(use_rare_music)
- if(L[1] == "rare" && L[2] == SSmapping.config.map_name)
+ if(L[1] == "rare" && L[2] == SSmapping.current_map.map_name)
music += S
- else if(L[2] == "rare" && L[1] == SSmapping.config.map_name)
+ else if(L[2] == "rare" && L[1] == SSmapping.current_map.map_name)
music += S
if(2) //rare+sound.ogg or MAP+sound.ogg -- Rare sounds or Map-specific sounds
- if((use_rare_music && L[1] == "rare") || (L[1] == SSmapping.config.map_name))
+ if((use_rare_music && L[1] == "rare") || (L[1] == SSmapping.current_map.map_name))
music += S
if(1) //sound.ogg -- common sound
if(L[1] == "exclude")
@@ -157,7 +157,7 @@ SUBSYSTEM_DEF(ticker)
for(var/client/C in GLOB.clients)
window_flash(C, ignorepref = TRUE) //let them know lobby has opened up.
to_chat(world, span_notice("Welcome to [station_name()]!"))
- send2chat(new /datum/tgs_message_content("New round starting on [SSmapping.config.map_name]!"), CONFIG_GET(string/channel_announce_new_game))
+ send2chat(new /datum/tgs_message_content("New round starting on [SSmapping.current_map.map_name]!"), CONFIG_GET(string/channel_announce_new_game))
current_state = GAME_STATE_PREGAME
SEND_SIGNAL(src, COMSIG_TICKER_ENTER_PREGAME)
@@ -211,7 +211,6 @@ SUBSYSTEM_DEF(ticker)
toggle_ooc(TRUE) // Turn it on
toggle_dooc(TRUE)
declare_completion(force_ending)
- check_maprotate()
Master.SetRunLevel(RUNLEVEL_POSTGAME)
/// Checks if the round should be ending, called every ticker tick
@@ -236,14 +235,14 @@ SUBSYSTEM_DEF(ticker)
can_continue = SSdynamic.pre_setup() //Choose antagonists
CHECK_TICK
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_PRE_JOBS_ASSIGNED, src)
- can_continue = can_continue && SSjob.DivideOccupations() //Distribute jobs
+ can_continue = can_continue && SSjob.divide_occupations() //Distribute jobs
CHECK_TICK
if(!GLOB.Debug2)
if(!can_continue)
log_game("Game failed pre_setup")
to_chat(world, "Error setting up game. Reverting to pre-game lobby.")
- SSjob.ResetOccupations()
+ SSjob.reset_occupations()
return FALSE
else
message_admins(span_notice("DEBUG: Bypassing prestart checks..."))
@@ -416,7 +415,7 @@ SUBSYSTEM_DEF(ticker)
continue
var/datum/job/player_assigned_role = new_player_living.mind.assigned_role
if(player_assigned_role.job_flags & JOB_EQUIP_RANK)
- SSjob.EquipRank(new_player_living, player_assigned_role, new_player_mob.client)
+ SSjob.equip_rank(new_player_living, player_assigned_role, new_player_mob.client)
player_assigned_role.after_roundstart_spawn(new_player_living, new_player_mob.client)
if(picked_spare_id_candidate == new_player_mob)
captainless = FALSE
@@ -489,7 +488,7 @@ SUBSYSTEM_DEF(ticker)
list_clear_nulls(queued_players)
for (var/mob/dead/new_player/new_player in queued_players)
to_chat(new_player, span_userdanger("The alive players limit has been released! [html_encode(">>Join Game<<")]"))
- SEND_SOUND(new_player, sound('sound/misc/notice1.ogg'))
+ SEND_SOUND(new_player, sound('sound/announcer/notice/notice1.ogg'))
GLOB.latejoin_menu.ui_interact(new_player)
queued_players.len = 0
queue_delay = 0
@@ -504,7 +503,7 @@ SUBSYSTEM_DEF(ticker)
if(living_player_count() < hard_popcap)
if(next_in_line?.client)
to_chat(next_in_line, span_userdanger("A slot has opened! You have approximately 20 seconds to join. \>\>Join Game\<\<"))
- SEND_SOUND(next_in_line, sound('sound/misc/notice1.ogg'))
+ SEND_SOUND(next_in_line, sound('sound/announcer/notice/notice1.ogg'))
next_in_line.ui_interact(next_in_line)
return
queued_players -= next_in_line //Client disconnected, remove he
@@ -514,13 +513,6 @@ SUBSYSTEM_DEF(ticker)
queued_players -= next_in_line
queue_delay = 0
-/datum/controller/subsystem/ticker/proc/check_maprotate()
- if(!CONFIG_GET(flag/maprotation))
- return
- if(world.time - SSticker.round_start_time < 10 MINUTES) //Not forcing map rotation for very short rounds.
- return
- INVOKE_ASYNC(SSmapping, TYPE_PROC_REF(/datum/controller/subsystem/mapping/, maprotate))
-
/datum/controller/subsystem/ticker/proc/HasRoundStarted()
return current_state >= GAME_STATE_PLAYING
diff --git a/code/controllers/subsystem/time_track.dm b/code/controllers/subsystem/time_track.dm
index b3a4fe7e8698f..4c706fdaf6db3 100644
--- a/code/controllers/subsystem/time_track.dm
+++ b/code/controllers/subsystem/time_track.dm
@@ -42,7 +42,7 @@ SUBSYSTEM_DEF(time_track)
)
/datum/controller/subsystem/time_track/Initialize()
- GLOB.perf_log = "[GLOB.log_directory]/perf-[GLOB.round_id ? GLOB.round_id : "NULL"]-[SSmapping.config?.map_name].csv"
+ GLOB.perf_log = "[GLOB.log_directory]/perf-[GLOB.round_id ? GLOB.round_id : "NULL"]-[SSmapping.current_map.map_name].csv"
world.Profile(PROFILE_RESTART, type = "sendmaps")
//Need to do the sendmaps stuff in its own file, since it works different then everything else
var/list/sendmaps_headers = list()
diff --git a/code/controllers/subsystem/title.dm b/code/controllers/subsystem/title.dm
index dbaa7ddeb48c0..8df9349ff0398 100644
--- a/code/controllers/subsystem/title.dm
+++ b/code/controllers/subsystem/title.dm
@@ -25,7 +25,7 @@ SUBSYSTEM_DEF(title)
for(var/S in provisional_title_screens)
var/list/L = splittext(S,"+")
- if((L.len == 1 && (L[1] != "exclude" && L[1] != "blank.png")) || (L.len > 1 && ((use_rare_screens && LOWER_TEXT(L[1]) == "rare") || (LOWER_TEXT(L[1]) == LOWER_TEXT(SSmapping.config.map_name)))))
+ if((L.len == 1 && (L[1] != "exclude" && L[1] != "blank.png")) || (L.len > 1 && ((use_rare_screens && LOWER_TEXT(L[1]) == "rare") || (LOWER_TEXT(L[1]) == LOWER_TEXT(SSmapping.current_map.map_name)))))
title_screens += S
if(length(title_screens))
diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm
index 7bed63cc36fc8..d0e642bd3aa2d 100644
--- a/code/controllers/subsystem/vote.dm
+++ b/code/controllers/subsystem/vote.dm
@@ -101,24 +101,28 @@ SUBSYSTEM_DEF(vote)
// stringify the winners to prevent potential unimplemented serialization errors.
// Perhaps this can be removed in the future and we assert that vote choices must implement serialization.
- var/final_winner_string = final_winner && "[final_winner]"
+ var/final_winner_string = (final_winner && "[final_winner]") || "NO WINNER"
var/list/winners_string = list()
- for(var/winner in winners)
- winners_string += "[winner]"
+
+ if(length(winners))
+ for(var/winner in winners)
+ winners_string += "[winner]"
+ else
+ winners_string = list("NO WINNER")
var/list/vote_log_data = list(
+ "type" = "[current_vote.type]",
"choices" = vote_choice_data,
"total" = total_votes,
"winners" = winners_string,
"final_winner" = final_winner_string,
)
- var/log_string = replacetext(to_display, "\n", "\\n") // 'keep' the newlines, but dont actually print them as newlines
- log_vote(log_string, vote_log_data)
- to_chat(world, span_infoplain(vote_font("\n[to_display]")))
+ log_vote("vote finalized", vote_log_data)
+ if(to_display)
+ to_chat(world, span_infoplain(vote_font("\n[to_display]")))
// Finally, doing any effects on vote completion
- if (final_winner) // if no one voted, or the vote cannot be won, final_winner will be null
- current_vote.finalize_vote(final_winner)
+ current_vote.finalize_vote(final_winner)
/**
* One selection per person, and the selection with the most votes wins.
diff --git a/code/datums/actions/action.dm b/code/datums/actions/action.dm
index 39e69ba9fa8fd..2f297f480ae66 100644
--- a/code/datums/actions/action.dm
+++ b/code/datums/actions/action.dm
@@ -52,6 +52,8 @@
/// Toggles whether this action is usable or not
var/action_disabled = FALSE
+ /// Can this action be shared with our rider?
+ var/can_be_shared = TRUE
/datum/action/New(Target)
link_to(Target)
@@ -112,7 +114,8 @@
RegisterSignal(owner, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(update_status_on_signal))
if(check_flags & AB_CHECK_PHASED)
RegisterSignals(owner, list(SIGNAL_ADDTRAIT(TRAIT_MAGICALLY_PHASED), SIGNAL_REMOVETRAIT(TRAIT_MAGICALLY_PHASED)), PROC_REF(update_status_on_signal))
-
+ if(check_flags & AB_CHECK_OPEN_TURF)
+ RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(update_status_on_signal))
if(owner_has_control)
RegisterSignal(grant_to, COMSIG_MOB_KEYDOWN, PROC_REF(keydown), override = TRUE)
GiveAction(grant_to)
@@ -139,6 +142,7 @@
UnregisterSignal(owner, list(
COMSIG_LIVING_SET_BODY_POSITION,
COMSIG_MOB_STATCHANGE,
+ COMSIG_MOVABLE_MOVED,
SIGNAL_ADDTRAIT(TRAIT_HANDS_BLOCKED),
SIGNAL_ADDTRAIT(TRAIT_IMMOBILIZED),
SIGNAL_ADDTRAIT(TRAIT_INCAPACITATED),
@@ -198,6 +202,10 @@
if (feedback)
owner.balloon_alert(owner, "incorporeal!")
return FALSE
+ if((check_flags & AB_CHECK_OPEN_TURF) && !isopenturf(owner.loc))
+ if (feedback)
+ owner.balloon_alert(owner, "not enough space!")
+ return FALSE
return TRUE
/// Builds / updates all buttons we have shared or given out
diff --git a/code/datums/actions/mobs/blood_warp.dm b/code/datums/actions/mobs/blood_warp.dm
index 1e48c6e5aa419..d65c941f5df4a 100644
--- a/code/datums/actions/mobs/blood_warp.dm
+++ b/code/datums/actions/mobs/blood_warp.dm
@@ -57,11 +57,11 @@
shuffle_inplace(pools)
found_bloodpool = pick(pools)
if(found_bloodpool)
- owner.visible_message("[owner] sinks into the blood...")
- playsound(owner_turf, 'sound/magic/enter_blood.ogg', 100, TRUE, -1)
+ owner.visible_message(span_danger("[owner] sinks into the blood..."))
+ playsound(owner_turf, 'sound/effects/magic/enter_blood.ogg', 100, TRUE, -1)
owner.forceMove(get_turf(found_bloodpool))
- playsound(get_turf(owner), 'sound/magic/exit_blood.ogg', 100, TRUE, -1)
- owner.visible_message("And springs back out!")
+ playsound(get_turf(owner), 'sound/effects/magic/exit_blood.ogg', 100, TRUE, -1)
+ owner.visible_message(span_danger("And springs back out!"))
SEND_SIGNAL(owner, COMSIG_BLOOD_WARP)
return TRUE
return FALSE
diff --git a/code/datums/actions/mobs/chase_target.dm b/code/datums/actions/mobs/chase_target.dm
index c88285dd636be..c64293a863b3e 100644
--- a/code/datums/actions/mobs/chase_target.dm
+++ b/code/datums/actions/mobs/chase_target.dm
@@ -31,7 +31,7 @@
/// This is the proc that actually does the throwing. Charge only adds a timer for this.
/datum/action/cooldown/mob_cooldown/chase_target/proc/throw_thyself()
- playsound(owner, 'sound/weapons/sonic_jackhammer.ogg', 50, TRUE)
+ playsound(owner, 'sound/items/weapons/sonic_jackhammer.ogg', 50, TRUE)
owner.throw_at(target, 7, 1.1, owner, FALSE, FALSE, CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), owner, 'sound/effects/meteorimpact.ogg', 50 * size, TRUE, 2), INFINITY)
/// Resets the charge buffs.
diff --git a/code/datums/actions/mobs/create_legion_turrets.dm b/code/datums/actions/mobs/create_legion_turrets.dm
index 5fb668ebc36d1..71427893f43da 100644
--- a/code/datums/actions/mobs/create_legion_turrets.dm
+++ b/code/datums/actions/mobs/create_legion_turrets.dm
@@ -18,7 +18,7 @@
/// Creates new legion turrets around the owner between the minimum and maximum
/datum/action/cooldown/mob_cooldown/create_legion_turrets/proc/create(atom/target)
- playsound(owner, 'sound/magic/RATTLEMEBONES.ogg', 100, TRUE)
+ playsound(owner, 'sound/effects/magic/RATTLEMEBONES.ogg', 100, TRUE)
var/list/possible_locations = list()
for(var/turf/checked_turf in oview(owner, 4)) //Only place the turrets on open turfs
if(checked_turf.is_blocked_turf())
@@ -80,7 +80,7 @@
var/angle = get_angle(our_turf, target_turf)
var/datum/point/vector/V = new(our_turf.x, our_turf.y, our_turf.z, 0, 0, angle)
generate_tracer_between_points(V, V.return_vector_after_increments(6), /obj/effect/projectile/tracer/legion/tracer, 0, shot_delay, 0, 0, 0, null)
- playsound(src, 'sound/machines/airlockopen.ogg', 100, TRUE)
+ playsound(src, 'sound/machines/airlock/airlockopen.ogg', 100, TRUE)
addtimer(CALLBACK(src, PROC_REF(fire_beam), angle), shot_delay)
/// Called shot_delay after the turret shot the tracer. Shoots a projectile into the same direction.
@@ -88,13 +88,13 @@
var/obj/projectile/ouchie = new projectile_type(loc)
ouchie.firer = src
ouchie.fire(angle)
- playsound(src, 'sound/effects/bin_close.ogg', 100, TRUE)
+ playsound(src, 'sound/effects/bin/bin_close.ogg', 100, TRUE)
QDEL_IN(src, 0.5 SECONDS)
/// Used for the legion turret.
/obj/projectile/beam/legion
name = "blood pulse"
- hitsound = 'sound/magic/magic_missile.ogg'
+ hitsound = 'sound/effects/magic/magic_missile.ogg'
damage = 19
range = 6
light_color = COLOR_SOFT_RED
diff --git a/code/datums/actions/mobs/dash.dm b/code/datums/actions/mobs/dash.dm
index 81d6f8165d92c..ad87ab93f9a79 100644
--- a/code/datums/actions/mobs/dash.dm
+++ b/code/datums/actions/mobs/dash.dm
@@ -52,11 +52,11 @@
new /obj/effect/temp_visual/small_smoke/halfsecond(step_forward_turf)
var/obj/effect/temp_visual/decoy/fading/halfsecond/D = new (own_turf, owner)
owner.forceMove(step_back_turf)
- playsound(own_turf, 'sound/weapons/punchmiss.ogg', 40, TRUE, -1)
+ playsound(own_turf, 'sound/items/weapons/punchmiss.ogg', 40, TRUE, -1)
owner.alpha = 0
animate(owner, alpha = 255, time = 5)
SLEEP_CHECK_DEATH(0.2 SECONDS, owner)
D.forceMove(step_forward_turf)
owner.forceMove(target_turf)
- playsound(target_turf, 'sound/weapons/punchmiss.ogg', 40, TRUE, -1)
+ playsound(target_turf, 'sound/items/weapons/punchmiss.ogg', 40, TRUE, -1)
SLEEP_CHECK_DEATH(0.1 SECONDS, owner)
diff --git a/code/datums/actions/mobs/fire_breath.dm b/code/datums/actions/mobs/fire_breath.dm
index e52fa14d0d905..11ad04fa0df20 100644
--- a/code/datums/actions/mobs/fire_breath.dm
+++ b/code/datums/actions/mobs/fire_breath.dm
@@ -7,7 +7,7 @@
/// The range of the fire
var/fire_range = 15
/// The sound played when you use this ability
- var/fire_sound = 'sound/magic/fireball.ogg'
+ var/fire_sound = 'sound/effects/magic/fireball.ogg'
/// Time to wait between spawning each fire turf
var/fire_delay = 1.5 DECISECONDS
/// How hot is our fire
diff --git a/code/datums/actions/mobs/lava_swoop.dm b/code/datums/actions/mobs/lava_swoop.dm
index aa512b2d28e8d..2b07734b4a852 100644
--- a/code/datums/actions/mobs/lava_swoop.dm
+++ b/code/datums/actions/mobs/lava_swoop.dm
@@ -39,7 +39,7 @@
return
// stop swooped target movement
swooping = TRUE
- ADD_TRAIT(owner, TRAIT_UNDENSE, SWOOPING_TRAIT)
+ owner.add_traits(list(TRAIT_GODMODE, TRAIT_UNDENSE), SWOOPING_TRAIT)
owner.visible_message(span_boldwarning("[owner] swoops up high!"))
var/negative
@@ -66,7 +66,6 @@
animate(owner, alpha = 255, transform = oldtransform, time = 0, flags = ANIMATION_END_NOW) //reset immediately
return
animate(owner, alpha = 100, transform = matrix()*0.7, time = 7)
- owner.status_flags |= GODMODE
SEND_SIGNAL(owner, COMSIG_SWOOP_INVULNERABILITY_STARTED)
owner.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
@@ -112,12 +111,11 @@
for(var/mob/observer in range(7, owner))
shake_camera(observer, 15, 1)
- REMOVE_TRAIT(owner, TRAIT_UNDENSE, SWOOPING_TRAIT)
+ owner.remove_traits(list(TRAIT_GODMODE, TRAIT_UNDENSE), SWOOPING_TRAIT)
SLEEP_CHECK_DEATH(1, owner)
swooping = FALSE
if(!lava_success)
SEND_SIGNAL(owner, COMSIG_LAVA_ARENA_FAILED)
- owner.status_flags &= ~GODMODE
/datum/action/cooldown/mob_cooldown/lava_swoop/proc/lava_pools(atom/target, amount = 30, delay = 0.8)
if(!target)
diff --git a/code/datums/actions/mobs/personality_commune.dm b/code/datums/actions/mobs/personality_commune.dm
index 26cf483449204..8481d451fb1dd 100644
--- a/code/datums/actions/mobs/personality_commune.dm
+++ b/code/datums/actions/mobs/personality_commune.dm
@@ -31,7 +31,7 @@
var/mob/living/split_personality/non_controller = usr
var/client/non_controller_client = non_controller.client
- var/to_send = tgui_input_text(non_controller, "What would you like to tell your other self?", "Commune")
+ var/to_send = tgui_input_text(non_controller, "What would you like to tell your other self?", "Commune", max_length = MAX_MESSAGE_LEN)
if(QDELETED(src) || QDELETED(trauma) || !to_send)
return FALSE
diff --git a/code/datums/actions/mobs/projectileattack.dm b/code/datums/actions/mobs/projectileattack.dm
index d8f8e6bdf6427..933f94d0025f3 100644
--- a/code/datums/actions/mobs/projectileattack.dm
+++ b/code/datums/actions/mobs/projectileattack.dm
@@ -126,7 +126,7 @@
desc = "Fires projectiles in a spiral pattern."
cooldown_time = 3 SECONDS
projectile_type = /obj/projectile/colossus
- projectile_sound = 'sound/magic/clockwork/invoke_general.ogg'
+ projectile_sound = 'sound/effects/magic/clockwork/invoke_general.ogg'
/// Whether or not the attack is the enraged form
var/enraged = FALSE
@@ -186,7 +186,7 @@
desc = "Fires projectiles in all directions."
cooldown_time = 3 SECONDS
projectile_type = /obj/projectile/colossus
- projectile_sound = 'sound/magic/clockwork/invoke_general.ogg'
+ projectile_sound = 'sound/effects/magic/clockwork/invoke_general.ogg'
/datum/action/cooldown/mob_cooldown/projectile_attack/random_aoe/attack_sequence(mob/living/firer, atom/target)
var/turf/U = get_turf(firer)
@@ -208,7 +208,7 @@
desc = "Fires projectiles in a shotgun pattern."
cooldown_time = 2 SECONDS
projectile_type = /obj/projectile/colossus
- projectile_sound = 'sound/magic/clockwork/invoke_general.ogg'
+ projectile_sound = 'sound/effects/magic/clockwork/invoke_general.ogg'
var/list/shot_angles = list(12.5, 7.5, 2.5, -2.5, -7.5, -12.5)
/datum/action/cooldown/mob_cooldown/projectile_attack/shotgun_blast/attack_sequence(mob/living/firer, atom/target)
@@ -263,7 +263,7 @@
desc = "Fires projectiles in specific directions."
cooldown_time = 4 SECONDS
projectile_type = /obj/projectile/colossus
- projectile_sound = 'sound/magic/clockwork/invoke_general.ogg'
+ projectile_sound = 'sound/effects/magic/clockwork/invoke_general.ogg'
var/list/firing_directions
/datum/action/cooldown/mob_cooldown/projectile_attack/dir_shots/New(Target)
@@ -308,7 +308,7 @@
desc = "Fires a kinetic accelerator projectile at the target."
cooldown_time = 1.5 SECONDS
projectile_type = /obj/projectile/kinetic/miner
- projectile_sound = 'sound/weapons/kinetic_accel.ogg'
+ projectile_sound = 'sound/items/weapons/kinetic_accel.ogg'
/datum/action/cooldown/mob_cooldown/projectile_attack/kinetic_accelerator/Activate(atom/target_atom)
. = ..()
diff --git a/code/datums/ai/_ai_behavior.dm b/code/datums/ai/_ai_behavior.dm
index eb8f7370dc298..4a277c0e86119 100644
--- a/code/datums/ai/_ai_behavior.dm
+++ b/code/datums/ai/_ai_behavior.dm
@@ -25,7 +25,7 @@
///Called when the action is finished. This needs the same args as perform besides the default ones
/datum/ai_behavior/proc/finish_action(datum/ai_controller/controller, succeeded, ...)
- LAZYREMOVE(controller.current_behaviors, src)
+ controller.dequeue_behavior(src)
controller.behavior_args -= type
if(!(behavior_flags & AI_BEHAVIOR_REQUIRE_MOVEMENT)) //If this was a movement task, reset our movement target if necessary
return
diff --git a/code/datums/ai/_ai_controller.dm b/code/datums/ai/_ai_controller.dm
index a2fc1cdc62e18..239f02b05b8b2 100644
--- a/code/datums/ai/_ai_controller.dm
+++ b/code/datums/ai/_ai_controller.dm
@@ -20,9 +20,9 @@ multiple modular subtrees with behaviors
///Bitfield of traits for this AI to handle extra behavior
var/ai_traits = NONE
///Current actions planned to be performed by the AI in the upcoming plan
- var/list/planned_behaviors
+ var/list/planned_behaviors = list()
///Current actions being performed by the AI.
- var/list/current_behaviors
+ var/list/current_behaviors = list()
///Current actions and their respective last time ran as an assoc list.
var/list/behavior_cooldowns = list()
///Current status of AI (OFF/ON)
@@ -39,8 +39,6 @@ multiple modular subtrees with behaviors
var/continue_processing_when_client = FALSE
///distance to give up on target
var/max_target_distance = 14
- ///Cooldown for new plans, to prevent AI from going nuts if it can't think of new plans and looping on end
- COOLDOWN_DECLARE(failed_planning_cooldown)
///All subtrees this AI has available, will run them in order, so make sure they're in the order you want them to run. On initialization of this type, it will start as a typepath(s) and get converted to references of ai_subtrees found in SSai_controllers when init_subtrees() is called
var/list/planning_subtrees
@@ -62,19 +60,22 @@ multiple modular subtrees with behaviors
var/can_idle = TRUE
///What distance should we be checking for interesting things when considering idling/deidling? Defaults to AI_DEFAULT_INTERESTING_DIST
var/interesting_dist = AI_DEFAULT_INTERESTING_DIST
-
/// TRUE if we're able to run, FALSE if we aren't
/// Should not be set manually, override get_able_to_run() instead
/// Make sure you hook update_able_to_run() in setup_able_to_run() to whatever parameters changing that you added
/// Otherwise we will not pay attention to them changing
var/able_to_run = FALSE
+ /// are we even able to plan?
+ var/able_to_plan = TRUE
+ /// are we currently on failed planning timeout?
+ var/on_failed_planning_timeout = FALSE
/datum/ai_controller/New(atom/new_pawn)
change_ai_movement_type(ai_movement)
init_subtrees()
if(idle_behavior)
- idle_behavior = new idle_behavior()
+ idle_behavior = SSidle_ai_behaviors.idle_behaviors[idle_behavior]
if(!isnull(new_pawn)) // unit tests need the ai_controller to exist in isolation due to list schenanigans i hate it here
PossessPawn(new_pawn)
@@ -238,7 +239,7 @@ multiple modular subtrees with behaviors
if(!pawn_turf)
CRASH("AI controller [src] controlling pawn ([pawn]) is not on a turf.")
#endif
- if(!length(SSmobs.clients_by_zlevel[pawn_turf.z]))
+ if(!length(SSmobs.clients_by_zlevel[pawn_turf.z]) || on_failed_planning_timeout || !able_to_run)
return AI_STATUS_OFF
if(should_idle())
return AI_STATUS_IDLE
@@ -298,6 +299,9 @@ multiple modular subtrees with behaviors
/datum/ai_controller/proc/update_able_to_run()
SIGNAL_HANDLER
able_to_run = get_able_to_run()
+ if(!able_to_run)
+ GLOB.move_manager.stop_looping(pawn) //stop moving
+ set_ai_status(get_expected_ai_status())
///Returns TRUE if the ai controller can actually run at the moment, FALSE otherwise
/datum/ai_controller/proc/get_able_to_run()
@@ -307,14 +311,36 @@ multiple modular subtrees with behaviors
return FALSE
return TRUE
+///Can this pawn interact with objects?
+/datum/ai_controller/proc/ai_can_interact()
+ SHOULD_CALL_PARENT(TRUE)
+ return !QDELETED(pawn)
+
+///Interact with objects
+/datum/ai_controller/proc/ai_interact(target, combat_mode, list/modifiers)
+ if(!ai_can_interact())
+ return FALSE
+
+ var/atom/final_target = isdatum(target) ? target : blackboard[target] //incase we got a blackboard key instead
+
+ if(QDELETED(final_target))
+ return FALSE
+ var/params = list2params(modifiers)
+ var/mob/living/living_pawn = pawn
+ if(isnull(combat_mode))
+ living_pawn.ClickOn(final_target, params)
+ return TRUE
+
+ var/old_combat_mode = living_pawn.combat_mode
+ living_pawn.set_combat_mode(combat_mode)
+ living_pawn.ClickOn(final_target, params)
+ living_pawn.set_combat_mode(old_combat_mode)
+ return TRUE
+
///Runs any actions that are currently running
/datum/ai_controller/process(seconds_per_tick)
- if(!able_to_run)
- GLOB.move_manager.stop_looping(pawn) //stop moving
- return //this should remove them from processing in the future through event-based stuff.
-
- if(!LAZYLEN(current_behaviors) && idle_behavior)
+ if(!length(current_behaviors) && idle_behavior)
idle_behavior.perform_idle_behavior(seconds_per_tick, src) //Do some stupid shit while we have nothing to do
return
@@ -336,59 +362,44 @@ multiple modular subtrees with behaviors
// Action cooldowns cannot happen faster than seconds_per_tick, so seconds_per_tick should be the value used in this scenario.
var/action_seconds_per_tick = max(current_behavior.get_cooldown(src) * 0.1, seconds_per_tick)
- if(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_MOVEMENT) //Might need to move closer
- if(!current_movement_target)
- stack_trace("[pawn] wants to perform action type [current_behavior.type] which requires movement, but has no current movement target!")
- return //This can cause issues, so don't let these slide.
- ///Stops pawns from performing such actions that should require the target to be adjacent.
- var/atom/movable/moving_pawn = pawn
- var/can_reach = !(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_REACH) || moving_pawn.CanReach(current_movement_target)
- if(can_reach && current_behavior.required_distance >= get_dist(moving_pawn, current_movement_target)) ///Are we close enough to engage?
- if(ai_movement.moving_controllers[src] == current_movement_target) //We are close enough, if we're moving stop.
- ai_movement.stop_moving_towards(src)
-
- if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown
- continue
- ProcessBehavior(action_seconds_per_tick, current_behavior)
- return
-
- else if(ai_movement.moving_controllers[src] != current_movement_target) //We're too far, if we're not already moving start doing it.
- ai_movement.start_moving_towards(src, current_movement_target, current_behavior.required_distance) //Then start moving
-
- if(current_behavior.behavior_flags & AI_BEHAVIOR_MOVE_AND_PERFORM) //If we can move and perform then do so.
- if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown
- continue
- ProcessBehavior(action_seconds_per_tick, current_behavior)
- return
- else //No movement required
+ if(!(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_MOVEMENT))
if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown
continue
ProcessBehavior(action_seconds_per_tick, current_behavior)
return
-///Determines whether the AI can currently make a new plan
-/datum/ai_controller/proc/able_to_plan()
- . = TRUE
- if(QDELETED(pawn))
- return FALSE
- for(var/datum/ai_behavior/current_behavior as anything in current_behaviors)
- if(!(current_behavior.behavior_flags & AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION)) //We have a behavior that blocks planning
- . = FALSE
- break
+ if(!current_movement_target)
+ stack_trace("[pawn] wants to perform action type [current_behavior.type] which requires movement, but has no current movement target!")
+ return //This can cause issues, so don't let these slide.
+ ///Stops pawns from performing such actions that should require the target to be adjacent.
+ var/atom/movable/moving_pawn = pawn
+ var/can_reach = !(current_behavior.behavior_flags & AI_BEHAVIOR_REQUIRE_REACH) || moving_pawn.CanReach(current_movement_target)
+ if(can_reach && current_behavior.required_distance >= get_dist(moving_pawn, current_movement_target)) ///Are we close enough to engage?
+ if(ai_movement.moving_controllers[src] == current_movement_target) //We are close enough, if we're moving stop.
+ ai_movement.stop_moving_towards(src)
+
+ if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown
+ continue
+ ProcessBehavior(action_seconds_per_tick, current_behavior)
+ return
+
+ if(ai_movement.moving_controllers[src] != current_movement_target) //We're too far, if we're not already moving start doing it.
+ ai_movement.start_moving_towards(src, current_movement_target, current_behavior.required_distance) //Then start moving
+
+ if(current_behavior.behavior_flags & AI_BEHAVIOR_MOVE_AND_PERFORM) //If we can move and perform then do so.
+ if(behavior_cooldowns[current_behavior] > world.time) //Still on cooldown
+ continue
+ ProcessBehavior(action_seconds_per_tick, current_behavior)
+ return
///This is where you decide what actions are taken by the AI.
/datum/ai_controller/proc/SelectBehaviors(seconds_per_tick)
SHOULD_NOT_SLEEP(TRUE) //Fuck you don't sleep in procs like this.
- if(!COOLDOWN_FINISHED(src, failed_planning_cooldown))
- return FALSE
-
- LAZYINITLIST(current_behaviors)
- LAZYCLEARLIST(planned_behaviors)
+ planned_behaviors.Cut()
- if(LAZYLEN(planning_subtrees))
- for(var/datum/ai_planning_subtree/subtree as anything in planning_subtrees)
- if(subtree.SelectBehaviors(src, seconds_per_tick) == SUBTREE_RETURN_FINISH_PLANNING)
- break
+ for(var/datum/ai_planning_subtree/subtree as anything in planning_subtrees)
+ if(subtree.SelectBehaviors(src, seconds_per_tick) == SUBTREE_RETURN_FINISH_PLANNING)
+ break
SEND_SIGNAL(src, COMSIG_AI_CONTROLLER_PICKED_BEHAVIORS, current_behaviors, planned_behaviors)
for(var/datum/ai_behavior/forgotten_behavior as anything in current_behaviors - planned_behaviors)
@@ -441,21 +452,37 @@ multiple modular subtrees with behaviors
var/list/arguments = args.Copy()
arguments[1] = src
- if(LAZYACCESS(current_behaviors, behavior)) ///It's still in the plan, don't add it again to current_behaviors but do keep it in the planned behavior list so its not cancelled
- LAZYADDASSOC(planned_behaviors, behavior, TRUE)
+ if(current_behaviors[behavior]) ///It's still in the plan, don't add it again to current_behaviors but do keep it in the planned behavior list so its not cancelled
+ planned_behaviors[behavior] = TRUE
return
if(!behavior.setup(arglist(arguments)))
return
- LAZYADDASSOC(current_behaviors, behavior, TRUE)
- LAZYADDASSOC(planned_behaviors, behavior, TRUE)
+
+ planned_behaviors[behavior] = TRUE
+ current_behaviors[behavior] = TRUE
+
arguments.Cut(1, 2)
if(length(arguments))
behavior_args[behavior_type] = arguments
else
behavior_args -= behavior_type
+
+ if(!(behavior.behavior_flags & AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION)) //this one blocks planning!
+ able_to_plan = FALSE
+
SEND_SIGNAL(src, AI_CONTROLLER_BEHAVIOR_QUEUED(behavior_type), arguments)
+/datum/ai_controller/proc/check_able_to_plan()
+ for(var/datum/ai_behavior/current_behavior as anything in current_behaviors)
+ if(!(current_behavior.behavior_flags & AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION)) //We have a behavior that blocks planning
+ return FALSE
+ return TRUE
+
+/datum/ai_controller/proc/dequeue_behavior(datum/ai_behavior/behavior)
+ current_behaviors -= behavior
+ able_to_plan = check_able_to_plan()
+
/datum/ai_controller/proc/ProcessBehavior(seconds_per_tick, datum/ai_behavior/behavior)
var/list/arguments = list(seconds_per_tick, src)
var/list/stored_arguments = behavior_args[behavior.type]
@@ -475,7 +502,7 @@ multiple modular subtrees with behaviors
behavior.finish_action(arglist(arguments))
/datum/ai_controller/proc/CancelActions()
- if(!LAZYLEN(current_behaviors))
+ if(!length(current_behaviors))
return
for(var/datum/ai_behavior/current_behavior as anything in current_behaviors)
var/list/arguments = list(src, FALSE)
@@ -488,6 +515,7 @@ multiple modular subtrees with behaviors
/datum/ai_controller/proc/on_stat_changed(mob/living/source, new_stat)
SIGNAL_HANDLER
reset_ai_status()
+ update_able_to_run()
/datum/ai_controller/proc/on_sentience_gained()
SIGNAL_HANDLER
@@ -528,6 +556,15 @@ multiple modular subtrees with behaviors
minimum_distance = iter_behavior.required_distance
return minimum_distance
+/datum/ai_controller/proc/planning_failed()
+ on_failed_planning_timeout = TRUE
+ set_ai_status(get_expected_ai_status())
+ addtimer(CALLBACK(src, PROC_REF(resume_planning)), AI_FAILED_PLANNING_COOLDOWN)
+
+/datum/ai_controller/proc/resume_planning()
+ on_failed_planning_timeout = FALSE
+ set_ai_status(get_expected_ai_status())
+
/// Returns true if we have a blackboard key with the provided key and it is not qdeleting
/datum/ai_controller/proc/blackboard_key_exists(key)
var/datum/key_value = blackboard[key]
diff --git a/code/datums/ai/babies/babies_behaviors.dm b/code/datums/ai/babies/babies_behaviors.dm
index ad57d309a2c72..aa8a15a03e40b 100644
--- a/code/datums/ai/babies/babies_behaviors.dm
+++ b/code/datums/ai/babies/babies_behaviors.dm
@@ -58,17 +58,9 @@
var/mob/target = controller.blackboard[target_key]
if(QDELETED(target) || target.stat != CONSCIOUS)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
- var/mob/living/basic/living_pawn = controller.pawn
- living_pawn.set_combat_mode(FALSE)
- living_pawn.melee_attack(target)
+ controller.ai_interact(target = target, combat_mode = FALSE)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
/datum/ai_behavior/make_babies/finish_action(datum/ai_controller/controller, succeeded, target_key)
. = ..()
controller.clear_blackboard_key(target_key)
- if(!succeeded)
- return
- var/mob/living/living_pawn = controller.pawn
- if(QDELETED(living_pawn)) // pawn can be null at this point
- return
- living_pawn.set_combat_mode(initial(living_pawn.combat_mode))
diff --git a/code/datums/ai/basic_mobs/base_basic_controller.dm b/code/datums/ai/basic_mobs/base_basic_controller.dm
index a14630fa0e83a..f21d31b05000c 100644
--- a/code/datums/ai/basic_mobs/base_basic_controller.dm
+++ b/code/datums/ai/basic_mobs/base_basic_controller.dm
@@ -19,9 +19,12 @@
/datum/ai_controller/basic_controller/setup_able_to_run()
. = ..()
RegisterSignal(pawn, COMSIG_MOB_INCAPACITATE_CHANGED, PROC_REF(update_able_to_run))
+ if(ai_traits & PAUSE_DURING_DO_AFTER)
+ RegisterSignals(pawn, list(COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED), PROC_REF(update_able_to_run))
+
/datum/ai_controller/basic_controller/clear_able_to_run()
- UnregisterSignal(pawn, list(COMSIG_MOB_INCAPACITATE_CHANGED, COMSIG_MOB_STATCHANGE))
+ UnregisterSignal(pawn, list(COMSIG_MOB_INCAPACITATE_CHANGED, COMSIG_MOB_STATCHANGE, COMSIG_DO_AFTER_BEGAN, COMSIG_DO_AFTER_ENDED))
return ..()
/datum/ai_controller/basic_controller/get_able_to_run()
diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm
index 883c157a96ba9..aba62f2dc7b79 100644
--- a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm
+++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm
@@ -8,8 +8,6 @@
. = ..()
if(!controller.blackboard[targeting_strategy_key])
CRASH("No targeting strategy was supplied in the blackboard for [controller.pawn]")
- if(HAS_TRAIT(controller.pawn, TRAIT_HANDS_BLOCKED))
- return FALSE
//Hiding location is priority
var/atom/target = controller.blackboard[hiding_location_key] || controller.blackboard[target_key]
if(QDELETED(target))
@@ -35,11 +33,8 @@
controller.set_blackboard_key(hiding_location_key, hiding_target)
- if(hiding_target) //Slap it!
- basic_mob.melee_attack(hiding_target)
- else
- basic_mob.melee_attack(target)
-
+ var/atom/final_target = hiding_target || target
+ controller.ai_interact(target = final_target, combat_mode = TRUE)
if(terminate_after_action)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
return AI_BEHAVIOR_DELAY
diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/emote_with_target.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/emote_with_target.dm
new file mode 100644
index 0000000000000..7960301d70440
--- /dev/null
+++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/emote_with_target.dm
@@ -0,0 +1,28 @@
+/datum/ai_behavior/emote_on_target
+ behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH
+
+
+/datum/ai_behavior/emote_on_target/setup(datum/ai_controller/controller, target_key)
+ . = ..()
+ var/atom/hunt_target = controller.blackboard[target_key]
+ if (isnull(hunt_target))
+ return FALSE
+ set_movement_target(controller, hunt_target)
+
+
+/datum/ai_behavior/emote_on_target/perform(seconds_per_tick, datum/ai_controller/controller, target_key, list/emote_list)
+ var/atom/target = controller.blackboard[target_key]
+ if(!length(emote_list) || isnull(target))
+ return AI_BEHAVIOR_FAILED | AI_BEHAVIOR_DELAY
+ run_emote(controller.pawn, target, emote_list)
+ return AI_BEHAVIOR_SUCCEEDED | AI_BEHAVIOR_DELAY
+
+
+/datum/ai_behavior/emote_on_target/finish_action(datum/ai_controller/controller, succeeded, target_key)
+ . = ..()
+ if(succeeded)
+ controller.clear_blackboard_key(target_key)
+
+
+/datum/ai_behavior/emote_on_target/proc/run_emote(mob/living/living_pawn, atom/target, list/emote_list)
+ living_pawn.manual_emote("[pick(emote_list)] [target]")
diff --git a/code/datums/ai/basic_mobs/basic_subtrees/mine_walls.dm b/code/datums/ai/basic_mobs/basic_subtrees/mine_walls.dm
index dc3f6ddcf9015..12875f9a3f345 100644
--- a/code/datums/ai/basic_mobs/basic_subtrees/mine_walls.dm
+++ b/code/datums/ai/basic_mobs/basic_subtrees/mine_walls.dm
@@ -24,9 +24,8 @@
var/mob/living/basic/living_pawn = controller.pawn
var/turf/closed/mineral/target = controller.blackboard[target_key]
var/is_gibtonite_turf = istype(target, /turf/closed/mineral/gibtonite)
- if(QDELETED(target))
+ if(!controller.ai_interact(target = target))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
- living_pawn.melee_attack(target)
if(is_gibtonite_turf)
living_pawn.manual_emote("sighs...") //accept whats about to happen to us
diff --git a/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm b/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm
index 3640a2052b55e..43a3d400bc58d 100644
--- a/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm
+++ b/code/datums/ai/basic_mobs/basic_subtrees/ranged_skirmish.dm
@@ -43,6 +43,5 @@
if (distance > max_range || distance < min_range)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
- var/mob/living/basic/gunman = controller.pawn
- gunman.RangedAttack(target)
+ controller.ai_interact(target = target, combat_mode = TRUE)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
diff --git a/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm b/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm
index 5bd0f8404883d..7d877731e2b05 100644
--- a/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm
+++ b/code/datums/ai/basic_mobs/basic_subtrees/speech_subtree.dm
@@ -46,7 +46,7 @@
/datum/ai_planning_subtree/random_speech/insect
speech_chance = 5
- sound = list('sound/creatures/chitter.ogg')
+ sound = list('sound/mobs/non-humanoids/insect/chitter.ogg')
emote_hear = list("chitters.")
/datum/ai_planning_subtree/random_speech/mothroach
@@ -56,7 +56,7 @@
/datum/ai_planning_subtree/random_speech/mouse
speech_chance = 1
speak = list("Squeak!", "SQUEAK!", "Squeak?")
- sound = list('sound/creatures/mousesqueek.ogg')
+ sound = list('sound/mobs/non-humanoids/mouse/mousesqueek.ogg')
emote_hear = list("squeaks.")
emote_see = list("runs in a circle.", "shakes.")
@@ -72,7 +72,7 @@
/datum/ai_planning_subtree/random_speech/sheep
speech_chance = 5
speak = list("baaa","baaaAAAAAH!","baaah")
- sound = list('sound/creatures/sheep1.ogg', 'sound/creatures/sheep2.ogg', 'sound/creatures/sheep3.ogg')
+ sound = list('sound/mobs/non-humanoids/sheep/sheep1.ogg', 'sound/mobs/non-humanoids/sheep/sheep2.ogg', 'sound/mobs/non-humanoids/sheep/sheep3.ogg')
emote_hear = list("bleats.")
emote_see = list("shakes her head.", "stares into the distance.")
@@ -101,21 +101,21 @@
/datum/ai_planning_subtree/random_speech/chicken
speech_chance = 15 // really talkative ladies
speak = list("Cluck!", "BWAAAAARK BWAK BWAK BWAK!", "Bwaak bwak.")
- sound = list('sound/creatures/clucks.ogg', 'sound/creatures/bagawk.ogg')
+ sound = list('sound/mobs/non-humanoids/chicken/clucks.ogg', 'sound/mobs/non-humanoids/chicken/bagawk.ogg')
emote_hear = list("clucks.", "croons.")
emote_see = list("pecks at the ground.","flaps her wings viciously.")
/datum/ai_planning_subtree/random_speech/chick
speech_chance = 4
speak = list("Cherp.", "Cherp?", "Chirrup.", "Cheep!")
- sound = list('sound/creatures/chick_peep.ogg')
+ sound = list('sound/mobs/non-humanoids/chicken/chick_peep.ogg')
emote_hear = list("cheeps.")
emote_see = list("pecks at the ground.","flaps her tiny wings.")
/datum/ai_planning_subtree/random_speech/cow
speech_chance = 1
speak = list("moo?","moo","MOOOOOO")
- sound = list('sound/creatures/cow.ogg')
+ sound = list('sound/mobs/non-humanoids/cow/cow.ogg')
emote_hear = list("brays.")
emote_see = list("shakes her head.")
@@ -164,19 +164,19 @@
/datum/ai_planning_subtree/random_speech/pig
speech_chance = 3
speak = list("oink?","oink","snurf")
- sound = list('sound/creatures/pig1.ogg', 'sound/creatures/pig2.ogg')
+ sound = list('sound/mobs/non-humanoids/pig/pig1.ogg', 'sound/mobs/non-humanoids/pig/pig2.ogg')
emote_hear = list("snorts.")
emote_see = list("sniffs around.")
/datum/ai_planning_subtree/random_speech/pony
speech_chance = 3
- sound = list('sound/creatures/pony/whinny01.ogg', 'sound/creatures/pony/whinny02.ogg', 'sound/creatures/pony/whinny03.ogg')
+ sound = list('sound/mobs/non-humanoids/pony/whinny01.ogg', 'sound/mobs/non-humanoids/pony/whinny02.ogg', 'sound/mobs/non-humanoids/pony/whinny03.ogg')
emote_hear = list("whinnies!")
emote_see = list("horses around.")
/datum/ai_planning_subtree/random_speech/pony/tamed
speech_chance = 3
- sound = list('sound/creatures/pony/snort.ogg')
+ sound = list('sound/mobs/non-humanoids/pony/snort.ogg')
emote_hear = list("snorts.")
emote_see = list("snorts.")
@@ -188,7 +188,7 @@
/datum/ai_planning_subtree/random_speech/ant
speech_chance = 1
speak = list("BZZZZT!", "CHTCHTCHT!", "Bzzz", "ChtChtCht")
- sound = list('sound/creatures/chitter.ogg')
+ sound = list('sound/mobs/non-humanoids/insect/chitter.ogg')
emote_hear = list("buzzes.", "clacks.")
emote_see = list("shakes their head.", "twitches their antennae.")
@@ -200,7 +200,7 @@
/datum/ai_planning_subtree/random_speech/crab
speech_chance = 1
- sound = list('sound/creatures/claw_click.ogg')
+ sound = list('sound/mobs/non-humanoids/crab/claw_click.ogg')
emote_hear = list("clicks.")
emote_see = list("clacks.")
@@ -216,11 +216,9 @@
/datum/ai_planning_subtree/random_speech/cats
speech_chance = 10
- speak = list(
- "mrawww!",
- "meow!",
- "maw!",
- )
+ sound = list(SFX_CAT_MEOW)
+ emote_hear = list("meows.")
+ emote_see = list("meows.")
/datum/ai_planning_subtree/random_speech/blackboard //literal tower of babel, subtree form
speech_chance = 1
diff --git a/code/datums/ai/basic_mobs/pet_commands/fetch.dm b/code/datums/ai/basic_mobs/pet_commands/fetch.dm
index 87606fa0c6555..5ff208560d2a1 100644
--- a/code/datums/ai/basic_mobs/pet_commands/fetch.dm
+++ b/code/datums/ai/basic_mobs/pet_commands/fetch.dm
@@ -109,7 +109,7 @@
if(!basic_pawn.Adjacent(snack))
return AI_BEHAVIOR_DELAY
- basic_pawn.melee_attack(snack) // snack attack!
+ controller.ai_interact(target = snack)
if(QDELETED(snack)) // we ate it!
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
diff --git a/code/datums/ai/basic_mobs/targeting_strategies/basic_targeting_strategy.dm b/code/datums/ai/basic_mobs/targeting_strategies/basic_targeting_strategy.dm
index 709acb8d5e892..d552b69c142dc 100644
--- a/code/datums/ai/basic_mobs/targeting_strategies/basic_targeting_strategy.dm
+++ b/code/datums/ai/basic_mobs/targeting_strategies/basic_targeting_strategy.dm
@@ -25,8 +25,7 @@
if(ismob(the_target)) //Target is in godmode, ignore it.
if(living_mob.loc == the_target)
return FALSE // We've either been eaten or are shapeshifted, let's assume the latter because we're still alive
- var/mob/M = the_target
- if(M.status_flags & GODMODE)
+ if(HAS_TRAIT(the_target, TRAIT_GODMODE))
return FALSE
if (vision_range && get_dist(living_mob, the_target) > vision_range)
diff --git a/code/datums/ai/cursed/cursed_controller.dm b/code/datums/ai/cursed/cursed_controller.dm
index 4d0f6c6f5fdc6..aa32496f35724 100644
--- a/code/datums/ai/cursed/cursed_controller.dm
+++ b/code/datums/ai/cursed/cursed_controller.dm
@@ -27,9 +27,9 @@
return ..() //Run parent at end
///signal called by the pawn hitting something after a throw
-/datum/ai_controller/cursed/proc/on_throw_hit(datum/source, atom/hit_atom, datum/thrownthing/throwingdatum)
+/datum/ai_controller/cursed/proc/on_throw_hit(datum/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
SIGNAL_HANDLER
- if(!iscarbon(hit_atom))
+ if(caught || !iscarbon(hit_atom))
return
//equipcode has sleeps all over it.
INVOKE_ASYNC(src, PROC_REF(try_equipping_to_target_slot), hit_atom)
diff --git a/code/datums/ai/dog/dog_behaviors.dm b/code/datums/ai/dog/dog_behaviors.dm
index 00a2f789e12b5..958b1f3d03de1 100644
--- a/code/datums/ai/dog/dog_behaviors.dm
+++ b/code/datums/ai/dog/dog_behaviors.dm
@@ -44,7 +44,7 @@
if(!SPT_PROB(20, seconds_per_tick))
return
living_pawn.do_attack_animation(target, ATTACK_EFFECT_DISARM)
- playsound(target, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ playsound(target, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE, -1)
target.visible_message(span_danger("[living_pawn] paws ineffectually at [target]!"), span_danger("[living_pawn] paws ineffectually at you!"))
/// Let them know we mean business
@@ -54,4 +54,4 @@
living_pawn.manual_emote("[pick("barks", "growls", "stares")] menacingly at [target]!")
if(!SPT_PROB(40, seconds_per_tick))
return
- playsound(living_pawn, pick('sound/creatures/dog/growl1.ogg', 'sound/creatures/dog/growl2.ogg'), 50, TRUE, -1)
+ playsound(living_pawn, pick('sound/mobs/non-humanoids/dog/growl1.ogg', 'sound/mobs/non-humanoids/dog/growl2.ogg'), 50, TRUE, -1)
diff --git a/code/datums/ai/generic/find_and_set.dm b/code/datums/ai/generic/find_and_set.dm
index 41f256c9ba73f..5a424f304f28f 100644
--- a/code/datums/ai/generic/find_and_set.dm
+++ b/code/datums/ai/generic/find_and_set.dm
@@ -177,3 +177,17 @@
var/mob/living/living_pawn = controller.pawn
var/potential_friend = living_pawn.faction.Find(REF(friend)) ? friend : null
return potential_friend
+
+
+/datum/ai_behavior/find_and_set/in_list/turf_types
+
+
+/datum/ai_behavior/find_and_set/in_list/turf_types/search_tactic(datum/ai_controller/controller, locate_paths, search_range)
+ var/list/found = RANGE_TURFS(search_range, controller.pawn)
+ shuffle_inplace(found)
+ for(var/turf/possible_turf as anything in found)
+ if(!is_type_in_typecache(possible_turf, locate_paths))
+ continue
+ if(can_see(controller.pawn, possible_turf, search_range))
+ return possible_turf
+ return null
diff --git a/code/datums/ai/generic/generic_behaviors.dm b/code/datums/ai/generic/generic_behaviors.dm
index 1c0e1f65adf96..c6fcbcfb57265 100644
--- a/code/datums/ai/generic/generic_behaviors.dm
+++ b/code/datums/ai/generic/generic_behaviors.dm
@@ -101,11 +101,10 @@
if(QDELETED(target))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
- pawn.set_combat_mode(FALSE)
if(held_item)
held_item.melee_attack_chain(pawn, target)
else
- pawn.UnarmedAttack(target, TRUE)
+ controller.ai_interact(target = target, combat_mode = FALSE)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
diff --git a/code/datums/ai/hunting_behavior/hunting_behaviors.dm b/code/datums/ai/hunting_behavior/hunting_behaviors.dm
index ba2da1c2d04e8..c202c4be6a7d8 100644
--- a/code/datums/ai/hunting_behavior/hunting_behaviors.dm
+++ b/code/datums/ai/hunting_behavior/hunting_behaviors.dm
@@ -117,31 +117,23 @@
if(always_reset_target && hunting_target_key)
controller.clear_blackboard_key(hunting_target_key)
-/datum/ai_behavior/hunt_target/unarmed_attack_target
- ///do we toggle combat mode before interacting with the object?
- var/switch_combat_mode = FALSE
+/datum/ai_behavior/hunt_target/interact_with_target
+ ///what combat mode should we use to interact with
+ var/behavior_combat_mode = TRUE
-/datum/ai_behavior/hunt_target/unarmed_attack_target/target_caught(mob/living/hunter, obj/structure/cable/hunted)
- if(switch_combat_mode)
- hunter.combat_mode = !(hunter.combat_mode)
- hunter.UnarmedAttack(hunted, TRUE)
+/datum/ai_behavior/hunt_target/interact_with_target/target_caught(mob/living/hunter, obj/structure/cable/hunted)
+ var/datum/ai_controller/controller = hunter.ai_controller
+ controller.ai_interact(target = hunted, combat_mode = behavior_combat_mode)
-/datum/ai_behavior/hunt_target/unarmed_attack_target/finish_action(datum/ai_controller/controller, succeeded, hunting_target_key, hunting_cooldown_key)
- . = ..()
- if(!switch_combat_mode)
- return
- var/mob/living/living_pawn = controller.pawn
- living_pawn.combat_mode = initial(living_pawn.combat_mode)
-
-/datum/ai_behavior/hunt_target/unarmed_attack_target/switch_combat_mode
- switch_combat_mode = TRUE
+/datum/ai_behavior/hunt_target/interact_with_target/combat_mode_off
+ behavior_combat_mode = FALSE
-/datum/ai_behavior/hunt_target/unarmed_attack_target/reset_target
+/datum/ai_behavior/hunt_target/interact_with_target/reset_target
always_reset_target = TRUE
-/datum/ai_behavior/hunt_target/unarmed_attack_target/reset_target_combat_mode
+/datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off
always_reset_target = TRUE
- switch_combat_mode = TRUE
+ behavior_combat_mode = FALSE
/datum/ai_behavior/hunt_target/use_ability_on_target
always_reset_target = TRUE
diff --git a/code/datums/ai/hunting_behavior/hunting_corpses.dm b/code/datums/ai/hunting_behavior/hunting_corpses.dm
index e720e4da947af..89d100263fb1a 100644
--- a/code/datums/ai/hunting_behavior/hunting_corpses.dm
+++ b/code/datums/ai/hunting_behavior/hunting_corpses.dm
@@ -1,7 +1,7 @@
/// Find and attack corpses
/datum/ai_planning_subtree/find_and_hunt_target/corpses
finding_behavior = /datum/ai_behavior/find_hunt_target/corpses
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target
hunt_targets = list(/mob/living)
/// Find nearby dead mobs
diff --git a/code/datums/ai/hunting_behavior/hunting_lights.dm b/code/datums/ai/hunting_behavior/hunting_lights.dm
index 6b82e87f2693b..5062a8aaf929e 100644
--- a/code/datums/ai/hunting_behavior/hunting_lights.dm
+++ b/code/datums/ai/hunting_behavior/hunting_lights.dm
@@ -1,11 +1,11 @@
/datum/ai_planning_subtree/find_and_hunt_target/look_for_light_fixtures
target_key = BB_LOW_PRIORITY_HUNTING_TARGET
finding_behavior = /datum/ai_behavior/find_hunt_target/light_fixtures
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/light_fixtures
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/light_fixtures
hunt_targets = list(/obj/machinery/light)
hunt_range = 7
-/datum/ai_behavior/hunt_target/unarmed_attack_target/light_fixtures
+/datum/ai_behavior/hunt_target/interact_with_target/light_fixtures
hunt_cooldown = 10 SECONDS
always_reset_target = TRUE
diff --git a/code/datums/ai/hunting_behavior/hunting_mouse.dm b/code/datums/ai/hunting_behavior/hunting_mouse.dm
index d0e7161fd2de6..f97ebf27ddf6f 100644
--- a/code/datums/ai/hunting_behavior/hunting_mouse.dm
+++ b/code/datums/ai/hunting_behavior/hunting_mouse.dm
@@ -1,13 +1,13 @@
// Mouse subtree to hunt down delicious cheese.
/datum/ai_planning_subtree/find_and_hunt_target/look_for_cheese
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/mouse
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/mouse
hunt_targets = list(/obj/item/food/cheese)
hunt_range = 1
// Mouse subtree to hunt down ... delicious cabling?
/datum/ai_planning_subtree/find_and_hunt_target/look_for_cables
target_key = BB_LOW_PRIORITY_HUNTING_TARGET
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/mouse
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/mouse
finding_behavior = /datum/ai_behavior/find_hunt_target/mouse_cable
hunt_targets = list(/obj/structure/cable)
hunt_range = 0 // Only look below us
@@ -28,5 +28,5 @@
return below_the_cable.underfloor_accessibility >= UNDERFLOOR_INTERACTABLE
// Our hunts have a decent cooldown.
-/datum/ai_behavior/hunt_target/unarmed_attack_target/mouse
+/datum/ai_behavior/hunt_target/interact_with_target/mouse
hunt_cooldown = 20 SECONDS
diff --git a/code/datums/ai/monkey/monkey_behaviors.dm b/code/datums/ai/monkey/monkey_behaviors.dm
index e6720d7d96a78..126c08daa1e8b 100644
--- a/code/datums/ai/monkey/monkey_behaviors.dm
+++ b/code/datums/ai/monkey/monkey_behaviors.dm
@@ -186,7 +186,7 @@
if(weapon)
weapon.melee_attack_chain(living_pawn, target)
else
- living_pawn.UnarmedAttack(target, null, disarm ? list("right" = TRUE) : null) //Fake a right click if we're disarmin
+ controller.ai_interact(target = target, modifiers = disarm ? list(RIGHT_CLICK = TRUE) : null)
controller.set_blackboard_key(BB_MONKEY_GUN_WORKED, TRUE) // We reset their memory of the gun being 'broken' if they accomplish some other attack
else if(weapon)
var/atom/real_target = target
diff --git a/code/datums/ai/movement/_ai_movement.dm b/code/datums/ai/movement/_ai_movement.dm
index c1b3aae5bd60f..33b7e4e214f6b 100644
--- a/code/datums/ai/movement/_ai_movement.dm
+++ b/code/datums/ai/movement/_ai_movement.dm
@@ -11,6 +11,7 @@
controller.consecutive_pathing_attempts = 0
controller.set_blackboard_key(BB_CURRENT_MIN_MOVE_DISTANCE, min_distance)
moving_controllers[controller] = current_movement_target
+ SEND_SIGNAL(controller.pawn, COMSIG_MOB_AI_MOVEMENT_STARTED, current_movement_target)
/datum/ai_movement/proc/stop_moving_towards(datum/ai_controller/controller)
controller.consecutive_pathing_attempts = 0
diff --git a/code/datums/ai_laws/ai_laws.dm b/code/datums/ai_laws/ai_laws.dm
index 0dbc6839430ba..a0d1d629fc8d3 100644
--- a/code/datums/ai_laws/ai_laws.dm
+++ b/code/datums/ai_laws/ai_laws.dm
@@ -192,7 +192,7 @@ GLOBAL_VAR(round_default_lawset)
var/datum/ai_laws/default_laws = get_round_default_lawset()
default_laws = new default_laws()
inherent = default_laws.inherent
- var/datum/job/human_ai_job = SSjob.GetJob(JOB_HUMAN_AI)
+ var/datum/job/human_ai_job = SSjob.get_job(JOB_HUMAN_AI)
if(human_ai_job && human_ai_job.current_positions && !zeroth) //there is a human AI so we "slave" to that.
zeroth = "Follow the orders of Big Brother."
protected_zeroth = TRUE
diff --git a/code/datums/announcers/default_announcer.dm b/code/datums/announcers/default_announcer.dm
index 9db822e02feff..bf24f611af842 100644
--- a/code/datums/announcers/default_announcer.dm
+++ b/code/datums/announcers/default_announcer.dm
@@ -1,20 +1,20 @@
/datum/centcom_announcer/default
- welcome_sounds = list('sound/ai/default/welcome.ogg')
- alert_sounds = list('sound/ai/default/attention.ogg')
- command_report_sounds = list('sound/ai/default/commandreport.ogg')
- event_sounds = list(ANNOUNCER_AIMALF = 'sound/ai/default/aimalf.ogg',
- ANNOUNCER_ALIENS = 'sound/ai/default/aliens.ogg',
- ANNOUNCER_ANIMES = 'sound/ai/default/animes.ogg',
- ANNOUNCER_GRANOMALIES = 'sound/ai/default/granomalies.ogg',
- ANNOUNCER_INTERCEPT = 'sound/ai/default/intercept.ogg',
- ANNOUNCER_IONSTORM = 'sound/ai/default/ionstorm.ogg',
- ANNOUNCER_METEORS = 'sound/ai/default/meteors.ogg',
- ANNOUNCER_OUTBREAK5 = 'sound/ai/default/outbreak5.ogg',
- ANNOUNCER_OUTBREAK7 = 'sound/ai/default/outbreak7.ogg',
- ANNOUNCER_POWEROFF = 'sound/ai/default/poweroff.ogg',
- ANNOUNCER_POWERON = 'sound/ai/default/poweron.ogg',
- ANNOUNCER_RADIATION = 'sound/ai/default/radiation.ogg',
- ANNOUNCER_SHUTTLECALLED = 'sound/ai/default/shuttlecalled.ogg',
- ANNOUNCER_SHUTTLEDOCK = 'sound/ai/default/shuttledock.ogg',
- ANNOUNCER_SHUTTLERECALLED = 'sound/ai/default/shuttlerecalled.ogg',
- ANNOUNCER_SPANOMALIES = 'sound/ai/default/spanomalies.ogg')
+ welcome_sounds = list('sound/announcer/default/welcome.ogg')
+ alert_sounds = list('sound/announcer/default/attention.ogg')
+ command_report_sounds = list('sound/announcer/default/commandreport.ogg')
+ event_sounds = list(ANNOUNCER_AIMALF = 'sound/announcer/default/aimalf.ogg',
+ ANNOUNCER_ALIENS = 'sound/announcer/default/aliens.ogg',
+ ANNOUNCER_ANIMES = 'sound/announcer/default/animes.ogg',
+ ANNOUNCER_GRANOMALIES = 'sound/announcer/default/granomalies.ogg',
+ ANNOUNCER_INTERCEPT = 'sound/announcer/default/intercept.ogg',
+ ANNOUNCER_IONSTORM = 'sound/announcer/default/ionstorm.ogg',
+ ANNOUNCER_METEORS = 'sound/announcer/default/meteors.ogg',
+ ANNOUNCER_OUTBREAK5 = 'sound/announcer/default/outbreak5.ogg',
+ ANNOUNCER_OUTBREAK7 = 'sound/announcer/default/outbreak7.ogg',
+ ANNOUNCER_POWEROFF = 'sound/announcer/default/poweroff.ogg',
+ ANNOUNCER_POWERON = 'sound/announcer/default/poweron.ogg',
+ ANNOUNCER_RADIATION = 'sound/announcer/default/radiation.ogg',
+ ANNOUNCER_SHUTTLECALLED = 'sound/announcer/default/shuttlecalled.ogg',
+ ANNOUNCER_SHUTTLEDOCK = 'sound/announcer/default/shuttledock.ogg',
+ ANNOUNCER_SHUTTLERECALLED = 'sound/announcer/default/shuttlerecalled.ogg',
+ ANNOUNCER_SPANOMALIES = 'sound/announcer/default/spanomalies.ogg')
diff --git a/code/datums/announcers/intern_announcer.dm b/code/datums/announcers/intern_announcer.dm
index 5e8544c18710f..635508256b781 100644
--- a/code/datums/announcers/intern_announcer.dm
+++ b/code/datums/announcers/intern_announcer.dm
@@ -1,46 +1,46 @@
/datum/centcom_announcer/intern
- welcome_sounds = list('sound/ai/intern/welcome/1.ogg',
- 'sound/ai/intern/welcome/2.ogg',
- 'sound/ai/intern/welcome/3.ogg',
- 'sound/ai/intern/welcome/4.ogg',
- 'sound/ai/intern/welcome/5.ogg',
- 'sound/ai/intern/welcome/6.ogg')
+ welcome_sounds = list('sound/announcer/intern/welcome/1.ogg',
+ 'sound/announcer/intern/welcome/2.ogg',
+ 'sound/announcer/intern/welcome/3.ogg',
+ 'sound/announcer/intern/welcome/4.ogg',
+ 'sound/announcer/intern/welcome/5.ogg',
+ 'sound/announcer/intern/welcome/6.ogg')
- alert_sounds = list('sound/ai/intern/alerts/1.ogg',
- 'sound/ai/intern/alerts/2.ogg',
- 'sound/ai/intern/alerts/3.ogg',
- 'sound/ai/intern/alerts/4.ogg',
- 'sound/ai/intern/alerts/5.ogg',
- 'sound/ai/intern/alerts/6.ogg',
- 'sound/ai/intern/alerts/7.ogg',
- 'sound/ai/intern/alerts/8.ogg',
- 'sound/ai/intern/alerts/9.ogg',
- 'sound/ai/intern/alerts/10.ogg',
- 'sound/ai/intern/alerts/11.ogg',
- 'sound/ai/intern/alerts/12.ogg',
- 'sound/ai/intern/alerts/13.ogg',
- 'sound/ai/intern/alerts/14.ogg')
+ alert_sounds = list('sound/announcer/intern/alerts/1.ogg',
+ 'sound/announcer/intern/alerts/2.ogg',
+ 'sound/announcer/intern/alerts/3.ogg',
+ 'sound/announcer/intern/alerts/4.ogg',
+ 'sound/announcer/intern/alerts/5.ogg',
+ 'sound/announcer/intern/alerts/6.ogg',
+ 'sound/announcer/intern/alerts/7.ogg',
+ 'sound/announcer/intern/alerts/8.ogg',
+ 'sound/announcer/intern/alerts/9.ogg',
+ 'sound/announcer/intern/alerts/10.ogg',
+ 'sound/announcer/intern/alerts/11.ogg',
+ 'sound/announcer/intern/alerts/12.ogg',
+ 'sound/announcer/intern/alerts/13.ogg',
+ 'sound/announcer/intern/alerts/14.ogg')
- command_report_sounds = list('sound/ai/intern/commandreport/1.ogg',
- 'sound/ai/intern/commandreport/2.ogg',
- 'sound/ai/intern/commandreport/3.ogg')
+ command_report_sounds = list('sound/announcer/intern/commandreport/1.ogg',
+ 'sound/announcer/intern/commandreport/2.ogg',
+ 'sound/announcer/intern/commandreport/3.ogg')
- event_sounds = list(ANNOUNCER_AIMALF = 'sound/ai/default/aimalf.ogg',
- ANNOUNCER_ALIENS = 'sound/ai/intern/aliens.ogg',
- ANNOUNCER_ANIMES = 'sound/ai/intern/animes.ogg',
- ANNOUNCER_GRANOMALIES = 'sound/ai/intern/granomalies.ogg',
- ANNOUNCER_INTERCEPT = 'sound/ai/intern/intercept.ogg',
- ANNOUNCER_IONSTORM = 'sound/ai/intern/ionstorm.ogg',
- ANNOUNCER_METEORS = 'sound/ai/intern/meteors.ogg',
- ANNOUNCER_OUTBREAK5 = 'sound/ai/intern/outbreak5.ogg',
- ANNOUNCER_OUTBREAK7 = 'sound/ai/intern/outbreak7.ogg',
- ANNOUNCER_POWEROFF = 'sound/ai/intern/poweroff.ogg',
- ANNOUNCER_POWERON = 'sound/ai/intern/poweron.ogg',
- ANNOUNCER_RADIATION = 'sound/ai/intern/radiation.ogg',
- ANNOUNCER_SHUTTLECALLED = 'sound/ai/intern/shuttlecalled.ogg',
- ANNOUNCER_SHUTTLEDOCK = 'sound/ai/intern/shuttledock.ogg',
- ANNOUNCER_SHUTTLERECALLED = 'sound/ai/intern/shuttlerecalled.ogg',
- ANNOUNCER_SPANOMALIES = 'sound/ai/intern/spanomalies.ogg')
+ event_sounds = list(ANNOUNCER_AIMALF = 'sound/announcer/default/aimalf.ogg',
+ ANNOUNCER_ALIENS = 'sound/announcer/intern/aliens.ogg',
+ ANNOUNCER_ANIMES = 'sound/announcer/intern/animes.ogg',
+ ANNOUNCER_GRANOMALIES = 'sound/announcer/intern/granomalies.ogg',
+ ANNOUNCER_INTERCEPT = 'sound/announcer/intern/intercept.ogg',
+ ANNOUNCER_IONSTORM = 'sound/announcer/intern/ionstorm.ogg',
+ ANNOUNCER_METEORS = 'sound/announcer/intern/meteors.ogg',
+ ANNOUNCER_OUTBREAK5 = 'sound/announcer/intern/outbreak5.ogg',
+ ANNOUNCER_OUTBREAK7 = 'sound/announcer/intern/outbreak7.ogg',
+ ANNOUNCER_POWEROFF = 'sound/announcer/intern/poweroff.ogg',
+ ANNOUNCER_POWERON = 'sound/announcer/intern/poweron.ogg',
+ ANNOUNCER_RADIATION = 'sound/announcer/intern/radiation.ogg',
+ ANNOUNCER_SHUTTLECALLED = 'sound/announcer/intern/shuttlecalled.ogg',
+ ANNOUNCER_SHUTTLEDOCK = 'sound/announcer/intern/shuttledock.ogg',
+ ANNOUNCER_SHUTTLERECALLED = 'sound/announcer/intern/shuttlerecalled.ogg',
+ ANNOUNCER_SPANOMALIES = 'sound/announcer/intern/spanomalies.ogg')
- custom_alert_message = "Please stand by for an important message from our new intern. "
+ custom_alert_message = span_alert("Please stand by for an important message from our new intern. ")
diff --git a/code/datums/announcers/medbot_announcer.dm b/code/datums/announcers/medbot_announcer.dm
index 17e8555221320..7269fe85c5703 100644
--- a/code/datums/announcers/medbot_announcer.dm
+++ b/code/datums/announcers/medbot_announcer.dm
@@ -1,21 +1,21 @@
/datum/centcom_announcer/medbot
- welcome_sounds = list('sound/ai/medbot/welcome.ogg',
- 'sound/ai/medbot/newAI.ogg')
- alert_sounds = list('sound/ai/medbot/attention.ogg')
- command_report_sounds = list('sound/ai/medbot/commandreport.ogg')
- event_sounds = list(ANNOUNCER_AIMALF = 'sound/ai/default/aimalf.ogg',
- ANNOUNCER_ALIENS = 'sound/ai/medbot/aliens.ogg',
- ANNOUNCER_ANIMES = 'sound/ai/medbot/animes.ogg',
- ANNOUNCER_GRANOMALIES = 'sound/ai/medbot/granomalies.ogg',
- ANNOUNCER_INTERCEPT = 'sound/ai/medbot/intercept.ogg',
- ANNOUNCER_IONSTORM = 'sound/ai/medbot/ionstorm.ogg',
- ANNOUNCER_METEORS = 'sound/ai/medbot/meteors.ogg',
- ANNOUNCER_OUTBREAK5 = 'sound/ai/medbot/outbreak5.ogg',
- ANNOUNCER_OUTBREAK7 = 'sound/ai/medbot/outbreak7.ogg',
- ANNOUNCER_POWEROFF = 'sound/ai/medbot/poweroff.ogg',
- ANNOUNCER_POWERON = 'sound/ai/medbot/poweron.ogg',
- ANNOUNCER_RADIATION = 'sound/ai/medbot/radiation.ogg',
- ANNOUNCER_SHUTTLECALLED = 'sound/ai/medbot/shuttlecalled.ogg',
- ANNOUNCER_SHUTTLEDOCK = 'sound/ai/medbot/shuttledock.ogg',
- ANNOUNCER_SHUTTLERECALLED = 'sound/ai/medbot/shuttlerecalled.ogg',
- ANNOUNCER_SPANOMALIES = 'sound/ai/medbot/spanomalies.ogg')
+ welcome_sounds = list('sound/announcer/medbot/welcome.ogg',
+ 'sound/announcer/medbot/newAI.ogg')
+ alert_sounds = list('sound/announcer/medbot/attention.ogg')
+ command_report_sounds = list('sound/announcer/medbot/commandreport.ogg')
+ event_sounds = list(ANNOUNCER_AIMALF = 'sound/announcer/default/aimalf.ogg',
+ ANNOUNCER_ALIENS = 'sound/announcer/medbot/aliens.ogg',
+ ANNOUNCER_ANIMES = 'sound/announcer/medbot/animes.ogg',
+ ANNOUNCER_GRANOMALIES = 'sound/announcer/medbot/granomalies.ogg',
+ ANNOUNCER_INTERCEPT = 'sound/announcer/medbot/intercept.ogg',
+ ANNOUNCER_IONSTORM = 'sound/announcer/medbot/ionstorm.ogg',
+ ANNOUNCER_METEORS = 'sound/announcer/medbot/meteors.ogg',
+ ANNOUNCER_OUTBREAK5 = 'sound/announcer/medbot/outbreak5.ogg',
+ ANNOUNCER_OUTBREAK7 = 'sound/announcer/medbot/outbreak7.ogg',
+ ANNOUNCER_POWEROFF = 'sound/announcer/medbot/poweroff.ogg',
+ ANNOUNCER_POWERON = 'sound/announcer/medbot/poweron.ogg',
+ ANNOUNCER_RADIATION = 'sound/announcer/medbot/radiation.ogg',
+ ANNOUNCER_SHUTTLECALLED = 'sound/announcer/medbot/shuttlecalled.ogg',
+ ANNOUNCER_SHUTTLEDOCK = 'sound/announcer/medbot/shuttledock.ogg',
+ ANNOUNCER_SHUTTLERECALLED = 'sound/announcer/medbot/shuttlerecalled.ogg',
+ ANNOUNCER_SPANOMALIES = 'sound/announcer/medbot/spanomalies.ogg')
diff --git a/code/datums/beam.dm b/code/datums/beam.dm
index fe34b0c7eddee..ad27ee5ee3edf 100644
--- a/code/datums/beam.dm
+++ b/code/datums/beam.dm
@@ -122,10 +122,10 @@
/datum/beam/proc/Draw()
if(SEND_SIGNAL(src, COMSIG_BEAM_BEFORE_DRAW) & BEAM_CANCEL_DRAW)
return
- var/origin_px = isnull(override_origin_pixel_x) ? origin.pixel_x : override_origin_pixel_x
- var/origin_py = isnull(override_origin_pixel_y) ? origin.pixel_y : override_origin_pixel_y
- var/target_px = isnull(override_target_pixel_x) ? target.pixel_x : override_target_pixel_x
- var/target_py = isnull(override_target_pixel_y) ? target.pixel_y : override_target_pixel_y
+ var/origin_px = (isnull(override_origin_pixel_x) ? origin.pixel_x : override_origin_pixel_x) + origin.pixel_w
+ var/origin_py = (isnull(override_origin_pixel_y) ? origin.pixel_y : override_origin_pixel_y) + origin.pixel_z
+ var/target_px = (isnull(override_target_pixel_x) ? target.pixel_x : override_target_pixel_x) + target.pixel_w
+ var/target_py = (isnull(override_target_pixel_y) ? target.pixel_y : override_target_pixel_y) + target.pixel_z
var/Angle = get_angle_raw(origin.x, origin.y, origin_px, origin_py, target.x , target.y, target_px, target_py)
///var/Angle = round(get_angle(origin,target))
var/matrix/rot_matrix = matrix()
@@ -212,6 +212,9 @@
/obj/effect/ebeam/singularity_act()
return
+/obj/effect/ebeam/Process_Spacemove(movement_dir, continuous_move)
+ return TRUE
+
/// A beam subtype used for advanced beams, to react to atoms entering the beam
/obj/effect/ebeam/reacting
/// If TRUE, atoms that exist in the beam's loc when inited count as "entering" the beam
diff --git a/code/datums/brain_damage/creepy_trauma.dm b/code/datums/brain_damage/creepy_trauma.dm
index 742f1fe57e9db..d908dfc0e613c 100644
--- a/code/datums/brain_damage/creepy_trauma.dm
+++ b/code/datums/brain_damage/creepy_trauma.dm
@@ -66,7 +66,8 @@
/datum/brain_trauma/special/obsessed/on_lose()
..()
- owner.mind.remove_antag_datum(/datum/antagonist/obsessed)
+ if (owner.mind.remove_antag_datum(/datum/antagonist/obsessed))
+ owner.mind.add_antag_datum(/datum/antagonist/former_obsessed)
owner.clear_mood_event("creeping")
if(obsession)
log_game("[key_name(owner)] is no longer obsessed with [key_name(obsession)].")
diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index f4c78bc9007e6..ad60f6cd9a6a5 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -1,3 +1,8 @@
+
+#define IMAGINARY_FRIEND_RANGE 9
+#define IMAGINARY_FRIEND_SPEECH_RANGE IMAGINARY_FRIEND_RANGE
+#define IMAGINARY_FRIEND_EXTENDED_SPEECH_RANGE 999
+
/datum/brain_trauma/special/imaginary_friend
name = "Imaginary Friend"
desc = "Patient can see and hear an imaginary person."
@@ -88,11 +93,15 @@
var/mob/living/owner
var/bubble_icon = "default"
+ /// Whether our host and other imaginary friends can hear us only when nearby or practically anywhere.
+ var/extended_message_range = TRUE
+
/mob/camera/imaginary_friend/Login()
. = ..()
if(!. || !client)
return FALSE
- greet()
+ if(owner)
+ greet()
Show()
/mob/camera/imaginary_friend/proc/greet()
@@ -119,6 +128,7 @@
if(!owner.imaginary_group)
owner.imaginary_group = list(owner)
owner.imaginary_group += src
+ greet()
/// Copies appearance from passed player prefs, or randomises them if none are provided
/mob/camera/imaginary_friend/proc/setup_appearance(datum/preferences/appearance_from_prefs = null)
@@ -156,11 +166,11 @@
for(var/job in appearance_from_prefs.job_preferences)
var/this_pref = appearance_from_prefs.job_preferences[job]
if(this_pref > highest_pref)
- appearance_job = SSjob.GetJob(job)
+ appearance_job = SSjob.get_job(job)
highest_pref = this_pref
if(!appearance_job)
- appearance_job = SSjob.GetJob(JOB_ASSISTANT)
+ appearance_job = SSjob.get_job(JOB_ASSISTANT)
if(istype(appearance_job, /datum/job/ai))
human_image = icon('icons/mob/silicon/ai.dmi', icon_state = resolve_ai_icon(appearance_from_prefs.read_preference(/datum/preference/choiced/ai_core_display)), dir = SOUTH)
@@ -212,7 +222,7 @@
create_chat_message(speaker, message_language, raw_message, spans)
to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mods))
-/mob/camera/imaginary_friend/send_speech(message, range = 7, obj/source = src, bubble_type = bubble_icon, list/spans = list(), datum/language/message_language = null, list/message_mods = list(), forced = null)
+/mob/camera/imaginary_friend/send_speech(message, range = IMAGINARY_FRIEND_SPEECH_RANGE, obj/source = src, bubble_type = bubble_icon, list/spans = list(), datum/language/message_language = null, list/message_mods = list(), forced = null)
message = get_message_mods(message, message_mods)
message = capitalize(message)
@@ -232,6 +242,9 @@
message = "[randomnote] [capitalize(message)] [randomnote]"
spans |= SPAN_SINGING
+ if(extended_message_range)
+ range = IMAGINARY_FRIEND_EXTENDED_SPEECH_RANGE
+
var/eavesdrop_range = 0
if (message_mods[MODE_CUSTOM_SAY_ERASE_INPUT])
@@ -383,7 +396,7 @@
var/obj/visual = image('icons/hud/screen_gen.dmi', our_tile, "arrow", FLY_LAYER)
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay_global), visual, group_clients(), 2.5 SECONDS)
- animate(visual, pixel_x = (tile.x - our_tile.x) * world.icon_size + pointed_atom.pixel_x, pixel_y = (tile.y - our_tile.y) * world.icon_size + pointed_atom.pixel_y, time = 1.7, easing = EASE_OUT)
+ animate(visual, pixel_x = (tile.x - our_tile.x) * ICON_SIZE_X + pointed_atom.pixel_x, pixel_y = (tile.y - our_tile.y) * ICON_SIZE_Y + pointed_atom.pixel_y, time = 1.7, easing = EASE_OUT)
/mob/camera/imaginary_friend/create_thinking_indicator()
if(active_thinking_indicator || active_typing_indicator || !HAS_TRAIT(src, TRAIT_THINKING_IN_CHARACTER))
@@ -528,3 +541,7 @@
real_name = "[owner.real_name]?"
name = real_name
human_image = icon('icons/mob/simple/lavaland/lavaland_monsters.dmi', icon_state = "curseblob")
+
+#undef IMAGINARY_FRIEND_RANGE
+#undef IMAGINARY_FRIEND_SPEECH_RANGE
+#undef IMAGINARY_FRIEND_EXTENDED_SPEECH_RANGE
diff --git a/code/datums/brain_damage/magic.dm b/code/datums/brain_damage/magic.dm
index 441d220a5ded3..fde1e5d2421f1 100644
--- a/code/datums/brain_damage/magic.dm
+++ b/code/datums/brain_damage/magic.dm
@@ -104,14 +104,14 @@
create_stalker()
if(get_dist(owner, stalker) <= 1)
- playsound(owner, 'sound/magic/demon_attack1.ogg', 50)
+ playsound(owner, 'sound/effects/magic/demon_attack1.ogg', 50)
owner.visible_message(span_warning("[owner] is torn apart by invisible claws!"), span_userdanger("Ghostly claws tear your body apart!"))
owner.take_bodypart_damage(rand(20, 45), wound_bonus=CANT_WOUND)
else if(SPT_PROB(30, seconds_per_tick))
stalker.forceMove(get_step_towards(stalker, owner))
if(get_dist(owner, stalker) <= 8)
if(!close_stalker)
- var/sound/slowbeat = sound('sound/health/slowbeat.ogg', repeat = TRUE)
+ var/sound/slowbeat = sound('sound/effects/health/slowbeat.ogg', repeat = TRUE)
owner.playsound_local(owner, slowbeat, 40, 0, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE)
close_stalker = TRUE
else
diff --git a/code/datums/brain_damage/special.dm b/code/datums/brain_damage/special.dm
index 1bf011e0fab49..f49a6d0c0bc52 100644
--- a/code/datums/brain_damage/special.dm
+++ b/code/datums/brain_damage/special.dm
@@ -45,7 +45,7 @@
else
message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_neutral")
- playsound(get_turf(owner), 'sound/magic/clockwork/invoke_general.ogg', 200, TRUE, 5)
+ playsound(get_turf(owner), 'sound/effects/magic/clockwork/invoke_general.ogg', 200, TRUE, 5)
voice_of_god(message, owner, list("colossus","yell"), 2.5, include_owner, name, TRUE)
/datum/brain_trauma/special/bluespace_prophet
@@ -218,7 +218,7 @@
linked = FALSE
return
to_chat(owner, span_warning("Your connection to [linked_target] suddenly feels extremely strong... you can feel it pulling you!"))
- owner.playsound_local(owner, 'sound/magic/lightning_chargeup.ogg', 75, FALSE)
+ owner.playsound_local(owner, 'sound/effects/magic/lightning_chargeup.ogg', 75, FALSE)
returning = TRUE
addtimer(CALLBACK(src, PROC_REF(snapback)), 10 SECONDS)
@@ -231,7 +231,7 @@
return
to_chat(owner, span_warning("You're pulled through spacetime!"))
do_teleport(owner, get_turf(linked_target), null, channel = TELEPORT_CHANNEL_QUANTUM)
- owner.playsound_local(owner, 'sound/magic/repulse.ogg', 100, FALSE)
+ owner.playsound_local(owner, 'sound/effects/magic/repulse.ogg', 100, FALSE)
linked_target = null
linked = FALSE
@@ -388,17 +388,17 @@
if(owner.stat != CONSCIOUS)
if(prob(20))
- owner.playsound_local(beepsky, 'sound/voice/beepsky/iamthelaw.ogg', 50)
+ owner.playsound_local(beepsky, 'sound/mobs/non-humanoids/beepsky/iamthelaw.ogg', 50)
return
if(get_dist(owner, beepsky) <= 1)
- owner.playsound_local(owner, 'sound/weapons/egloves.ogg', 50)
+ owner.playsound_local(owner, 'sound/items/weapons/egloves.ogg', 50)
owner.visible_message(span_warning("[owner]'s body jerks as if it was shocked."), span_userdanger("You feel the fist of the LAW."))
owner.adjustStaminaLoss(rand(40, 70))
QDEL_NULL(beepsky)
if(prob(20) && get_dist(owner, beepsky) <= 8)
- owner.playsound_local(beepsky, 'sound/voice/beepsky/criminal.ogg', 40)
+ owner.playsound_local(beepsky, 'sound/mobs/non-humanoids/beepsky/criminal.ogg', 40)
/obj/effect/client_image_holder/securitron
name = "Securitron"
diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm
index 6d0f8fc565415..198b674631750 100644
--- a/code/datums/brain_damage/split_personality.dm
+++ b/code/datums/brain_damage/split_personality.dm
@@ -305,7 +305,7 @@
addtimer(TRAIT_CALLBACK_REMOVE(owner, TRAIT_DISCOORDINATED_TOOL_USER, TRAUMA_TRAIT), 10 SECONDS)
addtimer(CALLBACK(owner, TYPE_PROC_REF(/atom, balloon_alert), owner, "dexterity regained!"), 10 SECONDS)
if(prob(15))
- playsound(owner,'sound/effects/sf_hiccup_male_01.ogg', 50)
+ playsound(owner,'sound/mobs/humanoids/human/hiccup/sf_hiccup_male_01.ogg', 50)
owner.emote("hiccup")
//too drunk to feel anything
//if they're to this point, they're likely dying of liver damage
diff --git a/code/datums/candidate_poll.dm b/code/datums/candidate_poll.dm
index f1fa9812014ed..9afec6f371bb6 100644
--- a/code/datums/candidate_poll.dm
+++ b/code/datums/candidate_poll.dm
@@ -74,7 +74,7 @@
if(time_left() <= 0)
if(!silent)
to_chat(candidate, span_danger("Sorry, you were too late for the consideration!"))
- SEND_SOUND(candidate, 'sound/machines/buzz-sigh.ogg')
+ SEND_SOUND(candidate, 'sound/machines/buzz/buzz-sigh.ogg')
return FALSE
signed_up += candidate
diff --git a/code/datums/cinematics/malf_doomsday.dm b/code/datums/cinematics/malf_doomsday.dm
index 2eb330d9a484f..02297065afc45 100644
--- a/code/datums/cinematics/malf_doomsday.dm
+++ b/code/datums/cinematics/malf_doomsday.dm
@@ -5,6 +5,6 @@
flick("intro_malf", screen)
stoplag(7.6 SECONDS)
flick("station_explode_fade_red", screen)
- play_cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
+ play_cinematic_sound(sound('sound/effects/explosion/explosion_distant.ogg'))
special_callback?.Invoke()
screen.icon_state = "summary_malf"
diff --git a/code/datums/cinematics/narsie_summon.dm b/code/datums/cinematics/narsie_summon.dm
index 2fecac2c63a80..1e0a5d1d48f94 100644
--- a/code/datums/cinematics/narsie_summon.dm
+++ b/code/datums/cinematics/narsie_summon.dm
@@ -5,9 +5,9 @@
screen.icon_state = null
flick("intro_cult", screen)
stoplag(2.5 SECONDS)
- play_cinematic_sound(sound('sound/magic/enter_blood.ogg'))
+ play_cinematic_sound(sound('sound/effects/magic/enter_blood.ogg'))
stoplag(2.8 SECONDS)
- play_cinematic_sound(sound('sound/machines/terminal_off.ogg'))
+ play_cinematic_sound(sound('sound/machines/terminal/terminal_off.ogg'))
stoplag(2 SECONDS)
flick("station_corrupted", screen)
play_cinematic_sound(sound('sound/effects/ghost.ogg'))
@@ -20,10 +20,10 @@
/datum/cinematic/cult_fail/play_cinematic()
screen.icon_state = "station_intact"
stoplag(2 SECONDS)
- play_cinematic_sound(sound('sound/creatures/narsie_rises.ogg'))
+ play_cinematic_sound(sound('sound/music/antag/bloodcult/narsie_rises.ogg'))
stoplag(6 SECONDS)
- play_cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
+ play_cinematic_sound(sound('sound/effects/explosion/explosion_distant.ogg'))
stoplag(1 SECONDS)
- play_cinematic_sound(sound('sound/magic/demon_dies.ogg'))
+ play_cinematic_sound(sound('sound/effects/magic/demon_dies.ogg'))
stoplag(3 SECONDS)
special_callback?.Invoke()
diff --git a/code/datums/cinematics/nuke_cinematics.dm b/code/datums/cinematics/nuke_cinematics.dm
index dd827f7c0b9fd..858d95c7e5102 100644
--- a/code/datums/cinematics/nuke_cinematics.dm
+++ b/code/datums/cinematics/nuke_cinematics.dm
@@ -22,7 +22,7 @@
/datum/cinematic/nuke/ops_victory/play_nuke_effect()
flick("station_explode_fade_red", screen)
- play_cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
+ play_cinematic_sound(sound('sound/effects/explosion/explosion_distant.ogg'))
/// The syndicate nuclear bomb was activated, but just barely missed the station!
/datum/cinematic/nuke/ops_miss
@@ -30,7 +30,7 @@
/datum/cinematic/nuke/ops_miss/play_nuke_effect()
flick("station_intact_fade_red", screen)
- play_cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
+ play_cinematic_sound(sound('sound/effects/explosion/explosion_distant.ogg'))
/// The self destruct, or another station-destroying entity like a blob, destroyed the station!
/datum/cinematic/nuke/self_destruct
@@ -38,14 +38,14 @@
/datum/cinematic/nuke/self_destruct/play_nuke_effect()
flick("station_explode_fade_red", screen)
- play_cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
+ play_cinematic_sound(sound('sound/effects/explosion/explosion_distant.ogg'))
/// The self destruct was activated, yet somehow avoided destroying the station!
/datum/cinematic/nuke/self_destruct_miss
after_nuke_summary_state = "station_intact"
/datum/cinematic/nuke/self_destruct_miss/play_nuke_effect()
- play_cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
+ play_cinematic_sound(sound('sound/effects/explosion/explosion_distant.ogg'))
special_callback?.Invoke()
/// The syndicate nuclear bomb was activated, and the nuclear operatives failed to extract on their shuttle before it detonated on the station!
@@ -54,7 +54,7 @@
/datum/cinematic/nuke/mutual_destruction/play_nuke_effect()
flick("station_explode_fade_red", screen)
- play_cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
+ play_cinematic_sound(sound('sound/effects/explosion/explosion_distant.ogg'))
/// A blood cult summoned Nar'sie, but central command deployed a nuclear package to stop them.
/datum/cinematic/nuke/cult
@@ -62,7 +62,7 @@
/datum/cinematic/nuke/cult/play_nuke_effect()
flick("station_explode_fade_red", screen)
- play_cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
+ play_cinematic_sound(sound('sound/effects/explosion/explosion_distant.ogg'))
/// A fake version of the nuclear detonation, where it winds up, but doesn't explode.
/datum/cinematic/nuke/fake
@@ -77,7 +77,7 @@
cleanup_time = 10 SECONDS
/datum/cinematic/nuke/clown/play_nuke_effect()
- play_cinematic_sound(sound('sound/items/airhorn.ogg'))
+ play_cinematic_sound(sound('sound/items/airhorn/airhorn.ogg'))
flick("summary_selfdes", screen) //???
/// A fake version of the nuclear detonation, where it winds up, but doesn't explode as the nuke core within was missing.
@@ -86,7 +86,7 @@
/datum/cinematic/nuke/no_core/play_nuke_effect()
flick("station_intact", screen)
- play_cinematic_sound(sound('sound/ambience/signal.ogg'))
+ play_cinematic_sound(sound('sound/ambience/misc/signal.ogg'))
stoplag(10 SECONDS)
/// The syndicate nuclear bomb was activated, but just missed the station by a whole z-level!
@@ -96,5 +96,5 @@
/datum/cinematic/nuke/far_explosion/play_cinematic()
// This one has no intro sequence.
// It's actually just a global sound, which makes you wonder why it's a cinematic.
- play_cinematic_sound(sound('sound/effects/explosion_distant.ogg'))
+ play_cinematic_sound(sound('sound/effects/explosion/explosion_distant.ogg'))
special_callback?.Invoke()
diff --git a/code/datums/cogbar.dm b/code/datums/cogbar.dm
index 0b5ead1e51e8f..6505158b58d88 100644
--- a/code/datums/cogbar.dm
+++ b/code/datums/cogbar.dm
@@ -44,7 +44,7 @@
/// Adds the cog to the user, visible by other players
/datum/cogbar/proc/add_cog_to_user()
- cog = SSvis_overlays.add_vis_overlay(user,
+ cog = SSvis_overlays.add_vis_overlay(user,
icon = 'icons/effects/progressbar.dmi',
iconstate = "cog",
plane = HIGH_GAME_PLANE,
@@ -52,7 +52,7 @@
unique = TRUE,
alpha = 0,
)
- cog.pixel_y = world.icon_size + offset_y
+ cog.pixel_y = ICON_SIZE_Y + offset_y
animate(cog, alpha = 255, time = COGBAR_ANIMATION_TIME)
if(isnull(user_client))
@@ -61,7 +61,7 @@
blank = image('icons/blanks/32x32.dmi', cog, "nothing")
SET_PLANE_EXPLICIT(blank, HIGH_GAME_PLANE, user)
blank.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
- blank.override = TRUE
+ blank.override = TRUE
user_client.images += blank
@@ -74,7 +74,7 @@
animate(cog, alpha = 0, time = COGBAR_ANIMATION_TIME)
- QDEL_IN(src, COGBAR_ANIMATION_TIME)
+ QDEL_IN(src, COGBAR_ANIMATION_TIME)
/// When the user is deleted, remove the cog
@@ -82,6 +82,6 @@
SIGNAL_HANDLER
qdel(src)
-
+
#undef COGBAR_ANIMATION_TIME
diff --git a/code/datums/communications.dm b/code/datums/communications.dm
index 92e5fdcfd74ac..6df6b1e07bb34 100644
--- a/code/datums/communications.dm
+++ b/code/datums/communications.dm
@@ -37,9 +37,9 @@ GLOBAL_DATUM_INIT(communications_controller, /datum/communciations_controller, n
else
var/list/message_data = user.treat_message(input)
if(syndicate)
- priority_announce(html_decode(message_data["message"]), null, 'sound/misc/announce_syndi.ogg', ANNOUNCEMENT_TYPE_SYNDICATE, has_important_message = TRUE, players = players, color_override = "red")
+ priority_announce(html_decode(message_data["message"]), null, 'sound/announcer/announcement/announce_syndi.ogg', ANNOUNCEMENT_TYPE_SYNDICATE, has_important_message = TRUE, players = players, color_override = "red")
else
- priority_announce(html_decode(message_data["message"]), null, 'sound/misc/announce.ogg', ANNOUNCEMENT_TYPE_CAPTAIN, has_important_message = TRUE, players = players)
+ priority_announce(html_decode(message_data["message"]), null, 'sound/announcer/announcement/announce.ogg', ANNOUNCEMENT_TYPE_CAPTAIN, has_important_message = TRUE, players = players)
COOLDOWN_START(src, nonsilicon_message_cooldown, COMMUNICATION_COOLDOWN)
user.log_talk(input, LOG_SAY, tag="priority announcement")
message_admins("[ADMIN_LOOKUPFLW(user)] has made a priority announcement.")
diff --git a/code/datums/components/adjust_fishing_difficulty.dm b/code/datums/components/adjust_fishing_difficulty.dm
new file mode 100644
index 0000000000000..4e329b039409c
--- /dev/null
+++ b/code/datums/components/adjust_fishing_difficulty.dm
@@ -0,0 +1,110 @@
+///Influences the difficulty of the minigame when worn or if buckled to.
+/datum/component/adjust_fishing_difficulty
+ ///The additive numerical modifier to the difficulty of the minigame
+ var/modifier
+ ///For items, in which slot it has to be worn to influence the difficulty of the minigame
+ var/slots
+
+/datum/component/adjust_fishing_difficulty/Initialize(modifier, slots = NONE)
+ if(!ismovable(parent) || !modifier)
+ return COMPONENT_INCOMPATIBLE
+
+ if(!isitem(parent))
+ var/atom/movable/movable_parent = parent
+ if(!movable_parent.can_buckle)
+ return COMPONENT_INCOMPATIBLE
+
+ src.modifier = modifier
+ src.slots = slots
+
+/datum/component/adjust_fishing_difficulty/RegisterWithParent()
+ if(isitem(parent))
+ RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_equipped))
+ RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_dropped))
+ RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_item_examine))
+ else
+ RegisterSignal(parent, COMSIG_MOVABLE_BUCKLE, PROC_REF(on_buckle))
+ RegisterSignal(parent, COMSIG_MOVABLE_UNBUCKLE, PROC_REF(on_unbuckle))
+ RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_buckle_examine))
+
+ update_check()
+
+/datum/component/adjust_fishing_difficulty/UnregisterFromParent()
+ UnregisterSignal(parent, list(
+ COMSIG_ATOM_EXAMINE,
+ COMSIG_MOVABLE_BUCKLE,
+ COMSIG_MOVABLE_UNBUCKLE,
+ COMSIG_ITEM_EQUIPPED,
+ COMSIG_ITEM_DROPPED,
+ ))
+
+ update_check(TRUE)
+
+/datum/component/adjust_fishing_difficulty/proc/update_check(removing = FALSE)
+ var/atom/movable/movable_parent = parent
+ for(var/mob/living/buckled_mob as anything in movable_parent.buckled_mobs)
+ update_user(buckled_mob, removing)
+ if(!isitem(movable_parent) || !isliving(movable_parent.loc))
+ return
+ var/mob/living/holder = movable_parent.loc
+ var/obj/item/item = parent
+ if(holder.get_slot_by_item(movable_parent) & (slots || item.slot_flags))
+ update_user(holder, removing)
+
+/datum/component/adjust_fishing_difficulty/proc/on_item_examine(obj/item/item, mob/user, list/examine_text)
+ SIGNAL_HANDLER
+ if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISH))
+ return
+ var/method = "[(slots || item.slot_flags) & ITEM_SLOT_HANDS ? "Holding" : "Wearing"] [item.p_them()]"
+ add_examine_line(user, examine_text, method)
+
+/datum/component/adjust_fishing_difficulty/proc/on_buckle_examine(atom/movable/source, mob/user, list/examine_text)
+ SIGNAL_HANDLER
+ if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISH))
+ return
+ add_examine_line(user, examine_text, "Buckling to [source.p_them()]")
+
+/datum/component/adjust_fishing_difficulty/proc/add_examine_line(mob/user, list/examine_text, method)
+ var/percent = HAS_MIND_TRAIT(user, TRAIT_EXAMINE_DEEPER_FISH) ? "[abs(modifier)]% " : ""
+ var/text = "[method] will make fishing [percent][modifier < 0 ? "easier" : "harder"]."
+ if(modifier < 0)
+ examine_text += span_nicegreen(text)
+ else
+ examine_text += span_danger(text)
+
+/datum/component/adjust_fishing_difficulty/proc/on_buckle(atom/movable/source, mob/living/buckled_mob, forced)
+ SIGNAL_HANDLER
+ update_user(buckled_mob)
+
+/datum/component/adjust_fishing_difficulty/proc/on_unbuckle(atom/movable/source, mob/living/buckled_mob, forced)
+ SIGNAL_HANDLER
+ update_user(buckled_mob, TRUE)
+
+/datum/component/adjust_fishing_difficulty/proc/on_equipped(obj/item/source, mob/living/wearer, slot)
+ SIGNAL_HANDLER
+ if(slot & (slots || source.slot_flags))
+ update_user(wearer)
+
+/datum/component/adjust_fishing_difficulty/proc/on_dropped(obj/item/source, mob/living/dropper)
+ SIGNAL_HANDLER
+ update_user(dropper, TRUE)
+
+/datum/component/adjust_fishing_difficulty/proc/update_user(mob/living/user, removing = FALSE)
+ var/datum/fishing_challenge/challenge = GLOB.fishing_challenges_by_user[user]
+ if(removing)
+ UnregisterSignal(user, COMSIG_MOB_BEGIN_FISHING)
+ if(challenge)
+ UnregisterSignal(challenge, COMSIG_FISHING_CHALLENGE_GET_DIFFICULTY)
+ else
+ RegisterSignal(user, COMSIG_MOB_BEGIN_FISHING, PROC_REF(on_minigame_started))
+ if(challenge)
+ RegisterSignal(challenge, COMSIG_FISHING_CHALLENGE_GET_DIFFICULTY, PROC_REF(adjust_difficulty))
+ challenge?.update_difficulty()
+
+/datum/component/adjust_fishing_difficulty/proc/on_minigame_started(mob/living/source, datum/fishing_challenge/challenge)
+ SIGNAL_HANDLER
+ RegisterSignal(challenge, COMSIG_FISHING_CHALLENGE_GET_DIFFICULTY, PROC_REF(adjust_difficulty), TRUE)
+
+/datum/component/adjust_fishing_difficulty/proc/adjust_difficulty(datum/fishing_challenge/challenge, reward_path, obj/item/fishing_rod/rod, mob/living/user, list/holder)
+ SIGNAL_HANDLER
+ holder[1] += modifier
diff --git a/code/datums/components/appearance_on_aggro.dm b/code/datums/components/appearance_on_aggro.dm
index 8c0df88e6fdbc..143c0b260cdbd 100644
--- a/code/datums/components/appearance_on_aggro.dm
+++ b/code/datums/components/appearance_on_aggro.dm
@@ -13,8 +13,6 @@
var/alpha_on_aggro
/// visibility of our icon when deaggroed
var/alpha_on_deaggro
- /// do we currently have a target
- var/atom/current_target
/datum/component/appearance_on_aggro/Initialize(aggro_state, overlay_icon, overlay_state, alpha_on_aggro, alpha_on_deaggro)
if (!isliving(parent))
@@ -27,7 +25,7 @@
/datum/component/appearance_on_aggro/RegisterWithParent()
RegisterSignal(parent, COMSIG_AI_BLACKBOARD_KEY_SET(target_key), PROC_REF(on_set_target))
- RegisterSignal(parent, COMSIG_AI_BLACKBOARD_KEY_CLEARED(target_key), PROC_REF(on_clear_target))
+ RegisterSignals(parent, list(COMSIG_AI_BLACKBOARD_KEY_CLEARED(target_key), COMSIG_LIVING_DEATH, COMSIG_MOB_LOGIN), PROC_REF(revert_appearance))
if (!isnull(aggro_state))
RegisterSignal(parent, COMSIG_ATOM_UPDATE_ICON_STATE, PROC_REF(on_icon_state_updated))
if (!isnull(aggro_overlay))
@@ -35,32 +33,31 @@
/datum/component/appearance_on_aggro/UnregisterFromParent()
. = ..()
- UnregisterSignal(parent, list(COMSIG_AI_BLACKBOARD_KEY_SET(target_key), COMSIG_AI_BLACKBOARD_KEY_CLEARED(target_key)))
+ UnregisterSignal(parent, list(
+ COMSIG_AI_BLACKBOARD_KEY_SET(target_key),
+ COMSIG_AI_BLACKBOARD_KEY_CLEARED(target_key),
+ COMSIG_LIVING_DEATH,
+ COMSIG_MOB_LOGIN,
+ ))
/datum/component/appearance_on_aggro/proc/on_set_target(mob/living/source)
SIGNAL_HANDLER
- var/atom/target = source.ai_controller.blackboard[target_key]
+ var/atom/target = source.ai_controller?.blackboard[target_key]
if (QDELETED(target))
return
- current_target = target
if (!isnull(aggro_overlay) || !isnull(aggro_state))
source.update_appearance(UPDATE_ICON)
if (!isnull(alpha_on_aggro))
animate(source, alpha = alpha_on_aggro, time = 2 SECONDS)
/datum/component/appearance_on_aggro/Destroy()
- if (!isnull(current_target))
- revert_appearance(parent)
- return ..()
-
-/datum/component/appearance_on_aggro/proc/on_clear_target(atom/source)
- SIGNAL_HANDLER
revert_appearance(parent)
+ return ..()
/datum/component/appearance_on_aggro/proc/revert_appearance(mob/living/source)
- current_target = null
+ SIGNAL_HANDLER
if (!isnull(aggro_overlay) || !isnull(aggro_state))
source.update_appearance(UPDATE_ICON)
if (!isnull(alpha_on_deaggro))
@@ -70,11 +67,11 @@
SIGNAL_HANDLER
if (source.stat == DEAD)
return
- source.icon_state = isnull(current_target) ? initial(source.icon_state) : aggro_state
+ source.icon_state = source.ai_controller?.blackboard_key_exists(target_key) ? aggro_state : initial(source.icon_state)
-/datum/component/appearance_on_aggro/proc/on_overlays_updated(atom/source, list/overlays)
+/datum/component/appearance_on_aggro/proc/on_overlays_updated(mob/living/basic/source, list/overlays)
SIGNAL_HANDLER
- if (isnull(current_target))
+ if(!(source.ai_controller?.blackboard_key_exists(target_key)))
return
overlays += aggro_overlay
diff --git a/code/datums/components/aquarium_content.dm b/code/datums/components/aquarium_content.dm
index 57d62cdb0ee26..d956b39928a47 100644
--- a/code/datums/components/aquarium_content.dm
+++ b/code/datums/components/aquarium_content.dm
@@ -15,14 +15,7 @@
var/obj/structure/aquarium/current_aquarium
//This is visual effect holder that will end up in aquarium's vis_contents
- var/obj/effect/vc_obj
-
- /// Base px offset of the visual object in current aquarium aka current base position
- var/base_px = 0
- /// Base px offset of the visual object in current aquarium aka current base position
- var/base_py = 0
- //Current layer for the visual object
- var/base_layer
+ var/obj/effect/aquarium/vc_obj
/**
* Fish sprite how to:
@@ -33,37 +26,12 @@
* cover the extra pixels anyway.
*/
- /// Icon used for in aquarium sprite
- var/icon = 'icons/obj/aquarium/fish.dmi'
- /// If this is set this icon state will be used for the holder while icon_state will only be used for item/catalog. Transformation from source_width/height WON'T be applied.
- var/icon_state
- /// Applied to vc object only for use with greyscaled icons.
- var/aquarium_vc_color
- /// Transformation applied to the visual holder - used when scaled down sprites are used as in aquarium visual
- var/matrix/base_transform
-
- /// How the thing will be layered
- var/layer_mode = AQUARIUM_LAYER_MODE_AUTO
-
- /// If the starting position is randomised within bounds when inserted into aquarium.
- var/randomize_position = FALSE
-
- //Target sprite size for path/position calculations.
- var/sprite_height = 3
- var/sprite_width = 3
-
/// Currently playing animation
var/current_animation
/// Does this behviour need additional processing in aquarium, will be added to SSobj processing on insertion
var/processing = FALSE
- /// TODO: Change this into trait checked on aquarium insertion
- var/unique = FALSE
-
- /// Proc used to retrieve current animation state from the parent, optional
- var/animation_getter
-
/// Signals of the parent that will trigger animation update
var/animation_update_signals
@@ -73,50 +41,27 @@
/// The original value of the beauty this component had when initialized
var/original_beauty
-/datum/component/aquarium_content/Initialize(icon, animation_getter, animation_update_signals, beauty)
+/datum/component/aquarium_content/Initialize(animation_update_signals, beauty)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
- src.animation_getter = animation_getter
src.animation_update_signals = animation_update_signals
src.beauty = original_beauty = beauty
if(animation_update_signals)
RegisterSignals(parent, animation_update_signals, PROC_REF(generate_animation))
- if(istype(parent,/obj/item/fish))
- InitializeFromFish()
- else if(istype(parent,/obj/item/aquarium_prop))
- InitializeFromProp()
- else
- InitializeOther()
-
ADD_TRAIT(parent, TRAIT_FISH_CASE_COMPATIBILE, REF(src))
RegisterSignal(parent, COMSIG_TRY_INSERTING_IN_AQUARIUM, PROC_REF(is_ready_to_insert))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(enter_aquarium))
- RegisterSignal(parent, COMSIG_FISH_PETTED, PROC_REF(on_fish_petted))
+
+ if(isfish(parent))
+ RegisterSignal(parent, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_fish_status_changed))
//If component is added to something already in aquarium at the time initialize it properly.
var/atom/movable/movable_parent = parent
if(istype(movable_parent.loc, /obj/structure/aquarium))
on_inserted(movable_parent.loc)
-/// Sets visuals properties for fish
-/datum/component/aquarium_content/proc/InitializeFromFish()
- var/obj/item/fish/fish = parent
-
- icon = fish.icon
- sprite_height = fish.sprite_height
- sprite_width = fish.sprite_width
- aquarium_vc_color = fish.aquarium_vc_color
-
- icon = fish.dedicated_in_aquarium_icon
- icon_state = fish.dedicated_in_aquarium_icon_state
- base_transform = matrix()
-
- randomize_position = TRUE
-
- RegisterSignal(fish, COMSIG_FISH_STATUS_CHANGED, PROC_REF(on_fish_status_changed))
-
/datum/component/aquarium_content/proc/on_fish_status_changed(obj/item/fish/source)
SIGNAL_HANDLER
var/old_beauty = beauty
@@ -127,31 +72,6 @@
change_aquarium_beauty(beauty - old_beauty)
generate_animation()
-/// Sets visuals properties for fish
-/datum/component/aquarium_content/proc/InitializeFromProp()
- var/obj/item/aquarium_prop/prop = parent
-
- icon = prop.icon
- icon_state = prop.icon_state
- layer_mode = prop.layer_mode
- sprite_height = 32
- sprite_width = 32
- base_transform = matrix()
-
- unique = TRUE
-
-/// Mostly for admin abuse
-/datum/component/aquarium_content/proc/InitializeOther()
- sprite_width = 8
- sprite_height = 8
-
- var/matrix/matrix = matrix()
- var/x_scale = sprite_width / 32
- var/y_scale = sprite_height / 32
- matrix.Scale(x_scale, y_scale)
- base_transform = matrix
-
-
/datum/component/aquarium_content/PreTransfer()
. = ..()
REMOVE_TRAIT(parent, TRAIT_FISH_CASE_COMPATIBILE, REF(src))
@@ -170,12 +90,11 @@
/datum/component/aquarium_content/proc/is_ready_to_insert(datum/source, obj/structure/aquarium/aquarium)
SIGNAL_HANDLER
- //This is kinda awful but we're unaware of other fish
- if(unique)
- for(var/atom/movable/fish_or_prop in aquarium)
- if(fish_or_prop == parent)
+ if(HAS_TRAIT(parent, TRAIT_UNIQUE_AQUARIUM_CONTENT))
+ for(var/atom/movable/content as anything in aquarium)
+ if(content == parent)
continue
- if(fish_or_prop.type == parent.type)
+ if(content.type == parent.type)
return COMSIG_CANNOT_INSERT_IN_AQUARIUM
return COMSIG_CAN_INSERT_IN_AQUARIUM
@@ -190,7 +109,7 @@
//If we don't have vc object yet build it
if(!vc_obj)
- vc_obj = generate_base_vc()
+ generate_base_vc()
//Set default position and layer
set_vc_base_position()
@@ -225,112 +144,28 @@
SIGNAL_HANDLER
generate_animation()
+///Sends a signal to the parent to get them to update the aquarium animation of the visual object
+/datum/component/aquarium_content/proc/generate_animation(reset=FALSE)
+ if(!current_aquarium)
+ return
+ SEND_SIGNAL(parent, COMSIG_AQUARIUM_CONTENT_DO_ANIMATION, reset ? null : current_animation, current_aquarium, vc_obj)
+
/datum/component/aquarium_content/proc/remove_visual_from_aquarium()
current_aquarium.vis_contents -= vc_obj
- if(base_layer)
- current_aquarium.free_layer(base_layer)
+ if(vc_obj.layer)
+ current_aquarium.free_layer(vc_obj.layer)
/// Generates common visual object, propeties that don't depend on aquarium surface
/datum/component/aquarium_content/proc/generate_base_vc()
- var/obj/effect/visual = new
- apply_appearance(visual)
- visual.vis_flags |= VIS_INHERIT_ID | VIS_INHERIT_PLANE //plane so it shows properly in containers on inventory ui for handheld cases
- return visual
-
-/// Applies icon,color and base scaling to our visual holder
-/datum/component/aquarium_content/proc/apply_appearance(obj/effect/holder)
- holder.icon = icon
- holder.icon_state = icon_state
- holder.transform = matrix(base_transform)
- if(aquarium_vc_color)
- holder.color = aquarium_vc_color
-
-
-/// Actually animates the vc holder
-/datum/component/aquarium_content/proc/generate_animation(reset=FALSE)
- if(!current_aquarium)
- return
- var/next_animation = animation_getter ? call(parent,animation_getter)() : null
- if(current_animation == next_animation && !reset)
- return
- current_animation = next_animation
- switch(current_animation)
- if(AQUARIUM_ANIMATION_FISH_SWIM)
- swim_animation()
- return
- if(AQUARIUM_ANIMATION_FISH_DEAD)
- dead_animation()
- return
-
-/// Create looping random path animation, pixel offsets parameters include offsets already
-/datum/component/aquarium_content/proc/swim_animation()
- var/avg_width = round(sprite_width / 2)
- var/avg_height = round(sprite_height / 2)
-
- var/list/aq_properties = current_aquarium.get_surface_properties()
- var/px_min = aq_properties[AQUARIUM_PROPERTIES_PX_MIN] + avg_width - 16
- var/px_max = aq_properties[AQUARIUM_PROPERTIES_PX_MAX] - avg_width - 16
- var/py_min = aq_properties[AQUARIUM_PROPERTIES_PY_MIN] + avg_height - 16
- var/py_max = aq_properties[AQUARIUM_PROPERTIES_PY_MAX] - avg_width - 16
-
- var/origin_x = base_px
- var/origin_y = base_py
- var/prev_x = origin_x
- var/prev_y = origin_y
- animate(vc_obj, pixel_x = origin_x, time = 0, loop = -1) //Just to start the animation
- var/move_number = rand(3, 5) //maybe unhardcode this
- for(var/i in 1 to move_number)
- //If it's last movement, move back to start otherwise move to some random point
- var/target_x = i == move_number ? origin_x : rand(px_min,px_max) //could do with enforcing minimal delta for prettier zigzags
- var/target_y = i == move_number ? origin_y : rand(py_min,py_max)
- var/dx = prev_x - target_x
- var/dy = prev_y - target_y
- prev_x = target_x
- prev_y = target_y
- var/dist = abs(dx) + abs(dy)
- var/eyeballed_time = dist * 2 //2ds per px
- //Face the direction we're going
- var/matrix/dir_mx = matrix(base_transform)
- if(dx <= 0) //assuming default sprite is facing left here
- dir_mx.Scale(-1, 1)
- animate(transform = dir_mx, time = 0, loop = -1)
- animate(pixel_x = target_x, pixel_y = target_y, time = eyeballed_time, loop = -1)
-
-/datum/component/aquarium_content/proc/dead_animation()
- //Set base_py to lowest possible value
- var/avg_height = round(sprite_height / 2)
- var/list/aq_properties = current_aquarium.get_surface_properties()
- var/py_min = aq_properties[AQUARIUM_PROPERTIES_PY_MIN] + avg_height - 16
- base_py = py_min
- animate(vc_obj, pixel_y = py_min, time = 1) //flop to bottom and end current animation.
+ vc_obj = new
+ vc_obj.vis_flags |= VIS_INHERIT_ID | VIS_INHERIT_PLANE //plane so it shows properly in containers on inventory ui for handheld cases
+ SEND_SIGNAL(parent, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, vc_obj)
/datum/component/aquarium_content/proc/set_vc_base_position()
- if(randomize_position)
- randomize_base_position()
- if(base_layer)
- current_aquarium.free_layer(base_layer)
- base_layer = current_aquarium.request_layer(layer_mode)
- vc_obj.layer = base_layer
-
-/datum/component/aquarium_content/proc/on_fish_petted()
- SIGNAL_HANDLER
-
- new /obj/effect/temp_visual/heart(get_turf(parent))
-
-/datum/component/aquarium_content/proc/randomize_base_position()
- var/list/aq_properties = current_aquarium.get_surface_properties()
- var/avg_width = round(sprite_width / 2)
- var/avg_height = round(sprite_height / 2)
- var/px_min = aq_properties[AQUARIUM_PROPERTIES_PX_MIN] + avg_width - 16
- var/px_max = aq_properties[AQUARIUM_PROPERTIES_PX_MAX] - avg_width - 16
- var/py_min = aq_properties[AQUARIUM_PROPERTIES_PY_MIN] + avg_height - 16
- var/py_max = aq_properties[AQUARIUM_PROPERTIES_PY_MAX] - avg_width - 16
-
- base_px = rand(px_min,px_max)
- base_py = rand(py_min,py_max)
-
- vc_obj.pixel_x = base_px
- vc_obj.pixel_y = base_py
+ SEND_SIGNAL(parent, AQUARIUM_CONTENT_RANDOMIZE_POSITION, current_aquarium, vc_obj)
+ if(vc_obj.layer)
+ current_aquarium.free_layer(vc_obj.layer)
+ vc_obj.layer = current_aquarium.request_layer(vc_obj.layer_mode)
/datum/component/aquarium_content/proc/on_removed(obj/structure/aquarium/source, atom/movable/gone, direction)
SIGNAL_HANDLER
@@ -344,6 +179,16 @@
remove_visual_from_aquarium()
current_aquarium = null
+///The visual overlay of the aquarium content. It holds a few vars that we can modity them during signals.
+/obj/effect/aquarium
+ layer = 0 //set on set_vc_base_position
+ /// Base px offset of the visual object in current aquarium aka current base position
+ var/base_px = 0
+ /// Base px offset of the visual object in current aquarium aka current base position
+ var/base_py = 0
+ /// How the visual will be layered
+ var/layer_mode = AQUARIUM_LAYER_MODE_AUTO
+
#undef DEAD_FISH_BEAUTY
#undef MIN_DEAD_FISH_BEAUTY
#undef MAX_DEAD_FISH_BEAUTY
diff --git a/code/datums/components/area_based_godmode.dm b/code/datums/components/area_based_godmode.dm
index 4f03ae57794c8..b9447efbafbf8 100644
--- a/code/datums/components/area_based_godmode.dm
+++ b/code/datums/components/area_based_godmode.dm
@@ -34,8 +34,6 @@
var/mob/mob_target = parent
if(!istype(mob_target))
return COMPONENT_INCOMPATIBLE
- if(initial(mob_target.status_flags) & GODMODE)
- return COMPONENT_INCOMPATIBLE
sources_to_area_type = list()
src.gain_message = gain_message
@@ -102,11 +100,11 @@
/datum/component/area_based_godmode/proc/check_area(mob/source)
SIGNAL_HANDLER
- var/has_godmode = source.status_flags & GODMODE
+ var/has_godmode = HAS_TRAIT(source, TRAIT_GODMODE)
if(!check_in_valid_area(source))
if(has_godmode)
to_chat(source, lose_message)
- source.status_flags ^= GODMODE
+ REMOVE_TRAIT(source, TRAIT_GODMODE, REF(src))
check_area_cached_state = FALSE
return
@@ -115,7 +113,7 @@
return
to_chat(source, gain_message)
- source.status_flags ^= GODMODE
+ ADD_TRAIT(source, TRAIT_GODMODE, REF(src))
#undef MAP_AREA_TYPE
#undef MAP_ALLOW_AREA_SUBTYPES
diff --git a/code/datums/components/boomerang.dm b/code/datums/components/boomerang.dm
index 23dd63d146712..954e752da1ea1 100644
--- a/code/datums/components/boomerang.dm
+++ b/code/datums/components/boomerang.dm
@@ -60,12 +60,11 @@
* * hit_atom: The atom that has been hit by the boomerang component.
* * init_throwing_datum: The thrownthing datum that originally impacted the object, that we use to build the new throwing datum for the rebound.
*/
-/datum/component/boomerang/proc/return_hit_throw(datum/source, atom/hit_atom, datum/thrownthing/init_throwing_datum)
+/datum/component/boomerang/proc/return_hit_throw(datum/source, atom/hit_atom, datum/thrownthing/init_throwing_datum, caught)
SIGNAL_HANDLER
- if (!COOLDOWN_FINISHED(src, last_boomerang_throw))
+ if (!COOLDOWN_FINISHED(src, last_boomerang_throw) || caught)
return
- var/obj/item/true_parent = parent
- aerodynamic_swing(init_throwing_datum, true_parent)
+ aerodynamic_swing(init_throwing_datum, parent)
/**
* Proc that triggers when the thrown boomerang does not hit a target.
diff --git a/code/datums/components/callouts.dm b/code/datums/components/callouts.dm
index 98d489cc915a9..52a3e007905c3 100644
--- a/code/datums/components/callouts.dm
+++ b/code/datums/components/callouts.dm
@@ -136,7 +136,7 @@
color = colorize_string(creator.GetVoice(), 2, 0.9)
update_appearance()
var/turf/target_loc = get_turf(target)
- animate(src, pixel_x = (target_loc.x - loc.x) * world.icon_size + target.pixel_x, pixel_y = (target_loc.y - loc.y) * world.icon_size + target.pixel_y, time = 0.2 SECONDS, easing = EASE_OUT)
+ animate(src, pixel_x = (target_loc.x - loc.x) * ICON_SIZE_X + target.pixel_x, pixel_y = (target_loc.y - loc.y) * ICON_SIZE_Y + target.pixel_y, time = 0.2 SECONDS, easing = EASE_OUT)
/datum/callout_option
var/name = "ERROR"
diff --git a/code/datums/components/chasm.dm b/code/datums/components/chasm.dm
index 6cae053afd17d..0d65d2840f3c8 100644
--- a/code/datums/components/chasm.dm
+++ b/code/datums/components/chasm.dm
@@ -14,7 +14,7 @@
/obj/effect/constructing_effect,
/obj/effect/dummy/phased_mob,
/obj/effect/ebeam,
- /obj/effect/fishing_lure,
+ /obj/effect/fishing_float,
/obj/effect/hotspot,
/obj/effect/landmark,
/obj/effect/light_emitter/tendril,
diff --git a/code/datums/components/chuunibyou.dm b/code/datums/components/chuunibyou.dm
index 4e06f0fd47486..5373b3f798754 100644
--- a/code/datums/components/chuunibyou.dm
+++ b/code/datums/components/chuunibyou.dm
@@ -64,14 +64,14 @@
/datum/component/chuunibyou/proc/on_try_speech(datum/source, message, ignore_spam, forced)
SIGNAL_HANDLER
- if(casting_spell)
+ if(casting_spell && !HAS_TRAIT(src, TRAIT_MUTE))
return COMPONENT_IGNORE_CAN_SPEAK
///signal sent when the parent casts a spell that has a projectile
/datum/component/chuunibyou/proc/on_spell_projectile(mob/living/source, datum/action/cooldown/spell/spell, atom/cast_on, obj/projectile/to_fire)
SIGNAL_HANDLER
- playsound(to_fire,'sound/magic/staff_change.ogg', 75, TRUE)
+ playsound(to_fire,'sound/effects/magic/staff_change.ogg', 75, TRUE)
to_fire.color = "#f825f8"
to_fire.name = "chuuni-[to_fire.name]"
to_fire.set_light(2, 2, LIGHT_COLOR_PINK, l_on = TRUE)
@@ -101,7 +101,7 @@
COOLDOWN_START(src, heal_cooldown, CHUUNIBYOU_COOLDOWN_TIME)
source.heal_overall_damage(heal_amount)
- playsound(source, 'sound/magic/staff_healing.ogg', 30)
+ playsound(source, 'sound/effects/magic/staff_healing.ogg', 30)
to_chat(source, span_danger("You feel slightly healed by your chuuni powers."))
/datum/component/chuunibyou/no_healing
diff --git a/code/datums/components/cleaner.dm b/code/datums/components/cleaner.dm
index 3001fde9837fb..7072f271c7a6a 100644
--- a/code/datums/components/cleaner.dm
+++ b/code/datums/components/cleaner.dm
@@ -62,6 +62,9 @@
/datum/component/cleaner/proc/on_interaction(datum/source, mob/living/user, atom/target, list/modifiers)
SIGNAL_HANDLER
+ if(isitem(source) && SHOULD_SKIP_INTERACTION(target, source, user))
+ return NONE
+
// By default, give XP
var/give_xp = TRUE
if(pre_clean_callback)
@@ -93,8 +96,8 @@
ADD_TRAIT(target, TRAIT_CURRENTLY_CLEANING, REF(src))
// We need to update our planes on overlay changes
RegisterSignal(target, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(cleaning_target_moved))
- var/mutable_appearance/low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, target, GAME_PLANE)
- var/mutable_appearance/high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, target, ABOVE_GAME_PLANE)
+ var/mutable_appearance/low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", GAME_CLEAN_LAYER, target, GAME_PLANE)
+ var/mutable_appearance/high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", GAME_CLEAN_LAYER, target, ABOVE_GAME_PLANE)
var/list/icon_offsets = target.get_oversized_icon_offsets()
low_bubble.pixel_x = icon_offsets["x"]
low_bubble.pixel_y = icon_offsets["y"]
@@ -137,13 +140,13 @@
if(same_z_layer)
return
// First, get rid of the old overlay
- var/mutable_appearance/old_low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, old_turf, GAME_PLANE)
- var/mutable_appearance/old_high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, old_turf, ABOVE_GAME_PLANE)
+ var/mutable_appearance/old_low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", GAME_CLEAN_LAYER, old_turf, GAME_PLANE)
+ var/mutable_appearance/old_high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", GAME_CLEAN_LAYER, old_turf, ABOVE_GAME_PLANE)
source.cut_overlay(old_low_bubble)
source.cut_overlay(old_high_bubble)
// Now, add the new one
- var/mutable_appearance/new_low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, new_turf, GAME_PLANE)
- var/mutable_appearance/new_high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", FLOOR_CLEAN_LAYER, new_turf, ABOVE_GAME_PLANE)
+ var/mutable_appearance/new_low_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", GAME_CLEAN_LAYER, new_turf, GAME_PLANE)
+ var/mutable_appearance/new_high_bubble = mutable_appearance('icons/effects/effects.dmi', "bubbles", GAME_CLEAN_LAYER, new_turf, ABOVE_GAME_PLANE)
source.add_overlay(new_low_bubble)
source.add_overlay(new_high_bubble)
diff --git a/code/datums/components/crafting/crafting.dm b/code/datums/components/crafting/crafting.dm
index ec6df83b8042f..bf13b7cd5bae4 100644
--- a/code/datums/components/crafting/crafting.dm
+++ b/code/datums/components/crafting/crafting.dm
@@ -21,6 +21,8 @@
var/display_craftable_only = FALSE
var/display_compact = FALSE
var/forced_mode = FALSE
+ /// crafting flags we ignore when considering a recipe
+ var/ignored_flags = NONE
/* This is what procs do:
get_environment - gets a list of things accessable for crafting by user
@@ -205,16 +207,16 @@
if(!check_tools(crafter, recipe, contents))
return ", missing tool."
+ var/considered_flags = recipe.crafting_flags & ~(ignored_flags)
-
- if((recipe.crafting_flags & CRAFT_ONE_PER_TURF) && (locate(recipe.result) in dest_turf))
+ if((considered_flags & CRAFT_ONE_PER_TURF) && (locate(recipe.result) in dest_turf))
return ", already one here!"
- if(recipe.crafting_flags & CRAFT_CHECK_DIRECTION)
- if(!valid_build_direction(dest_turf, crafter.dir, is_fulltile = (recipe.crafting_flags & CRAFT_IS_FULLTILE)))
+ if(considered_flags & CRAFT_CHECK_DIRECTION)
+ if(!valid_build_direction(dest_turf, crafter.dir, is_fulltile = (considered_flags & CRAFT_IS_FULLTILE)))
return ", won't fit here!"
- if(recipe.crafting_flags & CRAFT_ON_SOLID_GROUND)
+ if(considered_flags & CRAFT_ON_SOLID_GROUND)
if(isclosedturf(dest_turf))
return ", cannot be made on a wall!"
@@ -222,7 +224,7 @@
if(!locate(/obj/structure/thermoplastic) in dest_turf) // for tram construction
return ", must be made on solid ground!"
- if(recipe.crafting_flags & CRAFT_CHECK_DENSITY)
+ if(considered_flags & CRAFT_CHECK_DENSITY)
for(var/obj/object in dest_turf)
if(object.density && !(object.obj_flags & IGNORE_DENSITY) || object.obj_flags & BLOCKS_CONSTRUCTION)
return ", something is in the way!"
@@ -268,9 +270,11 @@
qdel(thing)
var/datum/reagents/holder = locate() in parts
if(holder) //transfer reagents from ingredients to result
- if(!ispath(recipe.result, /obj/item/reagent_containers) && result.reagents)
- result.reagents.clear_reagents()
- holder.trans_to(result.reagents, holder.total_volume, no_react = TRUE)
+ if(!ispath(recipe.result, /obj/item/reagent_containers) && result.reagents)
+ if(recipe.crafting_flags & CRAFT_CLEARS_REAGENTS)
+ result.reagents.clear_reagents()
+ if(recipe.crafting_flags & CRAFT_TRANSFERS_REAGENTS)
+ holder.trans_to(result.reagents, holder.total_volume, no_react = TRUE)
parts -= holder
qdel(holder)
result.CheckParts(parts, recipe)
@@ -701,3 +705,20 @@
if(recipe == potential_recipe)
return TRUE
return FALSE
+
+/datum/component/personal_crafting/machine
+ ignored_flags = CRAFT_CHECK_DENSITY
+
+/datum/component/personal_crafting/machine/get_environment(atom/crafter, list/blacklist = null, radius_range = 1)
+ . = list()
+ for(var/atom/movable/content in crafter.contents)
+ if((content.flags_1 & HOLOGRAM_1) || (blacklist && (content.type in blacklist)))
+ continue
+ if(isitem(content))
+ var/obj/item/item = content
+ if(item.item_flags & ABSTRACT) //let's not tempt fate, shall we?
+ continue
+ . += content
+
+/datum/component/personal_crafting/machine/check_tools(atom/source, datum/crafting_recipe/recipe, list/surroundings)
+ return TRUE
diff --git a/code/datums/components/crafting/equipment.dm b/code/datums/components/crafting/equipment.dm
index dfd79e696224c..2546106d40327 100644
--- a/code/datums/components/crafting/equipment.dm
+++ b/code/datums/components/crafting/equipment.dm
@@ -282,3 +282,15 @@
)
category = CAT_EQUIPMENT
tool_behaviors = list(TOOL_WELDER, TOOL_WIRECUTTER)
+
+/datum/crafting_recipe/tether_anchor
+ name = "Tether Anchor"
+ result = /obj/item/tether_anchor
+ reqs = list(
+ /obj/item/stack/sheet/iron = 5,
+ /obj/item/stack/rods = 2,
+ /obj/item/stack/cable_coil = 15
+ )
+ tool_behaviors = list(TOOL_SCREWDRIVER, TOOL_WRENCH)
+ time = 5 SECONDS
+ category = CAT_EQUIPMENT
diff --git a/code/datums/components/crafting/ranged_weapon.dm b/code/datums/components/crafting/ranged_weapon.dm
index 174c0226a423e..e69d535a58b30 100644
--- a/code/datums/components/crafting/ranged_weapon.dm
+++ b/code/datums/components/crafting/ranged_weapon.dm
@@ -300,6 +300,22 @@
category = CAT_WEAPON_RANGED
crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_MUST_BE_LEARNED
+/datum/crafting_recipe/pipe_organ_gun
+ name = "Pipe Organ Gun"
+ tool_behaviors = list(TOOL_WELDER, TOOL_SCREWDRIVER)
+ result = /obj/structure/mounted_gun/pipe
+ reqs = list(
+ /obj/item/pipe = 8,
+ /obj/item/stack/sheet/mineral/wood = 15,
+ /obj/item/stack/sheet/iron = 10,
+ /obj/item/storage/toolbox = 1,
+ /obj/item/stack/rods = 10,
+ /obj/item/assembly/igniter = 2,
+ )
+ time = 15 SECONDS
+ category = CAT_WEAPON_RANGED
+ crafting_flags = CRAFT_CHECK_DENSITY
+
/datum/crafting_recipe/trash_cannon
name = "Trash Cannon"
tool_behaviors = list(TOOL_WELDER, TOOL_SCREWDRIVER)
@@ -323,8 +339,7 @@
/obj/item/stack/rods = 4,
/obj/item/stock_parts/micro_laser = 1,
/obj/item/stock_parts/capacitor = 1,
- /obj/item/clothing/glasses/regular = 1,
- /obj/item/reagent_containers/cup/glass/drinkingglass = 1,
+ /obj/item/reagent_containers/cup/glass/drinkingglass = 2,
)
tool_behaviors = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
time = 10 SECONDS
diff --git a/code/datums/components/crafting/structures.dm b/code/datums/components/crafting/structures.dm
index c4a9b48ec36b6..090ec31ce226f 100644
--- a/code/datums/components/crafting/structures.dm
+++ b/code/datums/components/crafting/structures.dm
@@ -74,3 +74,14 @@
)
category = CAT_STRUCTURE
crafting_flags = CRAFT_CHECK_DENSITY | CRAFT_MUST_BE_LEARNED
+
+/datum/crafting_recipe/manucrate
+ name = "Manufacturing Storage Unit"
+ result = /obj/machinery/power/manufacturing/storagebox
+ tool_behaviors = list(TOOL_SCREWDRIVER, TOOL_WELDER)
+ time = 6 SECONDS
+ reqs = list(
+ /obj/item/stack/sheet/iron = 10,
+ )
+ category = CAT_STRUCTURE
+ crafting_flags = CRAFT_CHECK_DENSITY
diff --git a/code/datums/components/crank_recharge.dm b/code/datums/components/crank_recharge.dm
index 1f2272a8debc2..4940a02b0553e 100644
--- a/code/datums/components/crank_recharge.dm
+++ b/code/datums/components/crank_recharge.dm
@@ -14,9 +14,11 @@
var/charge_sound_cooldown_time
/// Are we currently charging
var/is_charging = FALSE
+ /// Should you be able to move while charging, use IGNORE_USER_LOC_CHANGE if you want to move and crank
+ var/charge_move = NONE
COOLDOWN_DECLARE(charge_sound_cooldown)
-/datum/component/crank_recharge/Initialize(charging_cell, spin_to_win = FALSE, charge_amount = 500, cooldown_time = 2 SECONDS, charge_sound = 'sound/weapons/laser_crank.ogg', charge_sound_cooldown_time = 1.8 SECONDS)
+/datum/component/crank_recharge/Initialize(charging_cell, spin_to_win = FALSE, charge_amount = 500, cooldown_time = 2 SECONDS, charge_sound = 'sound/items/weapons/laser_crank.ogg', charge_sound_cooldown_time = 1.8 SECONDS, charge_move = NONE)
. = ..()
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
@@ -28,7 +30,7 @@
src.cooldown_time = cooldown_time
src.charge_sound = charge_sound
src.charge_sound_cooldown_time = charge_sound_cooldown_time
-
+ src.charge_move = charge_move
/datum/component/crank_recharge/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(on_attack_self))
@@ -57,7 +59,7 @@
COOLDOWN_START(src, charge_sound_cooldown, charge_sound_cooldown_time)
playsound(source, charge_sound, 40)
source.balloon_alert(user, "charging...")
- if(!do_after(user, cooldown_time, source, interaction_key = DOAFTER_SOURCE_CHARGE_CRANKRECHARGE))
+ if(!do_after(user, cooldown_time, source, interaction_key = DOAFTER_SOURCE_CHARGE_CRANKRECHARGE, timed_action_flags = charge_move))
is_charging = FALSE
return
charging_cell.give(charge_amount)
diff --git a/code/datums/components/cuff_n_stun.dm b/code/datums/components/cuff_n_stun.dm
index d238a81f06a24..fda9618e93c14 100644
--- a/code/datums/components/cuff_n_stun.dm
+++ b/code/datums/components/cuff_n_stun.dm
@@ -22,7 +22,7 @@
COOLDOWN_DECLARE(stun_cooldown)
/datum/component/stun_n_cuff/Initialize(list/blacklist_mobs = list(),
- stun_sound = 'sound/weapons/egloves.ogg',
+ stun_sound = 'sound/items/weapons/egloves.ogg',
stun_timer = 8 SECONDS,
handcuff_timer = 4 SECONDS,
stun_cooldown_timer = 10 SECONDS,
@@ -75,7 +75,7 @@
living_parent.balloon_alert(human_target, "already cuffed!")
return
- playsound(parent, 'sound/weapons/cablecuff.ogg', 30, TRUE)
+ playsound(parent, 'sound/items/weapons/cablecuff.ogg', 30, TRUE)
human_target.visible_message(span_danger("[parent] is trying to put zipties on [human_target]!"),\
span_danger("[parent] is trying to put zipties on you!"))
diff --git a/code/datums/components/cult_ritual_item.dm b/code/datums/components/cult_ritual_item.dm
index 71e07e6380e11..554e3d611ba2d 100644
--- a/code/datums/components/cult_ritual_item.dm
+++ b/code/datums/components/cult_ritual_item.dm
@@ -176,7 +176,7 @@
* cultist - the mob doing the destroying
*/
/datum/component/cult_ritual_item/proc/do_destroy_girder(obj/structure/girder/cult/cult_girder, mob/living/cultist)
- playsound(cult_girder, 'sound/weapons/resonator_blast.ogg', 40, TRUE, ignore_walls = FALSE)
+ playsound(cult_girder, 'sound/items/weapons/resonator_blast.ogg', 40, TRUE, ignore_walls = FALSE)
cultist.visible_message(
span_warning("[cultist] strikes [cult_girder] with [parent]!"),
span_notice("You demolish [cult_girder].")
@@ -320,7 +320,7 @@
if(scribe_failed)
failed = CALLBACK(GLOBAL_PROC, scribe_failed)
- SEND_SOUND(cultist, sound('sound/weapons/slice.ogg', 0, 1, 10))
+ SEND_SOUND(cultist, sound('sound/items/weapons/slice.ogg', 0, 1, 10))
if(!do_after(cultist, scribe_mod, target = get_turf(cultist), timed_action_flags = IGNORE_SLOWDOWNS))
cleanup_shields()
failed?.Invoke()
@@ -371,7 +371,7 @@
var/area/summon_location = get_area(cultist)
priority_announce(
text = "Figments from an eldritch god are being summoned by [cultist.real_name] into [summon_location.get_original_area_name()] from an unknown dimension. Disrupt the ritual at all costs!",
- sound = 'sound/ambience/antag/bloodcult/bloodcult_scribe.ogg',
+ sound = 'sound/music/antag/bloodcult/bloodcult_scribe.ogg',
sender_override = "[command_name()] Higher Dimensional Affairs",
has_important_message = TRUE,
)
diff --git a/code/datums/components/deployable.dm b/code/datums/components/deployable.dm
index f45a5b226c39d..ac0f006fb6cde 100644
--- a/code/datums/components/deployable.dm
+++ b/code/datums/components/deployable.dm
@@ -68,7 +68,7 @@
return
new_direction = user.dir //Gets the direction for thing_to_be_deployed if there is a user
source.balloon_alert(user, "deploying...")
- playsound(source, 'sound/items/ratchet.ogg', 50, TRUE)
+ playsound(source, 'sound/items/tools/ratchet.ogg', 50, TRUE)
if(!do_after(user, deploy_time))
return
else // If there is for some reason no user, then the location and direction are set here
diff --git a/code/datums/components/direct_explosive_trap.dm b/code/datums/components/direct_explosive_trap.dm
index e3a125eb928ed..1372c569bbade 100644
--- a/code/datums/components/direct_explosive_trap.dm
+++ b/code/datums/components/direct_explosive_trap.dm
@@ -74,7 +74,7 @@
to_chat(victim, span_bolddanger("[source] was boobytrapped!"))
if (!isnull(saboteur))
to_chat(saboteur, span_bolddanger("Success! Your trap on [source] caught [victim.name]!"))
- playsound(source, 'sound/effects/explosion2.ogg', 200, TRUE)
+ playsound(source, 'sound/effects/explosion/explosion2.ogg', 200, TRUE)
new /obj/effect/temp_visual/explosion(get_turf(source))
EX_ACT(victim, explosive_force)
qdel(src)
diff --git a/code/datums/components/drift.dm b/code/datums/components/drift.dm
deleted file mode 100644
index 7fba50d315178..0000000000000
--- a/code/datums/components/drift.dm
+++ /dev/null
@@ -1,194 +0,0 @@
-///Component that handles drifting
-///Manages a movement loop that actually does the legwork of moving someone
-///Alongside dealing with the post movement input blocking required to make things look nice
-/datum/component/drift
- var/atom/inertia_last_loc
- var/old_dir
- var/datum/move_loop/move/drifting_loop
- ///Should we ignore the next glide rate input we get?
- ///This is to some extent a hack around the order of operations
- ///Around COMSIG_MOVELOOP_POSTPROCESS. I'm sorry lad
- var/ignore_next_glide = FALSE
- ///Have we been delayed? IE: active, but not working right this second?
- var/delayed = FALSE
- var/block_inputs_until
-
-/// Accepts three args. The direction to drift in, if the drift is instant or not, and if it's not instant, the delay on the start
-/datum/component/drift/Initialize(direction, instant = FALSE, start_delay = 0)
- if(!ismovable(parent))
- return COMPONENT_INCOMPATIBLE
- . = ..()
-
- var/flags = MOVEMENT_LOOP_OUTSIDE_CONTROL
- if(instant)
- flags |= MOVEMENT_LOOP_START_FAST
- var/atom/movable/movable_parent = parent
- drifting_loop = GLOB.move_manager.move(moving = parent, direction = direction, delay = movable_parent.inertia_move_delay, subsystem = SSspacedrift, priority = MOVEMENT_SPACE_PRIORITY, flags = flags)
-
- if(!drifting_loop) //Really want to qdel here but can't
- return COMPONENT_INCOMPATIBLE
-
- RegisterSignal(drifting_loop, COMSIG_MOVELOOP_START, PROC_REF(drifting_start))
- RegisterSignal(drifting_loop, COMSIG_MOVELOOP_STOP, PROC_REF(drifting_stop))
- RegisterSignal(drifting_loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(before_move))
- RegisterSignal(drifting_loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(after_move))
- RegisterSignal(drifting_loop, COMSIG_QDELETING, PROC_REF(loop_death))
- RegisterSignal(movable_parent, COMSIG_MOVABLE_NEWTONIAN_MOVE, PROC_REF(newtonian_impulse))
- if(drifting_loop.status & MOVELOOP_STATUS_RUNNING)
- drifting_start(drifting_loop) // There's a good chance it'll autostart, gotta catch that
-
- var/visual_delay = movable_parent.inertia_move_delay
-
- // Start delay is essentially a more granular version of instant
- // Isn't used in the standard case, just for things that have odd wants
- if(!instant && start_delay)
- drifting_loop.pause_for(start_delay)
- visual_delay = start_delay
-
- apply_initial_visuals(visual_delay)
-
-/datum/component/drift/Destroy()
- inertia_last_loc = null
- if(!QDELETED(drifting_loop))
- qdel(drifting_loop)
- drifting_loop = null
- var/atom/movable/movable_parent = parent
- movable_parent.inertia_moving = FALSE
- return ..()
-
-/datum/component/drift/proc/apply_initial_visuals(visual_delay)
- // If something "somewhere" doesn't want us to apply our glidesize delays, don't
- if(SEND_SIGNAL(parent, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT) & DRIFT_VISUAL_FAILED)
- return
-
- // Ignore the next glide because it's literally just us
- ignore_next_glide = TRUE
- var/atom/movable/movable_parent = parent
- movable_parent.set_glide_size(MOVEMENT_ADJUSTED_GLIDE_SIZE(visual_delay, SSspacedrift.visual_delay))
- if(ismob(parent))
- var/mob/mob_parent = parent
- //Ok this is slightly weird, but basically, we need to force the client to glide at our rate
- //Make sure moving into a space move looks like a space move essentially
- //There is an inbuilt assumption that gliding will be added as a part of a move call, but eh
- //It's ok if it's not, it's just important if it is.
- mob_parent.client?.visual_delay = MOVEMENT_ADJUSTED_GLIDE_SIZE(visual_delay, SSspacedrift.visual_delay)
-
-/datum/component/drift/proc/newtonian_impulse(datum/source, inertia_direction)
- SIGNAL_HANDLER
- var/atom/movable/movable_parent = parent
- inertia_last_loc = movable_parent.loc
- if(drifting_loop)
- drifting_loop.direction = inertia_direction
- if(!inertia_direction)
- qdel(src)
- return COMPONENT_MOVABLE_NEWTONIAN_BLOCK
-
-/datum/component/drift/proc/drifting_start()
- SIGNAL_HANDLER
- var/atom/movable/movable_parent = parent
- inertia_last_loc = movable_parent.loc
- RegisterSignal(movable_parent, COMSIG_MOVABLE_MOVED, PROC_REF(handle_move))
- // We will use glide size to intuit how long to delay our loop's next move for
- // This way you can't ride two movements at once while drifting, since that'd be dumb as fuck
- RegisterSignal(movable_parent, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, PROC_REF(handle_glidesize_update))
- // If you stop pulling something mid drift, I want it to retain that momentum
- RegisterSignal(movable_parent, COMSIG_ATOM_NO_LONGER_PULLING, PROC_REF(stopped_pulling))
-
-/datum/component/drift/proc/drifting_stop()
- SIGNAL_HANDLER
- var/atom/movable/movable_parent = parent
- movable_parent.inertia_moving = FALSE
- ignore_next_glide = FALSE
- UnregisterSignal(movable_parent, list(COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, COMSIG_ATOM_NO_LONGER_PULLING))
-
-/datum/component/drift/proc/before_move(datum/source)
- SIGNAL_HANDLER
- var/atom/movable/movable_parent = parent
- movable_parent.inertia_moving = TRUE
- old_dir = movable_parent.dir
- delayed = FALSE
-
-/datum/component/drift/proc/after_move(datum/source, result, visual_delay)
- SIGNAL_HANDLER
- if(result == MOVELOOP_FAILURE)
- qdel(src)
- return
-
- var/atom/movable/movable_parent = parent
- movable_parent.setDir(old_dir)
- movable_parent.inertia_moving = FALSE
- if(movable_parent.Process_Spacemove(drifting_loop.direction, continuous_move = TRUE))
- glide_to_halt(visual_delay)
- return
-
- inertia_last_loc = movable_parent.loc
- ignore_next_glide = TRUE
-
-/datum/component/drift/proc/loop_death(datum/source)
- SIGNAL_HANDLER
- drifting_loop = null
- UnregisterSignal(parent, COMSIG_MOVABLE_NEWTONIAN_MOVE) // We won't block a component from replacing us anymore
-
-/datum/component/drift/proc/handle_move(datum/source, old_loc)
- SIGNAL_HANDLER
- // This can happen, because signals once sent cannot be stopped
- if(QDELETED(src))
- return
- var/atom/movable/movable_parent = parent
- if(!isturf(movable_parent.loc))
- qdel(src)
- return
- if(movable_parent.inertia_moving)
- return
- if(!movable_parent.Process_Spacemove(drifting_loop.direction, continuous_move = TRUE))
- return
- qdel(src)
-
-/// We're going to take the passed in glide size
-/// and use it to manually delay our loop for that period
-/// to allow the other movement to complete
-/datum/component/drift/proc/handle_glidesize_update(datum/source, glide_size)
- SIGNAL_HANDLER
- // If we aren't drifting, or this is us, fuck off
- var/atom/movable/movable_parent = parent
- if(!drifting_loop || movable_parent.inertia_moving)
- return
- // If we are drifting, but this set came from the moveloop itself, drop the input
- // I'm sorry man
- if(ignore_next_glide)
- ignore_next_glide = FALSE
- return
- var/glide_delay = round(world.icon_size / glide_size, 1) * world.tick_lag
- drifting_loop.pause_for(glide_delay)
- delayed = TRUE
-
-/// If we're pulling something and stop, we want it to continue at our rate and such
-/datum/component/drift/proc/stopped_pulling(datum/source, atom/movable/was_pulling)
- SIGNAL_HANDLER
- // This does mean it falls very slightly behind, but otherwise they'll potentially run into us
- var/next_move_in = drifting_loop.timer - world.time + world.tick_lag
- was_pulling.newtonian_move(drifting_loop.direction, start_delay = next_move_in)
-
-/datum/component/drift/proc/glide_to_halt(glide_for)
- if(!ismob(parent))
- qdel(src)
- return
-
- var/mob/mob_parent = parent
- var/client/our_client = mob_parent.client
- // If we're not active, don't do the glide because it'll look dumb as fuck
- if(!our_client || delayed)
- qdel(src)
- return
-
- block_inputs_until = world.time + glide_for
- QDEL_IN(src, glide_for + 1)
- qdel(drifting_loop)
- RegisterSignal(parent, COMSIG_MOB_CLIENT_PRE_MOVE, PROC_REF(allow_final_movement))
-
-/datum/component/drift/proc/allow_final_movement(datum/source)
- // Some things want to allow movement out of spacedrift, we should let them
- if(SEND_SIGNAL(parent, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT) & DRIFT_ALLOW_INPUT)
- return
- if(world.time < block_inputs_until)
- return COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE
diff --git a/code/datums/components/echolocation.dm b/code/datums/components/echolocation.dm
index 51ab89a2be564..4fda54ac0f50e 100644
--- a/code/datums/components/echolocation.dm
+++ b/code/datums/components/echolocation.dm
@@ -156,7 +156,7 @@
copied_appearance.pixel_x = 0
copied_appearance.pixel_y = 0
copied_appearance.transform = matrix()
- if(!iscarbon(input)) //wacky overlay people get generated everytime
+ if(input.icon && input.icon_state)
saved_appearances["[input.icon]-[input.icon_state]"] = copied_appearance
return copied_appearance
diff --git a/code/datums/components/effect_remover.dm b/code/datums/components/effect_remover.dm
index a67962250dbe1..c8490d760f1f8 100644
--- a/code/datums/components/effect_remover.dm
+++ b/code/datums/components/effect_remover.dm
@@ -66,6 +66,10 @@
if(!isliving(user))
return NONE
+ if(HAS_TRAIT(target, TRAIT_ILLUSORY_EFFECT))
+ to_chat(user, span_notice("You pass [parent] through the [target], but nothing seems to happen. Is it really even there?"))
+ return NONE
+
if(is_type_in_typecache(target, effects_we_clear)) // Make sure we get all subtypes and everything
INVOKE_ASYNC(src, PROC_REF(do_remove_effect), target, user)
return ITEM_INTERACT_SUCCESS
diff --git a/code/datums/components/embedded.dm b/code/datums/components/embedded.dm
index 84bfa8dfad0f0..6fc61db5e76a6 100644
--- a/code/datums/components/embedded.dm
+++ b/code/datums/components/embedded.dm
@@ -57,7 +57,7 @@
var/damage = weapon.throwforce
if(harmful)
victim.throw_alert(ALERT_EMBEDDED_OBJECT, /atom/movable/screen/alert/embeddedobject)
- playsound(victim,'sound/weapons/bladeslice.ogg', 40)
+ playsound(victim,'sound/items/weapons/bladeslice.ogg', 40)
if (limb.can_bleed())
weapon.add_mob_blood(victim)//it embedded itself in you, of course it's bloody!
damage += weapon.w_class * embed_data.impact_pain_mult
diff --git a/code/datums/components/engraved.dm b/code/datums/components/engraved.dm
index 60bfa5f617729..5db43b8076cd2 100644
--- a/code/datums/components/engraved.dm
+++ b/code/datums/components/engraved.dm
@@ -67,13 +67,13 @@
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
//supporting component transfer means putting these here instead of initialize
SSpersistence.wall_engravings += src
- ADD_TRAIT(parent, TRAIT_NOT_ENGRAVABLE, TRAIT_GENERIC)
+ ADD_TRAIT(parent, TRAIT_NOT_ENGRAVABLE, ENGRAVED_TRAIT)
/datum/component/engraved/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_ATOM_EXAMINE)
//supporting component transfer means putting these here instead of destroy
SSpersistence.wall_engravings -= src
- REMOVE_TRAIT(parent, TRAIT_NOT_ENGRAVABLE, TRAIT_GENERIC)
+ REMOVE_TRAIT(parent, TRAIT_NOT_ENGRAVABLE, ENGRAVED_TRAIT)
/// Used to maintain the acid overlay on the parent [/atom].
/datum/component/engraved/proc/on_update_overlays(atom/parent_atom, list/overlays)
diff --git a/code/datums/components/explodable.dm b/code/datums/components/explodable.dm
index 439b156352104..9dc8db3bbc4f1 100644
--- a/code/datums/components/explodable.dm
+++ b/code/datums/components/explodable.dm
@@ -60,10 +60,11 @@
return
check_if_detonate(I)
-/datum/component/explodable/proc/explodable_impact(datum/source, atom/hit_atom, datum/thrownthing/throwingdatum)
+/datum/component/explodable/proc/explodable_impact(datum/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
SIGNAL_HANDLER
- check_if_detonate(hit_atom)
+ if(!caught)
+ check_if_detonate(hit_atom)
/datum/component/explodable/proc/explodable_bump(datum/source, atom/A)
SIGNAL_HANDLER
diff --git a/code/datums/components/face_decal.dm b/code/datums/components/face_decal.dm
index f045a4f18df6e..df70f8a3f4989 100644
--- a/code/datums/components/face_decal.dm
+++ b/code/datums/components/face_decal.dm
@@ -53,6 +53,8 @@
carbon_parent.update_body_parts()
else
normal_overlay = get_normal_overlay()
+ normal_overlay.color = color
+
RegisterSignals(parent, list(
COMSIG_COMPONENT_CLEAN_ACT,
@@ -113,37 +115,39 @@
SIGNAL_HANDLER
qdel(src)
-/// Creampie subtype, handling signals and mood logic
+/// splat subtype, handling signals and mood logic
-GLOBAL_LIST_INIT(creamable, typecacheof(list(
- /mob/living/carbon/human,
- /mob/living/basic/pet/dog/corgi,
- /mob/living/silicon/ai,
+GLOBAL_LIST_INIT(splattable, zebra_typecacheof(list(
+ /mob/living/carbon/human = "human",
+ /mob/living/basic/pet/dog/corgi = "corgi",
+ /mob/living/silicon/ai = "ai",
)))
-/datum/component/face_decal/creampie/Initialize()
- . = ..()
- if(!is_type_in_typecache(parent, GLOB.creamable))
+/datum/component/face_decal/splat
+ ///The mood_event that we add
+ var/mood_event_type
+
+/datum/component/face_decal/splat/Initialize(icon_state, layers, color, memory_type = /datum/memory/witnessed_creampie, mood_event_type = /datum/mood_event/creampie)
+ if(!is_type_in_typecache(parent, GLOB.splattable))
return COMPONENT_INCOMPATIBLE
- SEND_SIGNAL(parent, COMSIG_MOB_CREAMED, src)
- add_memory_in_range(parent, 7, /datum/memory/witnessed_creampie, protagonist = parent)
+ . = ..()
-/datum/component/face_decal/creampie/get_normal_overlay()
- if(iscorgi(parent))
- return mutable_appearance('icons/mob/effects/creampie.dmi', "[icon_state]_corgi")
+ SEND_SIGNAL(parent, COMSIG_MOB_HIT_BY_SPLAT, src)
+ add_memory_in_range(parent, 7, memory_type, protagonist = parent)
+ src.mood_event_type = mood_event_type
- if(isAI(parent))
- return mutable_appearance('icons/mob/effects/creampie.dmi', "[icon_state]_ai")
+/datum/component/face_decal/splat/get_normal_overlay()
+ return mutable_appearance('icons/mob/effects/face_decal.dmi', "[icon_state]_[GLOB.splattable[type]]")
-/datum/component/face_decal/creampie/RegisterWithParent()
+/datum/component/face_decal/splat/RegisterWithParent()
. = ..()
if(iscarbon(parent))
var/mob/living/carbon/human/carbon_parent = parent
- carbon_parent.add_mood_event("creampie", /datum/mood_event/creampie)
+ carbon_parent.add_mood_event("splat", mood_event_type)
-/datum/component/face_decal/creampie/UnregisterFromParent()
+/datum/component/face_decal/splat/UnregisterFromParent()
. = ..()
if(iscarbon(parent))
var/mob/living/carbon/carbon_parent = parent
- carbon_parent.clear_mood_event("creampie")
+ carbon_parent.clear_mood_event("splat")
diff --git a/code/datums/components/fish_growth.dm b/code/datums/components/fish_growth.dm
index bc7c8a9869e44..3ec1427fd51a8 100644
--- a/code/datums/components/fish_growth.dm
+++ b/code/datums/components/fish_growth.dm
@@ -11,43 +11,90 @@
var/use_drop_loc
///Is the parent deleted once the result is spawned?
var/del_on_grow
+ ///Will the result inherit the name of the fish if that was changed from the initial name.
+ var/inherit_name
-/datum/component/fish_growth/Initialize(result_type, growth_rate, use_drop_loc = TRUE, del_on_grow = TRUE)
+/datum/component/fish_growth/Initialize(result_type, growth_time, use_drop_loc = TRUE, del_on_grow = TRUE, inherit_name = TRUE)
. = ..()
if(!isfish(parent))
return COMPONENT_INCOMPATIBLE
- RegisterSignal(parent, COMSIG_FISH_LIFE, PROC_REF(on_fish_life))
src.result_type = result_type
- src.growth_rate = growth_rate
+ growth_rate = 100 / growth_time
src.use_drop_loc = use_drop_loc
src.del_on_grow = del_on_grow
+ src.inherit_name = inherit_name
-/datum/component/fish_growth/CheckDupeComponent(result_type, growth_rate, use_drop_loc = TRUE, del_on_grow = TRUE)
+/datum/component/fish_growth/CheckDupeComponent(
+ datum/component/fish_growth/new_growth, // will be null
+ result_type,
+ growth_time,
+ use_drop_loc = TRUE,
+ del_on_grow = TRUE,
+ inherit_name = TRUE,
+)
if(result_type == src.result_type)
- src.growth_rate = growth_rate
+ growth_rate = 100 / growth_time
return TRUE //copy the growth rate and kill the new component
return FALSE
+/datum/component/fish_growth/RegisterWithParent()
+ var/evo_growth = ispath(result_type, /datum/fish_evolution)
+ RegisterSignal(parent, COMSIG_FISH_LIFE, PROC_REF(on_fish_life))
+ if(!evo_growth)
+ return
+ var/datum/fish_evolution/evolution = GLOB.fish_evolutions[result_type]
+ evolution.RegisterSignal(parent, COMSIG_FISH_BEFORE_GROWING, TYPE_PROC_REF(/datum/fish_evolution, growth_checks))
+ evolution.register_fish(parent)
+
+/datum/component/fish_growth/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_FISH_LIFE, COMSIG_FISH_BEFORE_GROWING))
+
/datum/component/fish_growth/proc/on_fish_life(obj/item/fish/source, seconds_per_tick)
SIGNAL_HANDLER
- if(SEND_SIGNAL(source, COMSIG_FISH_BEFORE_GROWING, seconds_per_tick) & COMPONENT_DONT_GROW)
+ if(source.status == FISH_DEAD) //It died just now.
return
- maturation += growth_rate * seconds_per_tick
+ var/deciseconds_elapsed = seconds_per_tick * 10
+ var/growth = growth_rate * deciseconds_elapsed
+ if(SEND_SIGNAL(source, COMSIG_FISH_BEFORE_GROWING, seconds_per_tick, growth) & COMPONENT_DONT_GROW)
+ return
+ maturation += growth
if(maturation >= 100)
finish_growing(source)
/datum/component/fish_growth/proc/finish_growing(obj/item/fish/source)
var/atom/location = use_drop_loc ? source.drop_location() : source.loc
- var/atom/movable/result = new result_type (location)
- if(location != source.loc)
- result.visible_message(span_boldnotice("\A [result] jumps out of [source.loc]!"))
- playsound(result, 'sound/effects/fish_splash.ogg', 60)
- if(isbasicmob(result))
- for(var/trait_type in source.fish_traits)
- var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
- trait.apply_to_mob(result)
-
- addtimer(CALLBACK(result, TYPE_PROC_REF(/mob/living/basic, hop_on_nearby_turf)), 0.1 SECONDS)
+ var/is_evo = ispath(result_type, /datum/fish_evolution)
+ var/atom/movable/result
+ if(is_evo)
+ var/datum/fish_evolution/evolution = GLOB.fish_evolutions[result_type]
+ result = source.create_offspring(evolution.new_fish_type, evolution = evolution)
+ var/obj/item/fish/fishie = result
+ fishie.breeding_wait = source.breeding_wait
+ fishie.last_feeding = source.last_feeding
+ var/health_percent = source.health / initial(source.health)
+ fishie.adjust_health(fishie.health * health_percent)
+ else
+ result = new result_type (location)
+ if(location != source.loc)
+ result.visible_message(span_boldnotice("\A [result] jumps out of [source.loc]!"))
+ playsound(result, 'sound/effects/fish_splash.ogg', 60)
+ if(isbasicmob(result))
+ for(var/trait_type in source.fish_traits)
+ var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
+ trait.apply_to_mob(result)
+
+ addtimer(CALLBACK(result, TYPE_PROC_REF(/mob/living/basic, hop_on_nearby_turf)), 0.1 SECONDS)
+
+ if(is_evo || location == source.loc)
+ var/message_verb = del_on_grow ? "grows into" : "generates"
+ location.visible_message(span_notice("[source] [message_verb] \a [result]."), vision_distance = 3)
+
+ if(inherit_name && source.name != initial(source.name))
+ if(ismob(result))
+ var/mob/mob = result
+ mob.fully_replace_character_name(mob.name, source.name)
+ else
+ result.name = source.name
SEND_SIGNAL(source, COMSIG_FISH_FINISH_GROWING, result)
diff --git a/code/datums/components/fishing_spot.dm b/code/datums/components/fishing_spot.dm
index cc21f22eb4b62..982b0da2df71a 100644
--- a/code/datums/components/fishing_spot.dm
+++ b/code/datums/components/fishing_spot.dm
@@ -19,11 +19,15 @@
RegisterSignal(parent, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(on_examined_more))
RegisterSignal(parent, COMSIG_NPC_FISHING, PROC_REF(return_fishing_spot))
RegisterSignal(parent, COMSIG_ATOM_EX_ACT, PROC_REF(explosive_fishing))
+ RegisterSignal(parent, COMSIG_FISH_RELEASED_INTO, PROC_REF(fish_released))
+ RegisterSignal(parent, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(link_to_fish_porter))
ADD_TRAIT(parent, TRAIT_FISHING_SPOT, REF(src))
/datum/component/fishing_spot/Destroy()
+ REMOVE_TRAIT(parent, TRAIT_FISHING_SPOT, REF(src))
fish_source.on_fishing_spot_del(src)
fish_source = null
+ REMOVE_TRAIT(parent, TRAIT_FISHING_SPOT, REF(src))
return ..()
/datum/component/fishing_spot/proc/handle_cast(datum/source, obj/item/fishing_rod/rod, mob/user)
@@ -44,15 +48,7 @@
if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT))
return
- var/has_known_fishes = FALSE
- for(var/reward in fish_source.fish_table)
- if(!ispath(reward, /obj/item/fish))
- continue
- var/obj/item/fish/prototype = reward
- if(initial(prototype.show_in_catalog))
- has_known_fishes = TRUE
- break
- if(!has_known_fishes)
+ if(!fish_source.has_known_fishes())
return
examine_text += span_tinynoticeital("This is a fishing spot. You can look again to list its fishes...")
@@ -62,25 +58,14 @@
if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT))
return
- var/list/known_fishes = list()
- for(var/reward in fish_source.fish_table)
- if(!ispath(reward, /obj/item/fish))
- continue
- var/obj/item/fish/prototype = reward
- if(initial(prototype.show_in_catalog))
- known_fishes += initial(prototype.name)
-
- if(!length(known_fishes))
- return
-
- examine_text += span_info("You can catch the following fish here: [english_list(known_fishes)].")
+ fish_source.get_catchable_fish_names(user, parent, examine_text)
/datum/component/fishing_spot/proc/try_start_fishing(obj/item/possibly_rod, mob/user)
SIGNAL_HANDLER
var/obj/item/fishing_rod/rod = possibly_rod
if(!istype(rod))
return
- if(HAS_TRAIT(user,TRAIT_GONE_FISHING) || rod.fishing_line)
+ if(GLOB.fishing_challenges_by_user[user] || rod.fishing_line)
user.balloon_alert(user, "already fishing")
return COMPONENT_NO_AFTERATTACK
var/denial_reason = fish_source.reason_we_cant_fish(rod, user, parent)
@@ -89,15 +74,10 @@
return COMPONENT_NO_AFTERATTACK
// In case the fishing source has anything else to do before beginning to fish.
fish_source.on_start_fishing(rod, user, parent)
- start_fishing_challenge(rod, user)
- return COMPONENT_NO_AFTERATTACK
-
-/datum/component/fishing_spot/proc/start_fishing_challenge(obj/item/fishing_rod/rod, mob/user)
- /// Roll what we caught based on modified table
- var/result = fish_source.roll_reward(rod, user)
- var/datum/fishing_challenge/challenge = new(src, result, rod, user)
+ var/datum/fishing_challenge/challenge = new(src, rod, user)
fish_source.pre_challenge_started(rod, user, challenge)
challenge.start(user)
+ return COMPONENT_NO_AFTERATTACK
/datum/component/fishing_spot/proc/return_fishing_spot(datum/source, list/fish_spot_container)
fish_spot_container[NPC_FISHING_SPOT] = fish_source
@@ -105,3 +85,13 @@
/datum/component/fishing_spot/proc/explosive_fishing(atom/location, severity)
SIGNAL_HANDLER
fish_source.spawn_reward_from_explosion(location, severity)
+
+/datum/component/fishing_spot/proc/link_to_fish_porter(atom/source, mob/user, obj/item/multitool/tool)
+ SIGNAL_HANDLER
+ if(istype(tool.buffer, /obj/machinery/fishing_portal_generator))
+ var/obj/machinery/fishing_portal_generator/portal = tool.buffer
+ return portal.link_fishing_spot(fish_source, source, user)
+
+/datum/component/fishing_spot/proc/fish_released(datum/source, obj/item/fish/fish, mob/living/releaser)
+ SIGNAL_HANDLER
+ fish_source.readd_fish(fish, releaser)
diff --git a/code/datums/components/food/edible.dm b/code/datums/components/food/edible.dm
index c034300f982fe..9e2964273fd48 100644
--- a/code/datums/components/food/edible.dm
+++ b/code/datums/components/food/edible.dm
@@ -40,8 +40,6 @@ Behavior that's still missing from this component that original food items had t
var/volume = 50
///The flavortext for taste (haha get it flavor text)
var/list/tastes
- ///Whether to tell the examiner that this is edible or not.
- var/show_examine = TRUE
/datum/component/edible/Initialize(
list/initial_reagents,
@@ -57,7 +55,6 @@ Behavior that's still missing from this component that original food items had t
datum/callback/on_consume,
datum/callback/check_liked,
reagent_purity = 0.5,
- show_examine = TRUE,
)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
@@ -73,7 +70,6 @@ Behavior that's still missing from this component that original food items had t
src.on_consume = on_consume
src.tastes = string_assoc_list(tastes)
src.check_liked = check_liked
- src.show_examine = show_examine
setup_initial_reagents(initial_reagents, reagent_purity)
@@ -81,9 +77,9 @@ Behavior that's still missing from this component that original food items had t
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(examine))
RegisterSignal(parent, COMSIG_ATOM_ATTACK_ANIMAL, PROC_REF(UseByAnimal))
RegisterSignal(parent, COMSIG_ATOM_CHECKPARTS, PROC_REF(OnCraft))
- RegisterSignal(parent, COMSIG_ATOM_CREATEDBY_PROCESSING, PROC_REF(OnProcessed))
- RegisterSignal(parent, COMSIG_FOOD_INGREDIENT_ADDED, PROC_REF(edible_ingredient_added))
RegisterSignal(parent, COMSIG_OOZE_EAT_ATOM, PROC_REF(on_ooze_eat))
+ RegisterSignal(parent, COMSIG_FOOD_INGREDIENT_ADDED, PROC_REF(edible_ingredient_added))
+ RegisterSignal(parent, COMSIG_ATOM_CREATEDBY_PROCESSING, PROC_REF(OnProcessed))
if(isturf(parent))
RegisterSignal(parent, COMSIG_ATOM_ENTERED, PROC_REF(on_entered))
@@ -216,7 +212,7 @@ Behavior that's still missing from this component that original food items had t
SIGNAL_HANDLER
var/atom/owner = parent
- if(!show_examine)
+ if(food_flags & FOOD_NO_EXAMINE)
return
if(foodtypes)
var/list/types = bitfield_to_list(foodtypes, FOOD_FLAGS)
@@ -316,7 +312,6 @@ Behavior that's still missing from this component that original food items had t
SIGNAL_HANDLER
var/atom/this_food = parent
-
for(var/obj/item/food/crafted_part in parts_list)
if(!crafted_part.reagents)
continue
@@ -325,7 +320,7 @@ Behavior that's still missing from this component that original food items had t
this_food.reagents.maximum_volume = ROUND_UP(this_food.reagents.maximum_volume) // Just because I like whole numbers for this.
- BLACKBOX_LOG_FOOD_MADE(this_food.type)
+ BLACKBOX_LOG_FOOD_MADE(parent.type)
///Makes sure the thing hasn't been destroyed or fully eaten to prevent eating phantom edibles
/datum/component/edible/proc/IsFoodGone(atom/owner, mob/living/feeder)
@@ -462,7 +457,7 @@ Behavior that's still missing from this component that original food items had t
var/atom/owner = parent
- if(!owner?.reagents)
+ if(!owner.reagents)
stack_trace("[eater] failed to bite [owner], because [owner] had no reagents.")
return FALSE
if(eater.satiety > -200)
@@ -479,7 +474,8 @@ Behavior that's still missing from this component that original food items had t
if(bitecount == 0)
apply_buff(eater)
- var/fraction = min(bite_consumption / owner.reagents.total_volume, 1)
+ var/fraction = 0.3
+ fraction = min(bite_consumption / owner.reagents.total_volume, 1)
owner.reagents.trans_to(eater, bite_consumption, transferred_by = feeder, methods = INGEST)
bitecount++
@@ -489,8 +485,7 @@ Behavior that's still missing from this component that original food items had t
On_Consume(eater, feeder)
//Invoke our after eat callback if it is valid
- if(after_eat)
- after_eat.Invoke(eater, feeder, bitecount)
+ after_eat?.Invoke(eater, feeder, bitecount)
//Invoke the eater's stomach's after_eat callback if valid
if(iscarbon(eater))
@@ -531,7 +526,7 @@ Behavior that's still missing from this component that original food items had t
if(recipe_complexity <= 0)
return
var/obj/item/food/food = parent
- if(!isnull(food.crafted_food_buff))
+ if(istype(food) && !isnull(food.crafted_food_buff))
buff = food.crafted_food_buff
else
buff = pick_weight(GLOB.food_buffs[min(recipe_complexity, FOOD_COMPLEXITY_5)])
@@ -666,7 +661,7 @@ Behavior that's still missing from this component that original food items had t
/datum/component/edible/proc/UseByAnimal(datum/source, mob/living/basic/pet/dog/doggy)
SIGNAL_HANDLER
- if(!isdog(doggy))
+ if(!isdog(doggy) || (food_flags & FOOD_NO_BITECOUNT)) //this entirely relies on bitecounts alas
return
var/atom/food = parent
diff --git a/code/datums/components/gps.dm b/code/datums/components/gps.dm
index 7e52f00def752..0b3751856b8a2 100644
--- a/code/datums/components/gps.dm
+++ b/code/datums/components/gps.dm
@@ -162,7 +162,7 @@ GLOBAL_LIST_EMPTY(GPS_list)
switch(action)
if("rename")
var/atom/parentasatom = parent
- var/a = tgui_input_text(usr, "Enter the desired tag", "GPS Tag", gpstag, 20)
+ var/a = tgui_input_text(usr, "Enter the desired tag", "GPS Tag", gpstag, max_length = 20)
if (QDELETED(ui) || ui.status != UI_INTERACTIVE)
return
if (!a)
diff --git a/code/datums/components/hide_weather_planes.dm b/code/datums/components/hide_weather_planes.dm
new file mode 100644
index 0000000000000..97f34f57d313e
--- /dev/null
+++ b/code/datums/components/hide_weather_planes.dm
@@ -0,0 +1,136 @@
+/**
+ * Component that manages a list of plane masters that are dependent on weather
+ * Force hides/shows them depending on the weather activity of their z stack
+ * Transparency is achieved by manipulating the alpha of the planes that are visible
+ * Applied to the plane master group that owns them
+ */
+/datum/component/hide_weather_planes
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+ var/list/datum/weather/active_weather = list()
+ var/list/atom/movable/screen/plane_master/plane_masters = list()
+
+/datum/component/hide_weather_planes/Initialize(atom/movable/screen/plane_master/care_about)
+ if(!istype(parent, /datum/plane_master_group))
+ return COMPONENT_INCOMPATIBLE
+ var/datum/plane_master_group/home = parent
+ plane_masters += care_about
+ RegisterSignal(care_about, COMSIG_QDELETING, PROC_REF(plane_master_deleted))
+
+ var/list/starting_signals = list()
+ var/list/ending_signals = list()
+ for(var/datum/weather/weather_type as anything in typesof(/datum/weather))
+ starting_signals += COMSIG_WEATHER_TELEGRAPH(weather_type)
+ ending_signals += COMSIG_WEATHER_END(weather_type)
+
+ RegisterSignals(SSdcs, starting_signals, PROC_REF(weather_started))
+ RegisterSignals(SSdcs, ending_signals, PROC_REF(weather_finished))
+
+ if(home.our_hud)
+ attach_hud(home.our_hud)
+ else
+ RegisterSignal(home, COMSIG_GROUP_HUD_CHANGED, PROC_REF(new_hud_attached))
+
+/datum/component/hide_weather_planes/Destroy(force)
+ hide_planes()
+ active_weather = null
+ plane_masters = null
+ return ..()
+
+/datum/component/hide_weather_planes/InheritComponent(datum/component/new_comp, i_am_original, atom/movable/screen/plane_master/care_about)
+ if(!i_am_original)
+ return
+ var/datum/plane_master_group/home = parent
+ var/mob/our_lad = home.our_hud?.mymob
+ var/our_offset = GET_TURF_PLANE_OFFSET(our_lad)
+ plane_masters += care_about
+ RegisterSignal(care_about, COMSIG_QDELETING, PROC_REF(plane_master_deleted))
+ if(length(active_weather))
+ //If there's weather to care about we unhide our new plane and adjust its alpha
+ care_about.unhide_plane(our_lad)
+
+ if(care_about.offset >= our_offset)
+ care_about.enable_alpha()
+ else
+ care_about.disable_alpha()
+ else
+ care_about.hide_plane(our_lad)
+
+/datum/component/hide_weather_planes/proc/new_hud_attached(datum/source, datum/hud/new_hud)
+ SIGNAL_HANDLER
+ attach_hud(new_hud)
+
+/datum/component/hide_weather_planes/proc/attach_hud(datum/hud/new_hud)
+ RegisterSignal(new_hud, COMSIG_HUD_Z_CHANGED, PROC_REF(z_changed))
+ var/mob/eye = new_hud?.mymob?.client?.eye
+ var/turf/eye_location = get_turf(eye)
+ z_changed(new_hud, eye_location?.z)
+
+/datum/component/hide_weather_planes/proc/plane_master_deleted(atom/movable/screen/plane_master/source)
+ SIGNAL_HANDLER
+ plane_masters -= source
+
+/**
+ * Unhides the relevant planes for the weather to be visible and manipulated.
+ * Also updates the alpha of the planes so enabled planes are either fully opaque or fully transparent
+ */
+/datum/component/hide_weather_planes/proc/display_planes()
+ var/datum/plane_master_group/home = parent
+ var/mob/our_lad = home.our_hud?.mymob
+ var/our_offset = GET_TURF_PLANE_OFFSET(our_lad)
+ for(var/atom/movable/screen/plane_master/weather_concious as anything in plane_masters)
+ //If the plane is hidden, unhide it
+ if(weather_concious.force_hidden)
+ weather_concious.unhide_plane(our_lad)
+
+ //Now we update the alpha of the plane based on our offset. Weather above us (lower offset) are transparent, weather at or below us (higher offset) are opaque.
+ if(weather_concious.offset >= our_offset)
+ weather_concious.enable_alpha()
+ else
+ weather_concious.disable_alpha()
+
+///Hides the planes from the mob when no weather is occuring
+/datum/component/hide_weather_planes/proc/hide_planes()
+ var/datum/plane_master_group/home = parent
+ var/mob/our_lad = home.our_hud?.mymob
+ for(var/atom/movable/screen/plane_master/weather_concious as anything in plane_masters)
+ weather_concious.hide_plane(our_lad)
+
+/datum/component/hide_weather_planes/proc/z_changed(datum/source, new_z)
+ SIGNAL_HANDLER
+ active_weather = list()
+ if(!SSmapping.initialized)
+ return
+
+ var/list/connected_levels = SSmapping.get_connected_levels(new_z)
+ for(var/datum/weather/active as anything in SSweather.processing)
+ if(length(connected_levels & active.impacted_z_levels))
+ active_weather += WEAKREF(active)
+
+ if(length(active_weather))
+ display_planes()
+ else
+ hide_planes()
+
+/datum/component/hide_weather_planes/proc/weather_started(datum/source, datum/weather/starting)
+ SIGNAL_HANDLER
+ var/datum/plane_master_group/home = parent
+ var/mob/eye = home.our_hud?.mymob?.client?.eye
+ var/turf/viewing_from = get_turf(eye)
+ if(!viewing_from)
+ return
+
+ var/list/connected_levels = SSmapping.get_connected_levels(viewing_from)
+ if(length(connected_levels & starting.impacted_z_levels))
+ active_weather += WEAKREF(starting)
+
+ if(!length(active_weather))
+ return
+ display_planes()
+
+/datum/component/hide_weather_planes/proc/weather_finished(datum/source, datum/weather/stopping)
+ SIGNAL_HANDLER
+ active_weather -= WEAKREF(stopping)
+
+ if(length(active_weather))
+ return
+ hide_planes()
diff --git a/code/datums/components/infective.dm b/code/datums/components/infective.dm
index bc7cc2e6af3c4..ecd2f1ff836fd 100644
--- a/code/datums/components/infective.dm
+++ b/code/datums/components/infective.dm
@@ -8,43 +8,78 @@
var/weak_infection_chance = 10
-/datum/component/infective/Initialize(list/datum/disease/_diseases, expire_in, weak = FALSE)
- if(islist(_diseases))
- diseases = _diseases
- else
- diseases = list(_diseases)
+/datum/component/infective/Initialize(list/datum/disease/diseases, expire_in, weak = FALSE, weak_infection_chance = 10)
+ if(!ismovable(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ if(!islist(diseases))
+ diseases = islist(diseases)
+
+ ///Make sure the diseases list is populated with instances of diseases so that it doesn't have to be for each AddComponent call.
+ for(var/datum/disease/disease as anything in diseases)
+ if(!disease) //empty entry, remove.
+ diseases -= disease
+ if(ispath(disease, /datum/disease))
+ var/datum/disease/instance = new disease
+ diseases -= disease
+ diseases += instance
+ else if(!istype(disease))
+ stack_trace("found [isdatum(disease) ? "an instance of [disease.type]" : disease] inside the diseases list argument for [type]")
+ diseases -= disease
+
+ src.diseases = diseases
+
if(expire_in)
expire_time = world.time + expire_in
QDEL_IN(src, expire_in)
- if(!ismovable(parent))
- return COMPONENT_INCOMPATIBLE
-
is_weak = weak
+ src.weak_infection_chance = weak_infection_chance
+
+/datum/component/infective/Destroy()
+ QDEL_LIST(diseases)
+ return ..()
+/datum/component/infective/RegisterWithParent()
if(is_weak && isitem(parent))
RegisterSignal(parent, COMSIG_FOOD_EATEN, PROC_REF(try_infect_eat))
RegisterSignal(parent, COMSIG_PILL_CONSUMED, PROC_REF(try_infect_eat))
- else
- var/static/list/disease_connections = list(
- COMSIG_ATOM_ENTERED = PROC_REF(try_infect_crossed),
- )
- AddComponent(/datum/component/connect_loc_behalf, parent, disease_connections)
-
- RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean))
- RegisterSignal(parent, COMSIG_MOVABLE_BUCKLE, PROC_REF(try_infect_buckle))
- RegisterSignal(parent, COMSIG_MOVABLE_BUMP, PROC_REF(try_infect_collide))
- RegisterSignal(parent, COMSIG_MOVABLE_IMPACT_ZONE, PROC_REF(try_infect_impact_zone))
- if(isitem(parent))
- RegisterSignal(parent, COMSIG_ITEM_ATTACK_ZONE, PROC_REF(try_infect_attack_zone))
- RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(try_infect_attack))
- RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(try_infect_equipped))
- RegisterSignal(parent, COMSIG_FOOD_EATEN, PROC_REF(try_infect_eat))
- RegisterSignal(parent, COMSIG_PILL_CONSUMED, PROC_REF(try_infect_eat))
- if(istype(parent, /obj/item/reagent_containers/cup))
- RegisterSignal(parent, COMSIG_GLASS_DRANK, PROC_REF(try_infect_drink))
- if(isorgan(parent))
- RegisterSignal(parent, COMSIG_ORGAN_IMPLANTED, PROC_REF(on_organ_insertion))
+ return
+ var/static/list/disease_connections = list(
+ COMSIG_ATOM_ENTERED = PROC_REF(try_infect_crossed),
+ )
+ AddComponent(/datum/component/connect_loc_behalf, parent, disease_connections)
+
+ RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(clean))
+ RegisterSignal(parent, COMSIG_MOVABLE_BUCKLE, PROC_REF(try_infect_buckle))
+ RegisterSignal(parent, COMSIG_MOVABLE_BUMP, PROC_REF(try_infect_collide))
+ RegisterSignal(parent, COMSIG_MOVABLE_IMPACT_ZONE, PROC_REF(try_infect_impact_zone))
+ if(isitem(parent))
+ RegisterSignal(parent, COMSIG_ITEM_ATTACK_ZONE, PROC_REF(try_infect_attack_zone))
+ RegisterSignal(parent, COMSIG_ITEM_ATTACK, PROC_REF(try_infect_attack))
+ RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(try_infect_equipped))
+ RegisterSignal(parent, COMSIG_FOOD_EATEN, PROC_REF(try_infect_eat))
+ RegisterSignal(parent, COMSIG_PILL_CONSUMED, PROC_REF(try_infect_eat))
+ if(istype(parent, /obj/item/reagent_containers/cup))
+ RegisterSignal(parent, COMSIG_GLASS_DRANK, PROC_REF(try_infect_drink))
+ if(isorgan(parent))
+ RegisterSignal(parent, COMSIG_ORGAN_IMPLANTED, PROC_REF(on_organ_insertion))
+
+/datum/component/infective/UnregisterFromParent()
+ . = ..()
+ UnregisterSignal(parent, list(
+ COMSIG_FOOD_EATEN,
+ COMSIG_PILL_CONSUMED,
+ COMSIG_COMPONENT_CLEAN_ACT,
+ COMSIG_MOVABLE_BUMP,
+ COMSIG_MOVABLE_IMPACT_ZONE,
+ COMSIG_ITEM_ATTACK_ZONE,
+ COMSIG_ITEM_ATTACK,
+ COMSIG_ITEM_EQUIPPED,
+ COMSIG_GLASS_DRANK,
+ COMSIG_ORGAN_IMPLANTED,
+ ))
+ qdel(GetComponent(/datum/component/connect_loc_behalf))
/datum/component/infective/proc/on_organ_insertion(obj/item/organ/target, mob/living/carbon/receiver)
SIGNAL_HANDLER
@@ -62,16 +97,16 @@
eater.add_mood_event("disgust", /datum/mood_event/disgust/dirty_food)
- if(is_weak && !prob(weak_infection_chance))
- return
-
- for(var/datum/disease/disease in diseases)
+ for(var/datum/disease/disease as anything in diseases)
+ if(is_weak && !prob(weak_infection_chance))
+ continue
if(!disease.has_required_infectious_organ(eater, ORGAN_SLOT_STOMACH))
continue
eater.ForceContractDisease(disease)
- try_infect(feeder, BODY_ZONE_L_ARM)
+ if(!is_weak)
+ try_infect(feeder, BODY_ZONE_L_ARM)
/datum/component/infective/proc/try_infect_drink(datum/source, mob/living/drinker, mob/living/feeder)
SIGNAL_HANDLER
@@ -79,11 +114,14 @@
if(HAS_TRAIT(drinker, TRAIT_STRONG_STOMACH))
return
- var/appendage_zone = feeder.held_items.Find(source)
- appendage_zone = appendage_zone == 0 ? BODY_ZONE_CHEST : appendage_zone % 2 ? BODY_ZONE_R_ARM : BODY_ZONE_L_ARM
- try_infect(feeder, appendage_zone)
+ if(!is_weak)
+ var/appendage_zone = feeder.held_items.Find(source)
+ appendage_zone = appendage_zone == 0 ? BODY_ZONE_CHEST : (appendage_zone % 2 ? BODY_ZONE_R_ARM : BODY_ZONE_L_ARM)
+ try_infect(feeder, appendage_zone)
- for(var/datum/disease/disease in diseases)
+ for(var/datum/disease/disease as anything in diseases)
+ if(is_weak && !prob(weak_infection_chance))
+ continue
if(!disease.has_required_infectious_organ(drinker, ORGAN_SLOT_STOMACH))
continue
@@ -163,19 +201,3 @@
/datum/component/infective/proc/try_infect(mob/living/L, target_zone)
for(var/V in diseases)
L.ContactContractDisease(V, target_zone)
-
-/datum/component/infective/UnregisterFromParent()
- . = ..()
- UnregisterSignal(parent, list(
- COMSIG_FOOD_EATEN,
- COMSIG_PILL_CONSUMED,
- COMSIG_COMPONENT_CLEAN_ACT,
- COMSIG_MOVABLE_BUMP,
- COMSIG_MOVABLE_IMPACT_ZONE,
- COMSIG_ITEM_ATTACK_ZONE,
- COMSIG_ITEM_ATTACK,
- COMSIG_ITEM_EQUIPPED,
- COMSIG_GLASS_DRANK,
- COMSIG_ORGAN_IMPLANTED,
- ))
- qdel(GetComponent(/datum/component/connect_loc_behalf))
diff --git a/code/datums/components/interaction_booby_trap.dm b/code/datums/components/interaction_booby_trap.dm
index 2ae22ffbb5ae5..ef8d3c78cfcb4 100644
--- a/code/datums/components/interaction_booby_trap.dm
+++ b/code/datums/components/interaction_booby_trap.dm
@@ -26,7 +26,7 @@
/datum/component/interaction_booby_trap/Initialize(
explosion_light_range = 3,
explosion_heavy_range = 1, // So we destroy some machine components
- triggered_sound = 'sound/machines/triple_beep.ogg',
+ triggered_sound = 'sound/machines/beep/triple_beep.ogg',
trigger_delay = 0.5 SECONDS,
sound_loop_type = /datum/looping_sound/trapped_machine_beep,
defuse_tool = TOOL_SCREWDRIVER,
diff --git a/code/datums/components/irradiated.dm b/code/datums/components/irradiated.dm
index 077539f49db8e..0f70e0d80b717 100644
--- a/code/datums/components/irradiated.dm
+++ b/code/datums/components/irradiated.dm
@@ -51,11 +51,13 @@
/datum/component/irradiated/RegisterWithParent()
RegisterSignal(parent, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_clean))
RegisterSignal(parent, COMSIG_GEIGER_COUNTER_SCAN, PROC_REF(on_geiger_counter_scan))
+ RegisterSignal(parent, COMSIG_LIVING_HEALTHSCAN, PROC_REF(on_healthscan))
/datum/component/irradiated/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_COMPONENT_CLEAN_ACT,
COMSIG_GEIGER_COUNTER_SCAN,
+ COMSIG_LIVING_HEALTHSCAN,
))
/datum/component/irradiated/Destroy(force)
@@ -138,7 +140,7 @@
if(human_parent.is_blind())
to_chat(human_parent, span_boldwarning("Your [affected_limb.plaintext_zone] feels like it's bubbling, then burns like hell!"))
- human_parent.apply_damage(RADIATION_BURN_SPLOTCH_DAMAGE, BURN, affected_limb)
+ human_parent.apply_damage(RADIATION_BURN_SPLOTCH_DAMAGE, BURN, affected_limb, wound_clothing = FALSE)
playsound(
human_parent,
pick('sound/effects/wounds/sizzle1.ogg', 'sound/effects/wounds/sizzle2.ogg'),
@@ -186,6 +188,12 @@
return COMSIG_GEIGER_COUNTER_SCAN_SUCCESSFUL
+/datum/component/irradiated/proc/on_healthscan(datum/source, list/render_list, advanced, mob/user, mode, tochat)
+ SIGNAL_HANDLER
+
+ render_list += conditional_tooltip("Subject is irradiated.", "Supply antiradiation or antitoxin, such as [/datum/reagent/medicine/potass_iodide::name] or [/datum/reagent/medicine/pen_acid::name].", tochat)
+ render_list += " "
+
/atom/movable/screen/alert/irradiated
name = "Irradiated"
desc = "You're irradiated! Heal your toxins quick, and stand under a shower to halt the incoming damage."
diff --git a/code/datums/components/itempicky.dm b/code/datums/components/itempicky.dm
index 74fbdff1caa91..bda8b1ae13881 100644
--- a/code/datums/components/itempicky.dm
+++ b/code/datums/components/itempicky.dm
@@ -5,13 +5,21 @@
var/whitelist
/// Message shown if you try to pick up an item not in the whitelist
var/message = "You don't like %TARGET, why would you hold it?"
+ /// An optional callback we check for overriding our whitelist
+ var/datum/callback/tertiary_condition = null
-/datum/component/itempicky/Initialize(whitelist, message)
+/datum/component/itempicky/Initialize(whitelist, message, tertiary_condition)
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
src.whitelist = whitelist
if(message)
src.message = message
+ if(tertiary_condition)
+ src.tertiary_condition = tertiary_condition
+
+/datum/component/itempicky/Destroy(force)
+ tertiary_condition = null
+ return ..()
/datum/component/itempicky/RegisterWithParent()
RegisterSignal(parent, COMSIG_LIVING_TRY_PUT_IN_HAND, PROC_REF(particularly))
@@ -30,6 +38,7 @@
/datum/component/itempicky/proc/particularly(datum/source, obj/item/pickingup)
SIGNAL_HANDLER
- if(!is_type_in_typecache(pickingup, whitelist))
+ // if we were passed the output of a callback, check against that
+ if(!tertiary_condition?.Invoke() && !is_type_in_typecache(pickingup, whitelist))
to_chat(source, span_warning("[replacetext(message, "%TARGET", pickingup)]"))
return COMPONENT_LIVING_CANT_PUT_IN_HAND
diff --git a/code/datums/components/jetpack.dm b/code/datums/components/jetpack.dm
index 437660abc82e0..1da8822091b90 100644
--- a/code/datums/components/jetpack.dm
+++ b/code/datums/components/jetpack.dm
@@ -17,17 +17,25 @@
var/datum/effect_system/trail_follow/trail
/// The typepath to instansiate our trail as, when we need it
var/effect_type
+ /// Drift force applied each movement tick
+ var/drift_force
+ /// Force that applied when stabiliziation is active and the player isn't moving in the same direction as the jetpack
+ var/stabilization_force
+ /// Our current user
+ var/mob/user
/**
* Arguments:
* * stabilize - If we should drift when we finish moving, or sit stable in space]
+ * * drift_force - How much force is applied whenever the user tries to move
+ * * stabilization_force - How much force is applied per tick when we try to stabilize the user
* * activation_signal - Signal we activate on
* * deactivation_signal - Signal we deactivate on
* * return_flag - Flag to return if activation fails
* * check_on_move - Callback we call each time we attempt a move, we expect it to retun true if the move is ok, false otherwise. It expects an arg, TRUE if fuel should be consumed, FALSE othewise
* * effect_type - Type of trail_follow to spawn
*/
-/datum/component/jetpack/Initialize(stabilize, activation_signal, deactivation_signal, return_flag, datum/callback/check_on_move, datum/effect_system/trail_follow/effect_type)
+/datum/component/jetpack/Initialize(stabilize, drift_force = 1 NEWTONS, stabilization_force = 1 NEWTONS, activation_signal, deactivation_signal, return_flag, datum/callback/check_on_move, datum/effect_system/trail_follow/effect_type)
. = ..()
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
@@ -44,8 +52,10 @@
src.deactivation_signal = deactivation_signal
src.return_flag = return_flag
src.effect_type = effect_type
+ src.drift_force = drift_force
+ src.stabilization_force = stabilization_force
-/datum/component/jetpack/InheritComponent(datum/component/component, original, stabilize, activation_signal, deactivation_signal, return_flag, datum/callback/check_on_move, datum/effect_system/trail_follow/effect_type)
+/datum/component/jetpack/InheritComponent(datum/component/component, original, stabilize, drift_force = 1 NEWTONS, stabilization_force = 1 NEWTONS, activation_signal, deactivation_signal, return_flag, datum/callback/check_on_move, datum/effect_system/trail_follow/effect_type)
UnregisterSignal(parent, src.activation_signal)
if(src.deactivation_signal)
UnregisterSignal(parent, src.deactivation_signal)
@@ -59,6 +69,8 @@
src.deactivation_signal = deactivation_signal
src.return_flag = return_flag
src.effect_type = effect_type
+ src.drift_force = drift_force
+ src.stabilization_force = stabilization_force
if(trail && trail.effect_type != effect_type)
setup_trail(trail.holder)
@@ -66,87 +78,86 @@
/datum/component/jetpack/Destroy(force)
if(trail)
QDEL_NULL(trail)
+ user = null
check_on_move = null
return ..()
/datum/component/jetpack/proc/setup_trail(mob/user)
if(trail)
QDEL_NULL(trail)
-
trail = new effect_type
trail.auto_process = FALSE
trail.set_up(user)
trail.start()
-/datum/component/jetpack/proc/activate(datum/source, mob/user)
+/datum/component/jetpack/proc/activate(datum/source, mob/new_user)
SIGNAL_HANDLER
if(!check_on_move.Invoke(TRUE))
return return_flag
+ user = new_user
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(move_react))
RegisterSignal(user, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(pre_move_react))
- RegisterSignal(user, COMSIG_MOVABLE_SPACEMOVE, PROC_REF(spacemove_react))
- RegisterSignal(user, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT, PROC_REF(block_starting_visuals))
- RegisterSignal(user, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT, PROC_REF(ignore_ending_block))
-
+ RegisterSignal(user, COMSIG_MOB_CLIENT_MOVE_NOGRAV, PROC_REF(on_client_move))
+ START_PROCESSING(SSnewtonian_movement, src)
setup_trail(user)
-/datum/component/jetpack/proc/deactivate(datum/source, mob/user)
+/datum/component/jetpack/proc/deactivate(datum/source, mob/old_user)
SIGNAL_HANDLER
- UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
- UnregisterSignal(user, COMSIG_MOVABLE_PRE_MOVE)
- UnregisterSignal(user, COMSIG_MOVABLE_SPACEMOVE)
- UnregisterSignal(user, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT)
- UnregisterSignal(user, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT)
+ UnregisterSignal(old_user, COMSIG_MOVABLE_MOVED)
+ UnregisterSignal(old_user, COMSIG_MOVABLE_PRE_MOVE)
+ UnregisterSignal(old_user, COMSIG_MOB_CLIENT_MOVE_NOGRAV)
+ STOP_PROCESSING(SSnewtonian_movement, src)
+ user = null
if(trail)
QDEL_NULL(trail)
-/datum/component/jetpack/proc/move_react(mob/user)
+/datum/component/jetpack/proc/move_react(mob/source)
SIGNAL_HANDLER
- if(!user || !user.client)//Don't allow jet self using
- return
- if(!isturf(user.loc))//You can't use jet in nowhere or from mecha/closet
- return
- if(!(user.movement_type & FLOATING) || user.buckled)//You don't want use jet in gravity or while buckled.
+ if (!should_trigger(source))
return
- if(user.pulledby)//You don't must use jet if someone pull you
- return
- if(user.throwing)//You don't must use jet if you thrown
- return
- if(user.client.intended_direction)//You use jet when press keys. yes.
- thrust()
-/datum/component/jetpack/proc/pre_move_react(mob/user)
- SIGNAL_HANDLER
- if(!trail)
- return FALSE
- trail.oldposition = get_turf(user)
+ if(source.client.intended_direction && check_on_move.Invoke(FALSE))//You use jet when press keys. yes.
+ trail.generate_effect()
-/datum/component/jetpack/proc/spacemove_react(mob/user, movement_dir, continuous_move)
- SIGNAL_HANDLER
- if(!continuous_move && movement_dir)
- return COMSIG_MOVABLE_STOP_SPACEMOVE
- // Check if we have the fuel to stop this. Do NOT cosume any fuel, just check
- // This is done because things other then us can use our fuel
- if(stabilize && check_on_move.Invoke(FALSE))
- return COMSIG_MOVABLE_STOP_SPACEMOVE
-
-/// Returns true if the thrust went well, false otherwise
-/datum/component/jetpack/proc/thrust()
- if(!check_on_move.Invoke(TRUE))
+/datum/component/jetpack/proc/should_trigger(mob/source)
+ if(!source || !source.client)//Don't allow jet self using
+ return FALSE
+ if(!isturf(source.loc))//You can't use jet in nowhere or from mecha/closet
+ return FALSE
+ if(!(source.movement_type & FLOATING) || source.buckled)//You don't want use jet in gravity or while buckled.
+ return FALSE
+ if(source.pulledby)//You don't must use jet if someone pull you
+ return FALSE
+ if(source.throwing)//You don't must use jet if you thrown
return FALSE
- trail.generate_effect()
return TRUE
-/// Basically, tell the drift component not to do its starting visuals, because they look dumb for us
-/datum/component/jetpack/proc/block_starting_visuals(datum/source)
+/datum/component/jetpack/proc/pre_move_react(mob/source)
SIGNAL_HANDLER
- return DRIFT_VISUAL_FAILED
+ if(!trail)
+ return FALSE
+ trail.oldposition = get_turf(source)
+
+/datum/component/jetpack/process(seconds_per_tick)
+ if (!should_trigger(user) || !stabilize || isnull(user.drift_handler))
+ return
-/// If we're on, don't let the drift component block movements at the end since we can speed
-/datum/component/jetpack/proc/ignore_ending_block(datum/source)
+ var/max_drift_force = (DEFAULT_INERTIA_SPEED / user.cached_multiplicative_slowdown - 1) / INERTIA_SPEED_COEF + 1
+ user.drift_handler.stabilize_drift(user.client.intended_direction ? dir2angle(user.client.intended_direction) : null, user.client.intended_direction ? max_drift_force : 0, stabilization_force * (seconds_per_tick * 1 SECONDS))
+
+/datum/component/jetpack/proc/on_client_move(mob/source, list/move_args)
SIGNAL_HANDLER
- return DRIFT_ALLOW_INPUT
+
+ if (!should_trigger(source))
+ return
+
+ if (!check_on_move.Invoke(TRUE))
+ return
+
+ var/max_drift_force = (DEFAULT_INERTIA_SPEED / source.cached_multiplicative_slowdown - 1) / INERTIA_SPEED_COEF + 1
+ source.newtonian_move(dir2angle(source.client.intended_direction), instant = TRUE, drift_force = drift_force, controlled_cap = max_drift_force)
+ source.setDir(source.client.intended_direction)
diff --git a/code/datums/components/jukebox.dm b/code/datums/components/jukebox.dm
index 7d453f8033db9..cac6023751c01 100644
--- a/code/datums/components/jukebox.dm
+++ b/code/datums/components/jukebox.dm
@@ -400,7 +400,7 @@
// Default track supplied for testing and also because it's a banger
/datum/track/default
- song_path = 'sound/ambience/title3.ogg'
+ song_path = 'sound/music/lobby_music/title3.ogg'
song_name = "Tintin on the Moon"
song_length = 3 MINUTES + 52 SECONDS
song_beat = 1 SECONDS
diff --git a/code/datums/components/life_link.dm b/code/datums/components/life_link.dm
index 628aceabc955a..314a3d7931bde 100644
--- a/code/datums/components/life_link.dm
+++ b/code/datums/components/life_link.dm
@@ -128,7 +128,7 @@
return
holder.icon_state = "hud[RoundHealth(host)]"
var/icon/size_check = icon(mob_parent.icon, mob_parent.icon_state, mob_parent.dir)
- holder.pixel_y = size_check.Height() - world.icon_size
+ holder.pixel_y = size_check.Height() - ICON_SIZE_Y
/// Update our vital status on the medical hud
/datum/component/life_link/proc/update_med_hud_status(mob/living/mob_parent)
@@ -136,7 +136,7 @@
if(isnull(holder))
return
var/icon/size_check = icon(mob_parent.icon, mob_parent.icon_state, mob_parent.dir)
- holder.pixel_y = size_check.Height() - world.icon_size
+ holder.pixel_y = size_check.Height() - ICON_SIZE_Y
if(host.stat == DEAD || HAS_TRAIT(host, TRAIT_FAKEDEATH))
holder.icon_state = "huddead"
else
diff --git a/code/datums/components/lockable_storage.dm b/code/datums/components/lockable_storage.dm
index 482cb134159e0..ca058cb3fbfab 100644
--- a/code/datums/components/lockable_storage.dm
+++ b/code/datums/components/lockable_storage.dm
@@ -62,7 +62,6 @@
UnregisterSignal(parent, list(
COMSIG_ATOM_TOOL_ACT(TOOL_SCREWDRIVER),
COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL),
- COMSIG_ATOM_STORAGE_ITEM_INTERACT_INSERT,
))
UnregisterSignal(parent, list(
COMSIG_ATOM_EXAMINE,
diff --git a/code/datums/components/mind_linker.dm b/code/datums/components/mind_linker.dm
index 4449a8fe36e8e..f6b2af5329716 100644
--- a/code/datums/components/mind_linker.dm
+++ b/code/datums/components/mind_linker.dm
@@ -242,7 +242,7 @@
var/datum/component/mind_linker/linker = target
var/mob/living/linker_parent = linker.parent
- var/message = tgui_input_text(owner, "Enter a message to transmit.", "[linker.network_name] Telepathy")
+ var/message = tgui_input_text(owner, "Enter a message to transmit.", "[linker.network_name] Telepathy", max_length = MAX_MESSAGE_LEN)
if(!message || QDELETED(src) || QDELETED(owner) || owner.stat == DEAD)
return
diff --git a/code/datums/components/mob_harvest.dm b/code/datums/components/mob_harvest.dm
index b9f9f86350be5..242161027b069 100644
--- a/code/datums/components/mob_harvest.dm
+++ b/code/datums/components/mob_harvest.dm
@@ -25,7 +25,7 @@
///how long it takes to harvest from the mob
var/item_harvest_time = 5 SECONDS
///typepath of harvest sound
- var/item_harvest_sound = 'sound/items/welder2.ogg'
+ var/item_harvest_sound = 'sound/items/tools/welder2.ogg'
//harvest_type, produced_item_typepath and speedup_type are typepaths, not reference
/datum/component/mob_harvest/Initialize(harvest_tool, fed_item, produced_item_typepath, produced_item_desc, max_ready, item_generation_wait, item_reduction_time, item_harvest_time, item_harvest_sound)
diff --git a/code/datums/components/pet_commands/pet_commands_basic.dm b/code/datums/components/pet_commands/pet_commands_basic.dm
index 9f4dda9cca394..fd4e1f922c8b4 100644
--- a/code/datums/components/pet_commands/pet_commands_basic.dm
+++ b/code/datums/components/pet_commands/pet_commands_basic.dm
@@ -298,5 +298,5 @@
return ..()
/datum/pet_command/point_targeting/fish/execute_action(datum/ai_controller/controller)
- controller.queue_behavior(/datum/ai_behavior/hunt_target/unarmed_attack_target/reset_target_combat_mode, BB_CURRENT_PET_TARGET)
+ controller.queue_behavior(/datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off, BB_CURRENT_PET_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
diff --git a/code/datums/components/plumbing/_plumbing.dm b/code/datums/components/plumbing/_plumbing.dm
index c59be2ed27651..a1be66654a2c0 100644
--- a/code/datums/components/plumbing/_plumbing.dm
+++ b/code/datums/components/plumbing/_plumbing.dm
@@ -343,7 +343,7 @@
parent_movable.update_appearance()
if(changer)
- playsound(changer, 'sound/items/ratchet.ogg', 10, TRUE) //sound
+ playsound(changer, 'sound/items/tools/ratchet.ogg', 10, TRUE) //sound
//quickly disconnect and reconnect the network.
if(active)
diff --git a/code/datums/components/profound_fisher.dm b/code/datums/components/profound_fisher.dm
index 947e9d26f5197..61f6543bd12bf 100644
--- a/code/datums/components/profound_fisher.dm
+++ b/code/datums/components/profound_fisher.dm
@@ -64,9 +64,12 @@
/datum/component/profound_fisher/proc/on_unarmed_attack(mob/living/source, atom/attack_target, proximity_flag, list/modifiers)
SIGNAL_HANDLER
- if(!source.client || !should_fish_on(source, attack_target))
+ if(!should_fish_on(source, attack_target))
return
- INVOKE_ASYNC(src, PROC_REF(begin_fishing), source, attack_target)
+ if(source.client)
+ INVOKE_ASYNC(src, PROC_REF(begin_fishing), source, attack_target)
+ else
+ INVOKE_ASYNC(src, PROC_REF(pretend_fish), source, attack_target)
return COMPONENT_CANCEL_ATTACK_CHAIN
/datum/component/profound_fisher/proc/pre_attack(mob/living/source, atom/target)
@@ -77,53 +80,55 @@
if(source.client)
INVOKE_ASYNC(src, PROC_REF(begin_fishing), source, target)
else
- INVOKE_ASYNC(src, PROC_REF(pretend_fish), target)
+ INVOKE_ASYNC(src, PROC_REF(pretend_fish), source, target)
return COMPONENT_HOSTILE_NO_ATTACK
/datum/component/profound_fisher/proc/should_fish_on(mob/living/user, atom/target)
- if(!HAS_TRAIT(target, TRAIT_FISHING_SPOT) || HAS_TRAIT(user, TRAIT_GONE_FISHING))
+ if(!HAS_TRAIT(target, TRAIT_FISHING_SPOT) || GLOB.fishing_challenges_by_user[user])
return FALSE
if(user.combat_mode || !user.CanReach(target))
return FALSE
return TRUE
/datum/component/profound_fisher/proc/begin_fishing(mob/living/user, atom/target)
- RegisterSignal(user, SIGNAL_ADDTRAIT(TRAIT_GONE_FISHING), PROC_REF(actually_fishing_with_internal_rod))
+ RegisterSignal(user, COMSIG_MOB_BEGIN_FISHING, PROC_REF(actually_fishing_with_internal_rod))
our_rod.melee_attack_chain(user, target)
- UnregisterSignal(user, SIGNAL_ADDTRAIT(TRAIT_GONE_FISHING))
+ UnregisterSignal(user, COMSIG_MOB_BEGIN_FISHING)
/datum/component/profound_fisher/proc/actually_fishing_with_internal_rod(datum/source)
SIGNAL_HANDLER
ADD_TRAIT(source, TRAIT_PROFOUND_FISHER, REF(parent))
- RegisterSignal(source, SIGNAL_REMOVETRAIT(TRAIT_GONE_FISHING), PROC_REF(remove_profound_fisher))
+ RegisterSignal(source, COMSIG_MOB_COMPLETE_FISHING, PROC_REF(remove_profound_fisher))
/datum/component/profound_fisher/proc/remove_profound_fisher(datum/source)
SIGNAL_HANDLER
REMOVE_TRAIT(source, TRAIT_PROFOUND_FISHER, TRAIT_GENERIC)
- UnregisterSignal(source, SIGNAL_REMOVETRAIT(TRAIT_GONE_FISHING))
+ UnregisterSignal(source, COMSIG_MOB_COMPLETE_FISHING)
-/datum/component/profound_fisher/proc/pretend_fish(atom/target)
- var/mob/living/living_parent = parent
- if(DOING_INTERACTION_WITH_TARGET(living_parent, target))
+/datum/component/profound_fisher/proc/pretend_fish(mob/living/source, atom/target)
+ if(DOING_INTERACTION_WITH_TARGET(source, target))
return
var/list/fish_spot_container[NPC_FISHING_SPOT]
SEND_SIGNAL(target, COMSIG_NPC_FISHING, fish_spot_container)
var/datum/fish_source/fish_spot = fish_spot_container[NPC_FISHING_SPOT]
if(isnull(fish_spot))
return null
- var/obj/effect/fishing_lure/lure = new(get_turf(target), target)
- playsound(lure, 'sound/effects/splash.ogg', 100)
- var/happiness_percentage = living_parent.ai_controller?.blackboard[BB_BASIC_HAPPINESS] / 100
- var/fishing_speed = 10 SECONDS - round(4 SECONDS * happiness_percentage)
- if(!do_after(living_parent, fishing_speed, target = target) && !QDELETED(fish_spot))
- qdel(lure)
- return
- var/reward_loot = fish_spot.roll_reward(our_rod, parent)
- fish_spot.dispense_reward(reward_loot, parent, target)
- playsound(lure, 'sound/effects/bigsplash.ogg', 100)
- qdel(lure)
+ var/obj/effect/fishing_float/float = new(get_turf(target), target)
+ playsound(float, 'sound/effects/splash.ogg', 100)
+ if(!PERFORM_ALL_TESTS(fish_sources))
+ var/happiness_percentage = source.ai_controller?.blackboard[BB_BASIC_HAPPINESS] * 0.01
+ var/fishing_speed = 10 SECONDS - round(4 SECONDS * happiness_percentage)
+ if(!do_after(source, fishing_speed, target = target) && !QDELETED(fish_spot))
+ qdel(float)
+ return
+ var/reward_loot = fish_spot.roll_reward(our_rod, source)
+ fish_spot.dispense_reward(reward_loot, source, target)
+ playsound(float, 'sound/effects/bigsplash.ogg', 100)
+ qdel(float)
/obj/item/fishing_rod/mob_fisher
line = /obj/item/fishing_line/reinforced
bait = /obj/item/food/bait/doughball/synthetic/unconsumable
resistance_flags = INDESTRUCTIBLE
+ reel_overlay = null
+ show_in_wiki = FALSE //abstract fishing rod
diff --git a/code/datums/components/ranged_attacks.dm b/code/datums/components/ranged_attacks.dm
index 2f9c6cb822da5..58883ce58111f 100644
--- a/code/datums/components/ranged_attacks.dm
+++ b/code/datums/components/ranged_attacks.dm
@@ -20,7 +20,7 @@
/datum/component/ranged_attacks/Initialize(
casing_type,
projectile_type,
- projectile_sound = 'sound/weapons/gun/pistol/shot.ogg',
+ projectile_sound = 'sound/items/weapons/gun/pistol/shot.ogg',
burst_shots,
burst_intervals = 0.2 SECONDS,
cooldown_time = 3 SECONDS,
diff --git a/code/datums/components/regenerative_shield.dm b/code/datums/components/regenerative_shield.dm
index 5ecf670820381..34d305b27e13f 100644
--- a/code/datums/components/regenerative_shield.dm
+++ b/code/datums/components/regenerative_shield.dm
@@ -59,7 +59,7 @@
if(damage >= damage_threshold || number_of_hits <= 0)
return NONE
- playsound(get_turf(parent), 'sound/weapons/tap.ogg', 20)
+ playsound(get_turf(parent), 'sound/items/weapons/tap.ogg', 20)
new /obj/effect/temp_visual/guardian/phase/out(get_turf(parent))
number_of_hits = max(0, number_of_hits - 1)
if(number_of_hits <= 0)
@@ -71,14 +71,14 @@
for(var/obj/effect/my_effect as anything in shield_overlays)
animate(my_effect, alpha = 0, time = 3 SECONDS)
my_effect.remove_filter(SHIELD_FILTER)
- playsound(parent, 'sound/mecha/mech_shield_drop.ogg', 20)
+ playsound(parent, 'sound/vehicles/mecha/mech_shield_drop.ogg', 20)
/datum/component/regenerative_shield/proc/enable_shield()
number_of_hits = initial(number_of_hits)
for(var/obj/effect/my_effect as anything in shield_overlays)
animate(my_effect, alpha = 255, time = 3 SECONDS)
addtimer(CALLBACK(src, PROC_REF(apply_filter_effects), my_effect), 5 SECONDS)
- playsound(parent, 'sound/mecha/mech_shield_raise.ogg', 20)
+ playsound(parent, 'sound/vehicles/mecha/mech_shield_raise.ogg', 20)
/datum/component/regenerative_shield/proc/apply_filter_effects(obj/effect/new_effect)
if(isnull(new_effect))
diff --git a/code/datums/components/religious_tool.dm b/code/datums/components/religious_tool.dm
index 37b62d1aa0e3c..969e6a9a3cec1 100644
--- a/code/datums/components/religious_tool.dm
+++ b/code/datums/components/religious_tool.dm
@@ -159,15 +159,15 @@
/datum/component/religious_tool/proc/perform_rite(mob/living/user, path)
if(user.mind.holy_role < HOLY_ROLE_PRIEST)
if(user.mind.holy_role == HOLY_ROLE_DEACON)
- to_chat(user, "You are merely a deacon of [GLOB.deity], and therefore cannot perform rites.")
+ to_chat(user, span_warning("You are merely a deacon of [GLOB.deity], and therefore cannot perform rites."))
else
- to_chat(user, "You are not holy, and therefore cannot perform rites.")
+ to_chat(user, span_warning("You are not holy, and therefore cannot perform rites."))
return
if(rite_types_allowlist && !is_path_in_list(path, rite_types_allowlist))
to_chat(user, span_warning("This cannot perform that kind of rite."))
return
if(performing_rite)
- to_chat(user, "There is a rite currently being performed here already.")
+ to_chat(user, span_notice("There is a rite currently being performed here already."))
return
if(!user.can_perform_action(parent, FORBID_TELEKINESIS_REACH))
to_chat(user,span_warning("You are not close enough to perform the rite."))
diff --git a/code/datums/components/riding/riding_mob.dm b/code/datums/components/riding/riding_mob.dm
index 1e3c94d84c6a3..7a18e923afebe 100644
--- a/code/datums/components/riding/riding_mob.dm
+++ b/code/datums/components/riding/riding_mob.dm
@@ -7,7 +7,9 @@
var/can_use_abilities = FALSE
/// shall we require riders to go through the riding minigame if they arent in our friends list
var/require_minigame = FALSE
- /// list of blacklisted abilities that cant be shared
+ /// unsharable abilities that we will force to be shared anyway
+ var/list/override_unsharable_abilities = list()
+ /// abilities that are always blacklisted from sharing
var/list/blacklist_abilities = list()
/datum/component/riding/creature/Initialize(mob/living/riding_mob, force = FALSE, ride_check_flags = NONE, potion_boost = FALSE)
@@ -168,6 +170,8 @@
for(var/datum/action/action as anything in ridden_creature.actions)
if(is_type_in_list(action, blacklist_abilities))
continue
+ if(!action.can_be_shared && !is_type_in_list(action, override_unsharable_abilities))
+ continue
action.GiveAction(rider)
/// Takes away the riding parent's abilities from the rider
@@ -385,7 +389,7 @@
if(human_user && is_clown_job(human_user.mind?.assigned_role))
// there's a new sheriff in town
- playsound(movable_parent, 'sound/creatures/pony/clown_gallup.ogg', 50)
+ playsound(movable_parent, 'sound/mobs/non-humanoids/pony/clown_gallup.ogg', 50)
COOLDOWN_START(src, pony_trot_cooldown, 500 MILLISECONDS)
/datum/component/riding/creature/bear/handle_specials()
@@ -504,7 +508,6 @@
/datum/component/riding/creature/leaper
can_force_unbuckle = FALSE
can_use_abilities = TRUE
- blacklist_abilities = list(/datum/action/cooldown/toggle_seethrough)
ride_check_flags = JUST_FRIEND_RIDERS
/datum/component/riding/creature/leaper/handle_specials()
diff --git a/code/datums/components/riding/riding_vehicle.dm b/code/datums/components/riding/riding_vehicle.dm
index f7ee78673e057..3c55eae46688a 100644
--- a/code/datums/components/riding/riding_vehicle.dm
+++ b/code/datums/components/riding/riding_vehicle.dm
@@ -15,19 +15,19 @@
if(!keycheck(rider))
if(z_move_flags & ZMOVE_FEEDBACK)
- to_chat(rider, "[movable_parent] has no key inserted!")
+ to_chat(rider, span_warning("[movable_parent] has no key inserted!"))
return COMPONENT_RIDDEN_STOP_Z_MOVE
if(HAS_TRAIT(rider, TRAIT_INCAPACITATED))
if(z_move_flags & ZMOVE_FEEDBACK)
- to_chat(rider, "You cannot operate [movable_parent] right now!")
+ to_chat(rider, span_warning("You cannot operate [movable_parent] right now!"))
return COMPONENT_RIDDEN_STOP_Z_MOVE
if(ride_check_flags & RIDER_NEEDS_LEGS && HAS_TRAIT(rider, TRAIT_FLOORED))
if(z_move_flags & ZMOVE_FEEDBACK)
- to_chat(rider, "You can't seem to manage that while unable to stand up enough to move [movable_parent]...")
+ to_chat(rider, span_warning("You can't seem to manage that while unable to stand up enough to move [movable_parent]..."))
return COMPONENT_RIDDEN_STOP_Z_MOVE
if(ride_check_flags & RIDER_NEEDS_ARMS && HAS_TRAIT(rider, TRAIT_HANDS_BLOCKED))
if(z_move_flags & ZMOVE_FEEDBACK)
- to_chat(rider, "You can't seem to hold onto [movable_parent] to move it...")
+ to_chat(rider, span_warning("You can't seem to hold onto [movable_parent] to move it..."))
return COMPONENT_RIDDEN_STOP_Z_MOVE
return COMPONENT_RIDDEN_ALLOW_Z_MOVE
diff --git a/code/datums/components/rotation.dm b/code/datums/components/rotation.dm
index 6ff8197e09319..40df294af12a8 100644
--- a/code/datums/components/rotation.dm
+++ b/code/datums/components/rotation.dm
@@ -76,7 +76,7 @@
var/obj/rotated_obj = parent
rotated_obj.setDir(turn(rotated_obj.dir, degrees))
if(rotation_flags & ROTATION_REQUIRE_WRENCH)
- playsound(rotated_obj, 'sound/items/ratchet.ogg', 50, TRUE)
+ playsound(rotated_obj, 'sound/items/tools/ratchet.ogg', 50, TRUE)
post_rotation.Invoke(user, degrees)
diff --git a/code/datums/components/scope.dm b/code/datums/components/scope.dm
index 087eb0c06d24c..46388a15e26e8 100644
--- a/code/datums/components/scope.dm
+++ b/code/datums/components/scope.dm
@@ -164,7 +164,7 @@
if(HAS_TRAIT(user, TRAIT_USER_SCOPED))
user.balloon_alert(user, "already zoomed!")
return
- user.playsound_local(parent, 'sound/weapons/scope.ogg', 75, TRUE)
+ user.playsound_local(parent, 'sound/items/weapons/scope.ogg', 75, TRUE)
tracker = user.overlay_fullscreen("scope", /atom/movable/screen/fullscreen/cursor_catcher/scope, isgun(parent))
tracker.assign_to_mob(user, range_modifier)
tracker_owner_ckey = user.ckey
@@ -210,7 +210,7 @@
))
REMOVE_TRAIT(user, TRAIT_USER_SCOPED, REF(src))
- user.playsound_local(parent, 'sound/weapons/scope.ogg', 75, TRUE, frequency = -1)
+ user.playsound_local(parent, 'sound/items/weapons/scope.ogg', 75, TRUE, frequency = -1)
user.clear_fullscreen("scope")
// if the client has ended up in another mob, find that mob so we can fix their cursor
@@ -246,18 +246,18 @@
if(isnull(icon_x))
icon_x = text2num(LAZYACCESS(modifiers, ICON_X))
if(isnull(icon_x))
- icon_x = view_list[1]*world.icon_size/2
+ icon_x = view_list[1]*ICON_SIZE_X/2
var/icon_y = text2num(LAZYACCESS(modifiers, VIS_Y))
if(isnull(icon_y))
icon_y = text2num(LAZYACCESS(modifiers, ICON_Y))
if(isnull(icon_y))
- icon_y = view_list[2]*world.icon_size/2
- var/x_cap = range_modifier * view_list[1]*world.icon_size / 2
- var/y_cap = range_modifier * view_list[2]*world.icon_size / 2
- var/uncapped_x = round(range_modifier * (icon_x - view_list[1]*world.icon_size/2) * MOUSE_POINTER_OFFSET_MULT)
- var/uncapped_y = round(range_modifier * (icon_y - view_list[2]*world.icon_size/2) * MOUSE_POINTER_OFFSET_MULT)
+ icon_y = view_list[2]*ICON_SIZE_Y/2
+ var/x_cap = range_modifier * view_list[1]*ICON_SIZE_X / 2
+ var/y_cap = range_modifier * view_list[2]*ICON_SIZE_Y / 2
+ var/uncapped_x = round(range_modifier * (icon_x - view_list[1]*ICON_SIZE_X/2) * MOUSE_POINTER_OFFSET_MULT)
+ var/uncapped_y = round(range_modifier * (icon_y - view_list[2]*ICON_SIZE_Y/2) * MOUSE_POINTER_OFFSET_MULT)
given_x = clamp(uncapped_x, -x_cap, x_cap)
given_y = clamp(uncapped_y, -y_cap, y_cap)
- given_turf = locate(owner.x+round(given_x/world.icon_size, 1),owner.y+round(given_y/world.icon_size, 1),owner.z)
+ given_turf = locate(owner.x+round(given_x/ICON_SIZE_X, 1),owner.y+round(given_y/ICON_SIZE_Y, 1),owner.z)
#undef MOUSE_POINTER_OFFSET_MULT
diff --git a/code/datums/components/seclight_attachable.dm b/code/datums/components/seclight_attachable.dm
index b1d4aebc93f83..6b3991c9c5e3c 100644
--- a/code/datums/components/seclight_attachable.dm
+++ b/code/datums/components/seclight_attachable.dm
@@ -97,8 +97,8 @@
RegisterSignal(parent, COMSIG_ITEM_UI_ACTION_CLICK, PROC_REF(on_action_click))
RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(on_attackby))
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
+ RegisterSignal(parent, COMSIG_ATOM_SABOTEUR_ACT, PROC_REF(on_hit_by_saboteur))
RegisterSignal(parent, COMSIG_QDELETING, PROC_REF(on_parent_deleted))
- RegisterSignal(parent, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
/datum/component/seclite_attachable/UnregisterFromParent()
UnregisterSignal(parent, list(
@@ -110,6 +110,7 @@
COMSIG_ITEM_UI_ACTION_CLICK,
COMSIG_ATOM_ATTACKBY,
COMSIG_ATOM_EXAMINE,
+ COMSIG_ATOM_SABOTEUR_ACT,
COMSIG_QDELETING,
))
@@ -296,8 +297,8 @@
// but that's the downside of using icon states over overlays.
source.icon_state = base_state
-/// Signal proc for [COMSIG_HIT_BY_SABOTEUR] that turns the light off for a few seconds.
-/datum/component/seclite_attachable/proc/on_saboteur(datum/source, disrupt_duration)
- SIGNAL_HANDLER
+//turns the light off for a few seconds.
+/datum/component/seclite_attachable/proc/on_hit_by_saboteur(datum/source, disrupt_duration)
. = light.on_saboteur(source, disrupt_duration)
update_light()
+ return .
diff --git a/code/datums/components/seethrough_mob.dm b/code/datums/components/seethrough_mob.dm
index bae87faf61583..b6951c5489b6d 100644
--- a/code/datums/components/seethrough_mob.dm
+++ b/code/datums/components/seethrough_mob.dm
@@ -122,6 +122,7 @@
background_icon_state = "bg_alien"
cooldown_time = 1 SECONDS
melee_cooldown_time = 0
+ can_be_shared = FALSE
/datum/action/cooldown/toggle_seethrough/Remove(mob/remove_from)
var/datum/component/seethrough_mob/transparency = target
diff --git a/code/datums/components/shielded.dm b/code/datums/components/shielded.dm
index da83c4ad2d29d..53fc330806245 100644
--- a/code/datums/components/shielded.dm
+++ b/code/datums/components/shielded.dm
@@ -101,7 +101,7 @@
var/obj/item/item_parent = parent
COOLDOWN_START(src, charge_add_cd, charge_increment_delay)
adjust_charge(charge_recovery) // set the number of charges to current + recovery per increment, clamped from zero to max_charges
- playsound(item_parent, 'sound/magic/charge.ogg', 50, TRUE)
+ playsound(item_parent, 'sound/effects/magic/charge.ogg', 50, TRUE)
if(current_charges == max_charges)
playsound(item_parent, 'sound/machines/ding.ogg', 50, TRUE)
diff --git a/code/datums/components/singularity.dm b/code/datums/components/singularity.dm
index 14aaedff7172a..0cd64d829a2fd 100644
--- a/code/datums/components/singularity.dm
+++ b/code/datums/components/singularity.dm
@@ -373,7 +373,7 @@
for(var/mob/living/target as anything in GLOB.mob_living_list)
if(target.z != atom_parent.z)
continue
- if(target.status_effects & GODMODE)
+ if(HAS_TRAIT(target, TRAIT_GODMODE))
continue
var/distance_from_target = get_dist(target, atom_parent)
if(distance_from_target < closest_distance)
diff --git a/code/datums/components/sisyphus_awarder.dm b/code/datums/components/sisyphus_awarder.dm
index 2a18a2889fc65..854ed26355f25 100644
--- a/code/datums/components/sisyphus_awarder.dm
+++ b/code/datums/components/sisyphus_awarder.dm
@@ -65,4 +65,4 @@
"reverse_dropoff_coords" = list(bottom_of_the_hill.x, bottom_of_the_hill.y, bottom_of_the_hill.z),
))
- SEND_SOUND(sisyphus, 'sound/ambience/music/sisyphus/sisyphus.ogg')
+ SEND_SOUND(sisyphus, 'sound/music/sisyphus/sisyphus.ogg')
diff --git a/code/datums/components/sitcomlaughter.dm b/code/datums/components/sitcomlaughter.dm
index 62e9276b1d75d..bc69a08b80c9d 100644
--- a/code/datums/components/sitcomlaughter.dm
+++ b/code/datums/components/sitcomlaughter.dm
@@ -1,10 +1,10 @@
/datum/component/wearertargeting/sitcomlaughter
valid_slots = list(ITEM_SLOT_HANDS, ITEM_SLOT_BELT, ITEM_SLOT_ID, ITEM_SLOT_LPOCKET, ITEM_SLOT_RPOCKET, ITEM_SLOT_SUITSTORE, ITEM_SLOT_DEX_STORAGE)
- signals = list(COMSIG_MOB_CREAMED, COMSIG_ON_CARBON_SLIP, COMSIG_POST_TILT_AND_CRUSH, COMSIG_MOB_CLUMSY_SHOOT_FOOT)
+ signals = list(COMSIG_MOB_HIT_BY_SPLAT, COMSIG_ON_CARBON_SLIP, COMSIG_POST_TILT_AND_CRUSH, COMSIG_MOB_CLUMSY_SHOOT_FOOT)
proctype = PROC_REF(EngageInComedy)
mobtype = /mob/living
///Sounds used for when user has a sitcom action occur
- var/list/comedysounds = list('sound/items/SitcomLaugh1.ogg', 'sound/items/SitcomLaugh2.ogg', 'sound/items/SitcomLaugh3.ogg')
+ var/list/comedysounds = list('sound/items/sitcom_laugh/sitcomLaugh1.ogg', 'sound/items/sitcom_laugh/sitcomLaugh2.ogg', 'sound/items/sitcom_laugh/sitcomLaugh3.ogg')
///Invoked in EngageInComedy is ran
var/datum/callback/post_comedy_callback
///Cooldown for inbetween laughs
diff --git a/code/datums/components/soapbox.dm b/code/datums/components/soapbox.dm
index 4d4577d5e12c8..9d15e5e69292c 100644
--- a/code/datums/components/soapbox.dm
+++ b/code/datums/components/soapbox.dm
@@ -14,15 +14,17 @@
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(parent_moved))
///Applies loud speech to our movable when entering the turf our parent is on
-/datum/component/soapbox/proc/on_loc_entered(datum/source, atom/movable/soapbox_arrive)
+/datum/component/soapbox/proc/on_loc_entered(datum/source, mob/living/soapbox_arrive)
SIGNAL_HANDLER
+ if(!isliving(soapbox_arrive))
+ return
if(QDELETED(soapbox_arrive))
return
RegisterSignal(soapbox_arrive, COMSIG_MOB_SAY, PROC_REF(soapbox_speech))
soapboxers += soapbox_arrive
///Takes away loud speech from our movable when it leaves the turf our parent is on
-/datum/component/soapbox/proc/on_loc_exited(datum/source, atom/movable/soapbox_leave)
+/datum/component/soapbox/proc/on_loc_exited(datum/source, mob/living/soapbox_leave)
SIGNAL_HANDLER
if(soapbox_leave in soapboxers)
UnregisterSignal(soapbox_leave, COMSIG_MOB_SAY)
diff --git a/code/datums/components/soulstoned.dm b/code/datums/components/soulstoned.dm
index bb22030c21042..d4e9e0eaf02e1 100644
--- a/code/datums/components/soulstoned.dm
+++ b/code/datums/components/soulstoned.dm
@@ -11,8 +11,7 @@
stoned.forceMove(container)
stoned.fully_heal()
- stoned.add_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED), SOULSTONE_TRAIT)
- stoned.status_flags |= GODMODE
+ stoned.add_traits(list(TRAIT_GODMODE, TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED), SOULSTONE_TRAIT)
RegisterSignal(stoned, COMSIG_MOVABLE_MOVED, PROC_REF(free_prisoner))
@@ -25,5 +24,4 @@
/datum/component/soulstoned/UnregisterFromParent()
var/mob/living/stoned = parent
- stoned.status_flags &= ~GODMODE
- stoned.remove_traits(list(TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED), SOULSTONE_TRAIT)
+ stoned.remove_traits(list(TRAIT_GODMODE, TRAIT_IMMOBILIZED, TRAIT_HANDS_BLOCKED), SOULSTONE_TRAIT)
diff --git a/code/datums/components/speechmod.dm b/code/datums/components/speechmod.dm
index 8ffa3e8624e49..fc01d8d2d846c 100644
--- a/code/datums/components/speechmod.dm
+++ b/code/datums/components/speechmod.dm
@@ -60,6 +60,8 @@
var/message = speech_args[SPEECH_MESSAGE]
if(message[1] == "*")
return
+ if(SEND_SIGNAL(source, COMSIG_TRY_MODIFY_SPEECH) & PREVENT_MODIFY_SPEECH)
+ return
if(!isnull(should_modify_speech) && !should_modify_speech.Invoke(source, speech_args))
return
diff --git a/code/datums/components/spin2win.dm b/code/datums/components/spin2win.dm
index 4524b403355f8..ce9dfa360b323 100644
--- a/code/datums/components/spin2win.dm
+++ b/code/datums/components/spin2win.dm
@@ -84,7 +84,7 @@
if(start_spin_message)
var/message = replacetext(start_spin_message, "%USER", spinning_user)
spinning_user.visible_message(message)
- playsound(spinning_user, 'sound/weapons/fwoosh.ogg', 75, FALSE)
+ playsound(spinning_user, 'sound/items/weapons/fwoosh.ogg', 75, FALSE)
stop_spinning_timer_id = addtimer(CALLBACK(src, PROC_REF(stop_spinning), spinning_user), spin_duration, TIMER_STOPPABLE|TIMER_DELETE_ME)
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(on_spin_equipped))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(on_spin_dropped))
@@ -95,7 +95,7 @@
STOP_PROCESSING(SSprocessing, src)
UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
deltimer(stop_spinning_timer_id)
- playsound(user, 'sound/weapons/fwoosh.ogg', 75, FALSE)
+ playsound(user, 'sound/items/weapons/fwoosh.ogg', 75, FALSE)
if(user && end_spin_message)
var/message = replacetext(end_spin_message, "%USER", user)
user.visible_message(message)
@@ -111,7 +111,7 @@
return PROCESS_KILL
var/mob/living/item_owner = spinning_item.loc
item_owner.emote("spin")
- playsound(item_owner, 'sound/weapons/fwoosh.ogg', 75, FALSE)
+ playsound(item_owner, 'sound/items/weapons/fwoosh.ogg', 75, FALSE)
for(var/mob/living/victim in orange(1, item_owner))
spinning_item.attack(victim, item_owner)
diff --git a/code/datums/components/spirit_holding.dm b/code/datums/components/spirit_holding.dm
index b510fde3523a4..11ceb778313a9 100644
--- a/code/datums/components/spirit_holding.dm
+++ b/code/datums/components/spirit_holding.dm
@@ -149,7 +149,7 @@
return // just in case
var/atom/movable/exorcised_movable = parent
to_chat(exorcist, span_notice("You begin to exorcise [parent]..."))
- playsound(parent, 'sound/hallucinations/veryfar_noise.ogg',40,TRUE)
+ playsound(parent, 'sound/effects/hallucinations/veryfar_noise.ogg',40,TRUE)
if(!do_after(exorcist, 4 SECONDS, target = exorcised_movable))
return
playsound(parent, 'sound/effects/pray_chaplain.ogg',60,TRUE)
diff --git a/code/datums/components/splat.dm b/code/datums/components/splat.dm
new file mode 100644
index 0000000000000..5d47d17b98c9c
--- /dev/null
+++ b/code/datums/components/splat.dm
@@ -0,0 +1,74 @@
+/datum/component/splat
+ ///The icon state to use for the decal
+ var/icon_state
+ ///The bodypart layer to use for the decal
+ var/layer
+ ///The type of memory to celebrate the event of getting hit by this
+ var/memory_type
+ ///The type of smudge we create on the floor
+ var/smudge_type
+ ///The moodlet passed down to the creamed component
+ var/moodlet_type
+ ///The color we give to the creamed component/overlay
+ var/splat_color
+ ///The callback called when a mob is hit by this
+ var/datum/callback/hit_callback
+
+/datum/component/splat/Initialize(
+ icon_state = "creampie",
+ layer = EXTERNAL_FRONT,
+ memory_type = /datum/memory/witnessed_creampie,
+ smudge_type = /obj/effect/decal/cleanable/food/pie_smudge,
+ moodlet_type = /datum/mood_event/creampie,
+ splat_color,
+ datum/callback/hit_callback,
+)
+ . = ..()
+ if(!ismovable(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ src.icon_state = icon_state
+ src.layer = layer
+ src.memory_type = memory_type
+ src.smudge_type = smudge_type
+ src.moodlet_type = moodlet_type
+ src.hit_callback = hit_callback
+ src.splat_color = splat_color
+
+/datum/component/splat/Destroy()
+ hit_callback = null
+ return ..()
+
+/datum/component/splat/RegisterWithParent()
+ if(isprojectile(parent))
+ RegisterSignal(parent, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(projectile_splat))
+ else
+ RegisterSignal(parent, COMSIG_MOVABLE_IMPACT, PROC_REF(throw_splat))
+
+/datum/component/splat/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_MOVABLE_IMPACT, COMSIG_PROJECTILE_SELF_ON_HIT))
+
+/datum/component/splat/proc/projectile_splat(obj/projectile/source, atom/firer, atom/target, angle, hit_limb_zone, blocked)
+ SIGNAL_HANDLER
+ if(blocked != 100)
+ splat(source, target)
+
+/datum/component/splat/proc/throw_splat(atom/movable/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
+ SIGNAL_HANDLER
+ if(caught) //someone caught us!
+ return
+ splat(source, hit_atom)
+
+/datum/component/splat/proc/splat(atom/movable/source, atom/hit_atom)
+ var/turf/hit_turf = get_turf(hit_atom)
+ new smudge_type(hit_turf)
+ var/can_splat_on = TRUE
+ if(isliving(hit_atom))
+ var/mob/living/living_target_getting_hit = hit_atom
+ if(iscarbon(living_target_getting_hit))
+ can_splat_on = !!(living_target_getting_hit.get_bodypart(BODY_ZONE_HEAD))
+ hit_callback?.Invoke(living_target_getting_hit, can_splat_on)
+ if(can_splat_on && is_type_in_typecache(hit_atom, GLOB.splattable))
+ hit_atom.AddComponent(/datum/component/face_decal/splat, icon_state, layer, splat_color || source.color, memory_type, moodlet_type)
+ SEND_SIGNAL(source, COMSIG_MOVABLE_SPLAT, hit_atom)
+ qdel(source)
diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm
index 94521486bcc5f..afd8cce49e8c7 100644
--- a/code/datums/components/squeak.dm
+++ b/code/datums/components/squeak.dm
@@ -1,5 +1,5 @@
/datum/component/squeak
- var/static/list/default_squeak_sounds = list('sound/items/toysqueak1.ogg'=1, 'sound/items/toysqueak2.ogg'=1, 'sound/items/toysqueak3.ogg'=1)
+ var/static/list/default_squeak_sounds = list('sound/items/toy_squeak/toysqueak1.ogg'=1, 'sound/items/toy_squeak/toysqueak2.ogg'=1, 'sound/items/toy_squeak/toysqueak3.ogg'=1)
var/list/override_squeak_sounds
var/mob/holder
diff --git a/code/datums/components/stationloving.dm b/code/datums/components/stationloving.dm
index 35f67d9cd0295..8b59717da70b8 100644
--- a/code/datums/components/stationloving.dm
+++ b/code/datums/components/stationloving.dm
@@ -61,7 +61,7 @@
CRASH("Unable to find a blobstart landmark for [type] to relocate [parent].")
var/atom/movable/movable_parent = parent
- playsound(movable_parent, 'sound/machines/synth_no.ogg', 5, TRUE)
+ playsound(movable_parent, 'sound/machines/synth/synth_no.ogg', 5, TRUE)
var/mob/holder = get(movable_parent, /mob)
if(holder)
diff --git a/code/datums/components/sticker.dm b/code/datums/components/sticker.dm
index a11ab10e7c6f8..b627f9923204b 100644
--- a/code/datums/components/sticker.dm
+++ b/code/datums/components/sticker.dm
@@ -84,8 +84,8 @@
var/atom/parent_atom = parent
sticker_overlay = mutable_appearance(icon = our_sticker.icon, icon_state = our_sticker.icon_state, layer = parent_atom.layer + 0.01, appearance_flags = RESET_COLOR)
- sticker_overlay.pixel_w = px - world.icon_size / 2
- sticker_overlay.pixel_z = py - world.icon_size / 2
+ sticker_overlay.pixel_w = px - ICON_SIZE_X / 2
+ sticker_overlay.pixel_z = py - ICON_SIZE_Y / 2
parent_atom.add_overlay(sticker_overlay)
stick_callback?.Invoke(parent)
diff --git a/code/datums/components/summoning.dm b/code/datums/components/summoning.dm
index 69ade1e2f1b56..4821f70d006d3 100644
--- a/code/datums/components/summoning.dm
+++ b/code/datums/components/summoning.dm
@@ -24,7 +24,7 @@
max_mobs = 3,
spawn_delay = 10 SECONDS,
spawn_text = "appears out of nowhere",
- spawn_sound = 'sound/magic/summon_magic.ogg',
+ spawn_sound = 'sound/effects/magic/summon_magic.ogg',
list/faction,
)
if(!isitem(parent) && !ishostile(parent) && !isgun(parent) && !ismachinery(parent) && !isstructure(parent) && !isprojectilespell(parent))
diff --git a/code/datums/components/supermatter_crystal.dm b/code/datums/components/supermatter_crystal.dm
index 81a29b56c6d81..53a0797c2e1c0 100644
--- a/code/datums/components/supermatter_crystal.dm
+++ b/code/datums/components/supermatter_crystal.dm
@@ -71,7 +71,7 @@
SIGNAL_HANDLER
if(isliving(user))
var/mob/living/living_mob = user
- if(living_mob.incorporeal_move || living_mob.status_flags & GODMODE)
+ if(living_mob.incorporeal_move || HAS_TRAIT(living_mob, TRAIT_GODMODE))
return
if(isalien(user))
dust_mob(source, user, cause = "alien attack")
@@ -80,7 +80,7 @@
/datum/component/supermatter_crystal/proc/animal_hit(datum/source, mob/living/simple_animal/user, list/modifiers)
SIGNAL_HANDLER
- if(user.incorporeal_move || user.status_flags & GODMODE)
+ if(user.incorporeal_move || HAS_TRAIT(user, TRAIT_GODMODE))
return
var/atom/atom_source = source
var/murder
@@ -101,7 +101,7 @@
SIGNAL_HANDLER
if(isliving(user))
var/mob/living/living_mob = user
- if(living_mob.incorporeal_move || living_mob.status_flags & GODMODE)
+ if(living_mob.incorporeal_move || HAS_TRAIT(living_mob, TRAIT_GODMODE))
return
var/atom/atom_source = source
if(iscyborg(user) && atom_source.Adjacent(user))
@@ -115,7 +115,7 @@
/datum/component/supermatter_crystal/proc/hand_hit(datum/source, mob/living/user, list/modifiers)
SIGNAL_HANDLER
- if(user.incorporeal_move || user.status_flags & GODMODE)
+ if(user.incorporeal_move || HAS_TRAIT(user, TRAIT_GODMODE))
return
if(user.zone_selected != BODY_ZONE_PRECISE_MOUTH)
dust_mob(source, user, cause = "hand")
@@ -202,7 +202,7 @@
return
if(atom_source.Adjacent(user)) //if the item is stuck to the person, kill the person too instead of eating just the item.
- if(user.incorporeal_move || user.status_flags & GODMODE)
+ if(user.incorporeal_move || HAS_TRAIT(user, TRAIT_GODMODE))
return
var/vis_msg = span_danger("[user] reaches out and touches [atom_source] with [item], inducing a resonance... [item] starts to glow briefly before the light continues up to [user]'s body. [user.p_They()] burst[user.p_s()] into flames before flashing into dust!")
var/mob_msg = span_userdanger("You reach out and touch [atom_source] with [item]. Everything starts burning and all you can hear is ringing. Your last thought is \"That was not a wise decision.\"")
@@ -219,7 +219,7 @@
SIGNAL_HANDLER
if(isliving(hit_object))
var/mob/living/hit_mob = hit_object
- if(hit_mob.incorporeal_move || hit_mob.status_flags & GODMODE)
+ if(hit_mob.incorporeal_move || HAS_TRAIT(hit_mob, TRAIT_GODMODE))
return
var/atom/atom_source = source
var/obj/machinery/power/supermatter_crystal/our_supermatter = parent // Why is this a component?
@@ -272,7 +272,7 @@
span_hear("You hear a loud crack as you are washed with a wave of heat."))
/datum/component/supermatter_crystal/proc/dust_mob(datum/source, mob/living/nom, vis_msg, mob_msg, cause)
- if(nom.incorporeal_move || nom.status_flags & GODMODE) //try to keep supermatter sliver's + hemostat's dust conditions in sync with this too
+ if(nom.incorporeal_move || HAS_TRAIT(nom, TRAIT_GODMODE)) //try to keep supermatter sliver's + hemostat's dust conditions in sync with this too
return
var/atom/atom_source = source
if(!vis_msg)
@@ -290,10 +290,8 @@
/datum/component/supermatter_crystal/proc/consume(atom/source, atom/movable/consumed_object)
if(consumed_object.flags_1 & SUPERMATTER_IGNORES_1)
return
- if(isliving(consumed_object))
- var/mob/living/consumed_mob = consumed_object
- if(consumed_mob.status_flags & GODMODE)
- return
+ if(HAS_TRAIT(consumed_object, TRAIT_GODMODE))
+ return
var/atom/atom_source = source
SEND_SIGNAL(consumed_object, COMSIG_SUPERMATTER_CONSUMED, atom_source)
diff --git a/code/datums/components/tackle.dm b/code/datums/components/tackle.dm
index 0c23733ea1658..baf1efaee1dd5 100644
--- a/code/datums/components/tackle.dm
+++ b/code/datums/components/tackle.dm
@@ -71,7 +71,7 @@
/datum/component/tackler/proc/checkTackle(mob/living/carbon/user, atom/clicked_atom, list/modifiers)
SIGNAL_HANDLER
- if(modifiers[ALT_CLICK] || modifiers[SHIFT_CLICK] || modifiers[CTRL_CLICK] || modifiers[MIDDLE_CLICK])
+ if(!modifiers[RIGHT_CLICK] || modifiers[ALT_CLICK] || modifiers[SHIFT_CLICK] || modifiers[CTRL_CLICK] || modifiers[MIDDLE_CLICK])
return
if(!user.throw_mode || user.get_active_held_item() || user.pulling || user.buckled || user.incapacitated)
@@ -104,7 +104,7 @@
tackling = TRUE
RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(checkObstacle))
- playsound(user, 'sound/weapons/thudswoosh.ogg', 40, TRUE, -1)
+ playsound(user, 'sound/items/weapons/thudswoosh.ogg', 40, TRUE, -1)
var/leap_word = isfelinid(user) ? "pounce" : "leap" //If cat, "pounce" instead of "leap".
if(can_see(user, clicked_atom, 7))
@@ -533,7 +533,7 @@
else
user.adjustBruteLoss(40, updating_health=FALSE)
user.adjustStaminaLoss(30)
- playsound(user, 'sound/effects/blobattack.ogg', 60, TRUE)
+ playsound(user, 'sound/effects/blob/blobattack.ogg', 60, TRUE)
playsound(user, 'sound/effects/splat.ogg', 70, TRUE)
playsound(user, 'sound/effects/wounds/crack2.ogg', 70, TRUE)
user.emote("scream")
@@ -550,7 +550,7 @@
user.adjustBruteLoss(40, updating_health = FALSE)
user.adjustStaminaLoss(30)
user.gain_trauma_type(BRAIN_TRAUMA_MILD)
- playsound(user, 'sound/effects/blobattack.ogg', 60, TRUE)
+ playsound(user, 'sound/effects/blob/blobattack.ogg', 60, TRUE)
playsound(user, 'sound/effects/splat.ogg', 70, TRUE)
user.emote("gurgle")
shake_camera(user, 7, 7)
@@ -562,7 +562,7 @@
user.adjustBruteLoss(30)
user.Unconscious(10 SECONDS)
user.gain_trauma_type(BRAIN_TRAUMA_MILD)
- user.playsound_local(get_turf(user), 'sound/weapons/flashbang.ogg', 100, TRUE, 8)
+ user.playsound_local(get_turf(user), 'sound/items/weapons/flashbang.ogg', 100, TRUE, 8)
shake_camera(user, 6, 6)
user.flash_act(1, TRUE, TRUE, length = 3.5)
@@ -573,7 +573,7 @@
user.adjust_confusion(15 SECONDS)
if(prob(80))
user.gain_trauma(/datum/brain_trauma/mild/concussion)
- user.playsound_local(get_turf(user), 'sound/weapons/flashbang.ogg', 100, TRUE, 8)
+ user.playsound_local(get_turf(user), 'sound/items/weapons/flashbang.ogg', 100, TRUE, 8)
user.Knockdown(4 SECONDS)
shake_camera(user, 5, 5)
user.flash_act(1, TRUE, TRUE, length = 2.5)
@@ -593,7 +593,7 @@
user.Knockdown(2 SECONDS)
shake_camera(user, 2, 2)
- playsound(user, 'sound/weapons/smash.ogg', 70, TRUE)
+ playsound(user, 'sound/items/weapons/smash.ogg', 70, TRUE)
/datum/component/tackler/proc/resetTackle()
@@ -603,7 +603,7 @@
///A special case for splatting for handling windows
/datum/component/tackler/proc/splatWindow(mob/living/carbon/user, obj/structure/window/W)
- playsound(user, 'sound/effects/Glasshit.ogg', 140, TRUE)
+ playsound(user, 'sound/effects/glass/Glasshit.ogg', 140, TRUE)
if(W.type in list(/obj/structure/window, /obj/structure/window/fulltile, /obj/structure/window/unanchored, /obj/structure/window/fulltile/unanchored)) // boring unreinforced windows
for(var/i in 1 to speed)
@@ -682,7 +682,7 @@
var/datum/thrownthing/tackle = tackle_ref?.resolve()
- playsound(owner, 'sound/weapons/smash.ogg', 70, TRUE)
+ playsound(owner, 'sound/items/weapons/smash.ogg', 70, TRUE)
if(tackle)
tackle.finalize(hit=TRUE)
resetTackle()
diff --git a/code/datums/components/tether.dm b/code/datums/components/tether.dm
index e76f5d5b53cd3..d5e00ddb39858 100644
--- a/code/datums/components/tether.dm
+++ b/code/datums/components/tether.dm
@@ -1,39 +1,193 @@
+/// Creates a tether between two objects that limits movement range. Tether requires LOS and can be adjusted by left/right clicking its
/datum/component/tether
- dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+ dupe_mode = COMPONENT_DUPE_ALLOWED
+ /// Other side of the tether
var/atom/tether_target
+ /// Maximum (and initial) distance that this tether can be adjusted to
var/max_dist
+ /// What the tether is going to be called
var/tether_name
+ /// Current extension distance
+ var/cur_dist
+ /// Embedded item that the tether "should" originate from
+ var/atom/embed_target
+ /// Beam effect
+ var/datum/beam/tether_beam
+ /// Tether module if we were created by one
+ var/obj/item/mod/module/tether/parent_module
-/datum/component/tether/Initialize(atom/tether_target, max_dist = 4, tether_name)
- if(!isliving(parent) || !istype(tether_target) || !tether_target.loc)
+/datum/component/tether/Initialize(atom/tether_target, max_dist = 7, tether_name, atom/embed_target = null, start_distance = null, parent_module = null)
+ if(!ismovable(parent) || !istype(tether_target) || !tether_target.loc)
return COMPONENT_INCOMPATIBLE
+
src.tether_target = tether_target
+ src.embed_target = embed_target
src.max_dist = max_dist
+ src.parent_module = parent_module
+ cur_dist = max_dist
+ if (start_distance != null)
+ cur_dist = start_distance
+ var/datum/beam/beam = tether_target.Beam(parent, "line", 'icons/obj/clothing/modsuit/mod_modules.dmi', emissive = FALSE, beam_type = /obj/effect/ebeam/tether)
+ tether_beam = beam
if (ispath(tether_name, /atom))
var/atom/tmp = tether_name
src.tether_name = initial(tmp.name)
else
src.tether_name = tether_name
- RegisterSignals(parent, list(COMSIG_MOVABLE_PRE_MOVE), PROC_REF(checkTether))
-/datum/component/tether/proc/checkTether(mob/mover, newloc)
+/datum/component/tether/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(check_tether))
+ RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(check_snap))
+ RegisterSignal(tether_target, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(check_tether))
+ RegisterSignal(tether_target, COMSIG_MOVABLE_MOVED, PROC_REF(check_snap))
+ RegisterSignal(tether_target, COMSIG_QDELETING, PROC_REF(on_delete))
+ RegisterSignal(tether_beam.visuals, COMSIG_CLICK, PROC_REF(beam_click))
+ // Also snap if the beam gets deleted, more of a backup check than anything
+ RegisterSignal(tether_beam.visuals, COMSIG_QDELETING, PROC_REF(on_delete))
+
+ if (!isnull(embed_target))
+ RegisterSignal(embed_target, COMSIG_ITEM_UNEMBEDDED, PROC_REF(on_embedded_removed))
+ RegisterSignal(embed_target, COMSIG_QDELETING, PROC_REF(on_delete))
+
+ if (!isnull(parent_module))
+ RegisterSignals(parent_module, list(COMSIG_QDELETING, COMSIG_MOVABLE_MOVED, COMSIG_MOD_TETHER_SNAP), PROC_REF(snap))
+
+/datum/component/tether/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_MOVABLE_PRE_MOVE, COMSIG_MOVABLE_MOVED))
+ if (!QDELETED(tether_target))
+ UnregisterSignal(tether_target, list(COMSIG_MOVABLE_PRE_MOVE, COMSIG_MOVABLE_MOVED, COMSIG_QDELETING))
+ if (!QDELETED(tether_beam))
+ UnregisterSignal(tether_beam.visuals, list(COMSIG_CLICK, COMSIG_QDELETING))
+ qdel(tether_beam)
+ if (!QDELETED(embed_target))
+ UnregisterSignal(embed_target, list(COMSIG_ITEM_UNEMBEDDED, COMSIG_QDELETING))
+
+/datum/component/tether/proc/check_tether(atom/source, new_loc)
SIGNAL_HANDLER
- if (get_dist(mover,newloc) > max_dist)
- to_chat(mover, span_userdanger("The [tether_name] runs out of slack and prevents you from moving!"))
+ if (check_snap())
+ return
+
+ if (!isturf(new_loc))
+ to_chat(source, span_warning("[tether_name] prevents you from entering [new_loc]!"))
return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
+ var/atom/movable/anchor = (source == tether_target ? parent : tether_target)
+ if (get_dist(anchor, new_loc) > cur_dist)
+ if (!istype(anchor) || anchor.anchored || !anchor.Move(get_step_towards(anchor, new_loc)))
+ to_chat(source, span_warning("[tether_name] runs out of slack and prevents you from moving!"))
+ return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
+
var/atom/blocker
- out:
- for(var/turf/T in get_line(tether_target,newloc))
- if (T.density)
- blocker = T
- break out
- for(var/a in T)
- var/atom/A = a
- if(A.density && A != mover && A != tether_target)
- blocker = A
- break out
+ var/anchor_dir = get_dir(source, anchor)
+ for (var/turf/line_turf in get_line(anchor, new_loc))
+ if (line_turf.density && line_turf != anchor.loc && line_turf != source.loc)
+ blocker = line_turf
+ break
+ if (line_turf == anchor.loc || line_turf == source.loc)
+ for (var/atom/in_turf in line_turf)
+ if ((in_turf.flags_1 & ON_BORDER_1) && (in_turf.dir & anchor_dir))
+ blocker = in_turf
+ break
+ else
+ for (var/atom/in_turf in line_turf)
+ if (in_turf.density && in_turf != source && in_turf != tether_target)
+ blocker = in_turf
+ break
+
+ if (!isnull(blocker))
+ break
+
if (blocker)
- to_chat(mover, span_userdanger("The [tether_name] catches on [blocker] and prevents you from moving!"))
+ to_chat(source, span_warning("[tether_name] catches on [blocker] and prevents you from moving!"))
return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
+
+ if (get_dist(anchor, new_loc) != cur_dist || !ismovable(source))
+ return
+
+ var/atom/movable/movable_source = source
+ var/datum/drift_handler/handler = movable_source.drift_handler
+ if (isnull(handler))
+ return
+ handler.remove_angle_force(get_angle(anchor, source))
+
+/datum/component/tether/proc/check_snap()
+ SIGNAL_HANDLER
+
+ var/atom/atom_target = parent
+ // Something broke us out, snap the tether
+ if (get_dist(atom_target, tether_target) > cur_dist + 1 || !isturf(atom_target.loc) || !isturf(tether_target.loc) || atom_target.z != tether_target.z)
+ snap()
+
+/datum/component/tether/proc/snap()
+ SIGNAL_HANDLER
+
+ var/atom/atom_target = parent
+ atom_target.visible_message(span_warning("[atom_target]'s [tether_name] snaps!"), span_userdanger("Your [tether_name] snaps!"), span_hear("You hear a cable snapping."))
+ playsound(atom_target, 'sound/effects/snap.ogg', 50, TRUE)
+ qdel(src)
+
+/datum/component/tether/proc/on_delete()
+ SIGNAL_HANDLER
+ qdel(src)
+
+/datum/component/tether/proc/on_embedded_removed(atom/source, mob/living/victim)
+ SIGNAL_HANDLER
+ parent.AddComponent(/datum/component/tether, source, max_dist, tether_name, cur_dist)
+ qdel(src)
+
+/datum/component/tether/proc/beam_click(atom/source, atom/location, control, params, mob/user)
+ SIGNAL_HANDLER
+
+ INVOKE_ASYNC(src, PROC_REF(process_beam_click), source, location, params, user)
+
+/datum/component/tether/proc/process_beam_click(atom/source, atom/location, params, mob/user)
+ var/list/modifiers = params2list(params)
+ if(LAZYACCESS(modifiers, CTRL_CLICK))
+ location.balloon_alert(user, "cutting the tether...")
+ if (!do_after(user, 1 SECONDS, user))
+ return
+
+ qdel(src)
+ location.balloon_alert(user, "tether cut!")
+ to_chat(parent, span_danger("Your [tether_name] has been cut!"))
+ return
+
+ if (LAZYACCESS(modifiers, RIGHT_CLICK))
+ if (cur_dist >= max_dist)
+ location.balloon_alert(user, "no coil remaining!")
+ return
+ cur_dist += 1
+ location.balloon_alert(user, "tether extended")
+ return
+
+ if (cur_dist <= 1)
+ location.balloon_alert(user, "too short!")
+ return
+
+ if (cur_dist > get_dist(parent, tether_target))
+ cur_dist -= 1
+ location.balloon_alert(user, "tether shortened")
+ return
+
+ if (!ismovable(parent) && !ismovable(tether_target))
+ location.balloon_alert(user, "too short!")
+ return
+
+ var/atom/movable/movable_parent = parent
+ var/atom/movable/movable_target = tether_target
+
+ if (istype(movable_parent) && movable_parent.Move(get_step(movable_parent.loc, get_dir(movable_parent, movable_target))))
+ cur_dist -= 1
+ location.balloon_alert(user, "tether shortened")
+ return
+
+ if (istype(movable_target) && movable_target.Move(get_step(movable_target.loc, get_dir(movable_target, movable_parent))))
+ cur_dist -= 1
+ location.balloon_alert(user, "tether shortened")
+ return
+
+ location.balloon_alert(user, "too short!")
+
+/obj/effect/ebeam/tether
+ mouse_opacity = MOUSE_OPACITY_ICON
diff --git a/code/datums/components/thermite.dm b/code/datums/components/thermite.dm
index 7ab8b755ca10a..1fac66c07cd64 100644
--- a/code/datums/components/thermite.dm
+++ b/code/datums/components/thermite.dm
@@ -116,7 +116,7 @@
*/
/datum/component/thermite/proc/thermite_melt(mob/user)
var/turf/parent_turf = parent
- playsound(parent_turf, 'sound/items/welder.ogg', 100, TRUE)
+ playsound(parent_turf, 'sound/items/tools/welder.ogg', 100, TRUE)
fakefire = new(parent_turf)
burn_callback = CALLBACK(src, PROC_REF(burn_parent), user)
burn_timer = addtimer(burn_callback, min(amount * 0.35 SECONDS, 20 SECONDS), TIMER_STOPPABLE)
diff --git a/code/datums/components/toggle_attached_clothing.dm b/code/datums/components/toggle_attached_clothing.dm
index 9ba42fe091732..8321119d85e58 100644
--- a/code/datums/components/toggle_attached_clothing.dm
+++ b/code/datums/components/toggle_attached_clothing.dm
@@ -198,9 +198,9 @@
on_removed?.Invoke(deployable)
var/obj/item/parent_gear = parent
- if (destroy_on_removal)
+ if(destroy_on_removal)
QDEL_NULL(deployable)
- else if (parent_icon_state_suffix)
+ if(parent_icon_state_suffix)
parent_gear.icon_state = "[initial(parent_gear.icon_state)]"
parent_gear.worn_icon_state = parent_gear.icon_state
parent_gear.update_slot_icon()
diff --git a/code/datums/components/transforming.dm b/code/datums/components/transforming.dm
index 5276f45dc0a75..622fb2ed7d31d 100644
--- a/code/datums/components/transforming.dm
+++ b/code/datums/components/transforming.dm
@@ -50,7 +50,7 @@
throwforce_on = 0,
throw_speed_on = 2,
sharpness_on = NONE,
- hitsound_on = 'sound/weapons/blade1.ogg',
+ hitsound_on = 'sound/items/weapons/blade1.ogg',
w_class_on = WEIGHT_CLASS_BULKY,
clumsy_check = TRUE,
clumsy_damage = 10,
@@ -174,7 +174,7 @@
/datum/component/transforming/proc/default_transform_message(obj/item/source, mob/user)
if(user)
source.balloon_alert(user, "[active ? "enabled" : "disabled"] [source]")
- playsound(source, 'sound/weapons/batonextend.ogg', 50, TRUE)
+ playsound(source, 'sound/items/weapons/batonextend.ogg', 50, TRUE)
/*
* Toggle active between true and false, and call
diff --git a/code/datums/components/trapdoor.dm b/code/datums/components/trapdoor.dm
index 32b72c48853e5..9efce370e82c7 100644
--- a/code/datums/components/trapdoor.dm
+++ b/code/datums/components/trapdoor.dm
@@ -243,10 +243,10 @@
return
if(SEND_GLOBAL_SIGNAL(COMSIG_GLOB_TRAPDOOR_LINK, src) & LINKED_UP)
playsound(assembly_turf, 'sound/machines/chime.ogg', 50, TRUE)
- assembly_turf.visible_message("[src] has linked up to a nearby trapdoor! \
- You may now use it to check where the trapdoor is... be careful!", vision_distance = SAMETILE_MESSAGE_RANGE)
+ assembly_turf.visible_message(span_notice("[src] has linked up to a nearby trapdoor! \
+ You may now use it to check where the trapdoor is... be careful!"), vision_distance = SAMETILE_MESSAGE_RANGE)
else
- playsound(assembly_turf, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(assembly_turf, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
assembly_turf.visible_message(span_warning("[src] has failed to find a trapdoor nearby to link to."), vision_distance = SAMETILE_MESSAGE_RANGE)
/**
@@ -321,7 +321,7 @@
return TRUE
user.balloon_alert(user, "trapdoor triggered")
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_prompt_confirm.ogg', 50, FALSE)
icon_state = "trapdoor_pressed"
addtimer(VARSET_CALLBACK(src, icon_state, initial(icon_state)), trapdoor_cooldown_time)
COOLDOWN_START(src, trapdoor_cooldown, trapdoor_cooldown_time)
diff --git a/code/datums/dash_weapon.dm b/code/datums/dash_weapon.dm
index 00437a2cdd8f1..146d3c2de0785 100644
--- a/code/datums/dash_weapon.dm
+++ b/code/datums/dash_weapon.dm
@@ -11,9 +11,9 @@
/// How long does it take to get a dash charge back?
var/charge_rate = 25 SECONDS
/// What sound do we play on dash?
- var/dash_sound = 'sound/magic/blink.ogg'
+ var/dash_sound = 'sound/effects/magic/blink.ogg'
/// What sound do we play on recharge?
- var/recharge_sound = 'sound/magic/charge.ogg'
+ var/recharge_sound = 'sound/effects/magic/charge.ogg'
/// What effect does our beam use?
var/beam_effect = "blur"
/// How long does our beam last?
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index a4169004fc982..d170aeca8522e 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -419,6 +419,11 @@
filter_data = null
atom_cast.filters = null
+/// Calls qdel on itself, because signals dont allow callbacks
+/datum/proc/selfdelete()
+ SIGNAL_HANDLER
+ qdel(src)
+
/// Return text from this proc to provide extra context to hard deletes that happen to it
/// Optional, you should use this for cases where replication is difficult and extra context is required
/// Can be called more then once per object, use harddel_deets_dumped to avoid duplicate calls (I am so sorry)
diff --git a/code/datums/diseases/advance/floor_diseases/carpellosis.dm b/code/datums/diseases/advance/floor_diseases/carpellosis.dm
index a0482215494c4..cdeb6051537e3 100644
--- a/code/datums/diseases/advance/floor_diseases/carpellosis.dm
+++ b/code/datums/diseases/advance/floor_diseases/carpellosis.dm
@@ -41,7 +41,7 @@
switch(stage)
if(2)
- if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS)
+ if(SPT_PROB(1, seconds_per_tick) && affected_mob.stat == CONSCIOUS && affected_mob.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL))
to_chat(affected_mob, span_warning("You want to wag your tail..."))
affected_mob.emote("wag")
if(3)
diff --git a/code/datums/diseases/chronic_illness.dm b/code/datums/diseases/chronic_illness.dm
index b1afd1d1939a9..617cfde763d11 100644
--- a/code/datums/diseases/chronic_illness.dm
+++ b/code/datums/diseases/chronic_illness.dm
@@ -42,7 +42,7 @@
need_mob_update += affected_mob.adjustStaminaLoss(70, updating_stamina = FALSE)
if(SPT_PROB(1, seconds_per_tick))
to_chat(affected_mob, span_danger("You feel a buzzing in your brain."))
- SEND_SOUND(affected_mob, sound('sound/weapons/flash_ring.ogg'))
+ SEND_SOUND(affected_mob, sound('sound/items/weapons/flash_ring.ogg'))
if(SPT_PROB(0.5, seconds_per_tick))
need_mob_update += affected_mob.adjustBruteLoss(1, updating_health = FALSE)
if(need_mob_update)
@@ -75,7 +75,7 @@
if(2)
to_chat(affected_mob, span_boldwarning("There is no place for you in this timeline."))
affected_mob.adjustStaminaLoss(100, forced = TRUE)
- playsound(affected_mob.loc, 'sound/magic/repulse.ogg', 100, FALSE)
+ playsound(affected_mob.loc, 'sound/effects/magic/repulse.ogg', 100, FALSE)
affected_mob.emote("scream")
for(var/mob/living/viewers in viewers(3, affected_mob.loc))
viewers.flash_act()
diff --git a/code/datums/diseases/heart_failure.dm b/code/datums/diseases/heart_failure.dm
index 45d4e6672fb69..419fc9efff3df 100644
--- a/code/datums/diseases/heart_failure.dm
+++ b/code/datums/diseases/heart_failure.dm
@@ -43,7 +43,7 @@
to_chat(affected_mob, span_warning("You feel [pick("full", "nauseated", "sweaty", "weak", "tired", "short of breath", "uneasy")]."))
if(3 to 4)
if(!sound)
- affected_mob.playsound_local(affected_mob, 'sound/health/slowbeat.ogg', 40, FALSE, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE)
+ affected_mob.playsound_local(affected_mob, 'sound/effects/health/slowbeat.ogg', 40, FALSE, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE)
sound = TRUE
if(SPT_PROB(1.5, seconds_per_tick))
to_chat(affected_mob, span_danger("You feel a sharp pain in your chest!"))
diff --git a/code/datums/drift_handler.dm b/code/datums/drift_handler.dm
new file mode 100644
index 0000000000000..27a4fd4bc9154
--- /dev/null
+++ b/code/datums/drift_handler.dm
@@ -0,0 +1,258 @@
+///Component that handles drifting
+///Manages a movement loop that actually does the legwork of moving someone
+///Alongside dealing with the post movement input blocking required to make things look nice
+/datum/drift_handler
+ var/atom/movable/parent
+ var/atom/inertia_last_loc
+ var/old_dir
+ var/datum/move_loop/smooth_move/drifting_loop
+ ///Should we ignore the next glide rate input we get?
+ ///This is to some extent a hack around the order of operations
+ ///Around COMSIG_MOVELOOP_POSTPROCESS. I'm sorry lad
+ var/ignore_next_glide = FALSE
+ ///Have we been delayed? IE: active, but not working right this second?
+ var/delayed = FALSE
+ var/block_inputs_until
+ /// How much force is behind this drift.
+ var/drift_force = 1
+
+/// Accepts three args. The direction to drift in, if the drift is instant or not, and if it's not instant, the delay on the start
+/datum/drift_handler/New(atom/movable/parent, inertia_angle, instant = FALSE, start_delay = 0, drift_force = 1)
+ . = ..()
+ src.parent = parent
+ parent.drift_handler = src
+ var/flags = MOVEMENT_LOOP_OUTSIDE_CONTROL
+ if(instant)
+ flags |= MOVEMENT_LOOP_START_FAST
+ 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)
+
+ if(!drifting_loop)
+ qdel(src)
+ return
+
+ RegisterSignal(drifting_loop, COMSIG_MOVELOOP_START, PROC_REF(drifting_start))
+ RegisterSignal(drifting_loop, COMSIG_MOVELOOP_STOP, PROC_REF(drifting_stop))
+ RegisterSignal(drifting_loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(before_move))
+ RegisterSignal(drifting_loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(after_move))
+ RegisterSignal(drifting_loop, COMSIG_QDELETING, PROC_REF(loop_death))
+ RegisterSignal(parent, COMSIG_MOB_ATTEMPT_HALT_SPACEMOVE, PROC_REF(attempt_halt))
+ if(drifting_loop.status & MOVELOOP_STATUS_RUNNING)
+ drifting_start(drifting_loop) // There's a good chance it'll autostart, gotta catch that
+
+ var/visual_delay = get_loop_delay(parent)
+
+ // Start delay is essentially a more granular version of instant
+ // Isn't used in the standard case, just for things that have odd wants
+ if(!instant && start_delay)
+ drifting_loop.pause_for(start_delay)
+ visual_delay = start_delay
+
+ apply_initial_visuals(visual_delay)
+
+/datum/drift_handler/Destroy()
+ inertia_last_loc = null
+ if(!QDELETED(drifting_loop))
+ qdel(drifting_loop)
+ drifting_loop = null
+ parent.inertia_moving = FALSE
+ parent.drift_handler = null
+ return ..()
+
+/datum/drift_handler/proc/apply_initial_visuals(visual_delay)
+ // If something "somewhere" doesn't want us to apply our glidesize delays, don't
+ if(SEND_SIGNAL(parent, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT) & DRIFT_VISUAL_FAILED)
+ return
+
+ // Ignore the next glide because it's literally just us
+ ignore_next_glide = TRUE
+ parent.set_glide_size(MOVEMENT_ADJUSTED_GLIDE_SIZE(visual_delay, SSnewtonian_movement.visual_delay))
+ if(!ismob(parent))
+ return
+ var/mob/mob_parent = parent
+ //Ok this is slightly weird, but basically, we need to force the client to glide at our rate
+ //Make sure moving into a space move looks like a space move essentially
+ //There is an inbuilt assumption that gliding will be added as a part of a move call, but eh
+ //It's ok if it's not, it's just important if it is.
+ mob_parent.client?.visual_delay = MOVEMENT_ADJUSTED_GLIDE_SIZE(visual_delay, SSnewtonian_movement.visual_delay)
+
+/datum/drift_handler/proc/newtonian_impulse(inertia_angle, start_delay, additional_force, controlled_cap)
+ SIGNAL_HANDLER
+ inertia_last_loc = parent.loc
+ // We've been told to move in the middle of deletion process, tell parent to create a new handler instead
+ if(!drifting_loop)
+ qdel(src)
+ return FALSE
+
+ var/applied_force = additional_force
+
+ var/force_x = sin(drifting_loop.angle) * drift_force + sin(inertia_angle) * applied_force / parent.inertia_force_weight
+ var/force_y = cos(drifting_loop.angle) * drift_force + cos(inertia_angle) * applied_force / parent.inertia_force_weight
+
+ drift_force = clamp(sqrt(force_x * force_x + force_y * force_y), 0, !isnull(controlled_cap) ? controlled_cap : INERTIA_FORCE_CAP)
+ if(drift_force < 0.1) // Rounding issues
+ qdel(src)
+ return TRUE
+
+ drifting_loop.set_angle(delta_to_angle(force_x, force_y))
+ drifting_loop.set_delay(get_loop_delay(parent))
+ return TRUE
+
+/datum/drift_handler/proc/drifting_start()
+ SIGNAL_HANDLER
+ inertia_last_loc = parent.loc
+ RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(handle_move))
+ // We will use glide size to intuit how long to delay our loop's next move for
+ // This way you can't ride two movements at once while drifting, since that'd be dumb as fuck
+ RegisterSignal(parent, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, PROC_REF(handle_glidesize_update))
+ // If you stop pulling something mid drift, I want it to retain that momentum
+ RegisterSignal(parent, COMSIG_ATOM_NO_LONGER_PULLING, PROC_REF(stopped_pulling))
+
+/datum/drift_handler/proc/drifting_stop()
+ SIGNAL_HANDLER
+ parent.inertia_moving = FALSE
+ ignore_next_glide = FALSE
+ UnregisterSignal(parent, list(COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, COMSIG_ATOM_NO_LONGER_PULLING))
+
+/datum/drift_handler/proc/before_move(datum/source)
+ SIGNAL_HANDLER
+ parent.inertia_moving = TRUE
+ old_dir = parent.dir
+ delayed = FALSE
+
+/datum/drift_handler/proc/after_move(datum/source, result, visual_delay)
+ SIGNAL_HANDLER
+ if(result == MOVELOOP_FAILURE)
+ qdel(src)
+ return
+
+ parent.setDir(old_dir)
+ parent.inertia_moving = FALSE
+ if(parent.Process_Spacemove(angle2dir(drifting_loop.angle), continuous_move = TRUE))
+ glide_to_halt(visual_delay)
+ return
+
+ inertia_last_loc = parent.loc
+ ignore_next_glide = TRUE
+
+/datum/drift_handler/proc/loop_death(datum/source)
+ SIGNAL_HANDLER
+ drifting_loop = null
+
+/datum/drift_handler/proc/handle_move(datum/source, old_loc)
+ SIGNAL_HANDLER
+ // This can happen, because signals once sent cannot be stopped
+ if(QDELETED(src))
+ return
+ if(!isturf(parent.loc))
+ qdel(src)
+ return
+ if(parent.inertia_moving)
+ return
+ if(!parent.Process_Spacemove(angle2dir(drifting_loop.angle), continuous_move = TRUE))
+ return
+ qdel(src)
+
+/// We're going to take the passed in glide size
+/// and use it to manually delay our loop for that period
+/// to allow the other movement to complete
+/datum/drift_handler/proc/handle_glidesize_update(datum/source, glide_size)
+ SIGNAL_HANDLER
+ // If we aren't drifting, or this is us, fuck off
+ if(!drifting_loop || parent.inertia_moving)
+ return
+ // If we are drifting, but this set came from the moveloop itself, drop the input
+ // I'm sorry man
+ if(ignore_next_glide)
+ ignore_next_glide = FALSE
+ return
+ var/glide_delay = round(ICON_SIZE_ALL / glide_size, 1) * world.tick_lag
+ drifting_loop.pause_for(glide_delay)
+ delayed = TRUE
+
+/// If we're pulling something and stop, we want it to continue at our rate and such
+/datum/drift_handler/proc/stopped_pulling(datum/source, atom/movable/was_pulling)
+ SIGNAL_HANDLER
+ // This does mean it falls very slightly behind, but otherwise they'll potentially run into us
+ var/next_move_in = drifting_loop.timer - world.time + world.tick_lag
+ was_pulling.newtonian_move(angle2dir(drifting_loop.angle), start_delay = next_move_in, drift_force = drift_force, controlled_cap = drift_force)
+
+/datum/drift_handler/proc/glide_to_halt(glide_for)
+ if(!ismob(parent))
+ qdel(src)
+ return
+
+ var/mob/mob_parent = parent
+ var/client/our_client = mob_parent.client
+ // If we're not active, don't do the glide because it'll look dumb as fuck
+ if(!our_client || delayed)
+ qdel(src)
+ return
+
+ block_inputs_until = world.time + glide_for + 1
+ QDEL_IN(src, glide_for + 1)
+ qdel(drifting_loop)
+ RegisterSignal(parent, COMSIG_MOB_CLIENT_PRE_MOVE, PROC_REF(allow_final_movement))
+
+/datum/drift_handler/proc/allow_final_movement(datum/source)
+ SIGNAL_HANDLER
+ // Some things want to allow movement out of spacedrift, we should let them
+ if(SEND_SIGNAL(parent, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT) & DRIFT_ALLOW_INPUT)
+ return
+ if(world.time < block_inputs_until)
+ return COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE
+
+/datum/drift_handler/proc/attempt_halt(mob/source, movement_dir, continuous_move, atom/backup)
+ SIGNAL_HANDLER
+
+ if (get_dir(source, backup) == movement_dir || source.loc == backup.loc)
+ if (drift_force >= INERTIA_FORCE_THROW_FLOOR)
+ source.throw_at(backup, 1, floor(1 + (drift_force - INERTIA_FORCE_THROW_FLOOR) / INERTIA_FORCE_PER_THROW_FORCE), spin = FALSE)
+ return
+
+ if (drift_force < INERTIA_FORCE_SPACEMOVE_GRAB || isnull(drifting_loop))
+ return
+
+ if (drift_force <= INERTIA_FORCE_SPACEMOVE_REDUCTION / source.inertia_force_weight)
+ glide_to_halt(get_loop_delay(source))
+ return COMPONENT_PREVENT_SPACEMOVE_HALT
+
+ drift_force -= INERTIA_FORCE_SPACEMOVE_REDUCTION / source.inertia_force_weight
+ drifting_loop.set_delay(get_loop_delay(source))
+ return COMPONENT_PREVENT_SPACEMOVE_HALT
+
+/datum/drift_handler/proc/get_loop_delay(atom/movable/movable)
+ return (DEFAULT_INERTIA_SPEED / ((1 - INERTIA_SPEED_COEF) + drift_force * INERTIA_SPEED_COEF)) * movable.inertia_move_multiplier
+
+/datum/drift_handler/proc/stabilize_drift(target_angle, target_force, stabilization_force)
+ /// We aren't drifting
+ if (isnull(drifting_loop))
+ return
+
+ /// Lack of angle means that we are trying to halt movement
+ if (isnull(target_angle))
+ // Going through newtonian_move ensures that all Process_Spacemove code runs properly, instead of directly adjusting forces
+ parent.newtonian_move(reverse_angle(drifting_loop.angle), drift_force = min(drift_force, stabilization_force))
+ return
+
+ // Force required to be applied in order to get to the desired movement vector, with projection of current movement onto desired vector to ensure that we only compensate for excess
+ var/drift_projection = max(0, cos(target_angle - drifting_loop.angle)) * drift_force
+ var/force_x = sin(target_angle) * target_force - sin(drifting_loop.angle) * drift_force
+ var/force_y = cos(target_angle) * target_force - cos(drifting_loop.angle) * drift_force
+ var/force_angle = delta_to_angle(force_x, force_y)
+ var/applied_force = sqrt(force_x * force_x + force_y * force_y)
+ var/force_projection = max(0, cos(target_angle - force_angle)) * applied_force
+ force_x -= min(force_projection, drift_projection) * sin(target_angle)
+ force_x -= min(force_projection, drift_projection) * cos(target_angle)
+ applied_force = min(sqrt(force_x * force_x + force_y * force_y), stabilization_force)
+ parent.newtonian_move(force_angle, instant = TRUE, drift_force = applied_force)
+
+/// Removes all force in a certain direction
+/datum/drift_handler/proc/remove_angle_force(target_angle)
+ /// We aren't drifting
+ if (isnull(drifting_loop))
+ return
+
+ var/projected_force = max(0, cos(target_angle - drifting_loop.angle)) * drift_force
+ if (projected_force > 0)
+ parent.newtonian_move(reverse_angle(target_angle), projected_force)
diff --git a/code/datums/elements/ai_held_item.dm b/code/datums/elements/ai_held_item.dm
index 053a1827fb23d..185e45da9aa2b 100644
--- a/code/datums/elements/ai_held_item.dm
+++ b/code/datums/elements/ai_held_item.dm
@@ -54,7 +54,7 @@
var/obj/item/carried_item = get_held_item(source)
if (!carried_item)
return
- examine_text += span_notice("[source.p_They()] [source.p_are()] carrying [carried_item.get_examine_string(user)].")
+ examine_text += span_notice("[source.p_They()] [source.p_are()] carrying [carried_item.examine_title(user)].")
/// If we died, drop anything we were carrying
/datum/element/ai_held_item/proc/on_death(mob/living/ol_yeller)
diff --git a/code/datums/elements/art.dm b/code/datums/elements/art.dm
index 81d388aa94af8..d5a642c23d0b6 100644
--- a/code/datums/elements/art.dm
+++ b/code/datums/elements/art.dm
@@ -74,7 +74,7 @@
var/datum/job_department/hater_department = SSjob.get_department_type(hater_department_type)
for(var/datum/job/hater_job as anything in hater_department.department_jobs)
haters += hater_job.title
- var/datum/job/quartermaster/fucking_quartermaster = SSjob.GetJobType(/datum/job/quartermaster)
+ var/datum/job/quartermaster/fucking_quartermaster = SSjob.get_job_type(/datum/job/quartermaster)
haters += fucking_quartermaster.title
if(!(user.mind.assigned_role.title in haters))
diff --git a/code/datums/elements/bed_tucking.dm b/code/datums/elements/bed_tucking.dm
index 58f5640c31c75..3b49f2a608f88 100644
--- a/code/datums/elements/bed_tucking.dm
+++ b/code/datums/elements/bed_tucking.dm
@@ -53,11 +53,11 @@
return COMPONENT_NO_AFTERATTACK
/datum/element/bed_tuckable/proc/tuck(obj/item/tucked, obj/structure/bed/target_bed)
- tucked.dir = target_bed.dir
- tucked.pixel_x = target_bed.dir & EAST ? -x_offset : x_offset
+ tucked.dir = target_bed.dir & target_bed.left_headrest_dirs ? EAST : WEST
+ tucked.pixel_x = target_bed.dir & target_bed.left_headrest_dirs ? -x_offset : x_offset
tucked.pixel_y = y_offset
if(starting_angle)
- rotation_degree = target_bed.dir & EAST ? starting_angle + 180 : starting_angle
+ rotation_degree = target_bed.dir & target_bed.left_headrest_dirs ? starting_angle + 180 : starting_angle
tucked.transform = turn(tucked.transform, rotation_degree)
RegisterSignal(tucked, COMSIG_ITEM_PICKUP, PROC_REF(untuck))
diff --git a/code/datums/elements/bugkiller_reagent.dm b/code/datums/elements/bugkiller_reagent.dm
index 57f2ae65d9209..d2c25926e966f 100644
--- a/code/datums/elements/bugkiller_reagent.dm
+++ b/code/datums/elements/bugkiller_reagent.dm
@@ -59,7 +59,7 @@
/datum/status_effect/bugkiller_death/on_apply()
if(owner.stat == DEAD)
return FALSE
- playsound(owner, 'sound/voice/human/malescream_1.ogg', 25, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, frequency = 5)
+ playsound(owner, 'sound/mobs/humanoids/human/scream/malescream_1.ogg', 25, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, frequency = 5)
to_chat(owner, span_userdanger("The world begins to go dark..."))
owner.spasm_animation(spasm_loops)
owner.adjust_eye_blur(duration)
diff --git a/code/datums/elements/can_shatter.dm b/code/datums/elements/can_shatter.dm
index be7e02e25b458..df19e4ef12344 100644
--- a/code/datums/elements/can_shatter.dm
+++ b/code/datums/elements/can_shatter.dm
@@ -45,9 +45,10 @@
shatter(source, impacted_turf)
/// Tells the parent to shatter if we are thrown and impact something
-/datum/element/can_shatter/proc/on_throw_impact(datum/source, atom/hit_atom)
+/datum/element/can_shatter/proc/on_throw_impact(datum/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
SIGNAL_HANDLER
-
+ if(caught)
+ return
shatter(source, hit_atom)
/// Handles the actual shattering part, throwing shards of whatever is defined on the component everywhere
diff --git a/code/datums/elements/climbable.dm b/code/datums/elements/climbable.dm
index 113cc8aaa90ef..5700ca3bc2e85 100644
--- a/code/datums/elements/climbable.dm
+++ b/code/datums/elements/climbable.dm
@@ -106,8 +106,8 @@
if(ISDIAGONALDIR(climbed_thing.dir) && same_loc)
if(params) //we check the icon x and y parameters of the click-drag to determine step_dir.
var/list/modifiers = params2list(params)
- var/x_dist = (text2num(LAZYACCESS(modifiers, ICON_X)) - world.icon_size/2) * (climbed_thing.dir & WEST ? -1 : 1)
- var/y_dist = (text2num(LAZYACCESS(modifiers, ICON_Y)) - world.icon_size/2) * (climbed_thing.dir & SOUTH ? -1 : 1)
+ var/x_dist = (text2num(LAZYACCESS(modifiers, ICON_X)) - ICON_SIZE_X/2) * (climbed_thing.dir & WEST ? -1 : 1)
+ var/y_dist = (text2num(LAZYACCESS(modifiers, ICON_Y)) - ICON_SIZE_Y/2) * (climbed_thing.dir & SOUTH ? -1 : 1)
dir_step = (x_dist >= y_dist ? (EAST|WEST) : (NORTH|SOUTH)) & climbed_thing.dir
else
dir_step = get_dir(user, get_step(climbed_thing, climbed_thing.dir))
diff --git a/code/datums/elements/consumable_mob.dm b/code/datums/elements/consumable_mob.dm
index 1a7c67a431220..fafdb8cbcab28 100644
--- a/code/datums/elements/consumable_mob.dm
+++ b/code/datums/elements/consumable_mob.dm
@@ -23,7 +23,7 @@
/datum/element/consumable_mob/proc/on_consume(atom/movable/source, mob/living/consumer)
SIGNAL_HANDLER
- if(!consumer.combat_mode || !consumer.reagents)
+ if(!consumer.combat_mode || !consumer.reagents || HAS_TRAIT(consumer, TRAIT_PACIFISM))
return
for(var/reagent_type in reagents_list)
if(isnull(reagents_list[reagent_type]))
diff --git a/code/datums/elements/corrupted_organ.dm b/code/datums/elements/corrupted_organ.dm
index 666ca3460fce5..504c6851e00c6 100644
--- a/code/datums/elements/corrupted_organ.dm
+++ b/code/datums/elements/corrupted_organ.dm
@@ -41,7 +41,7 @@
)
return
var/turf/origin_turf = get_turf(organ)
- playsound(organ, 'sound/magic/forcewall.ogg', vol = 100)
+ playsound(organ, 'sound/effects/magic/forcewall.ogg', vol = 100)
new /obj/effect/temp_visual/curse_blast(origin_turf)
organ.visible_message(span_revenwarning("[organ] explodes in a burst of dark energy!"))
for(var/mob/living/target in range(1, origin_turf))
diff --git a/code/datums/elements/damage_threshold.dm b/code/datums/elements/damage_threshold.dm
index 60c87dc5ed5c1..764f5d7a9bd6d 100644
--- a/code/datums/elements/damage_threshold.dm
+++ b/code/datums/elements/damage_threshold.dm
@@ -45,7 +45,7 @@
span_hear("You hear a thud."),
COMBAT_MESSAGE_RANGE,
)
- playsound(source, 'sound/weapons/tap.ogg', tap_vol, TRUE, -1)
+ playsound(source, 'sound/items/weapons/tap.ogg', tap_vol, TRUE, -1)
return SUCCESSFUL_BLOCK
return NONE
diff --git a/code/datums/elements/decals/blood.dm b/code/datums/elements/decals/blood.dm
index 857b9e2b678ea..16fd4241147d4 100644
--- a/code/datums/elements/decals/blood.dm
+++ b/code/datums/elements/decals/blood.dm
@@ -24,8 +24,8 @@
icon = I.icon
icon_state = I.icon_state
var/icon/icon_for_size = icon(icon, icon_state)
- var/scale_factor_x = icon_for_size.Width()/world.icon_size
- var/scale_factor_y = icon_for_size.Height()/world.icon_size
+ var/scale_factor_x = icon_for_size.Width()/ICON_SIZE_X
+ var/scale_factor_y = icon_for_size.Height()/ICON_SIZE_Y
var/mutable_appearance/blood_splatter = mutable_appearance('icons/effects/blood.dmi', "itemblood", appearance_flags = RESET_COLOR) //MA of the blood that we apply
blood_splatter.transform = blood_splatter.transform.Scale(scale_factor_x, scale_factor_y)
blood_splatter.blend_mode = BLEND_INSET_OVERLAY
diff --git a/code/datums/elements/deliver_first.dm b/code/datums/elements/deliver_first.dm
index 0fb83a2545603..ae1947bff02a8 100644
--- a/code/datums/elements/deliver_first.dm
+++ b/code/datums/elements/deliver_first.dm
@@ -80,7 +80,7 @@
if(user)
target.balloon_alert(user, "access denied until delivery!")
if(COOLDOWN_FINISHED(src, deny_cooldown))
- playsound(target, 'sound/machines/buzz-two.ogg', 30, TRUE)
+ playsound(target, 'sound/machines/buzz/buzz-two.ogg', 30, TRUE)
COOLDOWN_START(src, deny_cooldown, DENY_SOUND_COOLDOWN)
return BLOCK_OPEN
diff --git a/code/datums/elements/dextrous.dm b/code/datums/elements/dextrous.dm
index 681e2a9488d8c..240cfc88494d3 100644
--- a/code/datums/elements/dextrous.dm
+++ b/code/datums/elements/dextrous.dm
@@ -69,5 +69,5 @@
for(var/obj/item/held_item in examined.held_items)
if(held_item.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM))
continue
- examine_list += span_info("[examined.p_They()] [examined.p_have()] [held_item.get_examine_string(user)] in [examined.p_their()] \
+ examine_list += span_info("[examined.p_They()] [examined.p_have()] [held_item.examine_title(user)] in [examined.p_their()] \
[examined.get_held_index_name(examined.get_held_index_of_item(held_item))].")
diff --git a/code/datums/elements/disarm_attack.dm b/code/datums/elements/disarm_attack.dm
index a788cd9f35ed3..8b4b0b3ff8adf 100644
--- a/code/datums/elements/disarm_attack.dm
+++ b/code/datums/elements/disarm_attack.dm
@@ -6,13 +6,23 @@
if(!isitem(target))
return ELEMENT_INCOMPATIBLE
- RegisterSignal(target, COMSIG_ITEM_ATTACK_SECONDARY, PROC_REF(secondary_attack))
- RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(examine))
+ var/obj/item/item = target
+ RegisterSignal(item, COMSIG_ITEM_ATTACK_SECONDARY, PROC_REF(secondary_attack))
+ RegisterSignal(item, COMSIG_ATOM_EXAMINE, PROC_REF(examine))
+ item.item_flags |= ITEM_HAS_CONTEXTUAL_SCREENTIPS
+ RegisterSignal(item, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET, PROC_REF(add_item_context))
/datum/element/disarm_attack/Detach(datum/source)
- UnregisterSignal(source, list(COMSIG_ATOM_EXAMINE, COMSIG_ITEM_ATTACK_SECONDARY))
+ UnregisterSignal(source, list(COMSIG_ATOM_EXAMINE, COMSIG_ITEM_ATTACK_SECONDARY, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET))
return ..()
+/datum/element/disarm_attack/proc/add_item_context(obj/item/source, list/context, atom/target, mob/living/user)
+ SIGNAL_HANDLER
+ if(!isliving(target) || !can_disarm_attack(source, target, user, FALSE))
+ return NONE
+ context[SCREENTIP_CONTEXT_RMB] = "Shove"
+ return CONTEXTUAL_SCREENTIP_SET
+
/datum/element/disarm_attack/proc/secondary_attack(obj/item/source, mob/living/victim, mob/living/user, params)
SIGNAL_HANDLER
if(!user.can_disarm(victim) || !can_disarm_attack(source, victim, user))
diff --git a/code/datums/elements/door_pryer.dm b/code/datums/elements/door_pryer.dm
index 9f01e8be2b6ab..3e2bd2c5a43d6 100644
--- a/code/datums/elements/door_pryer.dm
+++ b/code/datums/elements/door_pryer.dm
@@ -60,7 +60,7 @@
message = span_warning("[attacker] starts forcing the [airlock_target] open!"),
blind_message = span_hear("You hear a metal screeching sound."),
)
- playsound(airlock_target, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE)
+ playsound(airlock_target, 'sound/machines/airlock/airlock_alien_prying.ogg', 100, TRUE)
airlock_target.balloon_alert(attacker, "prying...")
if(!do_after(attacker, pry_time, airlock_target))
airlock_target.balloon_alert(attacker, "interrupted!")
diff --git a/code/datums/elements/embed.dm b/code/datums/elements/embed.dm
index 4a8bda37c3a75..fbaf638bdd520 100644
--- a/code/datums/elements/embed.dm
+++ b/code/datums/elements/embed.dm
@@ -23,7 +23,7 @@
return
RegisterSignal(target, COMSIG_MOVABLE_IMPACT_ZONE, PROC_REF(check_embed))
- RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(examined))
+ RegisterSignal(target, COMSIG_ATOM_EXAMINE_TAGS, PROC_REF(examined_tags))
RegisterSignal(target, COMSIG_EMBED_TRY_FORCE, PROC_REF(try_force_embed))
RegisterSignal(target, COMSIG_ITEM_DISABLE_EMBED, PROC_REF(detach_from_weapon))
@@ -46,7 +46,7 @@
if(blocked || !istype(victim) || HAS_TRAIT(victim, TRAIT_PIERCEIMMUNE))
return FALSE
- if(victim.status_flags & GODMODE)
+ if(HAS_TRAIT(victim, TRAIT_GODMODE))
return FALSE
var/flying_speed = throwingdatum?.speed || weapon.throw_speed
@@ -82,13 +82,13 @@
Detach(weapon)
///Someone inspected our embeddable item
-/datum/element/embed/proc/examined(obj/item/I, mob/user, list/examine_list)
+/datum/element/embed/proc/examined_tags(obj/item/I, mob/user, list/examine_list)
SIGNAL_HANDLER
if(I.is_embed_harmless())
- examine_list += "[I] feels sticky, and could probably get stuck to someone if thrown properly!"
+ examine_list["sticky"] = "[I] feels sticky, and could probably get stuck to someone if thrown properly!"
else
- examine_list += "[I] has a fine point, and could probably embed in someone if thrown properly!"
+ examine_list["embeddable"] = "[I] has a fine point, and could probably embed in someone if thrown properly!"
/**
* check_embed_projectile() is what we get when a projectile with a defined shrapnel_type impacts a target.
@@ -117,6 +117,8 @@
if(!try_force_embed(payload, limb))
payload.failedEmbed()
+ else
+ SEND_SIGNAL(source, COMSIG_PROJECTILE_ON_EMBEDDED, payload, hit)
Detach(source)
/**
diff --git a/code/datums/elements/falling_hazard.dm b/code/datums/elements/falling_hazard.dm
index 355bcd92e4e01..65ac6b4569f0e 100644
--- a/code/datums/elements/falling_hazard.dm
+++ b/code/datums/elements/falling_hazard.dm
@@ -12,7 +12,7 @@
/// Does the target crush and flatten whoever it falls on
var/crushes_people = FALSE
/// What sound is played when the target falls onto a mob
- var/impact_sound = 'sound/magic/clockwork/fellowship_armory.ogg' //CLANG
+ var/impact_sound = 'sound/effects/magic/clockwork/fellowship_armory.ogg' //CLANG
/datum/element/falling_hazard/Attach(datum/target, damage, wound_bonus, hardhat_safety, crushes, impact_sound)
. = ..()
@@ -52,7 +52,7 @@
if(crushes_people)
poor_target.Knockdown(0.25 SECONDS * fall_damage) // For a piano, that would be 15 seconds
- playsound(poor_target, 'sound/weapons/parry.ogg', 50, TRUE) // You PARRIED the falling object with your EPIC hardhat
+ playsound(poor_target, 'sound/items/weapons/parry.ogg', 50, TRUE) // You PARRIED the falling object with your EPIC hardhat
return
var/obj/item/bodypart/target_head = poor_target.get_bodypart(BODY_ZONE_HEAD)
diff --git a/code/datums/elements/firestacker.dm b/code/datums/elements/firestacker.dm
index b7bad65cc6ced..a512e5e89c7d1 100644
--- a/code/datums/elements/firestacker.dm
+++ b/code/datums/elements/firestacker.dm
@@ -27,10 +27,10 @@
/datum/element/firestacker/proc/stack_on(datum/owner, mob/living/target)
target.adjust_fire_stacks(amount)
-/datum/element/firestacker/proc/impact(datum/source, atom/hit_atom, datum/thrownthing/throwingdatum)
+/datum/element/firestacker/proc/impact(datum/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
SIGNAL_HANDLER
- if(isliving(hit_atom))
+ if(!caught && isliving(hit_atom))
stack_on(source, hit_atom)
/datum/element/firestacker/proc/item_attack(datum/source, atom/movable/target, mob/living/user)
diff --git a/code/datums/elements/food/food_trash.dm b/code/datums/elements/food/food_trash.dm
index 6df36c82c4c41..244de8d7e84ab 100644
--- a/code/datums/elements/food/food_trash.dm
+++ b/code/datums/elements/food/food_trash.dm
@@ -22,11 +22,14 @@
RegisterSignal(target, COMSIG_ITEM_ATTACK_SELF, PROC_REF(open_trash))
if(flags & FOOD_TRASH_POPABLE)
RegisterSignal(target, COMSIG_FOOD_CROSSED, PROC_REF(food_crossed))
- RegisterSignal(target, COMSIG_ITEM_ON_GRIND, PROC_REF(generate_trash))
- RegisterSignal(target, COMSIG_ITEM_ON_JUICE, PROC_REF(generate_trash))
- RegisterSignal(target, COMSIG_ITEM_USED_AS_INGREDIENT, PROC_REF(generate_trash))
- RegisterSignal(target, COMSIG_ITEM_ON_COMPOSTED, PROC_REF(generate_trash))
- RegisterSignal(target, COMSIG_ITEM_SOLD_TO_CUSTOMER, PROC_REF(generate_trash))
+ RegisterSignals(target, list(
+ COMSIG_ITEM_ON_GRIND,
+ COMSIG_ITEM_ON_JUICE,
+ COMSIG_ITEM_USED_AS_INGREDIENT,
+ COMSIG_ITEM_ON_COMPOSTED,
+ COMSIG_ITEM_SOLD_TO_CUSTOMER,
+ COMSIG_MOVABLE_SPLAT,
+ ), PROC_REF(generate_trash))
/datum/element/food_trash/Detach(datum/target)
. = ..()
@@ -38,7 +41,9 @@
COMSIG_ITEM_ON_JUICE,
COMSIG_ITEM_USED_AS_INGREDIENT,
COMSIG_ITEM_ON_COMPOSTED,
- COMSIG_ITEM_SOLD_TO_CUSTOMER,))
+ COMSIG_ITEM_SOLD_TO_CUSTOMER,
+ COMSIG_MOVABLE_SPLAT,
+ ))
/datum/element/food_trash/proc/generate_trash(datum/source, mob/living/eater, mob/living/feeder)
SIGNAL_HANDLER
diff --git a/code/datums/elements/food/fried_item.dm b/code/datums/elements/food/fried_item.dm
index 2afab84d1cb43..bc21e51f24cd7 100644
--- a/code/datums/elements/food/fried_item.dm
+++ b/code/datums/elements/food/fried_item.dm
@@ -17,28 +17,27 @@
var/atom/this_food = target
switch(fry_time)
- if(0 to 15)
+ if(0 to 15 SECONDS)
this_food.add_atom_colour(fried_colors[1], FIXED_COLOUR_PRIORITY)
this_food.name = "lightly-fried [this_food.name]"
this_food.desc += " It's been lightly fried in a deep fryer."
- if(15 to 50)
+ if(15 SECONDS to 50 SECONDS)
this_food.add_atom_colour(fried_colors[2], FIXED_COLOUR_PRIORITY)
this_food.name = "fried [this_food.name]"
this_food.desc += " It's been fried, increasing its tastiness value by [rand(1, 75)]%."
- if(50 to 85)
+ if(50 SECONDS to 85 SECONDS)
this_food.add_atom_colour(fried_colors[3], FIXED_COLOUR_PRIORITY)
this_food.name = "deep-fried [this_food.name]"
this_food.desc += " Deep-fried to perfection."
- if(85 to INFINITY)
+ if(85 SECONDS to INFINITY)
this_food.add_atom_colour(fried_colors[4], FIXED_COLOUR_PRIORITY)
this_food.name = "\proper the physical manifestation of the very concept of fried foods"
this_food.desc = "A heavily-fried... something. Who can tell anymore?"
ADD_TRAIT(this_food, TRAIT_FOOD_FRIED, ELEMENT_TRAIT(type))
- SEND_SIGNAL(this_food, COMSIG_ITEM_FRIED, fry_time)
// Already edible items will inherent these parameters
// Otherwise, we will become edible.
this_food.AddComponent( \
@@ -49,6 +48,7 @@
foodtypes = FRIED, \
volume = this_food.reagents?.maximum_volume, \
)
+ SEND_SIGNAL(this_food, COMSIG_ITEM_FRIED, fry_time)
/datum/element/fried_item/Detach(atom/source, ...)
for(var/color in fried_colors)
diff --git a/code/datums/elements/food/grilled_item.dm b/code/datums/elements/food/grilled_item.dm
index 74e772eb73c92..6899f47faa475 100644
--- a/code/datums/elements/food/grilled_item.dm
+++ b/code/datums/elements/food/grilled_item.dm
@@ -28,10 +28,12 @@
if(grill_time > 30 SECONDS && isnull(this_food.GetComponent(/datum/component/edible)))
this_food.AddComponent(/datum/component/edible, foodtypes = FRIED)
- SEND_SIGNAL(this_food, COMSIG_ITEM_BARBEQUE_GRILLED)
+ SEND_SIGNAL(this_food, COMSIG_ITEM_BARBEQUE_GRILLED, grill_time)
+ ADD_TRAIT(this_food, TRAIT_FOOD_BBQ_GRILLED, ELEMENT_TRAIT(type))
/datum/element/grilled_item/Detach(atom/source, ...)
source.name = initial(source.name)
source.desc = initial(source.desc)
qdel(source.GetComponent(/datum/component/edible)) // Don't care if it was initially edible
+ REMOVE_TRAIT(src, TRAIT_FOOD_BBQ_GRILLED, ELEMENT_TRAIT(type))
return ..()
diff --git a/code/datums/elements/footstep.dm b/code/datums/elements/footstep.dm
index a162e58752d55..8b0699062e58d 100644
--- a/code/datums/elements/footstep.dm
+++ b/code/datums/elements/footstep.dm
@@ -103,6 +103,9 @@
/datum/element/footstep/proc/play_simplestep(mob/living/source, atom/oldloc, direction, forced, list/old_locs, momentum_change)
SIGNAL_HANDLER
+ if(source.moving_diagonally == SECOND_DIAG_STEP)
+ return // to prevent a diagonal step from counting as 2
+
if (forced || SHOULD_DISABLE_FOOTSTEPS(source))
return
@@ -122,6 +125,9 @@
/datum/element/footstep/proc/play_humanstep(mob/living/carbon/human/source, atom/oldloc, direction, forced, list/old_locs, momentum_change)
SIGNAL_HANDLER
+ if(source.moving_diagonally == SECOND_DIAG_STEP)
+ return // to prevent a diagonal step from counting as 2
+
if (forced || SHOULD_DISABLE_FOOTSTEPS(source) || !momentum_change)
return
@@ -172,6 +178,9 @@
/datum/element/footstep/proc/play_simplestep_machine(atom/movable/source, atom/oldloc, direction, forced, list/old_locs, momentum_change)
SIGNAL_HANDLER
+ if(source.moving_diagonally == SECOND_DIAG_STEP)
+ return // to prevent a diagonal step from counting as 2
+
if (forced || SHOULD_DISABLE_FOOTSTEPS(source))
return
diff --git a/code/datums/elements/frozen.dm b/code/datums/elements/frozen.dm
index d112ef31b5f91..df857cdd6efe6 100644
--- a/code/datums/elements/frozen.dm
+++ b/code/datums/elements/frozen.dm
@@ -28,7 +28,7 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
organ.organ_flags |= ORGAN_FROZEN
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
- RegisterSignal(target, COMSIG_MOVABLE_THROW_LANDED, PROC_REF(shatter_on_throw))
+ RegisterSignal(target, COMSIG_MOVABLE_THROW_LANDED, PROC_REF(shatter_on_landed))
RegisterSignal(target, COMSIG_MOVABLE_IMPACT, PROC_REF(shatter_on_throw))
RegisterSignal(target, COMSIG_OBJ_UNFREEZE, PROC_REF(on_unfreeze))
@@ -54,8 +54,13 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
SIGNAL_HANDLER
Detach(source)
-///signal handler for COMSIG_MOVABLE_POST_THROW that shatters our target after impacting after a throw
-/datum/element/frozen/proc/shatter_on_throw(datum/target, datum/thrownthing/throwingdatum)
+/datum/element/frozen/proc/shatter_on_throw(datum/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
+ SIGNAL_HANDLER
+ if(!caught)
+ shatter_on_landed(source, throwing_datum)
+
+///signal handler that shatters our target after impacting after a throw.
+/datum/element/frozen/proc/shatter_on_landed(datum/target, datum/thrownthing/throwingdatum)
SIGNAL_HANDLER
var/obj/obj_target = target
if(ismob(throwingdatum.thrower))
diff --git a/code/datums/elements/give_turf_traits.dm b/code/datums/elements/give_turf_traits.dm
index 3c53d4a5e7305..7e7c37d86e7ef 100644
--- a/code/datums/elements/give_turf_traits.dm
+++ b/code/datums/elements/give_turf_traits.dm
@@ -67,7 +67,7 @@
for(var/mob/living/living in location)
living.update_turf_movespeed()
-/// Signals and components are carried over when the turf is changed, so they've to be readded post-change.
+/// Signals are carried over when the turf is changed, but traits aren't, so they've to be readded post-change.
/datum/element/give_turf_traits/proc/pre_change_turf(turf/changed, path, list/new_baseturfs, flags, list/post_change_callbacks)
SIGNAL_HANDLER
post_change_callbacks += CALLBACK(src, PROC_REF(reoccupy_turf))
diff --git a/code/datums/elements/high_fiver.dm b/code/datums/elements/high_fiver.dm
index 6e4e9739cefc5..249a9f4059de4 100644
--- a/code/datums/elements/high_fiver.dm
+++ b/code/datums/elements/high_fiver.dm
@@ -54,7 +54,7 @@
taker.add_mood_event(descriptor, /datum/mood_event/high_five_full_hand) // not so successful now!
return COMPONENT_OFFER_INTERRUPT
- playsound(offerer, 'sound/weapons/slap.ogg', min(50 * slappers_giver, 300), TRUE, 1)
+ playsound(offerer, 'sound/items/weapons/slap.ogg', min(50 * slappers_giver, 300), TRUE, 1)
offerer.add_mob_memory(/datum/memory/high_five, deuteragonist = taker, high_five_type = descriptor, high_ten = high_ten)
taker.add_mob_memory(/datum/memory/high_five, deuteragonist = offerer, high_five_type = descriptor, high_ten = high_ten)
diff --git a/code/datums/elements/immerse.dm b/code/datums/elements/immerse.dm
index 65f7d45b9ab77..d50ae906c0a55 100644
--- a/code/datums/elements/immerse.dm
+++ b/code/datums/elements/immerse.dm
@@ -142,8 +142,8 @@
*/
/datum/element/immerse/proc/add_immerse_overlay(atom/movable/movable)
var/list/icon_dimensions = get_icon_dimensions(movable.icon)
- var/width = icon_dimensions["width"] || world.icon_size
- var/height = icon_dimensions["height"] || world.icon_size
+ var/width = icon_dimensions["width"] || ICON_SIZE_X
+ var/height = icon_dimensions["height"] || ICON_SIZE_Y
var/is_below_water = movable.layer < WATER_LEVEL_LAYER ? "underwater-" : ""
@@ -184,19 +184,19 @@
* but since we want the appearance to stay where it should be,
* we have to counteract this one.
*/
- var/extra_width = (width - world.icon_size) * 0.5
- var/extra_height = (height - world.icon_size) * 0.5
+ var/extra_width = (width - ICON_SIZE_X) * 0.5
+ var/extra_height = (height - ICON_SIZE_Y) * 0.5
var/mutable_appearance/overlay_appearance = new()
var/icon/immerse_icon = generated_immerse_icons["[icon]-[icon_state]-[mask_icon]"]
- var/last_i = width/world.icon_size
+ var/last_i = width/ICON_SIZE_X
for(var/i in -1 to last_i)
var/mutable_appearance/underwater = mutable_appearance(icon, icon_state)
- underwater.pixel_x = world.icon_size * i - extra_width
- underwater.pixel_y = -world.icon_size - extra_height
+ underwater.pixel_x = ICON_SIZE_X * i - extra_width
+ underwater.pixel_y = -ICON_SIZE_Y - extra_height
overlay_appearance.overlays += underwater
var/mutable_appearance/water_level = is_below_water ? underwater : mutable_appearance(immerse_icon)
- water_level.pixel_x = world.icon_size * i - extra_width
+ water_level.pixel_x = ICON_SIZE_X * i - extra_width
water_level.pixel_y = -extra_height
overlay_appearance.overlays += water_level
diff --git a/code/datums/elements/kneejerk.dm b/code/datums/elements/kneejerk.dm
index cd93fe31917ed..78c0ba7654d69 100644
--- a/code/datums/elements/kneejerk.dm
+++ b/code/datums/elements/kneejerk.dm
@@ -51,17 +51,17 @@
var/target_brain_damage = target_brain.damage
if(target_brain_damage < BRAIN_DAMAGE_MILD) //a healthy brain produces a normal reaction
- playsound(target, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
+ playsound(target, 'sound/items/weapons/punchmiss.ogg', 25, TRUE, -1)
target.visible_message(span_danger("[target]'s leg kicks out sharply!"), \
span_danger("Your leg kicks out sharply!"))
else if(target_brain_damage < BRAIN_DAMAGE_SEVERE) //a mildly damaged brain produces a delayed reaction
- playsound(target, 'sound/weapons/punchmiss.ogg', 15, TRUE, -1)
+ playsound(target, 'sound/items/weapons/punchmiss.ogg', 15, TRUE, -1)
target.visible_message(span_danger("After a moment, [target]'s leg kicks out sharply!"), \
span_danger("After a moment, your leg kicks out sharply!"))
else if(target_brain_damage < BRAIN_DAMAGE_DEATH) //a severely damaged brain produces a delayed + weaker reaction
- playsound(target, 'sound/weapons/punchmiss.ogg', 5, TRUE, -1)
+ playsound(target, 'sound/items/weapons/punchmiss.ogg', 5, TRUE, -1)
target.visible_message(span_danger("After a moment, [target]'s leg kicks out weakly!"), \
span_danger("After a moment, your leg kicks out weakly!"))
diff --git a/code/datums/elements/lazy_fishing_spot.dm b/code/datums/elements/lazy_fishing_spot.dm
index 1ba296bfe730d..67edcea2e88ed 100644
--- a/code/datums/elements/lazy_fishing_spot.dm
+++ b/code/datums/elements/lazy_fishing_spot.dm
@@ -20,10 +20,19 @@
RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(on_examined))
RegisterSignal(target, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(on_examined_more))
RegisterSignal(target, COMSIG_ATOM_EX_ACT, PROC_REF(explosive_fishing))
+ RegisterSignal(target, COMSIG_FISH_RELEASED_INTO, PROC_REF(fish_released))
+ RegisterSignal(target, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(link_to_fish_porter))
/datum/element/lazy_fishing_spot/Detach(datum/target)
- UnregisterSignal(target, list(COMSIG_PRE_FISHING, COMSIG_ATOM_EXAMINE, COMSIG_ATOM_EXAMINE_MORE, COMSIG_ATOM_EX_ACT))
- UnregisterSignal(target, list(COMSIG_PRE_FISHING, COMSIG_NPC_FISHING))
+ UnregisterSignal(target, list(
+ COMSIG_FISH_RELEASED_INTO,
+ COMSIG_PRE_FISHING,
+ COMSIG_NPC_FISHING,
+ COMSIG_ATOM_EXAMINE,
+ COMSIG_ATOM_EXAMINE_MORE,
+ COMSIG_ATOM_EX_ACT,
+ COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL),
+ ))
REMOVE_TRAIT(target, TRAIT_FISHING_SPOT, REF(src))
return ..()
@@ -41,15 +50,7 @@
var/datum/fish_source/fish_source = GLOB.preset_fish_sources[configuration]
- var/has_known_fishes = FALSE
- for(var/reward in fish_source.fish_table)
- if(!ispath(reward, /obj/item/fish))
- continue
- var/obj/item/fish/prototype = reward
- if(initial(prototype.show_in_catalog))
- has_known_fishes = TRUE
- break
- if(!has_known_fishes)
+ if(!fish_source.has_known_fishes())
return
examine_text += span_tinynoticeital("This is a fishing spot. You can look again to list its fishes...")
@@ -60,19 +61,7 @@
return
var/datum/fish_source/fish_source = GLOB.preset_fish_sources[configuration]
-
- var/list/known_fishes = list()
- for(var/reward in fish_source.fish_table)
- if(!ispath(reward, /obj/item/fish))
- continue
- var/obj/item/fish/prototype = reward
- if(initial(prototype.show_in_catalog))
- known_fishes += initial(prototype.name)
-
- if(!length(known_fishes))
- return
-
- examine_text += span_info("You can catch the following fish here: [english_list(known_fishes)].")
+ fish_source.get_catchable_fish_names(user, source, examine_text)
/datum/element/lazy_fishing_spot/proc/explosive_fishing(atom/location, severity)
SIGNAL_HANDLER
@@ -81,3 +70,16 @@
/datum/element/lazy_fishing_spot/proc/return_glob_fishing_spot(datum/source, list/fish_spot_container)
fish_spot_container[NPC_FISHING_SPOT] = GLOB.preset_fish_sources[configuration]
+
+/datum/element/lazy_fishing_spot/proc/link_to_fish_porter(atom/source, mob/user, obj/item/multitool/tool)
+ SIGNAL_HANDLER
+ if(!istype(tool.buffer, /obj/machinery/fishing_portal_generator))
+ return
+ var/datum/fish_source/fish_source = GLOB.preset_fish_sources[configuration]
+ var/obj/machinery/fishing_portal_generator/portal = tool.buffer
+ return portal.link_fishing_spot(fish_source, source, user)
+
+/datum/element/lazy_fishing_spot/proc/fish_released(datum/source, obj/item/fish/fish, mob/living/releaser)
+ SIGNAL_HANDLER
+ var/datum/fish_source/fish_source = GLOB.preset_fish_sources[configuration]
+ fish_source.readd_fish(fish, releaser)
diff --git a/code/datums/elements/light_eater.dm b/code/datums/elements/light_eater.dm
index 27500b066fefa..3f51590da1c6e 100644
--- a/code/datums/elements/light_eater.dm
+++ b/code/datums/elements/light_eater.dm
@@ -127,7 +127,19 @@
*/
/datum/element/light_eater/proc/on_interacting_with(obj/item/source, mob/living/user, atom/target)
SIGNAL_HANDLER
- eat_lights(target, source)
+ if(eat_lights(target, source))
+ // do a "pretend" attack if we're hitting something that can't normally be
+ if(isobj(target))
+ var/obj/smacking = target
+ if(smacking.obj_flags & CAN_BE_HIT)
+ return NONE
+ else if(!isturf(target))
+ return NONE
+ user.do_attack_animation(target)
+ user.changeNext_move(CLICK_CD_RAPID)
+ target.play_attack_sound()
+ // not particularly picky about what happens afterwards in the attack chain
+ return NONE
/**
* Called when a source object is used to block a thrown object, projectile, or attack
diff --git a/code/datums/elements/mirage_border.dm b/code/datums/elements/mirage_border.dm
index 999455a0b8343..ca7c422dd1127 100644
--- a/code/datums/elements/mirage_border.dm
+++ b/code/datums/elements/mirage_border.dm
@@ -24,9 +24,9 @@
var/turf/northeast = locate(clamp(x + (direction & EAST ? range : 0), 1, world.maxx), clamp(y + (direction & NORTH ? range : 0), 1, world.maxy), z)
holder.vis_contents += block(southwest, northeast)
if(direction & SOUTH)
- holder.pixel_y -= world.icon_size * range
+ holder.pixel_y -= ICON_SIZE_Y * range
if(direction & WEST)
- holder.pixel_x -= world.icon_size * range
+ holder.pixel_x -= ICON_SIZE_X * range
/datum/element/mirage_border/Detach(atom/movable/target)
. = ..()
diff --git a/code/datums/elements/movetype_handler.dm b/code/datums/elements/movetype_handler.dm
index 6d730d345e284..e88aac6e26515 100644
--- a/code/datums/elements/movetype_handler.dm
+++ b/code/datums/elements/movetype_handler.dm
@@ -8,7 +8,6 @@
element_flags = ELEMENT_DETACH_ON_HOST_DESTROY
var/list/attached_atoms = list()
- var/list/paused_floating_anim_atoms = list()
/datum/element/movetype_handler/Attach(datum/target)
. = ..()
@@ -22,7 +21,6 @@
RegisterSignals(movable_target, GLOB.movement_type_removetrait_signals, PROC_REF(on_movement_type_trait_loss))
RegisterSignal(movable_target, SIGNAL_ADDTRAIT(TRAIT_NO_FLOATING_ANIM), PROC_REF(on_no_floating_anim_trait_gain))
RegisterSignal(movable_target, SIGNAL_REMOVETRAIT(TRAIT_NO_FLOATING_ANIM), PROC_REF(on_no_floating_anim_trait_loss))
- RegisterSignal(movable_target, COMSIG_PAUSE_FLOATING_ANIM, PROC_REF(pause_floating_anim))
attached_atoms[movable_target] = TRUE
if(movable_target.movement_type & (FLOATING|FLYING) && !HAS_TRAIT(movable_target, TRAIT_NO_FLOATING_ANIM))
@@ -32,14 +30,12 @@
var/list/signals_to_remove = list(
SIGNAL_ADDTRAIT(TRAIT_NO_FLOATING_ANIM),
SIGNAL_REMOVETRAIT(TRAIT_NO_FLOATING_ANIM),
- COMSIG_PAUSE_FLOATING_ANIM
)
signals_to_remove += GLOB.movement_type_addtrait_signals
signals_to_remove += GLOB.movement_type_removetrait_signals
UnregisterSignal(source, signals_to_remove)
attached_atoms -= source
- paused_floating_anim_atoms -= source
STOP_FLOATING_ANIM(source)
return ..()
@@ -51,7 +47,7 @@
return
var/old_state = source.movement_type
source.movement_type |= flag
- if(!(old_state & (FLOATING|FLYING)) && (source.movement_type & (FLOATING|FLYING)) && !paused_floating_anim_atoms[source] && !HAS_TRAIT(source, TRAIT_NO_FLOATING_ANIM))
+ if(!(old_state & (FLOATING|FLYING)) && (source.movement_type & (FLOATING|FLYING)) && !HAS_TRAIT(source, TRAIT_NO_FLOATING_ANIM))
DO_FLOATING_ANIM(source)
SEND_SIGNAL(source, COMSIG_MOVETYPE_FLAG_ENABLED, flag, old_state)
@@ -78,24 +74,5 @@
/// Called when the TRAIT_NO_FLOATING_ANIM trait is removed from the mob. Restarts the bobbing animation.
/datum/element/movetype_handler/proc/on_no_floating_anim_trait_loss(atom/movable/source, trait)
SIGNAL_HANDLER
- if(source.movement_type & (FLOATING|FLYING) && !paused_floating_anim_atoms[source])
+ if(source.movement_type & (FLOATING|FLYING))
DO_FLOATING_ANIM(source)
-
-///Pauses the floating animation for the duration of the timer... plus [tickrate - (world.time + timer) % tickrate] to be precise.
-/datum/element/movetype_handler/proc/pause_floating_anim(atom/movable/source, timer)
- SIGNAL_HANDLER
- if(paused_floating_anim_atoms[source] < world.time + timer)
- STOP_FLOATING_ANIM(source)
- if(!length(paused_floating_anim_atoms))
- START_PROCESSING(SSdcs, src) //1 second tickrate.
- paused_floating_anim_atoms[source] = world.time + timer
-
-/datum/element/movetype_handler/process()
- for(var/_paused in paused_floating_anim_atoms)
- var/atom/movable/paused = _paused
- if(paused_floating_anim_atoms[paused] < world.time)
- if(paused.movement_type & (FLOATING|FLYING) && !HAS_TRAIT(paused, TRAIT_NO_FLOATING_ANIM))
- DO_FLOATING_ANIM(paused)
- paused_floating_anim_atoms -= paused
- if(!length(paused_floating_anim_atoms))
- STOP_PROCESSING(SSdcs, src)
diff --git a/code/datums/elements/pet_bonus.dm b/code/datums/elements/pet_bonus.dm
index 5ef8b515077ac..a802c363f44f5 100644
--- a/code/datums/elements/pet_bonus.dm
+++ b/code/datums/elements/pet_bonus.dm
@@ -8,6 +8,8 @@
element_flags = ELEMENT_BESPOKE
argument_hash_start_idx = 2
+ ///string key of the emote to do when pet.
+ var/emote_name
///optional cute message to send when you pet your pet!
var/emote_message
///actual moodlet given, defaults to the pet animal one
@@ -19,6 +21,7 @@
return ELEMENT_INCOMPATIBLE
src.emote_message = emote_message
+ src.emote_name = emote_name
src.moodlet = moodlet
RegisterSignal(target, COMSIG_ATOM_ATTACK_HAND, PROC_REF(on_attack_hand))
@@ -36,4 +39,6 @@
SEND_SIGNAL(pet, COMSIG_ANIMAL_PET, petter, modifiers)
if(emote_message && prob(33))
pet.manual_emote(emote_message)
+ if(emote_name)
+ INVOKE_ASYNC(pet, TYPE_PROC_REF(/mob, emote), emote_name)
petter.add_mood_event("petting_bonus", moodlet, pet)
diff --git a/code/datums/elements/pet_collar.dm b/code/datums/elements/pet_collar.dm
index 5c49de2eceb5b..f98767629e7e7 100644
--- a/code/datums/elements/pet_collar.dm
+++ b/code/datums/elements/pet_collar.dm
@@ -54,7 +54,7 @@
/datum/element/wears_collar/proc/on_content_enter(mob/living/source, obj/item/clothing/neck/petcollar/new_collar)
SIGNAL_HANDLER
- if(!istype(new_collar))
+ if(!istype(new_collar) || !new_collar.tagname)
return
source.fully_replace_character_name(null, "\proper [new_collar.tagname]")
diff --git a/code/datums/elements/slapcrafting.dm b/code/datums/elements/slapcrafting.dm
index 42776bf31f773..4b58bddd2a0f4 100644
--- a/code/datums/elements/slapcrafting.dm
+++ b/code/datums/elements/slapcrafting.dm
@@ -26,7 +26,7 @@
return //Don't do anything, it just shouldn't be used in crafting.
RegisterSignal(target, COMSIG_ATOM_ATTACKBY, PROC_REF(attempt_slapcraft))
- RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(get_examine_info))
+ RegisterSignal(target, COMSIG_ATOM_EXAMINE_TAGS, PROC_REF(get_examine_info))
RegisterSignal(target, COMSIG_ATOM_EXAMINE_MORE, PROC_REF(get_examine_more_info))
RegisterSignal(target, COMSIG_TOPIC, PROC_REF(topic_handler))
@@ -126,7 +126,7 @@
already_used_names += initial(result.name)
string_results += list("\a [initial(result.name)]")
- examine_list += span_notice("You think [source] could be used to make [english_list(string_results)]! Examine again to look at the details...")
+ examine_list["crafting component"] = "You think [source] could be used to make [english_list(string_results)]! Examine again to look at the details..."
/// Alerts any examiners to the details of the recipe.
/datum/element/slapcrafting/proc/get_examine_more_info(atom/source, mob/user, list/examine_list)
diff --git a/code/datums/elements/spooky.dm b/code/datums/elements/spooky.dm
index 30a04f6348b20..89d53c4e99734 100644
--- a/code/datums/elements/spooky.dm
+++ b/code/datums/elements/spooky.dm
@@ -40,7 +40,7 @@
if((!istype(H.dna.species, /datum/species/skeleton)) && (!istype(H.dna.species, /datum/species/golem)) && (!istype(H.dna.species, /datum/species/android)) && (!istype(H.dna.species, /datum/species/jelly)))
C.adjustStaminaLoss(18) //boneless humanoids don't lose the will to live
to_chat(C, "DOOT")
- to_chat(C, "You're feeling more bony.")
+ to_chat(C, span_robot("You're feeling more bony."))
INVOKE_ASYNC(src, PROC_REF(spectral_change), H)
else //the sound will spook monkeys.
diff --git a/code/datums/elements/wall_engraver.dm b/code/datums/elements/wall_engraver.dm
index 7204d8cacef5e..2b319b0609a28 100644
--- a/code/datums/elements/wall_engraver.dm
+++ b/code/datums/elements/wall_engraver.dm
@@ -31,12 +31,12 @@
/datum/element/wall_engraver/proc/try_chisel(obj/item/item, turf/closed/wall, mob/living/user)
if(!istype(wall) || !user.mind)
return
- if(HAS_TRAIT_FROM(wall, TRAIT_NOT_ENGRAVABLE, INNATE_TRAIT))
- user.balloon_alert(user, "wall cannot be engraved!")
- return
- if(HAS_TRAIT_FROM(wall, TRAIT_NOT_ENGRAVABLE, TRAIT_GENERIC))
+ if(HAS_TRAIT_FROM(wall, TRAIT_NOT_ENGRAVABLE, ENGRAVED_TRAIT))
user.balloon_alert(user, "wall has already been engraved!")
return
+ if(HAS_TRAIT(wall, TRAIT_NOT_ENGRAVABLE))
+ user.balloon_alert(user, "wall cannot be engraved!")
+ return
if(!length(user.mind?.memories))
user.balloon_alert(user, "nothing memorable to engrave!")
return
diff --git a/code/datums/elements/wall_tearer.dm b/code/datums/elements/wall_tearer.dm
index 2c9ff5416d59b..cf61de7300919 100644
--- a/code/datums/elements/wall_tearer.dm
+++ b/code/datums/elements/wall_tearer.dm
@@ -51,7 +51,7 @@
var/rip_time = (istype(target, /turf/closed/wall/r_wall) ? tear_time * reinforced_multiplier : tear_time) / 3
if (rip_time > 0)
tearer.visible_message(span_warning("[tearer] begins tearing through [target]!"))
- playsound(tearer, 'sound/machines/airlock_alien_prying.ogg', vol = 100, vary = TRUE)
+ playsound(tearer, 'sound/machines/airlock/airlock_alien_prying.ogg', vol = 100, vary = TRUE)
target.balloon_alert(tearer, "tearing...")
if (!do_after(tearer, delay = rip_time, target = target, interaction_key = do_after_key))
tearer.balloon_alert(tearer, "interrupted!")
diff --git a/code/datums/elements/weapon_description.dm b/code/datums/elements/weapon_description.dm
index 0897b571159bb..eda7ca59b49e6 100644
--- a/code/datums/elements/weapon_description.dm
+++ b/code/datums/elements/weapon_description.dm
@@ -73,6 +73,10 @@
// Doesn't show the base notes for items that have the override notes variable set to true
if(!source.override_notes)
+ if (source.sharpness & SHARP_EDGED)
+ readout += "It's sharp and could cause bleeding wounds."
+ if (source.sharpness & SHARP_POINTY)
+ readout += "It's pointy and could cause piercing wounds."
// Make sure not to divide by 0 on accident
if(source.force > 0)
readout += "It takes about [span_warning("[HITS_TO_CRIT(source.force)] melee hit\s")] to take down an enemy."
diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm
index 8d77c6fc6bdbb..0a8646df07084 100644
--- a/code/datums/emotes.dm
+++ b/code/datums/emotes.dm
@@ -100,8 +100,8 @@
user.log_message(msg, LOG_EMOTE)
var/tmp_sound = get_sound(user)
- if(tmp_sound && should_play_sound(user, intentional) && TIMER_COOLDOWN_FINISHED(user, type))
- TIMER_COOLDOWN_START(user, type, audio_cooldown)
+ if(tmp_sound && should_play_sound(user, intentional) && TIMER_COOLDOWN_FINISHED(user, "audible_emote_cooldown"))
+ TIMER_COOLDOWN_START(user, "audible_emote_cooldown", audio_cooldown)
playsound(source = user,soundin = tmp_sound,vol = 50, vary = vary, ignore_walls = sound_wall_ignore)
var/is_important = emote_type & EMOTE_IMPORTANT
@@ -125,22 +125,22 @@
runechat_flags = EMOTE_MESSAGE,
)
else if(is_important)
- to_chat(viewer, "[user] [msg]")
+ to_chat(viewer, span_emote("[user] [msg]"))
else if(is_audible && is_visual)
viewer.show_message(
- "[user] [msg]", MSG_AUDIBLE,
- "You see how [user] [msg]", MSG_VISUAL,
+ span_emote("[user] [msg]"), MSG_AUDIBLE,
+ span_emote("You see how [user] [msg]"), MSG_VISUAL,
)
else if(is_audible)
- viewer.show_message("[user] [msg]", MSG_AUDIBLE)
+ viewer.show_message(span_emote("[user] [msg]"), MSG_AUDIBLE)
else if(is_visual)
- viewer.show_message("[user] [msg]", MSG_VISUAL)
+ viewer.show_message(span_emote("[user] [msg]"), MSG_VISUAL)
return // Early exit so no dchat message
// The emote has some important information, and should always be shown to the user
else if(is_important)
for(var/mob/viewer as anything in viewers(user))
- to_chat(viewer, "[user] [msg]")
+ to_chat(viewer, span_emote("[user] [msg]"))
if(user.runechat_prefs_check(viewer, EMOTE_MESSAGE))
viewer.create_chat_message(
speaker = user,
@@ -152,7 +152,7 @@
else if(is_visual && is_audible)
user.audible_message(
message = msg,
- deaf_message = "You see how [user] [msg]",
+ deaf_message = span_emote("You see how [user] [msg]"),
self_message = msg,
audible_message_flags = EMOTE_MESSAGE|ALWAYS_SHOW_SELF_MESSAGE,
)
@@ -180,7 +180,7 @@
continue
if(!(get_chat_toggles(ghost.client) & CHAT_GHOSTSIGHT))
continue
- to_chat(ghost, "[FOLLOW_LINK(ghost, user)] [dchatmsg]")
+ to_chat(ghost, span_emote("[FOLLOW_LINK(ghost, user)] [dchatmsg]"))
return
diff --git a/code/datums/ert.dm b/code/datums/ert.dm
index 58500bb3cc33c..2000d59199908 100644
--- a/code/datums/ert.dm
+++ b/code/datums/ert.dm
@@ -1,13 +1,23 @@
/datum/ert
+ ///Antag datum team for this type of ERT.
var/team = /datum/team/ert
+ ///Do we open the doors to the "high-impact" weapon/explosive cabinets? Used for combat-focused ERTs.
var/opendoors = TRUE
+ ///Alternate antag datum given to the leader of the squad.
var/leader_role = /datum/antagonist/ert/commander
+ ///Do we humanize all spawned players or keep them the species in their current character prefs?
var/enforce_human = TRUE
- var/roles = list(/datum/antagonist/ert/security, /datum/antagonist/ert/medic, /datum/antagonist/ert/engineer) //List of possible roles to be assigned to ERT members.
+ ///A list of roles distributed to the selected candidates that are not the leader.
+ var/roles = list(/datum/antagonist/ert/security, /datum/antagonist/ert/medic, /datum/antagonist/ert/engineer)
+ ///The custom name assigned to this team, for their antag datum/roundend reporting.
var/rename_team
+ ///Defines the color/alert code of the response team. Unused if a polldesc is defined.
var/code
+ ///The mission given to this ERT type in their flavor text.
var/mission = "Assist the station."
+ ///The number of players for consideration.
var/teamsize = 5
+ ///The "would you like to play as XXX" message used when polling for players.
var/polldesc
/// If TRUE, gives the team members "[role] [random last name]" style names
var/random_names = TRUE
@@ -127,3 +137,14 @@
mission = "Having heard the station's request for aid, assist the crew in defending themselves."
polldesc = "an independent station defense militia"
random_names = TRUE
+
+/datum/ert/medical
+ opendoors = FALSE
+ teamsize = 4
+ leader_role = /datum/antagonist/ert/medical_commander
+ enforce_human = FALSE //All the best doctors I know are moths and cats
+ roles = list(/datum/antagonist/ert/medical_technician)
+ rename_team = "EMT Squad"
+ code = "Violet"
+ mission = "Provide emergency medical services to the crew."
+ polldesc = "an emergency medical response team"
diff --git a/code/datums/greyscale/json_configs/kitsune.json b/code/datums/greyscale/json_configs/kitsune.json
index bee6418321387..495520fbbd806 100644
--- a/code/datums/greyscale/json_configs/kitsune.json
+++ b/code/datums/greyscale/json_configs/kitsune.json
@@ -12,5 +12,19 @@
"blend_mode": "overlay",
"color_ids": [ 2 ]
}
+ ],
+ "kitsune_up": [
+ {
+ "type": "icon_state",
+ "icon_state": "kitsune_base_up",
+ "blend_mode": "overlay",
+ "color_ids": [ 1 ]
+ },
+ {
+ "type": "icon_state",
+ "icon_state": "kitsune_stripe_up",
+ "blend_mode": "overlay",
+ "color_ids": [ 2 ]
+ }
]
}
diff --git a/code/datums/helper_datums/getrev.dm b/code/datums/helper_datums/getrev.dm
index c3562aa598732..732323d28655c 100644
--- a/code/datums/helper_datums/getrev.dm
+++ b/code/datums/helper_datums/getrev.dm
@@ -89,4 +89,4 @@
msg += "Protect Assistant Role From Traitor: [CONFIG_GET(flag/protect_assistant_from_antagonist)]"
msg += "Enforce Human Authority: [CONFIG_GET(flag/enforce_human_authority)]"
msg += "Allow Latejoin Antagonists: [CONFIG_GET(flag/allow_latejoin_antagonists)]"
- to_chat(src, "[msg.Join(" ")]")
+ to_chat(src, span_infoplain(msg.Join(" ")))
diff --git a/code/datums/id_trim/_id_trim.dm b/code/datums/id_trim/_id_trim.dm
index 562232214b3d1..32bafcb41d3f7 100644
--- a/code/datums/id_trim/_id_trim.dm
+++ b/code/datums/id_trim/_id_trim.dm
@@ -29,6 +29,9 @@
///If set, IDs with this trim will give wearers arrows of different colors when pointing
var/pointer_color
+/datum/id_trim/proc/find_job()
+ return null
+
/// Returns the SecHUD job icon state for whatever this object's ID card is, if it has one.
/obj/item/proc/get_sechud_job_icon_state()
var/obj/item/card/id/id_card = GetID()
diff --git a/code/datums/id_trim/jobs.dm b/code/datums/id_trim/jobs.dm
index a42018406b2a5..34432a638db01 100644
--- a/code/datums/id_trim/jobs.dm
+++ b/code/datums/id_trim/jobs.dm
@@ -24,10 +24,10 @@
/datum/id_trim/job/New()
if(ispath(job))
- job = SSjob.GetJobType(job)
+ job = SSjob.get_job_type(job)
if(isnull(job_changes))
- job_changes = SSmapping.config.job_changes
+ job_changes = SSmapping.current_map.job_changes
if(!length(job_changes))
refresh_trim_access()
@@ -76,6 +76,9 @@
return TRUE
+/datum/id_trim/job/find_job()
+ return job
+
/datum/id_trim/job/assistant
assignment = JOB_ASSISTANT
trim_state = "trim_assistant"
@@ -156,6 +159,29 @@
)
job = /datum/job/bartender
+/datum/id_trim/job/pun_pun
+ assignment = "Busser"
+ trim_state = "trim_busser"
+ department_color = COLOR_SERVICE_LIME
+ subdepartment_color = COLOR_SERVICE_LIME
+ sechud_icon_state = SECHUD_BUSSER
+ minimal_access = list(
+ ACCESS_MINERAL_STOREROOM,
+ ACCESS_SERVICE,
+ ACCESS_THEATRE,
+ )
+ extra_access = list(
+ ACCESS_HYDROPONICS,
+ ACCESS_KITCHEN,
+ ACCESS_BAR,
+ )
+ template_access = list(
+ ACCESS_CAPTAIN,
+ ACCESS_CHANGE_IDS,
+ ACCESS_HOP,
+ )
+ job = /datum/job/pun_pun
+
/datum/id_trim/job/bitrunner
assignment = JOB_BITRUNNER
trim_state = "trim_bitrunner"
@@ -1070,7 +1096,7 @@
if(CONFIG_GET(number/depsec_access_level) == POPULATION_SCALED_ACCESS)
var/minimal_security_officers = 3 // We do not spawn in any more lockers if there are 5 or less security officers, so let's keep it lower than that number.
- var/datum/job/J = SSjob.GetJob(JOB_SECURITY_OFFICER)
+ var/datum/job/J = SSjob.get_job(JOB_SECURITY_OFFICER)
if((J.spawn_positions - minimal_security_officers) <= 0)
access |= elevated_access
diff --git a/code/datums/looping_sounds/acid.dm b/code/datums/looping_sounds/acid.dm
index e461e5d02ce9c..9dcf6e1750a0c 100644
--- a/code/datums/looping_sounds/acid.dm
+++ b/code/datums/looping_sounds/acid.dm
@@ -1,5 +1,5 @@
/// Soundloop for the acid component.
/datum/looping_sound/acid
- mid_sounds = list('sound/items/welder.ogg' = 1)
+ mid_sounds = list('sound/items/tools/welder.ogg' = 1)
mid_length = 10
volume = 150
diff --git a/code/datums/looping_sounds/breathing.dm b/code/datums/looping_sounds/breathing.dm
index 73474149ae4bb..a50e13a7fd5da 100644
--- a/code/datums/looping_sounds/breathing.dm
+++ b/code/datums/looping_sounds/breathing.dm
@@ -1,13 +1,13 @@
/datum/looping_sound/breathing
mid_sounds = list(
- 'sound/voice/breathing/internals_breathing1.ogg' = 1,
- 'sound/voice/breathing/internals_breathing2.ogg' = 1,
- 'sound/voice/breathing/internals_breathing3.ogg' = 1,
- 'sound/voice/breathing/internals_breathing4.ogg' = 1,
- 'sound/voice/breathing/internals_breathing5.ogg' = 1,
- 'sound/voice/breathing/internals_breathing6.ogg' = 1,
- 'sound/voice/breathing/internals_breathing7.ogg' = 1,
- 'sound/voice/breathing/internals_breathing8.ogg' = 1,
+ 'sound/mobs/humanoids/breathing/internals_breathing1.ogg' = 1,
+ 'sound/mobs/humanoids/breathing/internals_breathing2.ogg' = 1,
+ 'sound/mobs/humanoids/breathing/internals_breathing3.ogg' = 1,
+ 'sound/mobs/humanoids/breathing/internals_breathing4.ogg' = 1,
+ 'sound/mobs/humanoids/breathing/internals_breathing5.ogg' = 1,
+ 'sound/mobs/humanoids/breathing/internals_breathing6.ogg' = 1,
+ 'sound/mobs/humanoids/breathing/internals_breathing7.ogg' = 1,
+ 'sound/mobs/humanoids/breathing/internals_breathing8.ogg' = 1,
)
//Calculated this by using the average breathing time of an adult (12 to 20 per minute, which on average is 16 per minute)
// realism is overrated, make it longer to reduce ear fatigue
diff --git a/code/datums/looping_sounds/choking.dm b/code/datums/looping_sounds/choking.dm
index 444204efa1fa6..6d337b1c7d648 100644
--- a/code/datums/looping_sounds/choking.dm
+++ b/code/datums/looping_sounds/choking.dm
@@ -1,5 +1,5 @@
/datum/looping_sound/choking
- mid_sounds = list('sound/creatures/gag1.ogg' = 1, 'sound/creatures/gag2.ogg' = 1, 'sound/creatures/gag3.ogg' = 1, 'sound/creatures/gag4.ogg' = 1, 'sound/creatures/gag5.ogg' = 1)
+ mid_sounds = list('sound/mobs/humanoids/human/gag_vomit/gag1.ogg' = 1, 'sound/mobs/humanoids/human/gag_vomit/gag2.ogg' = 1, 'sound/mobs/humanoids/human/gag_vomit/gag3.ogg' = 1, 'sound/mobs/humanoids/human/gag_vomit/gag4.ogg' = 1, 'sound/mobs/humanoids/human/gag_vomit/gag5.ogg' = 1)
mid_length = 1.6 SECONDS
mid_length_vary = 0.3 SECONDS
each_once = TRUE
diff --git a/code/datums/looping_sounds/cyborg.dm b/code/datums/looping_sounds/cyborg.dm
index 499e61757c6dd..0a01a4c7116b9 100644
--- a/code/datums/looping_sounds/cyborg.dm
+++ b/code/datums/looping_sounds/cyborg.dm
@@ -1,10 +1,10 @@
/datum/looping_sound/wash
- mid_sounds = list('sound/creatures/cyborg/wash1.ogg' = 1, 'sound/creatures/cyborg/wash2.ogg' = 1)
+ mid_sounds = list('sound/mobs/non-humanoids/cyborg/wash1.ogg' = 1, 'sound/mobs/non-humanoids/cyborg/wash2.ogg' = 1)
mid_length = 1.5 SECONDS // This makes them overlap slightly, which works out well for masking the fade in/out
start_volume = 100
- start_sound = 'sound/creatures/cyborg/wash_start.ogg'
+ start_sound = 'sound/mobs/non-humanoids/cyborg/wash_start.ogg'
start_length = 3.6 SECONDS // again, slightly shorter then the real time of 4 seconds, will make the transition to midsounds more seemless
end_volume = 100
- end_sound = 'sound/creatures/cyborg/wash_end.ogg'
+ end_sound = 'sound/mobs/non-humanoids/cyborg/wash_end.ogg'
vary = TRUE
extra_range = 5
diff --git a/code/datums/looping_sounds/item_sounds.dm b/code/datums/looping_sounds/item_sounds.dm
index 00fd3063e4382..7800326bb0a35 100644
--- a/code/datums/looping_sounds/item_sounds.dm
+++ b/code/datums/looping_sounds/item_sounds.dm
@@ -5,7 +5,7 @@
/datum/looping_sound/reverse_bear_trap_beep
- mid_sounds = list('sound/machines/beep.ogg' = 1)
+ mid_sounds = list('sound/machines/beep/beep.ogg' = 1)
mid_length = 60
volume = 10
@@ -24,7 +24,7 @@
mid_length = 1 SECONDS
/datum/looping_sound/trapped_machine_beep
- mid_sounds = list('sound/machines/beep.ogg' = 1)
+ mid_sounds = list('sound/machines/beep/beep.ogg' = 1)
mid_length = 10 SECONDS
mid_length_vary = 5 SECONDS
falloff_exponent = 10
@@ -32,19 +32,19 @@
volume = 5
/datum/looping_sound/chainsaw
- start_sound = list('sound/weapons/chainsaw_start.ogg' = 1)
+ start_sound = list('sound/items/weapons/chainsaw_start.ogg' = 1)
start_length = 0.85 SECONDS
- mid_sounds = list('sound/weapons/chainsaw_loop.ogg' = 1)
+ mid_sounds = list('sound/items/weapons/chainsaw_loop.ogg' = 1)
mid_length = 0.85 SECONDS
- end_sound = list('sound/weapons/chainsaw_stop.ogg' = 1)
+ end_sound = list('sound/items/weapons/chainsaw_stop.ogg' = 1)
end_volume = 35
volume = 40
ignore_walls = FALSE
/datum/looping_sound/beesmoke
- mid_sounds = list('sound/weapons/beesmoke.ogg' = 1)
+ mid_sounds = list('sound/items/weapons/beesmoke.ogg' = 1)
volume = 5
/datum/looping_sound/zipline
- mid_sounds = list('sound/weapons/zipline_mid.ogg' = 1)
+ mid_sounds = list('sound/items/weapons/zipline_mid.ogg' = 1)
volume = 5
diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm
index bc32b03125660..c4648a929b300 100644
--- a/code/datums/looping_sounds/machinery_sounds.dm
+++ b/code/datums/looping_sounds/machinery_sounds.dm
@@ -47,7 +47,7 @@
volume = 15
/datum/looping_sound/clock
- mid_sounds = list('sound/ambience/ticking_clock.ogg' = 1)
+ mid_sounds = list('sound/ambience/misc/ticking_clock.ogg' = 1)
mid_length = 40
volume = 50
ignore_walls = FALSE
@@ -90,7 +90,7 @@
/datum/looping_sound/jackpot
mid_length = 11
- mid_sounds = list('sound/machines/roulettejackpot.ogg' = 1)
+ mid_sounds = list('sound/machines/roulette/roulettejackpot.ogg' = 1)
volume = 85
vary = TRUE
@@ -141,7 +141,7 @@
falloff_exponent = 20
/datum/looping_sound/firealarm
- mid_sounds = list('sound/machines/FireAlarm1.ogg' = 1,'sound/machines/FireAlarm2.ogg' = 1,'sound/machines/FireAlarm3.ogg' = 1,'sound/machines/FireAlarm4.ogg' = 1)
+ mid_sounds = list('sound/machines/fire_alarm/FireAlarm1.ogg' = 1,'sound/machines/fire_alarm/FireAlarm2.ogg' = 1,'sound/machines/fire_alarm/FireAlarm3.ogg' = 1,'sound/machines/fire_alarm/FireAlarm4.ogg' = 1)
mid_length = 2.4 SECONDS
volume = 30
@@ -151,34 +151,34 @@
falloff_exponent = 5
/datum/looping_sound/boiling
- mid_sounds = list('sound/effects/bubbles2.ogg' = 1)
+ mid_sounds = list('sound/effects/bubbles/bubbles2.ogg' = 1)
mid_length = 7 SECONDS
volume = 25
/datum/looping_sound/typing
mid_sounds = list(
- 'sound/machines/terminal_button01.ogg' = 1,
- 'sound/machines/terminal_button02.ogg' = 1,
- 'sound/machines/terminal_button03.ogg' = 1,
- 'sound/machines/terminal_button04.ogg' = 1,
- 'sound/machines/terminal_button05.ogg' = 1,
- 'sound/machines/terminal_button06.ogg' = 1,
- 'sound/machines/terminal_button07.ogg' = 1,
- 'sound/machines/terminal_button08.ogg' = 1,
+ 'sound/machines/terminal/terminal_button01.ogg' = 1,
+ 'sound/machines/terminal/terminal_button02.ogg' = 1,
+ 'sound/machines/terminal/terminal_button03.ogg' = 1,
+ 'sound/machines/terminal/terminal_button04.ogg' = 1,
+ 'sound/machines/terminal/terminal_button05.ogg' = 1,
+ 'sound/machines/terminal/terminal_button06.ogg' = 1,
+ 'sound/machines/terminal/terminal_button07.ogg' = 1,
+ 'sound/machines/terminal/terminal_button08.ogg' = 1,
)
mid_length = 0.3 SECONDS
/datum/looping_sound/soup
mid_sounds = list(
- 'sound/effects/soup_boil1.ogg' = 1,
- 'sound/effects/soup_boil2.ogg' = 1,
- 'sound/effects/soup_boil3.ogg' = 1,
- 'sound/effects/soup_boil4.ogg' = 1,
- 'sound/effects/soup_boil5.ogg' = 1,
+ 'sound/effects/soup_boil/soup_boil1.ogg' = 1,
+ 'sound/effects/soup_boil/soup_boil2.ogg' = 1,
+ 'sound/effects/soup_boil/soup_boil3.ogg' = 1,
+ 'sound/effects/soup_boil/soup_boil4.ogg' = 1,
+ 'sound/effects/soup_boil/soup_boil5.ogg' = 1,
)
mid_length = 3 SECONDS
volume = 80
- end_sound = 'sound/effects/soup_boil_end.ogg'
+ end_sound = 'sound/effects/soup_boil/soup_boil_end.ogg'
end_volume = 60
extra_range = MEDIUM_RANGE_SOUND_EXTRARANGE
falloff_exponent = 4
diff --git a/code/datums/looping_sounds/music.dm b/code/datums/looping_sounds/music.dm
index ac76e236bc784..cc35ab8a8ee37 100644
--- a/code/datums/looping_sounds/music.dm
+++ b/code/datums/looping_sounds/music.dm
@@ -1,6 +1,6 @@
/datum/looping_sound/local_forecast
mid_sounds = list(
- 'sound/ambience/music/elevator/robocop-short.ogg' = 1,
+ 'sound/music/elevator/robocop-short.ogg' = 1,
)
mid_length = 61 SECONDS
volume = 20
diff --git a/code/datums/looping_sounds/vents.dm b/code/datums/looping_sounds/vents.dm
index 2d0a3443631df..016b21db9cad0 100644
--- a/code/datums/looping_sounds/vents.dm
+++ b/code/datums/looping_sounds/vents.dm
@@ -1,7 +1,7 @@
/datum/looping_sound/vent_pump_overclock
- start_sound = 'sound/machines/fan_start.ogg'
+ start_sound = 'sound/machines/fan/fan_start.ogg'
start_length = 1.5 SECONDS
- end_sound = 'sound/machines/fan_stop.ogg'
+ end_sound = 'sound/machines/fan/fan_stop.ogg'
end_sound = 1.5 SECONDS
- mid_sounds = 'sound/machines/fan_loop.ogg'
+ mid_sounds = 'sound/machines/fan/fan_loop.ogg'
mid_length = 2 SECONDS
diff --git a/code/datums/looping_sounds/weather.dm b/code/datums/looping_sounds/weather.dm
index 6576cfb4e8d12..fa782a5f4ee5a 100644
--- a/code/datums/looping_sounds/weather.dm
+++ b/code/datums/looping_sounds/weather.dm
@@ -1,53 +1,53 @@
/datum/looping_sound/active_outside_ashstorm
mid_sounds = list(
- 'sound/weather/ashstorm/outside/active_mid1.ogg'=1,
- 'sound/weather/ashstorm/outside/active_mid1.ogg'=1,
- 'sound/weather/ashstorm/outside/active_mid1.ogg'=1
+ 'sound/ambience/weather/ashstorm/outside/active_mid1.ogg'=1,
+ 'sound/ambience/weather/ashstorm/outside/active_mid1.ogg'=1,
+ 'sound/ambience/weather/ashstorm/outside/active_mid1.ogg'=1
)
mid_length = 80
- start_sound = 'sound/weather/ashstorm/outside/active_start.ogg'
+ start_sound = 'sound/ambience/weather/ashstorm/outside/active_start.ogg'
start_length = 130
- end_sound = 'sound/weather/ashstorm/outside/active_end.ogg'
+ end_sound = 'sound/ambience/weather/ashstorm/outside/active_end.ogg'
volume = 80
/datum/looping_sound/active_inside_ashstorm
mid_sounds = list(
- 'sound/weather/ashstorm/inside/active_mid1.ogg'=1,
- 'sound/weather/ashstorm/inside/active_mid2.ogg'=1,
- 'sound/weather/ashstorm/inside/active_mid3.ogg'=1
+ 'sound/ambience/weather/ashstorm/inside/active_mid1.ogg'=1,
+ 'sound/ambience/weather/ashstorm/inside/active_mid2.ogg'=1,
+ 'sound/ambience/weather/ashstorm/inside/active_mid3.ogg'=1
)
mid_length = 80
- start_sound = 'sound/weather/ashstorm/inside/active_start.ogg'
+ start_sound = 'sound/ambience/weather/ashstorm/inside/active_start.ogg'
start_length = 130
- end_sound = 'sound/weather/ashstorm/inside/active_end.ogg'
+ end_sound = 'sound/ambience/weather/ashstorm/inside/active_end.ogg'
volume = 60
/datum/looping_sound/weak_outside_ashstorm
mid_sounds = list(
- 'sound/weather/ashstorm/outside/weak_mid1.ogg'=1,
- 'sound/weather/ashstorm/outside/weak_mid2.ogg'=1,
- 'sound/weather/ashstorm/outside/weak_mid3.ogg'=1
+ 'sound/ambience/weather/ashstorm/outside/weak_mid1.ogg'=1,
+ 'sound/ambience/weather/ashstorm/outside/weak_mid2.ogg'=1,
+ 'sound/ambience/weather/ashstorm/outside/weak_mid3.ogg'=1
)
mid_length = 80
- start_sound = 'sound/weather/ashstorm/outside/weak_start.ogg'
+ start_sound = 'sound/ambience/weather/ashstorm/outside/weak_start.ogg'
start_length = 130
- end_sound = 'sound/weather/ashstorm/outside/weak_end.ogg'
+ end_sound = 'sound/ambience/weather/ashstorm/outside/weak_end.ogg'
volume = 50
/datum/looping_sound/weak_inside_ashstorm
mid_sounds = list(
- 'sound/weather/ashstorm/inside/weak_mid1.ogg'=1,
- 'sound/weather/ashstorm/inside/weak_mid2.ogg'=1,
- 'sound/weather/ashstorm/inside/weak_mid3.ogg'=1
+ 'sound/ambience/weather/ashstorm/inside/weak_mid1.ogg'=1,
+ 'sound/ambience/weather/ashstorm/inside/weak_mid2.ogg'=1,
+ 'sound/ambience/weather/ashstorm/inside/weak_mid3.ogg'=1
)
mid_length = 80
- start_sound = 'sound/weather/ashstorm/inside/weak_start.ogg'
+ start_sound = 'sound/ambience/weather/ashstorm/inside/weak_start.ogg'
start_length = 130
- end_sound = 'sound/weather/ashstorm/inside/weak_end.ogg'
+ end_sound = 'sound/ambience/weather/ashstorm/inside/weak_end.ogg'
volume = 30
/datum/looping_sound/void_loop
- mid_sounds = list('sound/ambience/VoidsEmbrace.ogg'=1)
+ mid_sounds = list('sound/music/antag/heretic/VoidsEmbrace.ogg'=1)
mid_length = 1669 // exact length of the music in ticks
volume = 100
extra_range = 30
diff --git a/code/datums/martial/cqc.dm b/code/datums/martial/cqc.dm
index 28b820278e21b..35f8a254e177e 100644
--- a/code/datums/martial/cqc.dm
+++ b/code/datums/martial/cqc.dm
@@ -104,7 +104,7 @@
attacker,
)
to_chat(attacker, span_danger("You slam [defender] into the ground!"))
- playsound(attacker, 'sound/weapons/slam.ogg', 50, TRUE, -1)
+ playsound(attacker, 'sound/items/weapons/slam.ogg', 50, TRUE, -1)
defender.apply_damage(10, BRUTE)
defender.Paralyze(12 SECONDS)
log_combat(attacker, defender, "slammed (CQC)")
@@ -125,7 +125,7 @@
attacker,
)
to_chat(attacker, span_danger("You kick [defender]'s head, knocking [defender.p_them()] out!"))
- playsound(attacker, 'sound/weapons/genhit1.ogg', 50, TRUE, -1)
+ playsound(attacker, 'sound/items/weapons/genhit1.ogg', 50, TRUE, -1)
var/helmet_protection = defender.run_armor_check(BODY_ZONE_HEAD, MELEE)
defender.apply_effect(20 SECONDS, EFFECT_KNOCKDOWN, helmet_protection)
@@ -141,7 +141,7 @@
attacker,
)
to_chat(attacker, span_danger("You kick [defender] back!"))
- playsound(attacker, 'sound/weapons/cqchit1.ogg', 50, TRUE, -1)
+ playsound(attacker, 'sound/items/weapons/cqchit1.ogg', 50, TRUE, -1)
var/atom/throw_target = get_edge_target_turf(defender, attacker.dir)
defender.throw_at(throw_target, 1, 14, attacker)
defender.apply_damage(10, attacker.get_attack_type())
@@ -163,7 +163,7 @@
)
to_chat(attacker, span_danger("You punch [defender]'s neck!"))
defender.adjustStaminaLoss(60)
- playsound(attacker, 'sound/weapons/cqchit1.ogg', 50, TRUE, -1)
+ playsound(attacker, 'sound/items/weapons/cqchit1.ogg', 50, TRUE, -1)
return TRUE
/datum/martial_art/cqc/proc/Restrain(mob/living/attacker, mob/living/defender)
@@ -201,7 +201,7 @@
attacker,
)
to_chat(attacker, span_danger("You strike [defender]'s abdomen, neck and back consecutively!"))
- playsound(defender, 'sound/weapons/cqchit2.ogg', 50, TRUE, -1)
+ playsound(defender, 'sound/items/weapons/cqchit2.ogg', 50, TRUE, -1)
var/obj/item/held_item = defender.get_active_held_item()
if(held_item && defender.temporarilyRemoveItemFromInventory(held_item))
attacker.put_in_hands(held_item)
@@ -291,7 +291,7 @@
picked_hit_type = pick("kick", "stomp")
defender.apply_damage(bonus_damage, BRUTE)
- playsound(defender, (picked_hit_type == "kick" || picked_hit_type == "stomp") ? 'sound/weapons/cqchit2.ogg' : 'sound/weapons/cqchit1.ogg', 50, TRUE, -1)
+ playsound(defender, (picked_hit_type == "kick" || picked_hit_type == "stomp") ? 'sound/items/weapons/cqchit2.ogg' : 'sound/items/weapons/cqchit1.ogg', 50, TRUE, -1)
defender.visible_message(
span_danger("[attacker] [picked_hit_type]ed [defender]!"),
@@ -344,7 +344,7 @@
attacker,
)
to_chat(attacker, span_danger("You strike [defender]'s jaw,[disarmed_item ? " disarming [defender.p_them()] of [disarmed_item] and" : ""] leaving [defender.p_them()] disoriented!"))
- playsound(defender, 'sound/weapons/cqchit1.ogg', 50, TRUE, -1)
+ playsound(defender, 'sound/items/weapons/cqchit1.ogg', 50, TRUE, -1)
defender.set_jitter_if_lower(4 SECONDS)
defender.apply_damage(5, attacker.get_attack_type())
log_combat(attacker, defender, "disarmed (CQC)", addition = disarmed_item ? "(disarmed of [disarmed_item])" : null)
@@ -358,7 +358,7 @@
attacker,
)
to_chat(attacker, span_warning("You fail to disarm [defender]!"))
- playsound(defender, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
+ playsound(defender, 'sound/items/weapons/punchmiss.ogg', 25, TRUE, -1)
log_combat(attacker, defender, "failed to disarm (CQC)")
return MARTIAL_ATTACK_FAIL
diff --git a/code/datums/martial/krav_maga.dm b/code/datums/martial/krav_maga.dm
index 57e158cf66982..d670b8eb63806 100644
--- a/code/datums/martial/krav_maga.dm
+++ b/code/datums/martial/krav_maga.dm
@@ -201,7 +201,7 @@
attacker,
)
to_chat(attacker, span_danger("You disarm [defender]!"))
- playsound(defender, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ playsound(defender, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE, -1)
log_combat(attacker, defender, "disarmed (Krav Maga)", addition = "(disarmed of [stuff_in_hand])")
return MARTIAL_ATTACK_INVALID // normal shove
diff --git a/code/datums/martial/plasma_fist.dm b/code/datums/martial/plasma_fist.dm
index 47df74a3d4634..89981554f3ea3 100644
--- a/code/datums/martial/plasma_fist.dm
+++ b/code/datums/martial/plasma_fist.dm
@@ -41,7 +41,7 @@
/datum/martial_art/plasma_fist/proc/Tornado(mob/living/attacker, mob/living/defender)
attacker.say("TORNADO SWEEP!", forced="plasma fist")
- dance_rotate(attacker, CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), attacker, 'sound/weapons/punch1.ogg', 15, TRUE, -1))
+ dance_rotate(attacker, CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), attacker, 'sound/items/weapons/punch1.ogg', 15, TRUE, -1))
tornado_spell.cast(attacker)
log_combat(attacker, defender, "tornado sweeped (Plasma Fist)")
return TRUE
@@ -55,7 +55,7 @@
attacker,
)
to_chat(attacker, span_danger("You hit [defender] with Plasma Punch!"))
- playsound(defender, 'sound/weapons/punch1.ogg', 50, TRUE, -1)
+ playsound(defender, 'sound/items/weapons/punch1.ogg', 50, TRUE, -1)
var/atom/throw_target = get_edge_target_turf(defender, get_dir(defender, get_step_away(defender, attacker)))
defender.throw_at(throw_target, 200, 4,attacker)
attacker.say("HYAH!", forced="plasma fist")
@@ -66,7 +66,7 @@
var/hasclient = !!defender.client
attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
- playsound(defender, 'sound/weapons/punch1.ogg', 50, TRUE, -1)
+ playsound(defender, 'sound/items/weapons/punch1.ogg', 50, TRUE, -1)
attacker.say("PLASMA FIST!", forced="plasma fist")
defender.visible_message(
span_danger("[attacker] hits [defender] with THE PLASMA FIST TECHNIQUE!"),
@@ -125,7 +125,7 @@
user.apply_damage(rand(50, 70), BRUTE, wound_bonus = CANT_WOUND)
addtimer(CALLBACK(src, PROC_REF(Apotheosis_end), user), 6 SECONDS)
- playsound(boomspot, 'sound/weapons/punch1.ogg', 50, TRUE, -1)
+ playsound(boomspot, 'sound/items/weapons/punch1.ogg', 50, TRUE, -1)
explosion(user, devastation_range = plasma_power, heavy_impact_range = plasma_power*2, light_impact_range = plasma_power*4, ignorecap = TRUE, explosion_cause = src)
plasma_power = 1 //just in case there is any clever way to cause it to happen again
return TRUE
diff --git a/code/datums/martial/psychotic_brawl.dm b/code/datums/martial/psychotic_brawl.dm
index 454d23637f255..99c2b0e222464 100644
--- a/code/datums/martial/psychotic_brawl.dm
+++ b/code/datums/martial/psychotic_brawl.dm
@@ -67,7 +67,7 @@
attacker,
)
to_chat(attacker, span_danger("You [atk_verb] [defender]!"))
- playsound(defender, 'sound/weapons/punch1.ogg', 40, TRUE, -1)
+ playsound(defender, 'sound/items/weapons/punch1.ogg', 40, TRUE, -1)
defender.apply_damage(defender_damage, attacker.get_attack_type(), BODY_ZONE_HEAD)
attacker.apply_damage(rand(5, 10), attacker.get_attack_type(), BODY_ZONE_HEAD)
if(iscarbon(defender))
diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm
index 02d0452fad265..f52050d8d76d7 100644
--- a/code/datums/martial/sleeping_carp.dm
+++ b/code/datums/martial/sleeping_carp.dm
@@ -53,7 +53,7 @@
attacker,
)
to_chat(attacker, span_danger("You [atk_verb] [defender]!"))
- playsound(defender, 'sound/weapons/punch1.ogg', 25, TRUE, -1)
+ playsound(defender, 'sound/items/weapons/punch1.ogg', 25, TRUE, -1)
log_combat(attacker, defender, "strong punched (Sleeping Carp)")
defender.apply_damage(20, attacker.get_attack_type(), affecting)
return TRUE
@@ -106,7 +106,7 @@
var/grab_log_description = "grabbed"
attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
- playsound(defender, 'sound/weapons/punch1.ogg', 25, TRUE, -1)
+ playsound(defender, 'sound/items/weapons/punch1.ogg', 25, TRUE, -1)
if(defender.stat != DEAD && !defender.IsUnconscious() && defender.getStaminaLoss() >= 80) //We put our target to sleep.
defender.visible_message(
span_danger("[attacker] carefully pinch a nerve in [defender]'s neck, knocking them out cold!"),
@@ -161,7 +161,7 @@
)
to_chat(attacker, span_danger("You [atk_verb] [defender]!"))
defender.apply_damage(final_damage, attacker.get_attack_type(), affecting, wound_bonus = CANT_WOUND)
- playsound(defender, 'sound/weapons/punch1.ogg', 25, TRUE, -1)
+ playsound(defender, 'sound/items/weapons/punch1.ogg', 25, TRUE, -1)
log_combat(attacker, defender, "punched (Sleeping Carp)")
return MARTIAL_ATTACK_SUCCESS
@@ -176,7 +176,7 @@
return MARTIAL_ATTACK_SUCCESS
attacker.do_attack_animation(defender, ATTACK_EFFECT_PUNCH)
- playsound(defender, 'sound/weapons/punch1.ogg', 25, TRUE, -1)
+ playsound(defender, 'sound/items/weapons/punch1.ogg', 25, TRUE, -1)
defender.apply_damage(20, STAMINA)
log_combat(attacker, defender, "disarmed (Sleeping Carp)")
return MARTIAL_ATTACK_INVALID // normal disarm
@@ -204,7 +204,7 @@
span_danger("[carp_user] effortlessly swats [hitting_projectile] aside! [carp_user.p_They()] can block bullets with [carp_user.p_their()] bare hands!"),
span_userdanger("You deflect [hitting_projectile]!"),
)
- playsound(carp_user, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE)
+ playsound(carp_user, pick('sound/items/weapons/bulletflyby.ogg', 'sound/items/weapons/bulletflyby2.ogg', 'sound/items/weapons/bulletflyby3.ogg'), 75, TRUE)
hitting_projectile.firer = carp_user
hitting_projectile.set_angle(rand(0, 360))//SHING
return COMPONENT_BULLET_PIERCED
diff --git a/code/datums/memory/general_memories.dm b/code/datums/memory/general_memories.dm
index b8c44c6fca862..eca745d3283a6 100644
--- a/code/datums/memory/general_memories.dm
+++ b/code/datums/memory/general_memories.dm
@@ -162,6 +162,28 @@
"[protagonist_name] [mood_verb] as they lick off some of the pie",
)
+/// Witnessed someone get splashed with squid ink.
+/datum/memory/witnessed_inking
+ story_value = STORY_VALUE_OKAY
+ memory_flags = MEMORY_CHECK_BLINDNESS
+ // Protagonist - The mob that got pied
+
+/datum/memory/witnessed_inking/get_names()
+ return list("The inking of [protagonist_name].")
+
+/datum/memory/witnessed_inking/get_starts()
+ return list(
+ "[protagonist_name]'s face being covered in squid ink",
+ "[protagonist_name] getting squid-inked",
+ )
+
+/datum/memory/witnessed_inking/get_moods()
+ return list(
+ "[protagonist_name] [mood_verb] as ink drips off their face",
+ "[protagonist_name] [mood_verb] because of their now expanded laundry task.",
+ "[protagonist_name] [mood_verb] as they wipe the ink off their face.",
+ )
+
/// Got slipped by something.
/datum/memory/was_slipped
story_value = STORY_VALUE_MEH
diff --git a/code/datums/memory/tattoo_kit.dm b/code/datums/memory/tattoo_kit.dm
index 98d47eaa285d8..62b4d73be28ba 100644
--- a/code/datums/memory/tattoo_kit.dm
+++ b/code/datums/memory/tattoo_kit.dm
@@ -46,11 +46,11 @@
if(!tattoo_target)
balloon_alert(tattoo_artist, "no limb to tattoo!")
return
- if(HAS_TRAIT_FROM(tattoo_target, TRAIT_NOT_ENGRAVABLE, INNATE_TRAIT))
- balloon_alert(tattoo_artist, "bodypart cannot be engraved!")
+ if(HAS_TRAIT_FROM(tattoo_target, TRAIT_NOT_ENGRAVABLE, ENGRAVED_TRAIT))
+ balloon_alert(tattoo_artist, "bodypart already tattooed!")
return
- if(HAS_TRAIT_FROM(tattoo_target, TRAIT_NOT_ENGRAVABLE, TRAIT_GENERIC))
- balloon_alert(tattoo_artist, "bodypart has already been engraved!")
+ if(HAS_TRAIT(tattoo_target, TRAIT_NOT_ENGRAVABLE))
+ balloon_alert(tattoo_artist, "bodypart cannot be tattooed!")
return
var/datum/memory/memory_to_tattoo = tattoo_artist.mind.select_memory("tattoo")
if(!memory_to_tattoo || !tattoo_artist.Adjacent(tattoo_holder) || !tattoo_holder.get_bodypart(selected_zone))
diff --git a/code/datums/mind/_mind.dm b/code/datums/mind/_mind.dm
index 36e9d7ba6d024..397a1c5b13afa 100644
--- a/code/datums/mind/_mind.dm
+++ b/code/datums/mind/_mind.dm
@@ -106,7 +106,7 @@
/datum/mind/New(_key)
key = _key
init_known_skills()
- set_assigned_role(SSjob.GetJobType(/datum/job/unassigned)) // Unassigned by default.
+ set_assigned_role(SSjob.get_job_type(/datum/job/unassigned)) // Unassigned by default.
/datum/mind/Destroy()
SSticker.minds -= src
@@ -251,7 +251,7 @@
var/new_role = input("Select new role", "Assigned role", assigned_role.title) as null|anything in sort_list(SSjob.name_occupations)
if(isnull(new_role))
return
- var/datum/job/new_job = SSjob.GetJob(new_role)
+ var/datum/job/new_job = SSjob.get_job(new_role)
if (!new_job)
to_chat(usr, span_warning("Job not found."))
return
diff --git a/code/datums/mind/antag.dm b/code/datums/mind/antag.dm
index 11340ae56a6b4..ca10f3afe2071 100644
--- a/code/datums/mind/antag.dm
+++ b/code/datums/mind/antag.dm
@@ -286,7 +286,7 @@
/datum/mind/proc/make_wizard()
if(has_antag_datum(/datum/antagonist/wizard))
return
- set_assigned_role(SSjob.GetJobType(/datum/job/space_wizard))
+ set_assigned_role(SSjob.get_job_type(/datum/job/space_wizard))
special_role = ROLE_WIZARD
add_antag_datum(/datum/antagonist/wizard)
diff --git a/code/datums/mind/initialization.dm b/code/datums/mind/initialization.dm
index eb622cc5af549..e3b3e8225dc7a 100644
--- a/code/datums/mind/initialization.dm
+++ b/code/datums/mind/initialization.dm
@@ -21,17 +21,17 @@
//AI
/mob/living/silicon/ai/mind_initialize()
. = ..()
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/ai))
+ mind.set_assigned_role(SSjob.get_job_type(/datum/job/ai))
//BORG
/mob/living/silicon/robot/mind_initialize()
. = ..()
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/cyborg))
+ mind.set_assigned_role(SSjob.get_job_type(/datum/job/cyborg))
//PAI
/mob/living/silicon/pai/mind_initialize()
. = ..()
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/personal_ai))
+ mind.set_assigned_role(SSjob.get_job_type(/datum/job/personal_ai))
mind.special_role = ""
diff --git a/code/datums/mood.dm b/code/datums/mood.dm
index 3834ea866b8da..cffd955635817 100644
--- a/code/datums/mood.dm
+++ b/code/datums/mood.dm
@@ -406,7 +406,7 @@
clear_mood_event(MOOD_CATEGORY_AREA_BEAUTY)
return
- if(HAS_TRAIT(mob_parent, TRAIT_MORBID))
+ if(HAS_MIND_TRAIT(mob_parent, TRAIT_MORBID))
if(HAS_TRAIT(mob_parent, TRAIT_SNOB))
switch(area_to_beautify.beauty)
if(BEAUTY_LEVEL_DECENT to BEAUTY_LEVEL_GOOD)
diff --git a/code/datums/mood_events/food_events.dm b/code/datums/mood_events/food_events.dm
index 7d33e7e57ce06..e64d975902ea7 100644
--- a/code/datums/mood_events/food_events.dm
+++ b/code/datums/mood_events/food_events.dm
@@ -49,3 +49,8 @@
/datum/mood_event/food/top
quality = FOOD_QUALITY_TOP
+
+/datum/mood_event/pacifist_eating_fish_item
+ description = "I shouldn't be eating living creatures..."
+ mood_change = -1 //The disgusting food moodlet already has a pretty big negative value, this is just for context.
+ timeout = 4 MINUTES
diff --git a/code/datums/mood_events/generic_negative_events.dm b/code/datums/mood_events/generic_negative_events.dm
index 6ad0580e5557c..695bf43949653 100644
--- a/code/datums/mood_events/generic_negative_events.dm
+++ b/code/datums/mood_events/generic_negative_events.dm
@@ -33,6 +33,11 @@
mood_change = -2
timeout = 3 MINUTES
+/datum/mood_event/inked
+ description = "I've been splashed with squid ink. Tastes like salt."
+ mood_change = -3
+ timeout = 3 MINUTES
+
/datum/mood_event/slipped
description = "I slipped. I should be more careful next time..."
mood_change = -2
@@ -485,3 +490,14 @@
description = "I DIDN'T MEAN TO HURT THEM!"
mood_change = -20
timeout = 10 MINUTES
+
+//Gained when you're hit over the head with wrapping paper or cardboard roll
+/datum/mood_event/bapped
+ description = "Ow.. my head, I feel a bit foolish now!"
+ mood_change = -1
+ timeout = 3 MINUTES
+
+/datum/mood_event/bapped/add_effects()
+ // Felinids apparently hate being hit over the head with cardboard
+ if(isfelinid(owner))
+ mood_change = -2
diff --git a/code/datums/mood_events/generic_positive_events.dm b/code/datums/mood_events/generic_positive_events.dm
index 54b276fa71a3d..cdd28c1855ee8 100644
--- a/code/datums/mood_events/generic_positive_events.dm
+++ b/code/datums/mood_events/generic_positive_events.dm
@@ -333,9 +333,34 @@
/datum/mood_event/fishing
description = "Fishing is relaxing."
- mood_change = 5
+ mood_change = 4
timeout = 3 MINUTES
+/datum/mood_event/fish_released
+ description = "Go, fish, swim and be free!"
+ mood_change = 1
+ timeout = 2 MINUTES
+
+/datum/mood_event/fish_released/add_effects(morbid, obj/item/fish/fish)
+ if(!morbid)
+ description = "Go, [fish.name], swim and be free!"
+ return
+ if(fish.status == FISH_DEAD)
+ description = "Some scavenger will surely find a use for the remains of [fish.name]. How pragmatic."
+ else
+ description = "Returned to the burden of the deep. But is this truly a mercy, [fish.name]? There will always be bigger fish..."
+
+/datum/mood_event/fish_petting
+ description = "It felt nice to pet the fish."
+ mood_change = 2
+ timeout = 2 MINUTES
+
+/datum/mood_event/fish_petting/add_effects(obj/item/fish/fish, morbid)
+ if(!morbid)
+ description = "It felt nice to pet \the [fish]."
+ else
+ description = "I caress \the [fish] as [fish.p_they()] squirms under my touch, blissfully unaware of how cruel this world is."
+
/datum/mood_event/kobun
description = "You are all loved by the Universe. I’m not alone, and you aren’t either."
mood_change = 14
diff --git a/code/datums/mutations/body.dm b/code/datums/mutations/body.dm
index c86f7d7003334..e09a8337b72a4 100644
--- a/code/datums/mutations/body.dm
+++ b/code/datums/mutations/body.dm
@@ -474,7 +474,7 @@
if(prob(15))
owner.acid_act(rand(30, 50), 10)
owner.visible_message(span_warning("[owner]'s skin bubbles and pops."), span_userdanger("Your bubbling flesh pops! It burns!"))
- playsound(owner,'sound/weapons/sear.ogg', 50, TRUE)
+ playsound(owner,'sound/items/weapons/sear.ogg', 50, TRUE)
/datum/mutation/human/spastic
name = "Spastic"
diff --git a/code/datums/mutations/fire_breath.dm b/code/datums/mutations/fire_breath.dm
index 56915ff0130ea..d643bd98508d2 100644
--- a/code/datums/mutations/fire_breath.dm
+++ b/code/datums/mutations/fire_breath.dm
@@ -29,7 +29,7 @@
name = "Fire Breath"
desc = "You breathe a cone of fire directly in front of you."
button_icon_state = "fireball0"
- sound = 'sound/magic/demon_dies.ogg' //horrifying lizard noises
+ sound = 'sound/effects/magic/demon_dies.ogg' //horrifying lizard noises
school = SCHOOL_EVOCATION
cooldown_time = 40 SECONDS
diff --git a/code/datums/mutations/hulk.dm b/code/datums/mutations/hulk.dm
index 38ecb0fa07356..4eb04cdc03366 100644
--- a/code/datums/mutations/hulk.dm
+++ b/code/datums/mutations/hulk.dm
@@ -207,7 +207,7 @@
continue
yeeted_person.adjustBruteLoss(step*0.5)
- playsound(collateral_mob,'sound/weapons/punch1.ogg',50,TRUE)
+ playsound(collateral_mob,'sound/items/weapons/punch1.ogg',50,TRUE)
log_combat(the_hulk, collateral_mob, "has smacked with tail swing victim")
log_combat(the_hulk, yeeted_person, "has smacked this person into someone while tail swinging") // i have no idea how to better word this
diff --git a/code/datums/mutations/sight.dm b/code/datums/mutations/sight.dm
index 5a8d7458f3d0f..d3627167cb507 100644
--- a/code/datums/mutations/sight.dm
+++ b/code/datums/mutations/sight.dm
@@ -167,13 +167,13 @@
return
to_chat(source, span_warning("You shoot with your laser eyes!"))
source.changeNext_move(CLICK_CD_RANGE)
- source.newtonian_move(get_dir(target, source))
+ source.newtonian_move(get_angle(source, target))
var/obj/projectile/beam/laser/laser_eyes/LE = new(source.loc)
LE.firer = source
LE.def_zone = ran_zone(source.zone_selected)
LE.preparePixelProjectile(target, source, modifiers)
INVOKE_ASYNC(LE, TYPE_PROC_REF(/obj/projectile, fire))
- playsound(source, 'sound/weapons/taser2.ogg', 75, TRUE)
+ playsound(source, 'sound/items/weapons/taser2.ogg', 75, TRUE)
///Projectile type used by laser eyes
/obj/projectile/beam/laser/laser_eyes
diff --git a/code/datums/mutations/touch.dm b/code/datums/mutations/touch.dm
index 2483ef2e0fa1a..634a46ab217ba 100644
--- a/code/datums/mutations/touch.dm
+++ b/code/datums/mutations/touch.dm
@@ -28,7 +28,7 @@
name = "Shock Touch"
desc = "Channel electricity to your hand to shock people with."
button_icon_state = "zap"
- sound = 'sound/weapons/zapbang.ogg'
+ sound = 'sound/items/weapons/zapbang.ogg'
cooldown_time = 12 SECONDS
invocation_type = INVOCATION_NONE
spell_requirements = NONE
@@ -117,7 +117,7 @@
desc = "You can now lay your hands on other people to transfer a small amount of their physical injuries to yourself."
button_icon = 'icons/mob/actions/actions_genetic.dmi'
button_icon_state = "mending_touch"
- sound = 'sound/magic/staff_healing.ogg'
+ sound = 'sound/effects/magic/staff_healing.ogg'
cooldown_time = 12 SECONDS
school = SCHOOL_RESTORATION
invocation_type = INVOCATION_NONE
diff --git a/code/datums/position_point_vector.dm b/code/datums/position_point_vector.dm
index c963d0ad76025..b8b697c053bca 100644
--- a/code/datums/position_point_vector.dm
+++ b/code/datums/position_point_vector.dm
@@ -91,9 +91,9 @@
/datum/point/proc/initialize_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
if(!isnull(tile_x))
- x = ((tile_x - 1) * world.icon_size) + world.icon_size * 0.5 + p_x + 1
+ x = ((tile_x - 1) * ICON_SIZE_X) + ICON_SIZE_X * 0.5 + p_x + 1
if(!isnull(tile_y))
- y = ((tile_y - 1) * world.icon_size) + world.icon_size * 0.5 + p_y + 1
+ y = ((tile_y - 1) * ICON_SIZE_Y) + ICON_SIZE_Y * 0.5 + p_y + 1
if(!isnull(tile_z))
z = tile_z
@@ -107,23 +107,23 @@
AM.pixel_y = return_py()
/datum/point/proc/return_turf()
- return locate(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z)
+ return locate(CEILING(x / ICON_SIZE_X, 1), CEILING(y / ICON_SIZE_Y, 1), z)
/datum/point/proc/return_coordinates() //[turf_x, turf_y, z]
- return list(CEILING(x / world.icon_size, 1), CEILING(y / world.icon_size, 1), z)
+ return list(CEILING(x / ICON_SIZE_X, 1), CEILING(y / ICON_SIZE_Y, 1), z)
/datum/point/proc/return_position()
return new /datum/position(src)
/datum/point/proc/return_px()
- return MODULUS(x, world.icon_size) - 16 - 1
+ return MODULUS(x, ICON_SIZE_X) - (ICON_SIZE_X/2) - 1
/datum/point/proc/return_py()
- return MODULUS(y, world.icon_size) - 16 - 1
+ return MODULUS(y, ICON_SIZE_Y) - (ICON_SIZE_Y/2) - 1
/datum/point/vector
/// Pixels per iteration
- var/speed = 32
+ var/speed = ICON_SIZE_ALL
var/iteration = 0
var/angle = 0
/// Calculated x movement amounts to prevent having to do trig every step.
@@ -149,9 +149,9 @@
/// Same effect as initiliaze_location, but without setting the starting_x/y/z
/datum/point/vector/proc/set_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
if(!isnull(tile_x))
- x = ((tile_x - 1) * world.icon_size) + world.icon_size * 0.5 + p_x + 1
+ x = ((tile_x - 1) * ICON_SIZE_X) + ICON_SIZE_X * 0.5 + p_x + 1
if(!isnull(tile_y))
- y = ((tile_y - 1) * world.icon_size) + world.icon_size * 0.5 + p_y + 1
+ y = ((tile_y - 1) * ICON_SIZE_Y) + ICON_SIZE_Y * 0.5 + p_y + 1
if(!isnull(tile_z))
z = tile_z
diff --git a/code/datums/progressbar.dm b/code/datums/progressbar.dm
index b560a67aa6c90..676cf455bfc51 100644
--- a/code/datums/progressbar.dm
+++ b/code/datums/progressbar.dm
@@ -69,8 +69,8 @@
continue
progress_bar.listindex--
- progress_bar.bar.pixel_y = world.icon_size + offset_y + (PROGRESSBAR_HEIGHT * (progress_bar.listindex - 1))
- var/dist_to_travel = world.icon_size + offset_y + (PROGRESSBAR_HEIGHT * (progress_bar.listindex - 1)) - PROGRESSBAR_HEIGHT
+ progress_bar.bar.pixel_y = ICON_SIZE_Y + offset_y + (PROGRESSBAR_HEIGHT * (progress_bar.listindex - 1))
+ var/dist_to_travel = ICON_SIZE_Y + offset_y + (PROGRESSBAR_HEIGHT * (progress_bar.listindex - 1)) - PROGRESSBAR_HEIGHT
animate(progress_bar.bar, pixel_y = dist_to_travel, time = PROGRESSBAR_ANIMATION_TIME, easing = SINE_EASING)
LAZYREMOVEASSOC(user.progressbars, bar_loc, src)
@@ -123,7 +123,7 @@
bar.pixel_y = 0
bar.alpha = 0
user_client.images += bar
- animate(bar, pixel_y = world.icon_size + offset_y + (PROGRESSBAR_HEIGHT * (listindex - 1)), alpha = 255, time = PROGRESSBAR_ANIMATION_TIME, easing = SINE_EASING)
+ animate(bar, pixel_y = ICON_SIZE_Y + offset_y + (PROGRESSBAR_HEIGHT * (listindex - 1)), alpha = 255, time = PROGRESSBAR_ANIMATION_TIME, easing = SINE_EASING)
///Updates the progress bar image visually.
diff --git a/code/datums/proximity_monitor/field.dm b/code/datums/proximity_monitor/field.dm
index e3f0ade5e7dba..3ba3017bed0a5 100644
--- a/code/datums/proximity_monitor/field.dm
+++ b/code/datums/proximity_monitor/field.dm
@@ -40,7 +40,7 @@
var/list/old_edge_turfs = edge_turfs
field_turfs = new_turfs[FIELD_TURFS_KEY]
edge_turfs = new_turfs[EDGE_TURFS_KEY]
- if(!full_recalc)
+ if(full_recalc)
field_turfs = list()
edge_turfs = list()
@@ -62,12 +62,11 @@
for(var/turf/new_turf as anything in field_turfs - old_field_turfs)
if(QDELETED(src))
return
- field_turfs += new_turf
setup_field_turf(new_turf)
+
for(var/turf/new_turf as anything in edge_turfs - old_edge_turfs)
if(QDELETED(src))
return
- edge_turfs += new_turf
setup_edge_turf(new_turf)
/datum/proximity_monitor/advanced/on_initialized(turf/location, atom/created, init_flags)
diff --git a/code/datums/proximity_monitor/fields/timestop.dm b/code/datums/proximity_monitor/fields/timestop.dm
index 79996dee2dd36..3b8001426a03c 100644
--- a/code/datums/proximity_monitor/fields/timestop.dm
+++ b/code/datums/proximity_monitor/fields/timestop.dm
@@ -45,13 +45,13 @@
/obj/effect/timestop/Destroy()
QDEL_NULL(chronofield)
if(!hidden)
- playsound(src, 'sound/magic/timeparadox2.ogg', 75, TRUE, frequency = -1) //reverse!
+ playsound(src, 'sound/effects/magic/timeparadox2.ogg', 75, TRUE, frequency = -1) //reverse!
return ..()
/obj/effect/timestop/proc/timestop()
target = get_turf(src)
if(!hidden)
- playsound(src, 'sound/magic/timeparadox2.ogg', 75, TRUE, -1)
+ playsound(src, 'sound/effects/magic/timeparadox2.ogg', 75, TRUE, -1)
chronofield = new (src, freezerange, TRUE, immune, antimagic_flags, channelled)
if(!channelled)
QDEL_IN(src, duration)
diff --git a/code/datums/proximity_monitor/fields/void_storm.dm b/code/datums/proximity_monitor/fields/void_storm.dm
new file mode 100644
index 0000000000000..c9e3bbbbcff91
--- /dev/null
+++ b/code/datums/proximity_monitor/fields/void_storm.dm
@@ -0,0 +1,37 @@
+/*!
+ * Void storm for the void heretic ascension
+ *
+ * Follows the heretic around and acts like an aura with damaging effects for non-heretics
+ */
+/datum/proximity_monitor/advanced/void_storm
+ edge_is_a_field = TRUE
+ // lazylist that keeps track of the overlays added
+ var/list/turf_effects
+ var/static/image/storm_overlay = image('icons/effects/weather_effects.dmi', "snow_storm")
+
+/datum/proximity_monitor/advanced/void_storm/New(atom/_host, range, _ignore_if_not_on_turf)
+ . = ..()
+ recalculate_field(full_recalc = TRUE)
+
+/datum/proximity_monitor/advanced/void_storm/recalculate_field(full_recalc)
+ full_recalc = TRUE // We always perform a full recalc because we need to update ALL the sprites
+ return ..()
+
+/datum/proximity_monitor/advanced/void_storm/cleanup_field_turf(turf/target)
+ . = ..()
+ var/obj/effect/abstract/effect = LAZYACCESS(turf_effects, target)
+ LAZYREMOVE(turf_effects, target)
+ if(effect)
+ qdel(effect)
+
+/datum/proximity_monitor/advanced/void_storm/setup_field_turf(turf/target)
+ . = ..()
+ var/obj/effect/abstract/effect = new(target) // Makes the field visible to players.
+ effect.alpha = 255 - get_dist(target, host.loc) * 23
+ effect.color = COLOR_BLACK
+ effect.icon = storm_overlay.icon
+ effect.icon_state = storm_overlay.icon_state
+ effect.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ effect.layer = ABOVE_ALL_MOB_LAYER
+ SET_PLANE(effect, ABOVE_GAME_PLANE, target)
+ LAZYSET(turf_effects, target, effect)
diff --git a/code/datums/quirks/negative_quirks/prosopagnosia.dm b/code/datums/quirks/negative_quirks/prosopagnosia.dm
index 8634e13bf638c..9b41713e6cef9 100644
--- a/code/datums/quirks/negative_quirks/prosopagnosia.dm
+++ b/code/datums/quirks/negative_quirks/prosopagnosia.dm
@@ -7,3 +7,19 @@
medical_record_text = "Patient suffers from prosopagnosia and cannot recognize faces."
hardcore_value = 5
mail_goodies = list(/obj/item/skillchip/appraiser) // bad at recognizing faces but good at recognizing IDs
+
+/datum/quirk/prosopagnosia/add(client/client_source)
+ RegisterSignal(quirk_holder, COMSIG_MOB_REQUESTING_SCREENTIP_NAME_FROM_USER, PROC_REF(screentip_name_override))
+ quirk_holder.mob_flags |= MOB_HAS_SCREENTIPS_NAME_OVERRIDE
+
+/datum/quirk/prosopagnosia/remove()
+ UnregisterSignal(quirk_holder, COMSIG_MOB_REQUESTING_SCREENTIP_NAME_FROM_USER)
+
+/datum/quirk/prosopagnosia/proc/screentip_name_override(datum/source, list/returned_name, obj/item/held_item, atom/hovered)
+ SIGNAL_HANDLER
+
+ if(!ishuman(hovered))
+ return NONE
+
+ returned_name[1] = "Unknown"
+ return SCREENTIP_NAME_SET
diff --git a/code/datums/quirks/negative_quirks/prosthetic_organ.dm b/code/datums/quirks/negative_quirks/prosthetic_organ.dm
index cd7ca2a801481..4a377699b40ac 100644
--- a/code/datums/quirks/negative_quirks/prosthetic_organ.dm
+++ b/code/datums/quirks/negative_quirks/prosthetic_organ.dm
@@ -60,9 +60,9 @@
medical_record_text = "During physical examination, patient was found to have a low-budget prosthetic [slot_string]. \
Removal of these organs is known to be dangerous to the patient as well as the practitioner."
old_organ = human_holder.get_organ_slot(organ_slot)
- if(prosthetic.Insert(human_holder, special = TRUE))
- old_organ.moveToNullspace()
- STOP_PROCESSING(SSobj, old_organ)
+ prosthetic.Insert(human_holder, special = TRUE)
+ old_organ.moveToNullspace()
+ STOP_PROCESSING(SSobj, old_organ)
/datum/quirk/prosthetic_organ/post_add()
to_chat(quirk_holder, span_boldannounce("Your [slot_string] has been replaced with a surplus organ. It is weak and highly unstable. \
diff --git a/code/datums/quirks/neutral_quirks/monochromatic.dm b/code/datums/quirks/neutral_quirks/monochromatic.dm
index dd66220cb56a9..ef6735df25d93 100644
--- a/code/datums/quirks/neutral_quirks/monochromatic.dm
+++ b/code/datums/quirks/neutral_quirks/monochromatic.dm
@@ -17,7 +17,7 @@
/datum/quirk/monochromatic/post_add()
if(is_detective_job(quirk_holder.mind.assigned_role))
to_chat(quirk_holder, span_boldannounce("Mmm. Nothing's ever clear on this station. It's all shades of gray..."))
- quirk_holder.playsound_local(quirk_holder, 'sound/ambience/ambidet1.ogg', 50, FALSE)
+ quirk_holder.playsound_local(quirk_holder, 'sound/ambience/security/ambidet1.ogg', 50, FALSE)
/datum/quirk/monochromatic/remove()
quirk_holder.remove_client_colour(/datum/client_colour/monochrome)
diff --git a/code/datums/quirks/neutral_quirks/vegetarian.dm b/code/datums/quirks/neutral_quirks/vegetarian.dm
index 0ade72acafe57..0568e2f1e2293 100644
--- a/code/datums/quirks/neutral_quirks/vegetarian.dm
+++ b/code/datums/quirks/neutral_quirks/vegetarian.dm
@@ -7,17 +7,4 @@
lose_text = span_notice("You feel like eating meat isn't that bad.")
medical_record_text = "Patient reports a vegetarian diet."
mail_goodies = list(/obj/effect/spawner/random/food_or_drink/salad)
-
-/datum/quirk/vegetarian/add(client/client_source)
- var/obj/item/organ/internal/tongue/tongue = quirk_holder.get_organ_slot(ORGAN_SLOT_TONGUE)
- if(!tongue)
- return
- tongue.liked_foodtypes &= ~MEAT
- tongue.disliked_foodtypes |= MEAT
-
-/datum/quirk/vegetarian/remove()
- var/obj/item/organ/internal/tongue/tongue = quirk_holder.get_organ_slot(ORGAN_SLOT_TONGUE)
- if(!tongue)
- return
- tongue.liked_foodtypes = initial(tongue.liked_foodtypes)
- tongue.disliked_foodtypes = initial(tongue.disliked_foodtypes)
+ mob_trait = TRAIT_VEGETARIAN
diff --git a/code/datums/quirks/positive_quirks/spacer.dm b/code/datums/quirks/positive_quirks/spacer.dm
index 051798b4c06a6..344462703e906 100644
--- a/code/datums/quirks/positive_quirks/spacer.dm
+++ b/code/datums/quirks/positive_quirks/spacer.dm
@@ -43,7 +43,7 @@
check_z(quirk_holder, skip_timers = TRUE)
// drift slightly faster through zero G
- quirk_holder.inertia_move_delay *= 0.8
+ quirk_holder.inertia_move_multiplier *= 0.8
var/mob/living/carbon/human/human_quirker = quirk_holder
human_quirker.set_mob_height(modded_height)
@@ -73,7 +73,7 @@
if(QDELING(quirk_holder))
return
- quirk_holder.inertia_move_delay /= 0.8
+ quirk_holder.inertia_move_multiplier /= 0.8
quirk_holder.clear_mood_event("spacer")
quirk_holder.remove_movespeed_modifier(/datum/movespeed_modifier/spacer)
quirk_holder.remove_status_effect(/datum/status_effect/spacer)
diff --git a/code/datums/records/manifest.dm b/code/datums/records/manifest.dm
index fc2bb30ce8d32..afc5cef6aa4ec 100644
--- a/code/datums/records/manifest.dm
+++ b/code/datums/records/manifest.dm
@@ -31,7 +31,7 @@ GLOBAL_DATUM_INIT(manifest, /datum/manifest, new)
var/name = target.name
var/rank = target.rank // user-visible job
var/trim = target.trim // internal jobs by trim type
- var/datum/job/job = SSjob.GetJob(trim)
+ var/datum/job/job = SSjob.get_job(trim)
if(!job || !(job.job_flags & JOB_CREW_MANIFEST) || !LAZYLEN(job.departments_list)) // In case an unlawful custom rank is added.
var/list/misc_list = manifest_out[DEPARTMENT_UNASSIGNED]
misc_list[++misc_list.len] = list(
diff --git a/code/datums/ruins/icemoon.dm b/code/datums/ruins/icemoon.dm
index 2445b4da242e6..d6317b12ab15c 100644
--- a/code/datums/ruins/icemoon.dm
+++ b/code/datums/ruins/icemoon.dm
@@ -64,7 +64,7 @@
/datum/map_template/ruin/icemoon/frozen_phonebooth
name = "Ice-Ruin Frozen Phonebooth"
id = "frozen_phonebooth"
- description = "A venture by nanotrasen to help popularize the use of holopads. This one was sent to a icemoon."
+ description = "A venture by Nanotrasen to help popularize the use of holopads. This one was sent to a icemoon."
suffix = "icemoon_surface_phonebooth.dmm"
/datum/map_template/ruin/icemoon/smoking_room
@@ -172,6 +172,12 @@
description = "Radio signals are being detected and the source is this completely innocent pile of snow."
suffix = "icemoon_underground_comms_agent.dmm"
+/datum/map_template/ruin/icemoon/underground/syndie_lab
+ name = "Ice-Ruin Syndicate Lab"
+ id = "syndie_lab"
+ description = "A small laboratory and living space for Syndicate agents."
+ suffix = "icemoon_underground_syndielab.dmm"
+
//TODO: Bottom-Level ONLY Spawns after Refactoring Related Code
/datum/map_template/ruin/icemoon/underground/plasma_facility
name = "Ice-Ruin Abandoned Plasma Facility"
diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm
index bb5f58500e2d1..fb2d1d44c0c08 100644
--- a/code/datums/ruins/lavaland.dm
+++ b/code/datums/ruins/lavaland.dm
@@ -278,7 +278,7 @@
/datum/map_template/ruin/lavaland/lava_phonebooth
name = "Lava-Ruin Phonebooth"
id = "lava_phonebooth"
- description = "A venture by nanotrasen to help popularize the use of holopads. This one somehow made its way here."
+ description = "A venture by Nanotrasen to help popularize the use of holopads. This one somehow made its way here."
suffix = "lavaland_surface_phonebooth.dmm"
allow_duplicates = FALSE
cost = 5
diff --git a/code/datums/ruins/space.dm b/code/datums/ruins/space.dm
index e3ff61fad6c25..790ae33ade4b1 100644
--- a/code/datums/ruins/space.dm
+++ b/code/datums/ruins/space.dm
@@ -49,7 +49,7 @@
id = "asteroid6"
suffix = "asteroid6.dmm"
name = "Space-Ruin Asteroid 6"
- description = "This asteroid has brittle bone disease, so it is fortunate asteroids dont have bones."
+ description = "This asteroid has brittle bone disease, so it is fortunate asteroids don't have bones."
/datum/map_template/ruin/space/deep_storage
id = "deep-storage"
@@ -70,7 +70,7 @@
suffix = "derelict_construction.dmm"
name = "Space-Ruin Derelict Construction"
description = "Construction supplies are in high demand due to non-trivial damage routinely sustained by most space stations in this sector. \
- Space pirates who dont attempt to rob corporate research stations with only 3 collaborators live long enough to sell captured construction \
+ Space pirates who don't attempt to rob corporate research stations with only 3 collaborators live long enough to sell captured construction \
equipment back to the highest bidder."
/datum/map_template/ruin/space/derelict_sulaco
@@ -181,7 +181,7 @@
id = "spacehotel"
suffix = "spacehotel.dmm"
name = "Space-Ruin The Twin-Nexus Hotel"
- description = "An interstellar hotel, where the weary spaceman can rest their head and relax, assured that the residental staff will not murder them in their sleep. Probably."
+ description = "An interstellar hotel, where the weary spaceman can rest their head and relax, assured that the residential staff will not murder them in their sleep. Probably."
/datum/map_template/ruin/space/turreted_outpost
id = "turreted-outpost"
@@ -470,7 +470,7 @@
id = "Space_phonebooth"
suffix = "phonebooth.dmm"
name = "Space-Ruin Phonebooth"
- description = "A venture by nanotrasen to help popularize the use of holopads."
+ description = "A venture by Nanotrasen to help popularize the use of holopads."
/datum/map_template/ruin/space/the_outlet
id = "the_outlet"
@@ -500,7 +500,7 @@
id = "garbagetruck3"
suffix = "garbagetruck3.dmm"
name = "Space-Ruin Decommissioned Garbage Truck NX3"
- description = "An NX-760 interstellar transport barge. At the end of their life cycle, they are often filled with trash and launched into unexplored space to become someone else's problem. This one is full of industrial garbage, and a russian drug den."
+ description = "An NX-760 interstellar transport barge. At the end of their life cycle, they are often filled with trash and launched into unexplored space to become someone else's problem. This one is full of industrial garbage, and a Russian drug den."
/datum/map_template/ruin/space/garbagetruck4
id = "garbagetruck4"
diff --git a/code/datums/shuttles/_shuttle.dm b/code/datums/shuttles/_shuttle.dm
index 0100a3d85da3d..94c20d41b7365 100644
--- a/code/datums/shuttles/_shuttle.dm
+++ b/code/datums/shuttles/_shuttle.dm
@@ -14,7 +14,7 @@
var/description
/// The recommended occupancy limit for the shuttle (count chairs, beds, and benches then round to 5)
var/occupancy_limit
- /// Description of the prerequisition that has to be achieved for the shuttle to be purchased
+ /// Description of the prerequisite that has to be achieved for the shuttle to be purchased
var/prerequisites
/// Shuttle warnings and hazards to the admin who spawns the shuttle
var/admin_notes
@@ -40,7 +40,7 @@
. = ..()
/datum/map_template/shuttle/preload_size(path, cache)
- . = ..(path, TRUE) // Done this way because we still want to know if someone actualy wanted to cache the map
+ . = ..(path, TRUE) // Done this way because we still want to know if someone actually wanted to cache the map
if(!cached_map)
return
diff --git a/code/datums/shuttles/emergency.dm b/code/datums/shuttles/emergency.dm
index 42e3566ccfef1..3662727fa5c9a 100644
--- a/code/datums/shuttles/emergency.dm
+++ b/code/datums/shuttles/emergency.dm
@@ -31,13 +31,13 @@
mobile.event_list.Cut()
if(use_all_events)
for(var/path in events)
- mobile.event_list.Add(new path(mobile))
+ mobile.add_shuttle_event(path)
events -= path
else
for(var/i in 1 to event_amount)
var/path = pick_weight(events)
events -= path
- mobile.event_list.Add(new path(mobile))
+ mobile.add_shuttle_event(path)
/datum/map_template/shuttle/emergency/backup
suffix = "backup"
@@ -90,7 +90,7 @@
suffix = "bar"
name = "The Emergency Escape Bar"
description = "Features include sentient bar staff (a Bardrone and a Barmaid), bathroom, a quality lounge for the heads, and a large gathering table."
- admin_notes = "Bardrone and Barmaid are GODMODE, will be automatically sentienced by the fun balloon at 60 seconds before arrival. \
+ admin_notes = "Bardrone and Barmaid have TRAIT_GODMODE (basically invincibility), will be automatically sentienced by the fun balloon at 60 seconds before arrival. \
Has medical facilities."
credit_cost = CARGO_CRATE_VALUE * 10
occupancy_limit = "30"
@@ -164,7 +164,7 @@
/datum/map_template/shuttle/emergency/arena
suffix = "arena"
name = "The Arena"
- description = "The crew must pass through an otherworldy arena to board this shuttle. Expect massive casualties."
+ description = "The crew must pass through an otherworldly arena to board this shuttle. Expect massive casualties."
prerequisites = "The source of the Bloody Signal must be tracked down and eliminated to unlock this shuttle."
admin_notes = "RIP AND TEAR."
credit_cost = CARGO_CRATE_VALUE * 20
@@ -241,7 +241,7 @@
suffix = "kilo"
name = "Kilo Station Emergency Shuttle"
credit_cost = CARGO_CRATE_VALUE * 10
- description = "A fully functional shuttle including a complete infirmary, storage facilties and regular amenities."
+ description = "A fully functional shuttle including a complete infirmary, storage facilities and regular amenities."
occupancy_limit = "55"
/datum/map_template/shuttle/emergency/mini
@@ -400,15 +400,15 @@
/datum/map_template/shuttle/emergency/monkey
suffix = "nature"
name = "Dynamic Environmental Interaction Shuttle"
- description = "A large shuttle with a center biodome that is flourishing with life. Frolick with the monkeys! (Extra monkeys are stored on the bridge.)"
- admin_notes = "Pretty freakin' large, almost as big as Raven or Cere. Excercise caution with it."
+ description = "A large shuttle with a center biodome that is flourishing with life. Frolic with the monkeys! (Extra monkeys are stored on the bridge.)"
+ admin_notes = "Pretty freakin' large, almost as big as Raven or Cere. Exercise caution with it."
credit_cost = CARGO_CRATE_VALUE * 16
occupancy_limit = "45"
/datum/map_template/shuttle/emergency/casino
suffix = "casino"
name = "Lucky Jackpot Casino Shuttle"
- description = "A luxurious casino packed to the brim with everything you need to start new gambling addicitions!"
+ description = "A luxurious casino packed to the brim with everything you need to start new gambling addictions!"
admin_notes = "The ship is a bit chunky, so watch where you park it."
credit_cost = 7777
occupancy_limit = "85"
@@ -424,7 +424,7 @@
/datum/map_template/shuttle/emergency/fish
suffix = "fish"
name = "Angler's Choice Emergency Shuttle"
- description = "Trades such amenities as 'storage space' and 'sufficient seating' for an artifical environment ideal for fishing, plus ample supplies (also for fishing)."
+ description = "Trades such amenities as 'storage space' and 'sufficient seating' for an artificial environment ideal for fishing, plus ample supplies (also for fishing)."
admin_notes = "There's a chasm in it, it has railings but that won't stop determined players."
credit_cost = CARGO_CRATE_VALUE * 10
occupancy_limit = "35"
diff --git a/code/datums/station_traits/_station_trait.dm b/code/datums/station_traits/_station_trait.dm
index ef91a183d9f27..ddd8bc20a9110 100644
--- a/code/datums/station_traits/_station_trait.dm
+++ b/code/datums/station_traits/_station_trait.dm
@@ -33,10 +33,10 @@ GLOBAL_LIST_EMPTY(lobby_station_traits)
var/list/lobby_buttons = list()
/// The ID that we look for in dynamic.json. Not synced with 'name' because I can already see this go wrong
var/dynamic_threat_id
- /// If ran during dynamic, do we reduce the total threat? Will be overriden by config if set
+ /// If ran during dynamic, do we reduce the total threat? Will be overridden by config if set
var/threat_reduction = 0
- /// Which ruleset flags to allow dynamic to use. null to disregard
- var/dynamic_category = null
+ /// Which ruleset flags to allow dynamic to use. NONE to disregard
+ var/dynamic_category = NONE
/// Trait should not be instantiated in a round if its type matches this type
var/abstract_type = /datum/station_trait
@@ -51,15 +51,19 @@ GLOBAL_LIST_EMPTY(lobby_station_traits)
GLOB.dynamic_ruleset_categories = dynamic_category
if(sign_up_button)
GLOB.lobby_station_traits += src
+ if(SSstation.initialized)
+ SSstation.display_lobby_traits()
if(trait_processes)
START_PROCESSING(SSstation, src)
if(trait_to_give)
ADD_TRAIT(SSstation, trait_to_give, STATION_TRAIT)
/datum/station_trait/Destroy()
- SSstation.station_traits -= src
- GLOB.dynamic_station_traits.Remove(src)
destroy_lobby_buttons()
+ SSstation.station_traits -= src
+ GLOB.lobby_station_traits -= src
+ GLOB.dynamic_station_traits -= src
+ REMOVE_TRAIT(SSstation, trait_to_give, STATION_TRAIT)
return ..()
/// Returns the type of info the centcom report has on this trait, if any.
@@ -125,13 +129,15 @@ GLOBAL_LIST_EMPTY(lobby_station_traits)
/// Remove all of our active lobby buttons
/datum/station_trait/proc/destroy_lobby_buttons()
for (var/atom/movable/screen/button as anything in lobby_buttons)
- var/mob/hud_owner = button.get_mob()
- qdel(button)
+ var/mob/dead/new_player/hud_owner = button.get_mob()
if (QDELETED(hud_owner))
+ qdel(button)
+ continue
+ var/datum/hud/new_player/using_hud = hud_owner.hud_used
+ if(!using_hud)
+ qdel(button)
continue
- var/datum/hud/using_hud = hud_owner.hud_used
- using_hud?.show_hud(using_hud?.hud_version)
- lobby_buttons = list()
+ using_hud.remove_station_trait_button(src)
/// Called when overriding a pulsar star command report message.
/datum/station_trait/proc/get_pulsar_message()
diff --git a/code/datums/station_traits/job_traits.dm b/code/datums/station_traits/job_traits.dm
index f2bd456aaee77..3e8171d99c57b 100644
--- a/code/datums/station_traits/job_traits.dm
+++ b/code/datums/station_traits/job_traits.dm
@@ -41,6 +41,10 @@
else
LAZYADD(lobby_candidates, user)
+/datum/station_trait/job/on_lobby_button_destroyed(atom/movable/screen/lobby/button/sign_up/lobby_button)
+ . = ..()
+ LAZYREMOVE(lobby_candidates, lobby_button.get_mob())
+
/datum/station_trait/job/on_lobby_button_update_icon(atom/movable/screen/lobby/button/sign_up/lobby_button, updates)
if (LAZYFIND(lobby_candidates, lobby_button.get_mob()))
lobby_button.base_icon_state = "signup_on"
@@ -61,7 +65,7 @@
if (isnull(signee) || !signee.client || !signee.mind || signee.ready != PLAYER_READY_TO_PLAY)
LAZYREMOVE(lobby_candidates, signee)
- var/datum/job/our_job = SSjob.GetJobType(job_to_add)
+ var/datum/job/our_job = SSjob.get_job_type(job_to_add)
our_job.total_positions = position_amount
our_job.spawn_positions = position_amount
while(length(lobby_candidates) && position_amount > 0)
@@ -73,14 +77,14 @@
lobby_candidates = null
/datum/station_trait/job/can_display_lobby_button(client/player)
- var/datum/job/our_job = SSjob.GetJobType(job_to_add)
+ var/datum/job/our_job = SSjob.get_job_type(job_to_add)
return our_job.player_old_enough(player) && ..()
/// Adds a gorilla to the cargo department, replacing the sloth and the mech
/datum/station_trait/job/cargorilla
name = "Cargo Gorilla"
button_desc = "Sign up to become the Cargo Gorilla, a peaceful shepherd of boxes."
- weight = 1
+ weight = 0
show_in_report = FALSE // Selective attention test. Did you spot the gorilla?
can_roll_antag = CAN_ROLL_NEVER
job_to_add = /datum/job/cargo_gorilla
@@ -243,6 +247,27 @@
qdel(thing_on_table)
new /obj/machinery/fax/auto_name(picked_turf)
+/datum/station_trait/job/pun_pun
+ name = "Pun Pun is a Crewmember"
+ button_desc = "Ook ook ah ah, sign up to play as the bartender's monkey."
+ weight = 0 //Unrollable by default, available all day during monkey day.
+ report_message = "We've evaluated the bartender's monkey to have the mental capacity of the average crewmember. As such, we made them one."
+ show_in_report = TRUE
+ can_roll_antag = CAN_ROLL_ALWAYS
+ job_to_add = /datum/job/pun_pun
+
+/datum/station_trait/job/pun_pun/New()
+ . = ..()
+ //Make sure we don't have two Pun Puns if loaded before the start of the round.
+ if(SSticker.HasRoundStarted() || !GLOB.the_one_and_only_punpun)
+ return
+ new /obj/effect/landmark/start/pun_pun(GLOB.the_one_and_only_punpun.loc)
+ qdel(GLOB.the_one_and_only_punpun)
+
+/datum/station_trait/job/pun_pun/on_lobby_button_update_overlays(atom/movable/screen/lobby/button/sign_up/lobby_button, list/overlays)
+ . = ..()
+ overlays += LAZYFIND(lobby_candidates, lobby_button.get_mob()) ? "pun_pun_on" : "pun_pun_off"
+
#undef CAN_ROLL_ALWAYS
#undef CAN_ROLL_PROTECTED
#undef CAN_ROLL_NEVER
diff --git a/code/datums/station_traits/negative_traits.dm b/code/datums/station_traits/negative_traits.dm
index 83d1bfa14f4c9..8b5aadb3a06d6 100644
--- a/code/datums/station_traits/negative_traits.dm
+++ b/code/datums/station_traits/negative_traits.dm
@@ -26,7 +26,7 @@
report_message = "Due to an ongoing strike announced by the postal workers union, mail won't be delivered this shift."
/datum/station_trait/mail_blocked/on_round_start()
- //This is either a holiday or sunday... well then, let's flip the situation.
+ //This is either a holiday or Sunday... well then, let's flip the situation.
if(SSeconomy.mail_blocked)
name = "Postal system overtime"
report_message = "Despite being a day off, the postal system is working overtime today. Mail will be delivered this shift."
@@ -343,7 +343,7 @@
/datum/station_trait/random_event_weight_modifier/dust_storms
name = "Dust Stormfront"
- report_message = "The space around your station is clouded by heavy pockets of space dust. Expect an increased likelyhood of space dust storms damaging the station hull."
+ report_message = "The space around your station is clouded by heavy pockets of space dust. Expect an increased likelihood of space dust storms damaging the station hull."
trait_type = STATION_TRAIT_NEGATIVE
weight = 2
cost = STATION_TRAIT_COST_LOW
@@ -614,10 +614,10 @@
//Send a nebula shielding unit to engineering
var/datum/supply_pack/supply_pack_shielding = new /datum/supply_pack/engineering/rad_nebula_shielding_kit()
if(!send_supply_pod_to_area(supply_pack_shielding.generate(null), /area/station/engineering/main, /obj/structure/closet/supplypod/centcompod))
- //if engineering isnt valid, just send it to the bridge
+ //if engineering isn't valid, just send it to the bridge
send_supply_pod_to_area(supply_pack_shielding.generate(null), /area/station/command/bridge, /obj/structure/closet/supplypod/centcompod)
- // Let medical know resistence is futile
+ // Let medical know resistance is futile
if (/area/station/medical/virology in GLOB.areas_by_type)
send_fax_to_area(
new /obj/item/paper/fluff/radiation_nebula_virologist,
@@ -656,7 +656,7 @@
if(!istype(get_area(spawned_mob), radioactive_areas)) //only if you're spawned in the radioactive areas
return
- if(!isliving(spawned_mob)) // Dynamic shouldnt spawn non-living but uhhhhhhh why not
+ if(!isliving(spawned_mob)) // Dynamic shouldn't spawn non-living but uhhhhhhh why not
return
var/mob/living/spawnee = spawned_mob
@@ -696,7 +696,7 @@
/datum/station_trait/nebula/hostile/radiation/send_instructions()
var/obj/machinery/nebula_shielding/shielder = /obj/machinery/nebula_shielding/radiation
var/obj/machinery/gravity_generator/main/innate_shielding = /obj/machinery/gravity_generator/main
- //How long do we have untill the first shielding unit needs to be up?
+ //How long do we have until the first shielding unit needs to be up?
var/deadline = "[(initial(innate_shielding.radioactive_nebula_shielding) * intensity_increment_time) / (1 MINUTES)] minute\s"
//For how long each shielding unit will protect for
var/shielder_time = "[(initial(shielder.shielding_strength) * intensity_increment_time) / (1 MINUTES)] minute\s"
@@ -713,7 +713,7 @@
Every shielding unit will provide an additional [shielder_time] of protection, fully protecting the station with [max_shielders] shielding units.
"}
- priority_announce(announcement, sound = 'sound/misc/notice1.ogg')
+ priority_announce(announcement, sound = 'sound/announcer/notice/notice1.ogg')
//Set the display screens to the radiation alert
var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS)
diff --git a/code/datums/station_traits/positive_traits.dm b/code/datums/station_traits/positive_traits.dm
index 945fbe06934bc..052a8177345c7 100644
--- a/code/datums/station_traits/positive_traits.dm
+++ b/code/datums/station_traits/positive_traits.dm
@@ -285,6 +285,7 @@
/datum/job/paramedic = /obj/item/organ/internal/cyberimp/eyes/hud/medical,
/datum/job/prisoner = /obj/item/organ/internal/eyes/robotic/shield,
/datum/job/psychologist = /obj/item/organ/internal/ears/cybernetic/whisper,
+ /datum/job/pun_pun = /obj/item/organ/internal/cyberimp/arm/strongarm,
/datum/job/quartermaster = /obj/item/organ/internal/stomach/cybernetic/tier3,
/datum/job/research_director = /obj/item/organ/internal/cyberimp/bci,
/datum/job/roboticist = /obj/item/organ/internal/cyberimp/eyes/hud/diagnostic,
diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm
index 68462f1ae2ef2..cf3d1c88440b3 100644
--- a/code/datums/status_effects/buffs.dm
+++ b/code/datums/status_effects/buffs.dm
@@ -426,7 +426,7 @@
/datum/status_effect/mayhem/on_apply()
. = ..()
to_chat(owner, "RIP AND TEAR")
- SEND_SOUND(owner, sound('sound/hallucinations/veryfar_noise.ogg'))
+ SEND_SOUND(owner, sound('sound/effects/hallucinations/veryfar_noise.ogg'))
owner.cause_hallucination( \
/datum/hallucination/delusion/preset/demon, \
"[id] status effect", \
@@ -572,7 +572,7 @@
owner.add_stun_absorption(source = id, priority = 4)
owner.add_movespeed_mod_immunities(id, /datum/movespeed_modifier/damage_slowdown)
ADD_TRAIT(owner, TRAIT_FREE_HYPERSPACE_MOVEMENT, id)
- owner.playsound_local(get_turf(owner), 'sound/chemistry/ahaha.ogg', vol = 100, vary = TRUE, use_reverb = TRUE)
+ owner.playsound_local(get_turf(owner), 'sound/effects/chemistry/ahaha.ogg', vol = 100, vary = TRUE, use_reverb = TRUE)
return TRUE
/datum/status_effect/blessing_of_insanity/on_remove()
diff --git a/code/datums/status_effects/debuffs/choke.dm b/code/datums/status_effects/debuffs/choke.dm
index c16b946aa02bd..9113c8a1a023e 100644
--- a/code/datums/status_effects/debuffs/choke.dm
+++ b/code/datums/status_effects/debuffs/choke.dm
@@ -217,7 +217,7 @@
var/obj/item/bodypart/chest = carbon_victim.get_bodypart(BODY_ZONE_CHEST)
carbon_victim.cause_wound_of_type_and_severity(WOUND_BLUNT, chest, WOUND_SEVERITY_SEVERE, wound_source = "human force to the chest")
- playsound(owner, 'sound/creatures/crack_vomit.ogg', 120, extrarange = 5, falloff_exponent = 4)
+ playsound(owner, 'sound/mobs/humanoids/human/gag_vomit/crack_vomit.ogg', 120, extrarange = 5, falloff_exponent = 4)
vomit_up()
/datum/status_effect/choke/proc/mirror_dir(atom/source, old_dir, new_dir)
diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm
index 08f5ae101bf77..865e9869ebeea 100644
--- a/code/datums/status_effects/debuffs/debuffs.dm
+++ b/code/datums/status_effects/debuffs/debuffs.dm
@@ -488,7 +488,7 @@
wasting_effect.transform = owner.transform //if the owner has been stunned the overlay should inherit that position
wasting_effect.alpha = 255
animate(wasting_effect, alpha = 0, time = 32)
- playsound(owner, 'sound/effects/curse5.ogg', 20, TRUE, -1)
+ playsound(owner, 'sound/effects/curse/curse5.ogg', 20, TRUE, -1)
owner.adjustFireLoss(0.75)
if(effect_last_activation <= world.time)
effect_last_activation = world.time + effect_cooldown
@@ -511,7 +511,7 @@
/datum/status_effect/necropolis_curse/proc/grasp(turf/spawn_turf)
set waitfor = FALSE
new/obj/effect/temp_visual/dir_setting/curse/grasp_portal(spawn_turf, owner.dir)
- playsound(spawn_turf, 'sound/effects/curse2.ogg', 80, TRUE, -1)
+ playsound(spawn_turf, 'sound/effects/curse/curse2.ogg', 80, TRUE, -1)
var/obj/projectile/curse_hand/C = new (spawn_turf)
C.preparePixelProjectile(owner, spawn_turf)
C.fire()
diff --git a/code/datums/status_effects/debuffs/fire_stacks.dm b/code/datums/status_effects/debuffs/fire_stacks.dm
index 46c31c4578d1d..ae7f73d4e0de0 100644
--- a/code/datums/status_effects/debuffs/fire_stacks.dm
+++ b/code/datums/status_effects/debuffs/fire_stacks.dm
@@ -136,6 +136,12 @@
/// Type of mob light emitter we use when on fire
var/moblight_type = /obj/effect/dummy/lighting_obj/moblight/fire
+/datum/status_effect/fire_handler/fire_stacks/get_examine_text()
+ if(owner.on_fire)
+ return
+
+ return "[owner.p_They()] [owner.p_are()] covered in something flammable."
+
/datum/status_effect/fire_handler/fire_stacks/proc/owner_touched_sparks()
SIGNAL_HANDLER
@@ -221,8 +227,9 @@
amount_to_heat = amount_to_heat ** (BODYTEMP_FIRE_TEMP_SOFTCAP / owner.bodytemperature)
victim.adjust_bodytemperature(amount_to_heat)
- victim.add_mood_event("on_fire", /datum/mood_event/on_fire)
- victim.add_mob_memory(/datum/memory/was_burning)
+ if (!(HAS_TRAIT(victim, TRAIT_RESISTHEAT)))
+ victim.add_mood_event("on_fire", /datum/mood_event/on_fire)
+ victim.add_mob_memory(/datum/memory/was_burning)
/**
* Handles mob ignition, should be the only way to set on_fire to TRUE
@@ -294,6 +301,9 @@
enemy_types = list(/datum/status_effect/fire_handler/fire_stacks)
stack_modifier = -1
+/datum/status_effect/fire_handler/wet_stacks/get_examine_text()
+ return "[owner.p_They()] look[owner.p_s()] a little soaked."
+
/datum/status_effect/fire_handler/wet_stacks/tick(seconds_between_ticks)
adjust_stacks(-0.5 * seconds_between_ticks)
if(stacks <= 0)
diff --git a/code/datums/status_effects/debuffs/genetic_damage.dm b/code/datums/status_effects/debuffs/genetic_damage.dm
index 9a6944090775e..21b6f1db2185c 100644
--- a/code/datums/status_effects/debuffs/genetic_damage.dm
+++ b/code/datums/status_effects/debuffs/genetic_damage.dm
@@ -34,7 +34,7 @@
/datum/status_effect/genetic_damage/tick(seconds_between_ticks)
if(ismonkey(owner) && total_damage >= GORILLA_MUTATION_MINIMUM_DAMAGE && SPT_PROB(GORILLA_MUTATION_CHANCE_PER_SECOND, seconds_between_ticks))
var/mob/living/carbon/carbon_owner = owner
- carbon_owner.gorillize()
+ carbon_owner.gorillize(genetics_gorilla = TRUE)
qdel(src)
return
@@ -46,15 +46,20 @@
qdel(src)
return
-/datum/status_effect/genetic_damage/proc/on_healthscan(datum/source, list/render_list, advanced)
+/datum/status_effect/genetic_damage/proc/on_healthscan(datum/source, list/render_list, advanced, mob/user, mode, tochat)
SIGNAL_HANDLER
+ var/message = ""
if(advanced)
- render_list += "Genetic damage: [round(total_damage / minimum_before_tox_damage * 100, 0.1)]%\n"
+ message = "Genetic damage: [round(total_damage / minimum_before_tox_damage * 100, 0.1)]%"
else if(total_damage >= minimum_before_tox_damage)
- render_list += "Severe genetic damage detected.\n"
+ message = "Severe genetic damage detected."
else
- render_list += "Minor genetic damage detected.\n"
+ message = "Minor genetic damage detected."
+
+ if(message)
+ render_list += conditional_tooltip("[message]", "Irreparable under normal circumstances - will decay over time.", tochat)
+ render_list += " "
#undef GORILLA_MUTATION_CHANCE_PER_SECOND
#undef GORILLA_MUTATION_MINIMUM_DAMAGE
diff --git a/code/datums/status_effects/debuffs/hallucination.dm b/code/datums/status_effects/debuffs/hallucination.dm
index 5d67acc789ed3..0d8875c6b23dd 100644
--- a/code/datums/status_effects/debuffs/hallucination.dm
+++ b/code/datums/status_effects/debuffs/hallucination.dm
@@ -38,13 +38,13 @@
))
/// Signal proc for [COMSIG_LIVING_HEALTHSCAN]. Show we're hallucinating to (advanced) scanners.
-/datum/status_effect/hallucination/proc/on_health_scan(datum/source, list/render_list, advanced, mob/user, mode)
+/datum/status_effect/hallucination/proc/on_health_scan(datum/source, list/render_list, advanced, mob/user, mode, tochat)
SIGNAL_HANDLER
if(!advanced)
return
-
- render_list += "Subject is hallucinating.\n"
+ render_list += conditional_tooltip("Subject is hallucinating.", "Supply antipsychotic medication.", tochat)
+ render_list += " "
/// Signal proc for [COMSIG_CARBON_CHECKING_BODYPART],
/// checking bodyparts while hallucinating can cause them to appear more damaged than they are
diff --git a/code/datums/status_effects/debuffs/slime/slime_food.dm b/code/datums/status_effects/debuffs/slime/slime_food.dm
index aa711bb878f75..538e62e27c597 100644
--- a/code/datums/status_effects/debuffs/slime/slime_food.dm
+++ b/code/datums/status_effects/debuffs/slime/slime_food.dm
@@ -54,12 +54,3 @@
/datum/status_effect/slime_food/on_remove()
feeder = null
-
-/datum/status_effect/slime_food/update_particles()
- if(particle_effect)
- return
-
- particle_effect = new(owner, /particles/pollen)
-
- //particle coloured like the "pheromones" of the feeder
- particle_effect.particles.color = "[feeder.chat_color]a0"
diff --git a/code/datums/status_effects/debuffs/slime/slimed.dm b/code/datums/status_effects/debuffs/slime/slimed.dm
index 6c2c0fb5be342..2540c4df5136c 100644
--- a/code/datums/status_effects/debuffs/slime/slimed.dm
+++ b/code/datums/status_effects/debuffs/slime/slimed.dm
@@ -101,17 +101,6 @@
))
to_chat(owner, span_userdanger("[feedback_text] as the layer of slime eats away at you!"))
-/datum/status_effect/slimed/update_particles()
- if(particle_effect)
- return
-
- // taste the rainbow
- var/particle_type = rainbow ? /particles/slime/rainbow : /particles/slime
- particle_effect = new(owner, particle_type)
-
- if(!rainbow)
- particle_effect.particles.color = "[slime_color]a0"
-
/datum/status_effect/slimed/get_examine_text()
return span_warning("[owner.p_They()] [owner.p_are()] covered in bubbling slime!")
diff --git a/code/datums/status_effects/debuffs/staggered.dm b/code/datums/status_effects/debuffs/staggered.dm
index 1291da1307a61..88dd91c00e0d2 100644
--- a/code/datums/status_effects/debuffs/staggered.dm
+++ b/code/datums/status_effects/debuffs/staggered.dm
@@ -24,8 +24,6 @@
/datum/status_effect/staggered/on_remove()
UnregisterSignal(owner, COMSIG_LIVING_DEATH)
owner.remove_movespeed_modifier(/datum/movespeed_modifier/staggered)
- // Resetting both X on remove so we're back to normal
- animate(owner, pixel_x = owner.base_pixel_x, time = 0.2 SECONDS, flags = ANIMATION_PARALLEL)
/// Signal proc that self deletes our staggered effect
/datum/status_effect/staggered/proc/clear_staggered(datum/source)
@@ -45,11 +43,12 @@
/// Helper proc that causes the mob to do a stagger animation.
/// Doesn't change significantly, just meant to represent swaying back and forth
/mob/living/proc/do_stagger_animation()
- animate(src, pixel_x = 4, time = 0.2 SECONDS, flags = ANIMATION_RELATIVE|ANIMATION_PARALLEL)
- sleep(0.2 SECONDS)
- animate(src, pixel_x = -8, time = 0.2 SECONDS, flags = ANIMATION_RELATIVE|ANIMATION_PARALLEL)
- sleep(0.2 SECONDS)
- animate(src, pixel_x = 4, time = 0.2 SECONDS, flags = ANIMATION_RELATIVE|ANIMATION_PARALLEL)
+ var/normal_pos = base_pixel_x + body_position_pixel_x_offset
+ var/jitter_right = normal_pos + 4
+ var/jitter_left = normal_pos - 4
+ animate(src, pixel_x = jitter_left, 0.2 SECONDS, flags = ANIMATION_PARALLEL)
+ animate(pixel_x = jitter_right, time = 0.4 SECONDS)
+ animate(pixel_x = normal_pos, time = 0.2 SECONDS)
/// Status effect specifically for instances where someone is vulnerable to being stunned when shoved.
/datum/status_effect/next_shove_stuns
diff --git a/code/datums/status_effects/debuffs/terrified.dm b/code/datums/status_effects/debuffs/terrified.dm
index 6ed79372d01aa..61a6ecd4eda3b 100644
--- a/code/datums/status_effects/debuffs/terrified.dm
+++ b/code/datums/status_effects/debuffs/terrified.dm
@@ -55,7 +55,7 @@
owner.adjust_jitter_up_to(10 SECONDS * seconds_between_ticks, 10 SECONDS)
if(terror_buildup >= TERROR_PANIC_THRESHOLD) //If you reach this amount of buildup in an engagement, it's time to start looking for a way out.
- owner.playsound_local(get_turf(owner), 'sound/health/slowbeat.ogg', 40, 0, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE)
+ owner.playsound_local(get_turf(owner), 'sound/effects/health/slowbeat.ogg', 40, 0, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE)
owner.add_fov_trait(id, FOV_270_DEGREES) //Terror induced tunnel vision
owner.adjust_eye_blur_up_to(10 SECONDS * seconds_between_ticks, 10 SECONDS)
if(prob(5)) //We have a little panic attack. Consider it GENTLE ENCOURAGEMENT to start running away.
diff --git a/code/datums/status_effects/debuffs/tower_of_babel.dm b/code/datums/status_effects/debuffs/tower_of_babel.dm
index b3c1ae0c477c7..a56ea1ac6d9a9 100644
--- a/code/datums/status_effects/debuffs/tower_of_babel.dm
+++ b/code/datums/status_effects/debuffs/tower_of_babel.dm
@@ -41,7 +41,7 @@
return
owner.emote("mumble")
- owner.playsound_local(get_turf(owner), 'sound/magic/magic_block_mind.ogg', 75, vary = TRUE) // sound of creepy whispers
+ owner.playsound_local(get_turf(owner), 'sound/effects/magic/magic_block_mind.ogg', 75, vary = TRUE) // sound of creepy whispers
to_chat(owner, span_reallybig(span_hypnophrase("You feel a magical force affecting your speech patterns!")))
/datum/status_effect/tower_of_babel/magical/on_remove()
diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm
index 3d4bd7e93655c..3f4586d4d1ddd 100644
--- a/code/datums/status_effects/neutral.dm
+++ b/code/datums/status_effects/neutral.dm
@@ -119,7 +119,7 @@
/datum/status_effect/bounty/on_apply()
to_chat(owner, span_boldnotice("You hear something behind you talking... \"You have been marked for death by [rewarded]. If you die, they will be rewarded.\""))
- playsound(owner, 'sound/weapons/gun/shotgun/rack.ogg', 75, FALSE)
+ playsound(owner, 'sound/items/weapons/gun/shotgun/rack.ogg', 75, FALSE)
return ..()
/datum/status_effect/bounty/tick(seconds_between_ticks)
@@ -130,7 +130,7 @@
/datum/status_effect/bounty/proc/rewards()
if(rewarded && rewarded.mind && rewarded.stat != DEAD)
to_chat(owner, span_boldnotice("You hear something behind you talking... \"Bounty claimed.\""))
- playsound(owner, 'sound/weapons/gun/shotgun/shot.ogg', 75, FALSE)
+ playsound(owner, 'sound/items/weapons/gun/shotgun/shot.ogg', 75, FALSE)
to_chat(rewarded, span_greentext("You feel a surge of mana flow into you!"))
for(var/datum/action/cooldown/spell/spell in rewarded.actions)
spell.reset_spell_cooldown()
@@ -609,3 +609,32 @@
/datum/status_effect/gutted/proc/stop_gutting()
SIGNAL_HANDLER
qdel(src)
+
+/atom/movable/screen/alert/status_effect/shower_regen
+ name = "Washing"
+ desc = "A good wash fills me with energy!"
+ icon_state = "shower_regen"
+
+/atom/movable/screen/alert/status_effect/shower_regen/catgirl
+ name = "Washing"
+ desc = "Waaater... Fuck this WATER!!"
+ icon_state = "shower_regen_catgirl"
+
+/datum/status_effect/shower_regen
+ id = "shower_regen"
+ duration = -1
+ status_type = STATUS_EFFECT_UNIQUE
+ alert_type = /atom/movable/screen/alert/status_effect/shower_regen
+ /// How many heals from washing.
+ var/stamina_heal_per_tick = 4
+
+/datum/status_effect/shower_regen/on_apply()
+ . = ..()
+ if(isfelinid(owner))
+ alert_type = /atom/movable/screen/alert/status_effect/shower_regen/catgirl
+
+
+/datum/status_effect/shower_regen/tick(seconds_between_ticks)
+ . = ..()
+ var/heal_or_deal = isfelinid(owner) ? 1 : -1
+ owner.adjustStaminaLoss(stamina_heal_per_tick * heal_or_deal * seconds_between_ticks)
diff --git a/code/datums/status_effects/song_effects.dm b/code/datums/status_effects/song_effects.dm
index f61253c987d77..d846f47f169db 100644
--- a/code/datums/status_effects/song_effects.dm
+++ b/code/datums/status_effects/song_effects.dm
@@ -25,7 +25,7 @@
/datum/status_effect/song/antimagic/on_apply()
ADD_TRAIT(owner, TRAIT_ANTIMAGIC, MAGIC_TRAIT)
- playsound(owner, 'sound/weapons/fwoosh.ogg', 75, FALSE)
+ playsound(owner, 'sound/items/weapons/fwoosh.ogg', 75, FALSE)
return ..()
/datum/status_effect/song/antimagic/on_remove()
@@ -45,7 +45,7 @@
/datum/status_effect/song/light/on_apply()
mob_light_obj = owner.mob_light(3, 1.5, color = LIGHT_COLOR_DIM_YELLOW)
- playsound(owner, 'sound/weapons/fwoosh.ogg', 75, FALSE)
+ playsound(owner, 'sound/items/weapons/fwoosh.ogg', 75, FALSE)
return TRUE
/datum/status_effect/song/light/on_remove()
diff --git a/code/datums/status_effects/stacking_effect.dm b/code/datums/status_effects/stacking_effect.dm
index b54734155ad92..b0d00a92ba0c2 100644
--- a/code/datums/status_effects/stacking_effect.dm
+++ b/code/datums/status_effects/stacking_effect.dm
@@ -123,9 +123,9 @@
var/icon_height = I.Height()
status_overlay.pixel_x = -owner.pixel_x
status_overlay.pixel_y = FLOOR(icon_height * 0.25, 1)
- status_overlay.transform = matrix() * (icon_height/world.icon_size) //scale the status's overlay size based on the target's icon size
+ status_overlay.transform = matrix() * (icon_height/ICON_SIZE_Y) //scale the status's overlay size based on the target's icon size
status_underlay.pixel_x = -owner.pixel_x
- status_underlay.transform = matrix() * (icon_height/world.icon_size) * 3
+ status_underlay.transform = matrix() * (icon_height/ICON_SIZE_Y) * 3
status_underlay.alpha = 40
owner.add_overlay(status_overlay)
owner.underlays += status_underlay
diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm
index 8bdfa3f3de2a3..feda199d67071 100644
--- a/code/datums/storage/storage.dm
+++ b/code/datums/storage/storage.dm
@@ -189,20 +189,19 @@
/// Set the passed atom as the parent
/datum/storage/proc/set_parent(atom/new_parent)
- PRIVATE_PROC(TRUE)
+ PROTECTED_PROC(TRUE)
ASSERT(isnull(parent))
parent = new_parent
+ ADD_TRAIT(parent, TRAIT_COMBAT_MODE_SKIP_INTERACTION, REF(src))
// a few of theses should probably be on the real_location rather than the parent
- RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_interact))
RegisterSignals(parent, list(COMSIG_ATOM_ATTACK_PAW, COMSIG_ATOM_ATTACK_HAND), PROC_REF(on_attack))
RegisterSignal(parent, COMSIG_MOUSEDROP_ONTO, PROC_REF(on_mousedrop_onto))
RegisterSignal(parent, COMSIG_MOUSEDROPPED_ONTO, PROC_REF(on_mousedropped_onto))
RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK, PROC_REF(on_preattack))
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(mass_empty))
RegisterSignals(parent, list(COMSIG_ATOM_ATTACK_GHOST, COMSIG_ATOM_ATTACK_HAND_SECONDARY), PROC_REF(open_storage_on_signal))
- RegisterSignal(parent, COMSIG_ATOM_ITEM_INTERACTION_SECONDARY, PROC_REF(on_item_interact_secondary))
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(close_distance))
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(update_actions))
RegisterSignal(parent, COMSIG_TOPIC, PROC_REF(topic_handle))
@@ -827,26 +826,12 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
return
if(!iscarbon(user) && !isdrone(user))
return
- var/mob/living/user_living = user
- if(user_living.incapacitated)
- return
-
attempt_insert(dropping, user)
return COMPONENT_CANCEL_MOUSEDROPPED_ONTO
-/// Signal handler for whenever we're attacked by an object.
-/datum/storage/proc/on_item_interact(datum/source, mob/user, obj/item/thing, params)
- SIGNAL_HANDLER
-
- if(!insert_on_attack)
- return NONE
- if(!thing.storage_insert_on_interaction(src, parent, user))
- return NONE
- if(!parent.storage_insert_on_interacted_with(src, thing, user))
- return NONE
- if(SEND_SIGNAL(parent, COMSIG_ATOM_STORAGE_ITEM_INTERACT_INSERT, thing, user) & BLOCK_STORAGE_INSERT)
- return NONE
-
+/// Called directly from the attack chain if [insert_on_attack] is TRUE.
+/// Handles inserting an item into the storage when clicked.
+/datum/storage/proc/item_interact_insert(mob/living/user, obj/item/thing)
if(iscyborg(user))
return ITEM_INTERACT_BLOCKING
@@ -897,18 +882,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
return toreturn
-/// Signal handler for when we get attacked with secondary click by an item.
-/datum/storage/proc/on_item_interact_secondary(datum/source, mob/user, atom/weapon)
- SIGNAL_HANDLER
-
- if(istype(weapon, /obj/item/chameleon))
- var/obj/item/chameleon/chameleon_weapon = weapon
- chameleon_weapon.make_copy(source, user)
-
- if(open_storage_on_signal(source, user))
- return ITEM_INTERACT_BLOCKING
- return NONE
-
/// Signal handler to open up the storage when we receive a signal.
/datum/storage/proc/open_storage_on_signal(datum/source, mob/to_show)
SIGNAL_HANDLER
@@ -1001,7 +974,7 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
if(user.active_storage == src && user.client)
seeing += user
else
- is_using -= user
+ hide_contents(user)
return seeing
/**
@@ -1059,8 +1032,6 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
* * mob/to_hide - the mob to hide the storage from
*/
/datum/storage/proc/hide_contents(mob/to_hide)
- if(!to_hide.client)
- return TRUE
if(to_hide.active_storage == src)
to_hide.active_storage = null
@@ -1073,8 +1044,9 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
is_using -= to_hide
- to_hide.client.screen -= storage_interfaces[to_hide].list_ui_elements()
- to_hide.client.screen -= real_location.contents
+ if(to_hide.client)
+ to_hide.client.screen -= storage_interfaces[to_hide].list_ui_elements()
+ to_hide.client.screen -= real_location.contents
QDEL_NULL(storage_interfaces[to_hide])
storage_interfaces -= to_hide
@@ -1105,7 +1077,9 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches)
var/columns = clamp(max_slots, 1, screen_max_columns)
var/rows = clamp(CEILING(adjusted_contents / columns, 1) + additional_row, 1, screen_max_rows)
- for (var/ui_user in storage_interfaces)
+ for (var/mob/ui_user as anything in storage_interfaces)
+ if (isnull(storage_interfaces[ui_user]))
+ continue
storage_interfaces[ui_user].update_position(screen_start_x, screen_pixel_x, screen_start_y, screen_pixel_y, columns, rows)
var/current_x = screen_start_x
diff --git a/code/datums/view.dm b/code/datums/view.dm
index 90d07c667967c..702550a4e1874 100644
--- a/code/datums/view.dm
+++ b/code/datums/view.dm
@@ -134,7 +134,7 @@
_y = -offset
if(WEST)
_x = -offset
- animate(chief, pixel_x = world.icon_size*_x, pixel_y = world.icon_size*_y, 0, FALSE, LINEAR_EASING, ANIMATION_END_NOW)
+ animate(chief, pixel_x = ICON_SIZE_X*_x, pixel_y = ICON_SIZE_Y*_y, 0, FALSE, LINEAR_EASING, ANIMATION_END_NOW)
//Ready for this one?
setTo(radius)
diff --git a/code/datums/votes/map_vote.dm b/code/datums/votes/map_vote.dm
index abe452ce4fedf..b4f938a42e451 100644
--- a/code/datums/votes/map_vote.dm
+++ b/code/datums/votes/map_vote.dm
@@ -2,29 +2,18 @@
name = "Map"
default_message = "Vote for next round's map!"
count_method = VOTE_COUNT_METHOD_SINGLE
- winner_method = VOTE_WINNER_METHOD_WEIGHTED_RANDOM
+ winner_method = VOTE_WINNER_METHOD_NONE
display_statistics = FALSE
/datum/vote/map_vote/New()
. = ..()
-
- default_choices = list()
-
- // Fill in our default choices with all of the maps in our map config, if they are votable and not blocked.
- var/list/maps = shuffle(global.config.maplist)
- for(var/map in maps)
- var/datum/map_config/possible_config = config.maplist[map]
- if(!possible_config.votable || (possible_config.map_name in SSpersistence.blocked_maps))
- continue
-
- default_choices += possible_config.map_name
+ default_choices = SSmap_vote.get_valid_map_vote_choices()
/datum/vote/map_vote/create_vote()
. = ..()
if(!.)
return FALSE
- choices -= get_choices_invalid_for_population()
if(length(choices) == 1) // Only one choice, no need to vote. Let's just auto-rotate it to the only remaining map because it would just happen anyways.
var/datum/map_config/change_me_out = global.config.maplist[choices[1]]
finalize_vote(choices[1])// voted by not voting, very sad.
@@ -48,35 +37,16 @@
. = ..()
if(. != VOTE_AVAILABLE)
return .
- if(forced)
- return VOTE_AVAILABLE
- var/num_choices = length(default_choices - get_choices_invalid_for_population())
+
+ var/num_choices = length(default_choices)
if(num_choices <= 1)
return "There [num_choices == 1 ? "is only one map" : "are no maps"] to choose from."
- if(SSmapping.map_vote_rocked)
- return VOTE_AVAILABLE
- if(SSmapping.map_voted)
+ if(SSmap_vote.next_map_config)
return "The next map has already been selected."
return VOTE_AVAILABLE
-/// Returns a list of all map options that are invalid for the current population.
-/datum/vote/map_vote/proc/get_choices_invalid_for_population()
- var/filter_threshold = 0
- if(SSticker.HasRoundStarted())
- filter_threshold = get_active_player_count(alive_check = FALSE, afk_check = TRUE, human_check = FALSE)
- else
- filter_threshold = GLOB.clients.len
-
- var/list/invalid_choices = list()
- for(var/map in default_choices)
- var/datum/map_config/possible_config = config.maplist[map]
- if(possible_config.config_min_users > 0 && filter_threshold < possible_config.config_min_users)
- invalid_choices += map
-
- else if(possible_config.config_max_users > 0 && filter_threshold > possible_config.config_max_users)
- invalid_choices += map
-
- return invalid_choices
+/datum/vote/map_vote/get_result_text(list/all_winners, real_winner, list/non_voters)
+ return null
/datum/vote/map_vote/get_vote_result(list/non_voters)
// Even if we have default no vote off,
@@ -97,20 +67,4 @@
return ..()
/datum/vote/map_vote/finalize_vote(winning_option)
- var/datum/map_config/winning_map = global.config.maplist[winning_option]
- if(!istype(winning_map))
- CRASH("[type] wasn't passed a valid winning map choice. (Got: [winning_option || "null"] - [winning_map || "null"])")
-
- SSmapping.changemap(winning_map)
- SSmapping.map_voted = TRUE
- if(SSmapping.map_vote_rocked)
- SSmapping.map_vote_rocked = FALSE
-
-/proc/revert_map_vote()
- var/datum/map_config/override_map = SSmapping.config
- if(isnull(override_map))
- return
-
- SSmapping.changemap(override_map)
- log_game("The next map has been reset to [override_map.map_name].")
- send_to_playing_players(span_boldannounce("The next map is: [override_map.map_name]."))
+ SSmap_vote.finalize_map_vote(src)
diff --git a/code/datums/votes/restart_vote.dm b/code/datums/votes/restart_vote.dm
index 3c74d7e518e28..ba0fdf78083b1 100644
--- a/code/datums/votes/restart_vote.dm
+++ b/code/datums/votes/restart_vote.dm
@@ -57,10 +57,10 @@
return
// If there was a previous map vote, we revert the change.
- if(!isnull(SSmapping.next_map_config))
+ if(!isnull(SSmap_vote.next_map_config))
log_game("The next map has been reset due to successful restart vote.")
send_to_playing_players(span_boldannounce("The next map has been reset due to successful restart vote."))
- revert_map_vote()
+ SSmap_vote.revert_next_map()
SSticker.force_ending = FORCE_END_ROUND
log_game("End round forced by successful restart vote.")
diff --git a/code/datums/votes/rock_the_vote.dm b/code/datums/votes/rock_the_vote.dm
deleted file mode 100644
index 6c7ac4ff2572e..0000000000000
--- a/code/datums/votes/rock_the_vote.dm
+++ /dev/null
@@ -1,62 +0,0 @@
-#define CHOICE_TO_ROCK "Yes, re-do the map vote."
-#define CHOICE_NOT_TO_ROCK "No, keep the currently selected map."
-
-/// If a map vote is called before the emergency shuttle leaves the station, the players can call another vote to re-run the vote on the shuttle leaving.
-/datum/vote/rock_the_vote
- name = "Rock the Vote"
- override_question = "Rock the Vote?"
- contains_vote_in_name = TRUE //lol
- default_choices = list(
- CHOICE_TO_ROCK,
- CHOICE_NOT_TO_ROCK,
- )
- default_message = "Override the current map vote."
- /// The number of times we have rocked the vote thus far.
- var/rocking_votes = 0
-
-/datum/vote/rock_the_vote/toggle_votable()
- CONFIG_SET(flag/allow_rock_the_vote, !CONFIG_GET(flag/allow_rock_the_vote))
-
-/datum/vote/rock_the_vote/is_config_enabled()
- return CONFIG_GET(flag/allow_rock_the_vote)
-
-/datum/vote/rock_the_vote/can_be_initiated(forced)
- . = ..()
- if(. != VOTE_AVAILABLE)
- return .
-
- if(SSticker.current_state == GAME_STATE_FINISHED)
- return "The game is finished, no map votes can be initiated."
-
- if(rocking_votes >= CONFIG_GET(number/max_rocking_votes))
- return "The maximum number of times to rock the vote has been reached."
-
- if(SSmapping.map_vote_rocked)
- return "The vote has already been rocked! Initiate a map vote!"
-
- if(!SSmapping.map_voted)
- return "Rocking the vote is disabled because no map has been voted on yet!"
-
- if(SSmapping.map_force_chosen)
- return "Rocking the vote is disabled because an admin has forcibly set the map!"
-
- if(EMERGENCY_ESCAPED_OR_ENDGAMED && SSmapping.map_voted)
- return "The emergency shuttle has already left the station and the next map has already been chosen!"
-
- return VOTE_AVAILABLE
-
-/datum/vote/rock_the_vote/finalize_vote(winning_option)
- rocking_votes++
- if(winning_option == CHOICE_NOT_TO_ROCK)
- return
-
- if(winning_option == CHOICE_TO_ROCK)
- to_chat(world, span_boldannounce("The vote has been rocked! Players are now able to re-run the map vote once more."))
- message_admins("The players have successfully rocked the vote.")
- SSmapping.map_vote_rocked = TRUE
- return
-
- CRASH("[type] wasn't passed a valid winning choice. (Got: [winning_option || "null"])")
-
-#undef CHOICE_TO_ROCK
-#undef CHOICE_NOT_TO_ROCK
diff --git a/code/datums/weather/weather.dm b/code/datums/weather/weather.dm
index 16ffb326f8a86..85e5e74b02fba 100644
--- a/code/datums/weather/weather.dm
+++ b/code/datums/weather/weather.dm
@@ -13,7 +13,7 @@
/// description of weather
var/desc = "Heavy gusts of wind blanket the area, periodically knocking down anyone caught in the open."
/// The message displayed in chat to foreshadow the weather's beginning
- var/telegraph_message = "The wind begins to pick up."
+ var/telegraph_message = span_warning("The wind begins to pick up.")
/// In deciseconds, how long from the beginning of the telegraph until the weather begins
var/telegraph_duration = 300
/// The sound file played to everyone on an affected z-level
@@ -22,7 +22,7 @@
var/telegraph_overlay
/// Displayed in chat once the weather begins in earnest
- var/weather_message = "The wind begins to blow ferociously!"
+ var/weather_message = span_userdanger("The wind begins to blow ferociously!")
/// In deciseconds, how long the weather lasts once it begins
var/weather_duration = 1200
/// See above - this is the lowest possible duration
@@ -37,7 +37,7 @@
var/weather_color = null
/// Displayed once the weather is over
- var/end_message = "The wind relents its assault."
+ var/end_message = span_danger("The wind relents its assault.")
/// In deciseconds, how long the "wind-down" graphic will appear before vanishing entirely
var/end_duration = 300
/// Sound that plays while weather is ending
@@ -59,7 +59,7 @@
/// Since it's above everything else, this is the layer used by default.
var/overlay_layer = AREA_LAYER
/// Plane for the overlay
- var/overlay_plane = AREA_PLANE
+ var/overlay_plane = WEATHER_PLANE
/// If the weather has no purpose other than looks
var/aesthetic = FALSE
/// Used by mobs (or movables containing mobs, such as enviro bags) to prevent them from being affected by the weather.
@@ -99,7 +99,7 @@
/datum/weather/proc/telegraph()
if(stage == STARTUP_STAGE)
return
- SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_TELEGRAPH(type))
+ SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_TELEGRAPH(type), src)
stage = STARTUP_STAGE
var/list/affectareas = list()
for(var/V in get_areas(area_type))
@@ -129,14 +129,14 @@
/datum/weather/proc/start()
if(stage >= MAIN_STAGE)
return
- SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_START(type))
+ SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_START(type), src)
stage = MAIN_STAGE
update_areas()
send_alert(weather_message, weather_sound)
if(!perpetual)
addtimer(CALLBACK(src, PROC_REF(wind_down)), weather_duration)
for(var/area/impacted_area as anything in impacted_areas)
- SEND_SIGNAL(impacted_area, COMSIG_WEATHER_BEGAN_IN_AREA(type))
+ SEND_SIGNAL(impacted_area, COMSIG_WEATHER_BEGAN_IN_AREA(type), src)
/**
* Weather enters the winding down phase, stops effects
@@ -148,7 +148,7 @@
/datum/weather/proc/wind_down()
if(stage >= WIND_DOWN_STAGE)
return
- SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_WINDDOWN(type))
+ SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_WINDDOWN(type), src)
stage = WIND_DOWN_STAGE
update_areas()
send_alert(end_message, end_sound)
@@ -164,12 +164,12 @@
/datum/weather/proc/end()
if(stage == END_STAGE)
return
- SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_END(type))
+ SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_END(type), src)
stage = END_STAGE
SSweather.processing -= src
update_areas()
for(var/area/impacted_area as anything in impacted_areas)
- SEND_SIGNAL(impacted_area, COMSIG_WEATHER_ENDED_IN_AREA(type))
+ SEND_SIGNAL(impacted_area, COMSIG_WEATHER_ENDED_IN_AREA(type), src)
// handles sending all alerts
/datum/weather/proc/send_alert(alert_msg, alert_sfx)
@@ -260,12 +260,12 @@
// I prefer it to creating 2 extra plane masters however, so it's a cost I'm willing to pay
// LU
if(use_glow)
- var/mutable_appearance/glow_overlay = mutable_appearance('icons/effects/glow_weather.dmi', weather_state, overlay_layer, null, ABOVE_LIGHTING_PLANE, 100, offset_const = offset)
+ var/mutable_appearance/glow_overlay = mutable_appearance('icons/effects/glow_weather.dmi', weather_state, overlay_layer, null, WEATHER_GLOW_PLANE, 100, offset_const = offset)
glow_overlay.color = weather_color
gen_overlay_cache += glow_overlay
- var/mutable_appearance/weather_overlay = mutable_appearance('icons/effects/weather_effects.dmi', weather_state, overlay_layer, plane = overlay_plane, offset_const = offset)
- weather_overlay.color = weather_color
- gen_overlay_cache += weather_overlay
+ var/mutable_appearance/new_weather_overlay = mutable_appearance('icons/effects/weather_effects.dmi', weather_state, overlay_layer, plane = overlay_plane, offset_const = offset)
+ new_weather_overlay.color = weather_color
+ gen_overlay_cache += new_weather_overlay
return gen_overlay_cache
diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm
index bb4e5af63f3ad..7d432c1e488da 100644
--- a/code/datums/weather/weather_types/ash_storm.dm
+++ b/code/datums/weather/weather_types/ash_storm.dm
@@ -3,16 +3,16 @@
name = "ash storm"
desc = "An intense atmospheric storm lifts ash off of the planet's surface and billows it down across the area, dealing intense fire damage to the unprotected."
- telegraph_message = "An eerie moan rises on the wind. Sheets of burning ash blacken the horizon. Seek shelter."
+ telegraph_message = span_boldwarning("An eerie moan rises on the wind. Sheets of burning ash blacken the horizon. Seek shelter.")
telegraph_duration = 300
telegraph_overlay = "light_ash"
- weather_message = "Smoldering clouds of scorching ash billow down around you! Get inside!"
+ weather_message = span_userdanger("Smoldering clouds of scorching ash billow down around you! Get inside!")
weather_duration_lower = 600
weather_duration_upper = 1200
weather_overlay = "ash_storm"
- end_message = "The shrieking wind whips away the last of the ash and falls to its usual murmur. It should be safe to go outside now."
+ end_message = span_boldannounce("The shrieking wind whips away the last of the ash and falls to its usual murmur. It should be safe to go outside now.")
end_duration = 300
end_overlay = "light_ash"
@@ -81,10 +81,10 @@
name = "emberfall"
desc = "A passing ash storm blankets the area in harmless embers."
- weather_message = "Gentle embers waft down around you like grotesque snow. The storm seems to have passed you by..."
+ weather_message = span_notice("Gentle embers waft down around you like grotesque snow. The storm seems to have passed you by...")
weather_overlay = "light_ash"
- end_message = "The emberfall slows, stops. Another layer of hardened soot to the basalt beneath your feet."
+ end_message = span_notice("The emberfall slows, stops. Another layer of hardened soot to the basalt beneath your feet.")
end_sound = null
aesthetic = TRUE
diff --git a/code/datums/weather/weather_types/floor_is_lava.dm b/code/datums/weather/weather_types/floor_is_lava.dm
index 03ed0c68c311a..25037d433b5eb 100644
--- a/code/datums/weather/weather_types/floor_is_lava.dm
+++ b/code/datums/weather/weather_types/floor_is_lava.dm
@@ -3,15 +3,15 @@
name = "the floor is lava"
desc = "The ground turns into surprisingly cool lava, lightly damaging anything on the floor."
- telegraph_message = "You feel the ground beneath you getting hot. Waves of heat distort the air."
+ telegraph_message = span_warning("You feel the ground beneath you getting hot. Waves of heat distort the air.")
telegraph_duration = 150
- weather_message = "The floor is lava! Get on top of something!"
+ weather_message = span_userdanger("The floor is lava! Get on top of something!")
weather_duration_lower = 300
weather_duration_upper = 600
weather_overlay = "lava"
- end_message = "The ground cools and returns to its usual form."
+ end_message = span_danger("The ground cools and returns to its usual form.")
end_duration = 0
area_type = /area
diff --git a/code/datums/weather/weather_types/radiation_storm.dm b/code/datums/weather/weather_types/radiation_storm.dm
index 8a1cfff765733..8acf8be4b9e66 100644
--- a/code/datums/weather/weather_types/radiation_storm.dm
+++ b/code/datums/weather/weather_types/radiation_storm.dm
@@ -4,17 +4,17 @@
desc = "A cloud of intense radiation passes through the area dealing rad damage to those who are unprotected."
telegraph_duration = 400
- telegraph_message = "The air begins to grow warm."
+ telegraph_message = span_danger("The air begins to grow warm.")
- weather_message = "You feel waves of heat wash over you! Find shelter!"
+ weather_message = span_userdanger("You feel waves of heat wash over you! Find shelter!")
weather_overlay = "ash_storm"
weather_duration_lower = 600
weather_duration_upper = 1500
weather_color = "green"
- weather_sound = 'sound/misc/bloblarm.ogg'
+ weather_sound = 'sound/announcer/alarm/bloblarm.ogg'
end_duration = 100
- end_message = "The air seems to be cooling off again."
+ end_message = span_notice("The air seems to be cooling off again.")
area_type = /area
protected_areas = list(/area/station/maintenance, /area/station/ai_monitored/turret_protected/ai_upload, /area/station/ai_monitored/turret_protected/ai_upload_foyer,
@@ -34,28 +34,28 @@
status_alarm(TRUE)
-/datum/weather/rad_storm/weather_act(mob/living/L)
+/datum/weather/rad_storm/weather_act(mob/living/living)
if(!prob(mutate_chance))
return
- if(!ishuman(L))
+ if(!ishuman(living) || HAS_TRAIT(living, TRAIT_GODMODE))
return
- var/mob/living/carbon/human/H = L
- if(!H.can_mutate() || H.status_flags & GODMODE)
+ var/mob/living/carbon/human/human = living
+ if(!human.can_mutate())
return
- if(HAS_TRAIT(H, TRAIT_RADIMMUNE))
+ if(HAS_TRAIT(human, TRAIT_RADIMMUNE))
return
- if (SSradiation.wearing_rad_protected_clothing(H))
+ if (SSradiation.wearing_rad_protected_clothing(human))
return
- H.random_mutate_unique_identity()
- H.random_mutate_unique_features()
+ human.random_mutate_unique_identity()
+ human.random_mutate_unique_features()
if(prob(50))
- do_mutate(L)
+ do_mutate(human)
/datum/weather/rad_storm/end()
if(..())
diff --git a/code/datums/weather/weather_types/snow_storm.dm b/code/datums/weather/weather_types/snow_storm.dm
index c98ee9636a7aa..2b749cdbc84d1 100644
--- a/code/datums/weather/weather_types/snow_storm.dm
+++ b/code/datums/weather/weather_types/snow_storm.dm
@@ -3,18 +3,18 @@
desc = "Harsh snowstorms roam the topside of this arctic planet, burying any area unfortunate enough to be in its path."
probability = 90
- telegraph_message = "Drifting particles of snow begin to dust the surrounding area.."
+ telegraph_message = span_warning("Drifting particles of snow begin to dust the surrounding area..")
telegraph_duration = 300
telegraph_overlay = "light_snow"
- weather_message = "Harsh winds pick up as dense snow begins to fall from the sky! Seek shelter!"
+ weather_message = span_userdanger("Harsh winds pick up as dense snow begins to fall from the sky! Seek shelter!")
weather_overlay = "snow_storm"
weather_duration_lower = 600
weather_duration_upper = 1500
use_glow = FALSE
end_duration = 100
- end_message = "The snowfall dies down, it should be safe to go outside again."
+ end_message = span_boldannounce("The snowfall dies down, it should be safe to go outside again.")
area_type = /area
protect_indoors = TRUE
diff --git a/code/datums/weather/weather_types/void_storm.dm b/code/datums/weather/weather_types/void_storm.dm
index 90cc7d44cfbe1..617e3ff0230fd 100644
--- a/code/datums/weather/weather_types/void_storm.dm
+++ b/code/datums/weather/weather_types/void_storm.dm
@@ -6,7 +6,7 @@
telegraph_overlay = "light_snow"
weather_message = span_hypnophrase("You feel the air around you getting colder... and void's sweet embrace...")
- weather_overlay = "snow_storm"
+ weather_overlay = "light_snow"
weather_color = COLOR_BLACK
weather_duration_lower = 60 SECONDS
weather_duration_upper = 120 SECONDS
@@ -19,31 +19,5 @@
protect_indoors = FALSE
target_trait = ZTRAIT_VOIDSTORM
- immunity_type = TRAIT_VOIDSTORM_IMMUNE
-
barometer_predictable = FALSE
perpetual = TRUE
-
- /// List of areas that were once impacted areas but are not anymore. Used for updating the weather overlay based whether the ascended heretic is in the area.
- var/list/former_impacted_areas = list()
-
-/datum/weather/void_storm/can_weather_act(mob/living/mob_to_check)
- . = ..()
- if(IS_HERETIC_OR_MONSTER(mob_to_check))
- return FALSE
-
-/datum/weather/void_storm/weather_act(mob/living/victim)
- var/need_mob_update = FALSE
- need_mob_update += victim.adjustFireLoss(1, updating_health = FALSE)
- need_mob_update += victim.adjustOxyLoss(rand(1, 3), updating_health = FALSE)
- if(need_mob_update)
- victim.updatehealth()
- victim.adjust_eye_blur(rand(0 SECONDS, 2 SECONDS))
- victim.adjust_bodytemperature(-30 * TEMPERATURE_DAMAGE_COEFFICIENT)
-
-// Goes through former_impacted_areas and sets the overlay of each back to the telegraph overlay, to indicate the ascended heretic is no longer in that area.
-/datum/weather/void_storm/update_areas()
- for(var/area/former_area as anything in former_impacted_areas)
- former_area.icon_state = telegraph_overlay
- former_impacted_areas -= former_area
- return ..()
diff --git a/code/datums/wires/syndicatebomb.dm b/code/datums/wires/syndicatebomb.dm
index fa939d5b5607c..d7f98f07debd9 100644
--- a/code/datums/wires/syndicatebomb.dm
+++ b/code/datums/wires/syndicatebomb.dm
@@ -48,7 +48,7 @@
if(WIRE_PROCEED)
holder.visible_message(span_danger("[icon2html(B, viewers(holder))] The bomb buzzes ominously!"))
- playsound(B, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(B, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
var/seconds = B.seconds_remaining()
if(seconds >= 61) // Long fuse bombs can suddenly become more dangerous if you tinker with them.
B.detonation_timer = world.time + 600
diff --git a/code/datums/world_topic.dm b/code/datums/world_topic.dm
index f2bf896bb10b3..a9971f6068c98 100644
--- a/code/datums/world_topic.dm
+++ b/code/datums/world_topic.dm
@@ -212,7 +212,7 @@
.["admins"] = presentmins.len + afkmins.len //equivalent to the info gotten from adminwho
.["gamestate"] = SSticker.current_state
- .["map_name"] = SSmapping.config?.map_name || "Loading..."
+ .["map_name"] = SSmapping.current_map.map_name || "Loading..."
if(key_valid)
.["active_players"] = get_active_player_count()
diff --git a/code/datums/wounds/_wound_static_data.dm b/code/datums/wounds/_wound_static_data.dm
index f8b03d1856b5d..adc0923ee4f0a 100644
--- a/code/datums/wounds/_wound_static_data.dm
+++ b/code/datums/wounds/_wound_static_data.dm
@@ -95,7 +95,7 @@
if (random_roll && !can_be_randomly_generated)
return FALSE
- if (HAS_TRAIT(limb.owner, TRAIT_NEVER_WOUNDED) || (limb.owner.status_flags & GODMODE))
+ if (HAS_TRAIT(limb.owner, TRAIT_NEVER_WOUNDED) || HAS_TRAIT(limb.owner, TRAIT_GODMODE))
return FALSE
if (!wounding_types_valid(suggested_wounding_types))
diff --git a/code/datums/wounds/_wounds.dm b/code/datums/wounds/_wounds.dm
index eb531dd3dc92b..5e5258c86deb9 100644
--- a/code/datums/wounds/_wounds.dm
+++ b/code/datums/wounds/_wounds.dm
@@ -23,6 +23,8 @@
var/desc = ""
/// The basic treatment suggested by health analyzers
var/treat_text = ""
+ /// Even more basic treatment
+ var/treat_text_short = ""
/// What the limb looks like on a cursory examine
var/examine_desc = "is badly hurt"
@@ -643,22 +645,42 @@
return span_bold("[desc]!")
return "[desc]."
+/**
+ * Prints the details about the wound for the wound scanner on simple mode
+ */
/datum/wound/proc/get_scanner_description(mob/user)
- return "Type: [name]\nSeverity: [severity_text(simple = FALSE)]\nDescription: [desc]\nRecommended Treatment: [treat_text]"
+ return "Type: [name] \
+ Severity: [severity_text()] \
+ Description: [desc] \
+ Recommended Treatment: [treat_text]"
+/**
+ * Prints the details about the wound for the wound scanner on complex mode
+ */
/datum/wound/proc/get_simple_scanner_description(mob/user)
- return "[name] detected!\nRisk: [severity_text(simple = TRUE)]\nDescription: [simple_desc ? simple_desc : desc]\nTreatment Guide: [simple_treat_text]\nHomemade Remedies: [homemade_treat_text]"
+ var/severity_text_formatted = severity_text()
+ for(var/i in 1 to severity)
+ severity_text_formatted += "!"
-/datum/wound/proc/severity_text(simple = FALSE)
+ return "[name] detected! \
+ Risk: [severity_text_formatted] \
+ Description: [simple_desc || desc] \
+ Treatment Guide: [simple_treat_text] \
+ Homemade Remedies: [homemade_treat_text]"
+
+/**
+ * Returns what text describes this wound
+ */
+/datum/wound/proc/severity_text()
switch(severity)
if(WOUND_SEVERITY_TRIVIAL)
return "Trivial"
if(WOUND_SEVERITY_MODERATE)
- return "Moderate" + (simple ? "!" : "")
+ return "Moderate"
if(WOUND_SEVERITY_SEVERE)
- return "Severe" + (simple ? "!!" : "")
+ return "Severe"
if(WOUND_SEVERITY_CRITICAL)
- return "Critical" + (simple ? "!!!" : "")
+ return "Critical"
/// Returns TRUE if our limb is the head or chest, FALSE otherwise.
/// Essential in the sense of "we cannot live without it".
@@ -694,7 +716,7 @@
var/datum/wound_pregen_data/pregen_data = get_pregen_data()
- if (WOUND_BLUNT in pregen_data.required_wounding_types && severity >= WOUND_SEVERITY_CRITICAL)
+ if ((WOUND_BLUNT in pregen_data.required_wounding_types) && severity >= WOUND_SEVERITY_CRITICAL)
return WOUND_CRITICAL_BLUNT_DISMEMBER_BONUS // we only require mangled bone (T2 blunt), but if there's a critical blunt, we'll add 15% more
/// Returns our pregen data, which is practically guaranteed to exist, so this proc can safely be used raw.
diff --git a/code/datums/wounds/bones.dm b/code/datums/wounds/bones.dm
index 43385b47180ae..667684c0f9f15 100644
--- a/code/datums/wounds/bones.dm
+++ b/code/datums/wounds/bones.dm
@@ -147,14 +147,26 @@
if(1 to 6)
victim.bleed(blood_bled, TRUE)
if(7 to 13)
- victim.visible_message("A thin stream of blood drips from [victim]'s mouth from the blow to [victim.p_their()] chest.", span_danger("You cough up a bit of blood from the blow to your chest."), vision_distance=COMBAT_MESSAGE_RANGE)
+ victim.visible_message(
+ span_smalldanger("A thin stream of blood drips from [victim]'s mouth from the blow to [victim.p_their()] chest."),
+ span_danger("You cough up a bit of blood from the blow to your chest."),
+ vision_distance = COMBAT_MESSAGE_RANGE,
+ )
victim.bleed(blood_bled, TRUE)
if(14 to 19)
- victim.visible_message("Blood spews out of [victim]'s mouth from the blow to [victim.p_their()] chest!", span_danger("You spit out a string of blood from the blow to your chest!"), vision_distance=COMBAT_MESSAGE_RANGE)
+ victim.visible_message(
+ span_smalldanger("Blood spews out of [victim]'s mouth from the blow to [victim.p_their()] chest!"),
+ span_danger("You spit out a string of blood from the blow to your chest!"),
+ vision_distance = COMBAT_MESSAGE_RANGE,
+ )
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
victim.bleed(blood_bled)
if(20 to INFINITY)
- victim.visible_message(span_danger("Blood spurts out of [victim]'s mouth from the blow to [victim.p_their()] chest!"), span_danger("You choke up on a spray of blood from the blow to your chest!"), vision_distance=COMBAT_MESSAGE_RANGE)
+ victim.visible_message(
+ span_danger("Blood spurts out of [victim]'s mouth from the blow to [victim.p_their()] chest!"),
+ span_bolddanger("You choke up on a spray of blood from the blow to your chest!"),
+ vision_distance = COMBAT_MESSAGE_RANGE,
+ )
victim.bleed(blood_bled)
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
victim.add_splatter_floor(get_step(victim.loc, victim.dir))
@@ -187,7 +199,9 @@
/datum/wound/blunt/bone/moderate
name = "Joint Dislocation"
desc = "Patient's limb has been unset from socket, causing pain and reduced motor function."
- treat_text = "Recommended application of bonesetter to affected limb, though manual relocation by applying an aggressive grab to the patient and helpfully interacting with afflicted limb may suffice."
+ treat_text = "Apply Bonesetter to the affected limb. \
+ Manual relocation by via an aggressive grab and a tight hug to the affected limb may also suffice."
+ treat_text_short = "Apply Bonesetter, or manually relocate the limb."
examine_desc = "is awkwardly janked out of place"
occur_text = "janks violently and becomes unseated"
severity = WOUND_SEVERITY_MODERATE
@@ -322,7 +336,9 @@
/datum/wound/blunt/bone/severe
name = "Hairline Fracture"
desc = "Patient's bone has suffered a crack in the foundation, causing serious pain and reduced limb functionality."
- treat_text = "Recommended light surgical application of bone gel, though a sling of medical gauze will prevent worsening situation."
+ treat_text = "Repair surgically. In the event of an emergency, an application of bone gel over the affected area will fix over time. \
+ A splint or sling of medical gauze can also be used to prevent the fracture from worsening."
+ treat_text_short = "Repair surgically, or apply bone gel. A splint or gauze sling can also be used."
examine_desc = "appears grotesquely swollen, jagged bumps hinting at chips in the bone"
occur_text = "sprays chips of bone and develops a nasty looking bruise"
@@ -355,8 +371,11 @@
/// Compound Fracture (Critical Blunt)
/datum/wound/blunt/bone/critical
name = "Compound Fracture"
- desc = "Patient's bones have suffered multiple gruesome fractures, causing significant pain and near uselessness of limb."
- treat_text = "Immediate binding of affected limb, followed by surgical intervention ASAP."
+ desc = "Patient's bones have suffered multiple fractures, \
+ couped with a break in the skin, causing significant pain and near uselessness of limb."
+ treat_text = "Immediately bind the affected limb with gauze or a splint. Repair surgically. \
+ In the event of an emergency, bone gel and surgical tape can be applied to the affected area to fix over a long period of time."
+ treat_text_short = "Repair surgically, or apply bone gel and surgical tape. A splint or gauze sling should also be used."
examine_desc = "is thoroughly pulped and cracked, exposing shards of bone to open air"
occur_text = "cracks apart, exposing broken bones to open air"
diff --git a/code/datums/wounds/burns.dm b/code/datums/wounds/burns.dm
index 394486fef9a24..a4ef3bd7b7df7 100644
--- a/code/datums/wounds/burns.dm
+++ b/code/datums/wounds/burns.dm
@@ -41,7 +41,7 @@
return
. = ..()
- if(strikes_to_lose_limb == 0) // we've already hit sepsis, nothing more to do
+ if(strikes_to_lose_limb <= 0) // we've already hit sepsis, nothing more to do
victim.adjustToxLoss(0.25 * seconds_per_tick)
if(SPT_PROB(0.5, seconds_per_tick))
victim.visible_message(span_danger("The infection on the remnants of [victim]'s [limb.plaintext_zone] shift and bubble nauseatingly!"), span_warning("You can feel the infection on the remnants of your [limb.plaintext_zone] coursing through your veins!"), vision_distance = COMBAT_MESSAGE_RANGE)
@@ -135,6 +135,13 @@
threshold_penalty = 120 // piss easy to destroy
set_disabling(TRUE)
+/datum/wound/burn/flesh/set_disabling(new_value)
+ . = ..()
+ if(new_value && strikes_to_lose_limb <= 0)
+ treat_text_short = "Amputate or augment limb immediately, or place the patient into cryogenics."
+ else
+ treat_text_short = initial(treat_text_short)
+
/datum/wound/burn/flesh/get_wound_description(mob/user)
if(strikes_to_lose_limb <= 0)
return span_deadsay("[victim.p_Their()] [limb.plaintext_zone] has locked up completely and is non-functional.")
@@ -168,9 +175,25 @@
return "[condition.Join()]"
+/datum/wound/burn/flesh/severity_text(simple = FALSE)
+ . = ..()
+ . += " Burn / "
+ switch(infestation)
+ if(-INFINITY to WOUND_INFECTION_MODERATE)
+ . += "No"
+ if(WOUND_INFECTION_MODERATE to WOUND_INFECTION_SEVERE)
+ . += "Moderate"
+ if(WOUND_INFECTION_SEVERE to WOUND_INFECTION_CRITICAL)
+ . += "Severe"
+ if(WOUND_INFECTION_CRITICAL to WOUND_INFECTION_SEPTIC)
+ . += "Critical"
+ if(WOUND_INFECTION_SEPTIC to INFINITY)
+ . += "Total"
+ . += " Infection"
+
/datum/wound/burn/flesh/get_scanner_description(mob/user)
if(strikes_to_lose_limb <= 0) // Unclear if it can go below 0, best to not take the chance
- var/oopsie = "Type: [name]\nSeverity: [severity_text()]"
+ var/oopsie = "Type: [name] Severity: [severity_text()]"
oopsie += "
Infection Level: [span_deadsay("The body part has suffered complete sepsis and must be removed. Amputate or augment limb immediately, or place the patient in a cryotube.")]
"
return oopsie
@@ -249,7 +272,7 @@
// people complained about burns not healing on stasis beds, so in addition to checking if it's cured, they also get the special ability to very slowly heal on stasis beds if they have the healing effects stored
/datum/wound/burn/flesh/on_stasis(seconds_per_tick, times_fired)
. = ..()
- if(strikes_to_lose_limb == 0) // we've already hit sepsis, nothing more to do
+ if(strikes_to_lose_limb <= 0) // we've already hit sepsis, nothing more to do
if(SPT_PROB(0.5, seconds_per_tick))
victim.visible_message(span_danger("The infection on the remnants of [victim]'s [limb.plaintext_zone] shift and bubble nauseatingly!"), span_warning("You can feel the infection on the remnants of your [limb.plaintext_zone] coursing through your veins!"), vision_distance = COMBAT_MESSAGE_RANGE)
return
@@ -280,7 +303,8 @@
/datum/wound/burn/flesh/moderate
name = "Second Degree Burns"
desc = "Patient is suffering considerable burns with mild skin penetration, weakening limb integrity and increased burning sensations."
- treat_text = "Recommended application of topical ointment or regenerative mesh to affected region."
+ treat_text = "Apply topical ointment or regenerative mesh to the wound."
+ treat_text_short = "Apply healing aid such as regenerative mesh."
examine_desc = "is badly burned and breaking out in blisters"
occur_text = "breaks out with violent red burns"
severity = WOUND_SEVERITY_MODERATE
@@ -304,7 +328,11 @@
/datum/wound/burn/flesh/severe
name = "Third Degree Burns"
desc = "Patient is suffering extreme burns with full skin penetration, creating serious risk of infection and greatly reduced limb integrity."
- treat_text = "Recommended immediate disinfection and excision of any infected skin, followed by bandaging and ointment. If the limb has locked up, it must be amputated, augmented or treated with cryogenics."
+ treat_text = "Swiftly apply healing aids such as Synthflesh or regenerative mesh to the wound. \
+ Disinfect the wound and surgically debride any infected skin, and wrap in clean gauze / use ointment to prevent further infection. \
+ If the limb has locked up, it must be amputated, augmented or treated with cryogenics."
+ treat_text_short = "Apply healing aid such as regenerative mesh, Synthflesh, or cryogenics and disinfect / debride. \
+ Clean gauze or ointment will slow infection rate."
examine_desc = "appears seriously charred, with aggressive red splotches"
occur_text = "chars rapidly, exposing ruined tissue and spreading angry red burns"
severity = WOUND_SEVERITY_SEVERE
@@ -330,7 +358,11 @@
/datum/wound/burn/flesh/critical
name = "Catastrophic Burns"
desc = "Patient is suffering near complete loss of tissue and significantly charred muscle and bone, creating life-threatening risk of infection and negligible limb integrity."
- treat_text = "Immediate surgical debriding of any infected skin, followed by potent tissue regeneration formula and bandaging. If the limb has locked up, it must be amputated, augmented or treated with cryogenics."
+ treat_text = "Immediately apply healing aids such as Synthflesh or regenerative mesh to the wound. \
+ Disinfect the wound and surgically debride any infected skin, and wrap in clean gauze / use ointment to prevent further infection. \
+ If the limb has locked up, it must be amputated, augmented or treated with cryogenics."
+ treat_text_short = "Apply healing aid such as regenerative mesh, Synthflesh, or cryogenics and disinfect / debride. \
+ Clean gauze or ointment will slow infection rate."
examine_desc = "is a ruined mess of blanched bone, melted fat, and charred tissue"
occur_text = "vaporizes as flesh, bone, and fat melt together in a horrifying mess"
severity = WOUND_SEVERITY_CRITICAL
diff --git a/code/datums/wounds/cranial_fissure.dm b/code/datums/wounds/cranial_fissure.dm
index df973d3bdec90..8feebe8d2b624 100644
--- a/code/datums/wounds/cranial_fissure.dm
+++ b/code/datums/wounds/cranial_fissure.dm
@@ -29,7 +29,8 @@
/datum/wound/cranial_fissure
name = "Cranial Fissure"
desc = "Patient's crown is agape, revealing severe damage to the skull."
- treat_text = "Immediate surgical reconstruction of the skull."
+ treat_text = "Surgical reconstruction of the skull is necessary."
+ treat_text_short = "Surgical reconstruction required."
examine_desc = "is split open"
occur_text = "is split into two separated chunks"
@@ -95,7 +96,7 @@
victim.balloon_alert(user, "no eyes to take!")
return TRUE
- playsound(victim, 'sound/surgery/organ2.ogg', 50, TRUE)
+ playsound(victim, 'sound/items/handling/surgery/organ2.ogg', 50, TRUE)
victim.balloon_alert(user, "pulling out eyes...")
user.visible_message(
span_boldwarning("[user] reaches inside [victim]'s skull..."),
@@ -115,7 +116,7 @@
log_combat(user, victim, "pulled out the eyes of")
- playsound(victim, 'sound/surgery/organ1.ogg', 75, TRUE)
+ playsound(victim, 'sound/items/handling/surgery/organ1.ogg', 75, TRUE)
user.visible_message(
span_boldwarning("[user] rips out [victim]'s eyes!"),
span_boldwarning("You rip out [victim]'s eyes!"),
diff --git a/code/datums/wounds/pierce.dm b/code/datums/wounds/pierce.dm
index 4454b0024e441..2cdc2bab382ac 100644
--- a/code/datums/wounds/pierce.dm
+++ b/code/datums/wounds/pierce.dm
@@ -6,7 +6,7 @@
/datum/wound/pierce/bleed
name = "Piercing Wound"
- sound_effect = 'sound/weapons/slice.ogg'
+ sound_effect = 'sound/items/weapons/slice.ogg'
processes = TRUE
treatable_by = list(/obj/item/stack/medical/suture)
treatable_tools = list(TOOL_CAUTERY)
@@ -42,14 +42,26 @@
if(1 to 6)
victim.bleed(blood_bled, TRUE)
if(7 to 13)
- victim.visible_message("Blood droplets fly from the hole in [victim]'s [limb.plaintext_zone].", span_danger("You cough up a bit of blood from the blow to your [limb.plaintext_zone]."), vision_distance=COMBAT_MESSAGE_RANGE)
+ victim.visible_message(
+ span_smalldanger("Blood droplets fly from the hole in [victim]'s [limb.plaintext_zone]."),
+ span_danger("You cough up a bit of blood from the blow to your [limb.plaintext_zone]."),
+ vision_distance = COMBAT_MESSAGE_RANGE,
+ )
victim.bleed(blood_bled, TRUE)
if(14 to 19)
- victim.visible_message("A small stream of blood spurts from the hole in [victim]'s [limb.plaintext_zone]!", span_danger("You spit out a string of blood from the blow to your [limb.plaintext_zone]!"), vision_distance=COMBAT_MESSAGE_RANGE)
+ victim.visible_message(
+ span_smalldanger("A small stream of blood spurts from the hole in [victim]'s [limb.plaintext_zone]!"),
+ span_danger("You spit out a string of blood from the blow to your [limb.plaintext_zone]!"),
+ vision_distance = COMBAT_MESSAGE_RANGE,
+ )
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
victim.bleed(blood_bled)
if(20 to INFINITY)
- victim.visible_message(span_danger("A spray of blood streams from the gash in [victim]'s [limb.plaintext_zone]!"), span_danger("You choke up on a spray of blood from the blow to your [limb.plaintext_zone]!"), vision_distance=COMBAT_MESSAGE_RANGE)
+ victim.visible_message(
+ span_danger("A spray of blood streams from the gash in [victim]'s [limb.plaintext_zone]!"),
+ span_bolddanger("You choke up on a spray of blood from the blow to your [limb.plaintext_zone]!"),
+ vision_distance = COMBAT_MESSAGE_RANGE,
+ )
victim.bleed(blood_bled)
new /obj/effect/temp_visual/dir_setting/bloodsplatter(victim.loc, victim.dir)
victim.add_splatter_floor(get_step(victim.loc, victim.dir))
@@ -180,7 +192,10 @@
/datum/wound/pierce/bleed/moderate
name = "Minor Skin Breakage"
desc = "Patient's skin has been broken open, causing severe bruising and minor internal bleeding in affected area."
- treat_text = "Treat affected site with bandaging or exposure to extreme cold. In dire cases, brief exposure to vacuum may suffice." // space is cold in ss13, so it's like an ice pack!
+ treat_text = "Apply bandaging or suturing to the wound, make use of blood clotting agents, \
+ cauterization, or in extreme circumstances, exposure to extreme cold or vaccuum. \
+ Follow with food and a rest period."
+ treat_text_short = "Apply bandaging or suturing."
examine_desc = "has a small, circular hole, gently bleeding"
occur_text = "spurts out a thin stream of blood"
sound_effect = 'sound/effects/wounds/pierce1.ogg'
@@ -211,7 +226,10 @@
/datum/wound/pierce/bleed/severe
name = "Open Puncture"
desc = "Patient's internal tissue is penetrated, causing sizeable internal bleeding and reduced limb stability."
- treat_text = "Repair punctures in skin by suture or cautery, extreme cold may also work."
+ treat_text = "Swiftly apply bandaging or suturing to the wound, make use of blood clotting agents or saline-glucose, \
+ cauterization, or in extreme circumstances, exposure to extreme cold or vaccuum. \
+ Follow with iron supplements and a rest period."
+ treat_text_short = "Apply bandaging, suturing, clotting agents, or cauterization."
examine_desc = "is pierced clear through, with bits of tissue obscuring the open hole"
occur_text = "looses a violent spray of blood, revealing a pierced wound"
sound_effect = 'sound/effects/wounds/pierce2.ogg'
@@ -241,7 +259,10 @@
/datum/wound/pierce/bleed/critical
name = "Ruptured Cavity"
desc = "Patient's internal tissue and circulatory system is shredded, causing significant internal bleeding and damage to internal organs."
- treat_text = "Surgical repair of puncture wound, followed by supervised resanguination."
+ treat_text = "Immediately apply bandaging or suturing to the wound, make use of blood clotting agents or saline-glucose, \
+ cauterization, or in extreme circumstances, exposure to extreme cold or vaccuum. \
+ Follow with supervised resanguination."
+ treat_text_short = "Apply bandaging, suturing, clotting agents, or cauterization."
examine_desc = "is ripped clear through, barely held together by exposed bone"
occur_text = "blasts apart, sending chunks of viscera flying in all directions"
sound_effect = 'sound/effects/wounds/pierce3.ogg'
diff --git a/code/datums/wounds/slash.dm b/code/datums/wounds/slash.dm
index dd41d48620e99..fd3cb4bd7b2b1 100644
--- a/code/datums/wounds/slash.dm
+++ b/code/datums/wounds/slash.dm
@@ -5,7 +5,7 @@
/datum/wound/slash
name = "Slashing (Cut) Wound"
- sound_effect = 'sound/weapons/slice.ogg'
+ sound_effect = 'sound/items/weapons/slice.ogg'
/datum/wound_pregen_data/flesh_slash
abstract = TRUE
@@ -321,7 +321,9 @@
/datum/wound/slash/flesh/moderate
name = "Rough Abrasion"
desc = "Patient's skin has been badly scraped, generating moderate blood loss."
- treat_text = "Application of clean bandages or first-aid grade sutures, followed by food and rest."
+ treat_text = "Apply bandaging or suturing to the wound. \
+ Follow up with food and a rest period."
+ treat_text_short = "Apply bandaging or suturing."
examine_desc = "has an open cut"
occur_text = "is cut open, slowly leaking blood"
sound_effect = 'sound/effects/wounds/blood1.ogg'
@@ -350,7 +352,10 @@
/datum/wound/slash/flesh/severe
name = "Open Laceration"
desc = "Patient's skin is ripped clean open, allowing significant blood loss."
- treat_text = "Speedy application of first-aid grade sutures and clean bandages, followed by vitals monitoring to ensure recovery."
+ treat_text = "Swiftly apply bandaging or suturing to the wound, \
+ or make use of blood clotting agents or cauterization. \
+ Follow up with iron supplements or saline-glucose and a rest period."
+ treat_text_short = "Apply bandaging, suturing, clotting agents, or cauterization."
examine_desc = "has a severe cut"
occur_text = "is ripped open, veins spurting blood"
sound_effect = 'sound/effects/wounds/blood2.ogg'
@@ -380,7 +385,10 @@
/datum/wound/slash/flesh/critical
name = "Weeping Avulsion"
desc = "Patient's skin is completely torn open, along with significant loss of tissue. Extreme blood loss will lead to quick death without intervention."
- treat_text = "Immediate bandaging and either suturing or cauterization, followed by supervised resanguination."
+ treat_text = "Immediately apply bandaging or suturing to the wound, \
+ or make use of blood clotting agents or cauterization. \
+ Follow up supervised resanguination."
+ treat_text_short = "Apply bandaging, suturing, clotting agents, or cauterization."
examine_desc = "is carved down to the bone, spraying blood wildly"
occur_text = "is torn open, spraying blood wildly"
sound_effect = 'sound/effects/wounds/blood3.ogg'
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 04d26a5e50a5a..b988fa0b6daa8 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -91,7 +91,7 @@
///Does this area immediately play an ambience track upon enter?
var/forced_ambience = FALSE
///The background droning loop that plays 24/7
- var/ambient_buzz = 'sound/ambience/shipambience.ogg'
+ var/ambient_buzz = 'sound/ambience/general/shipambience.ogg'
///The volume of the ambient buzz
var/ambient_buzz_vol = 35
///Used to decide what the minimum time between ambience is
diff --git a/code/game/area/areas/ai_monitored.dm b/code/game/area/areas/ai_monitored.dm
index a6964d70f6ae0..4e63479987e85 100644
--- a/code/game/area/areas/ai_monitored.dm
+++ b/code/game/area/areas/ai_monitored.dm
@@ -23,9 +23,9 @@
// Turret protected
/area/station/ai_monitored/turret_protected
- ambientsounds = list('sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg')
+ ambientsounds = list('sound/ambience/engineering/ambitech.ogg', 'sound/ambience/engineering/ambitech2.ogg', 'sound/ambience/engineering/ambiatmos.ogg', 'sound/ambience/engineering/ambiatmos2.ogg')
///Some sounds (like the space jam) are terrible when on loop. We use this variable to add it to other AI areas, but override it to keep it from the AI's core.
- var/ai_will_not_hear_this = list('sound/ambience/ambimalf.ogg')
+ var/ai_will_not_hear_this = list('sound/ambience/misc/ambimalf.ogg')
airlock_wires = /datum/wires/airlock/ai
/area/station/ai_monitored/turret_protected/Initialize(mapload)
diff --git a/code/game/area/areas/away_content.dm b/code/game/area/areas/away_content.dm
index 5e2219ef857e0..5ff0143c0a1a9 100644
--- a/code/game/area/areas/away_content.dm
+++ b/code/game/area/areas/away_content.dm
@@ -23,7 +23,7 @@ Unused icons for new areas are "awaycontent1" ~ "awaycontent30"
base_lighting_alpha = 200
base_lighting_color = "#FFF4AA"
sound_environment = SOUND_ENVIRONMENT_PLAIN
- ambientsounds = list('sound/ambience/shore.ogg', 'sound/ambience/ambiodd.ogg','sound/ambience/ambinice.ogg')
+ ambientsounds = list('sound/ambience/beach/shore.ogg', 'sound/ambience/misc/ambiodd.ogg','sound/ambience/medical/ambinice.ogg')
/area/awaymission/museum/cafeteria
name = "Nanotrasen Museum Cafeteria"
diff --git a/code/game/area/areas/centcom.dm b/code/game/area/areas/centcom.dm
index 012e7a170f726..28b3496c4e18a 100644
--- a/code/game/area/areas/centcom.dm
+++ b/code/game/area/areas/centcom.dm
@@ -124,6 +124,7 @@
/area/centcom/tdome/arena
name = "Thunderdome Arena"
icon_state = "thunder"
+ area_flags = parent_type::area_flags | UNLIMITED_FISHING //for possible testing purposes
/area/centcom/tdome/tdome1
name = "Thunderdome (Team 1)"
diff --git a/code/game/area/areas/mining.dm b/code/game/area/areas/mining.dm
index 031a6dd5039d7..be6db4e077fec 100644
--- a/code/game/area/areas/mining.dm
+++ b/code/game/area/areas/mining.dm
@@ -4,7 +4,7 @@
icon_state = "mining"
has_gravity = STANDARD_GRAVITY
area_flags = VALID_TERRITORY | UNIQUE_AREA | FLORA_ALLOWED | CULT_PERMITTED
- ambient_buzz = 'sound/ambience/magma.ogg'
+ ambient_buzz = 'sound/ambience/lavaland/magma.ogg'
/area/mine/lobby
name = "Mining Station"
@@ -134,7 +134,7 @@
flags_1 = NONE
area_flags = VALID_TERRITORY | UNIQUE_AREA | FLORA_ALLOWED
sound_environment = SOUND_AREA_LAVALAND
- ambient_buzz = 'sound/ambience/magma.ogg'
+ ambient_buzz = 'sound/ambience/lavaland/magma.ogg'
/area/lavaland/surface
name = "Lavaland"
@@ -195,7 +195,7 @@
area_flags = UNIQUE_AREA | FLORA_ALLOWED
ambience_index = AMBIENCE_ICEMOON
sound_environment = SOUND_AREA_ICEMOON
- ambient_buzz = 'sound/ambience/magma.ogg'
+ ambient_buzz = 'sound/ambience/lavaland/magma.ogg'
/area/icemoon/surface
name = "Icemoon"
diff --git a/code/game/area/areas/ruins/icemoon.dm b/code/game/area/areas/ruins/icemoon.dm
index d0049e7007c49..061bd8f06d209 100644
--- a/code/game/area/areas/ruins/icemoon.dm
+++ b/code/game/area/areas/ruins/icemoon.dm
@@ -75,3 +75,7 @@
/area/ruin/powered/hermit
name = "\improper Hermit's Cabin"
+/area/ruin/syndielab
+ name = "\improper Syndicate Lab"
+ ambience_index = AMBIENCE_DANGER
+ sound_environment = SOUND_ENVIRONMENT_CAVE
diff --git a/code/game/area/areas/ruins/lavaland.dm b/code/game/area/areas/ruins/lavaland.dm
index f9c57510132f6..4e806bf1c1030 100644
--- a/code/game/area/areas/ruins/lavaland.dm
+++ b/code/game/area/areas/ruins/lavaland.dm
@@ -8,7 +8,7 @@
/area/ruin/powered/clownplanet
name = "\improper Clown Biodome"
- ambientsounds = list('sound/ambience/clown.ogg')
+ ambientsounds = list('sound/music/lobby_music/clown.ogg')
/area/ruin/unpowered/gaia
name = "\improper Patch of Eden"
@@ -38,7 +38,7 @@
/area/ruin/syndicate_lava_base
name = "\improper Secret Base"
ambience_index = AMBIENCE_DANGER
- ambient_buzz = 'sound/ambience/magma.ogg'
+ ambient_buzz = 'sound/ambience/lavaland/magma.ogg'
/area/ruin/unpowered/cultaltar
name = "\improper Cult Altar"
@@ -49,7 +49,7 @@
name = "\improper The Lizard's Gas"
icon_state = "lizardgas"
sound_environment = SOUND_ENVIRONMENT_ROOM
- ambient_buzz = 'sound/ambience/magma.ogg'
+ ambient_buzz = 'sound/ambience/lavaland/magma.ogg'
//Syndicate lavaland base
@@ -94,11 +94,11 @@
power_environ = FALSE
power_equip = FALSE
power_light = FALSE
- ambient_buzz = 'sound/ambience/magma.ogg'
+ ambient_buzz = 'sound/ambience/lavaland/magma.ogg'
//ash walker nest
/area/ruin/unpowered/ash_walkers
- ambient_buzz = 'sound/ambience/magma.ogg'
+ ambient_buzz = 'sound/ambience/lavaland/magma.ogg'
/area/ruin/unpowered/ratvar
outdoors = TRUE
- ambient_buzz = 'sound/ambience/magma.ogg'
+ ambient_buzz = 'sound/ambience/lavaland/magma.ogg'
diff --git a/code/game/area/areas/ruins/space.dm b/code/game/area/areas/ruins/space.dm
index 5eac7addb4ec2..ac687d6024a88 100644
--- a/code/game/area/areas/ruins/space.dm
+++ b/code/game/area/areas/ruins/space.dm
@@ -55,7 +55,7 @@
/area/ruin/space/has_grav/powered/aesthetic
name = "Aesthetic"
- ambientsounds = list('sound/ambience/ambivapor1.ogg')
+ ambientsounds = list('sound/ambience/misc/ambivapor1.ogg')
//Ruin of Hotel
@@ -335,7 +335,7 @@
/area/ruin/space/ancientstation/delta/ai
name = "\improper Delta Station AI Core"
icon_state = "os_delta_ai"
- ambientsounds = list('sound/ambience/ambimalf.ogg', 'sound/ambience/ambitech.ogg', 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg')
+ ambientsounds = list('sound/ambience/misc/ambimalf.ogg', 'sound/ambience/engineering/ambitech.ogg', 'sound/ambience/engineering/ambitech2.ogg', 'sound/ambience/engineering/ambiatmos.ogg', 'sound/ambience/engineering/ambiatmos2.ogg')
/area/ruin/space/ancientstation/delta/storage
name = "\improper Delta Station Storage"
@@ -546,14 +546,14 @@
/area/ruin/space/abandoned_tele
name = "\improper Abandoned Teleporter"
- ambientsounds = list('sound/ambience/ambimalf.ogg', 'sound/ambience/signal.ogg')
+ ambientsounds = list('sound/ambience/misc/ambimalf.ogg', 'sound/ambience/misc/signal.ogg')
//OLD AI SAT
/area/ruin/space/tcommsat_oldaisat // Since tcommsat was moved to /area/station/, this turf doesn't inhereit its properties anymore
name = "\improper Abandoned Satellite"
- ambientsounds = list('sound/ambience/ambisin2.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/signal.ogg', 'sound/ambience/ambigen9.ogg', 'sound/ambience/ambitech.ogg',\
- 'sound/ambience/ambitech2.ogg', 'sound/ambience/ambitech3.ogg', 'sound/ambience/ambimystery.ogg')
+ ambientsounds = list('sound/ambience/engineering/ambisin2.ogg', 'sound/ambience/misc/signal.ogg', 'sound/ambience/misc/signal.ogg', 'sound/ambience/general/ambigen9.ogg', 'sound/ambience/engineering/ambitech.ogg',\
+ 'sound/ambience/engineering/ambitech2.ogg', 'sound/ambience/engineering/ambitech3.ogg', 'sound/ambience/misc/ambimystery.ogg')
airlock_wires = /datum/wires/airlock/engineering
// CRASHED PRISON SHUTTLE
@@ -592,7 +592,7 @@
// The planet of the clowns
/area/ruin/space/has_grav/powered/clownplanet
name = "\improper Clown Planet"
- ambientsounds = list('sound/ambience/clown.ogg')
+ ambientsounds = list('sound/music/lobby_music/clown.ogg')
//DERELICT SULACO
/area/ruin/space/has_grav/derelictsulaco
@@ -671,7 +671,7 @@
icon = 'icons/area/areas_ruins.dmi'
icon_state = "ruins"
requires_power = FALSE
- ambientsounds = list('sound/ambience/ambigen12.ogg','sound/ambience/ambigen13.ogg','sound/ambience/ambinice.ogg')
+ ambientsounds = list('sound/ambience/general/ambigen12.ogg','sound/ambience/general/ambigen13.ogg','sound/ambience/medical/ambinice.ogg')
// the outlet
/area/ruin/space/has_grav/the_outlet/storefront
diff --git a/code/game/area/areas/shuttles.dm b/code/game/area/areas/shuttles.dm
index 504efe0742ad1..f128805924fe8 100644
--- a/code/game/area/areas/shuttles.dm
+++ b/code/game/area/areas/shuttles.dm
@@ -123,7 +123,7 @@
if(SSshuttle.arrivals?.mode == SHUTTLE_CALL)
var/atom/movable/screen/splash/Spl = new(null, boarder.client, TRUE)
Spl.Fade(TRUE)
- boarder.playsound_local(get_turf(boarder), 'sound/voice/ApproachingTG.ogg', 25)
+ boarder.playsound_local(get_turf(boarder), 'sound/announcer/ApproachingTG.ogg', 25)
boarder.update_parallax_teleport()
diff --git a/code/game/area/areas/station/command.dm b/code/game/area/areas/station/command.dm
index 23f2c7c61c0fc..ee4325d94aef8 100644
--- a/code/game/area/areas/station/command.dm
+++ b/code/game/area/areas/station/command.dm
@@ -2,7 +2,7 @@
name = "Command"
icon_state = "command"
ambientsounds = list(
- 'sound/ambience/signal.ogg',
+ 'sound/ambience/misc/signal.ogg',
)
airlock_wires = /datum/wires/airlock/command
sound_environment = SOUND_AREA_STANDARD_STATION
diff --git a/code/game/area/areas/station/maintenance.dm b/code/game/area/areas/station/maintenance.dm
index 53e6da606d085..5e636719e7a09 100644
--- a/code/game/area/areas/station/maintenance.dm
+++ b/code/game/area/areas/station/maintenance.dm
@@ -5,7 +5,7 @@
airlock_wires = /datum/wires/airlock/maint
sound_environment = SOUND_AREA_TUNNEL_ENCLOSED
forced_ambience = TRUE
- ambient_buzz = 'sound/ambience/source_corridor2.ogg'
+ ambient_buzz = 'sound/ambience/maintenance/source_corridor2.ogg'
ambient_buzz_vol = 20
/*
diff --git a/code/game/area/areas/station/medical.dm b/code/game/area/areas/station/medical.dm
index fc6c6ff3a7564..b45a1492b290f 100644
--- a/code/game/area/areas/station/medical.dm
+++ b/code/game/area/areas/station/medical.dm
@@ -11,7 +11,7 @@
name = "\improper Abandoned Medbay"
icon_state = "abandoned_medbay"
ambientsounds = list(
- 'sound/ambience/signal.ogg',
+ 'sound/ambience/misc/signal.ogg',
)
sound_environment = SOUND_AREA_SMALL_ENCLOSED
@@ -124,5 +124,5 @@
mood_bonus = 3
mood_message = "I feel at ease here."
ambientsounds = list(
- 'sound/ambience/aurora_caelus_short.ogg',
+ 'sound/ambience/aurora_caelus/aurora_caelus_short.ogg',
)
diff --git a/code/game/area/areas/station/security.dm b/code/game/area/areas/station/security.dm
index 93629f35628c2..ca158e69df87b 100644
--- a/code/game/area/areas/station/security.dm
+++ b/code/game/area/areas/station/security.dm
@@ -79,8 +79,8 @@
name = "\improper Detective's Office"
icon_state = "detective"
ambientsounds = list(
- 'sound/ambience/ambidet1.ogg',
- 'sound/ambience/ambidet2.ogg',
+ 'sound/ambience/security/ambidet1.ogg',
+ 'sound/ambience/security/ambidet2.ogg',
)
/area/station/security/detectives_office/private_investigators_office
diff --git a/code/game/area/areas/station/telecomm.dm b/code/game/area/areas/station/telecomm.dm
index 78ec16a59bf29..02101c28c1a90 100644
--- a/code/game/area/areas/station/telecomm.dm
+++ b/code/game/area/areas/station/telecomm.dm
@@ -5,14 +5,14 @@
/area/station/tcommsat
icon_state = "tcomsatcham"
ambientsounds = list(
- 'sound/ambience/ambisin2.ogg',
- 'sound/ambience/signal.ogg',
- 'sound/ambience/signal.ogg',
- 'sound/ambience/ambigen9.ogg',
- 'sound/ambience/ambitech.ogg',
- 'sound/ambience/ambitech2.ogg',
- 'sound/ambience/ambitech3.ogg',
- 'sound/ambience/ambimystery.ogg',
+ 'sound/ambience/engineering/ambisin2.ogg',
+ 'sound/ambience/misc/signal.ogg',
+ 'sound/ambience/misc/signal.ogg',
+ 'sound/ambience/general/ambigen9.ogg',
+ 'sound/ambience/engineering/ambitech.ogg',
+ 'sound/ambience/engineering/ambitech2.ogg',
+ 'sound/ambience/engineering/ambitech3.ogg',
+ 'sound/ambience/misc/ambimystery.ogg',
)
airlock_wires = /datum/wires/airlock/engineering
diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm
index c025428a17b6e..7f43cc2c6ee8c 100644
--- a/code/game/atom/_atom.dm
+++ b/code/game/atom/_atom.dm
@@ -690,10 +690,10 @@
created_atoms.Add(created_atom)
to_chat(user, span_notice("You manage to create [amount_to_create] [initial(atom_to_create.gender) == PLURAL ? "[initial(atom_to_create.name)]" : "[initial(atom_to_create.name)][plural_s(initial(atom_to_create.name))]"] from [src]."))
SEND_SIGNAL(src, COMSIG_ATOM_PROCESSED, user, process_item, created_atoms)
- UsedforProcessing(user, process_item, chosen_option)
+ UsedforProcessing(user, process_item, chosen_option, created_atoms)
return
-/atom/proc/UsedforProcessing(mob/living/user, obj/item/used_item, list/chosen_option)
+/atom/proc/UsedforProcessing(mob/living/user, obj/item/used_item, list/chosen_option, list/created_atoms)
qdel(src)
return
@@ -876,10 +876,18 @@
var/shift_lmb_ctrl_shift_lmb_line = ""
var/extra_lines = 0
var/extra_context = ""
+ var/used_name = name
if(isliving(user) || isovermind(user) || isaicamera(user) || (ghost_screentips && isobserver(user)))
var/obj/item/held_item = user.get_active_held_item()
+ if (user.mob_flags & MOB_HAS_SCREENTIPS_NAME_OVERRIDE)
+ var/list/returned_name = list(used_name)
+
+ var/name_override_returns = SEND_SIGNAL(user, COMSIG_MOB_REQUESTING_SCREENTIP_NAME_FROM_USER, returned_name, held_item, src)
+ if (name_override_returns & SCREENTIP_NAME_SET)
+ used_name = returned_name[1]
+
if (flags_1 & HAS_CONTEXTUAL_SCREENTIPS_1 || held_item?.item_flags & ITEM_HAS_CONTEXTUAL_SCREENTIPS)
var/list/context = list()
@@ -945,9 +953,9 @@
new_maptext = ""
else
//We inline a MAPTEXT() here, because there's no good way to statically add to a string like this
- new_maptext = "[name][extra_context]"
+ new_maptext = "[used_name][extra_context]"
- if (length(name) * 10 > active_hud.screentip_text.maptext_width)
+ if (length(used_name) * 10 > active_hud.screentip_text.maptext_width)
INVOKE_ASYNC(src, PROC_REF(set_hover_maptext), client, active_hud, new_maptext)
return
diff --git a/code/game/atom/alternate_appearance.dm b/code/game/atom/alternate_appearance.dm
index 108b72b95d286..8c50760ea45ea 100644
--- a/code/game/atom/alternate_appearance.dm
+++ b/code/game/atom/alternate_appearance.dm
@@ -212,3 +212,10 @@ GLOBAL_LIST_EMPTY(active_alternate_appearances)
return ..()
/datum/atom_hud/alternate_appearance/basic/food_demands
+
+/datum/atom_hud/alternate_appearance/basic/heretic
+
+/datum/atom_hud/alternate_appearance/basic/heretic/mobShouldSee(mob/M)
+ if(IS_HERETIC(M))
+ return TRUE
+ return FALSE
diff --git a/code/game/atom/atom_act.dm b/code/game/atom/atom_act.dm
index c9951b9209422..7b69f02340c87 100644
--- a/code/game/atom/atom_act.dm
+++ b/code/game/atom/atom_act.dm
@@ -115,11 +115,15 @@
* Im not sure why this the case, maybe to prevent lots of hitby's if the thrown object is
* deleted shortly after hitting something (during explosions or other massive events that
* throw lots of items around - singularity being a notable example)
+ *
+ * Worth of note: If hitby returns TRUE, it means the object has been blocked or catched by src.
+ * So far, this is only possible for living mobs and carbons, who can hold shields and catch thrown items.
*/
/atom/proc/hitby(atom/movable/hitting_atom, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
SEND_SIGNAL(src, COMSIG_ATOM_HITBY, hitting_atom, skipcatch, hitpush, blocked, throwingdatum)
if(density && !has_gravity(hitting_atom)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...).
addtimer(CALLBACK(src, PROC_REF(hitby_react), hitting_atom), 0.2 SECONDS)
+ return FALSE
/**
* We have have actually hit the passed in atom
diff --git a/code/game/atom/atom_defense.dm b/code/game/atom/atom_defense.dm
index 4a762e4de8b49..ce31eb81246f1 100644
--- a/code/game/atom/atom_defense.dm
+++ b/code/game/atom/atom_defense.dm
@@ -108,11 +108,11 @@
switch(damage_type)
if(BRUTE)
if(damage_amount)
- playsound(src, 'sound/weapons/smash.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/smash.ogg', 50, TRUE)
else
- playsound(src, 'sound/weapons/tap.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/tap.ogg', 50, TRUE)
if(BURN)
- playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
+ playsound(src.loc, 'sound/items/tools/welder.ogg', 100, TRUE)
///Called to get the damage that hulks will deal to the atom.
/atom/proc/hulk_damage()
diff --git a/code/game/atom/atom_examine.dm b/code/game/atom/atom_examine.dm
index ab94c73856365..fee219f7b4b50 100644
--- a/code/game/atom/atom_examine.dm
+++ b/code/game/atom/atom_examine.dm
@@ -1,6 +1,14 @@
/atom
- ///If non-null, overrides a/an/some in all cases
+ /// If non-null, overrides a/an/some in all cases
var/article
+ /// Text that appears preceding the name in examine()
+ var/examine_thats = "That's"
+
+/mob/living/carbon/human
+ examine_thats = "This is"
+
+/mob/living/silicon/robot
+ examine_thats = "This is"
/**
* Called when a mob examines (shift click or verb) this atom
@@ -11,22 +19,20 @@
* Produces a signal [COMSIG_ATOM_EXAMINE]
*/
/atom/proc/examine(mob/user)
- var/examine_string = get_examine_string(user, thats = TRUE)
- if(examine_string)
- . = list("[examine_string].")
- else
- . = list()
-
+ . = list()
. += get_name_chaser(user)
if(desc)
- . += desc
+ . += "[desc]"
- if(custom_materials)
- var/list/materials_list = list()
- for(var/custom_material in custom_materials)
- var/datum/material/current_material = GET_MATERIAL_REF(custom_material)
- materials_list += "[current_material.name]"
- . += "It is made out of [english_list(materials_list)]."
+ var/list/tags_list = examine_tags(user)
+ if (length(tags_list))
+ var/tag_string = list()
+ for (var/atom_tag in tags_list)
+ tag_string += (isnull(tags_list[atom_tag]) ? atom_tag : span_tooltip(tags_list[atom_tag], atom_tag))
+ // Weird bit but ensures that if the final element has its own "and" we don't add another one
+ tag_string = english_list(tag_string, and_text = (findtext(tag_string[length(tag_string)], " and ")) ? ", " : " and ")
+ var/post_descriptor = examine_post_descriptor(user)
+ . += "[p_They()] [p_are()] a [tag_string] [examine_descriptor(user)][length(post_descriptor) ? " [jointext(post_descriptor, " ")]" : ""]."
if(reagents)
var/user_sees_reagents = user.can_see_reagents()
@@ -52,6 +58,34 @@
SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE, user, .)
+/*
+ * A list of "tags" displayed after atom's description in examine.
+ * This should return an assoc list of tags -> tooltips for them. If item if null, then no tooltip is assigned.
+ * For example:
+ * list("small" = "This is a small size class item.", "fireproof" = "This item is impervious to fire.")
+ * will result in
+ * This is a small, fireproof item.
+ * where "item" is pulled from examine_descriptor() proc
+ */
+/atom/proc/examine_tags(mob/user)
+ . = list()
+ SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE_TAGS, user, .)
+
+/// What this atom should be called in examine tags
+/atom/proc/examine_descriptor(mob/user)
+ return "object"
+
+/// Returns a list of strings to be displayed after the descriptor
+/atom/proc/examine_post_descriptor(mob/user)
+ . = list()
+ if(!custom_materials)
+ return
+ var/mats_list = list()
+ for(var/custom_material in custom_materials)
+ var/datum/material/current_material = GET_MATERIAL_REF(custom_material)
+ mats_list += span_tooltip("It is made out of [current_material.name].", current_material.name)
+ . += "made of [english_list(mats_list)]"
+
/**
* Called when a mob examines (shift click or verb) this atom twice (or more) within EXAMINE_MORE_WINDOW (default 1 second)
*
@@ -75,7 +109,7 @@
* [COMSIG_ATOM_GET_EXAMINE_NAME] signal
*/
/atom/proc/get_examine_name(mob/user)
- var/list/override = list(article, null, "[name]")
+ var/list/override = list(article, null, "[get_visible_name()]")
SEND_SIGNAL(src, COMSIG_ATOM_GET_EXAMINE_NAME, user, override)
if(!isnull(override[EXAMINE_POSITION_ARTICLE]))
@@ -84,11 +118,24 @@
if(!isnull(override[EXAMINE_POSITION_BEFORE]))
override -= null // There is no article, don't try to join it
return "\a [jointext(override, " ")]"
- return "\a [src]"
+ return "\a [src]"
+
+/mob/living/get_examine_name(mob/user)
+ return get_visible_name()
+
+/// Icon displayed in examine
+/atom/proc/get_examine_icon(mob/user)
+ return icon2html(src, user)
-///Generate the full examine string of this atom (including icon for goonchat)
-/atom/proc/get_examine_string(mob/user, thats = FALSE)
- return "[icon2html(src, user)] [thats? "That's ":""][get_examine_name(user)]"
+/**
+ * Formats the atom's name into a string for use in examine (as the "title" of the atom)
+ *
+ * * user - the mob examining the atom
+ * * thats - whether to include "That's", or similar (mobs use "This is") before the name
+ */
+/atom/proc/examine_title(mob/user, thats = FALSE)
+ var/examine_icon = get_examine_icon(user)
+ return "[examine_icon ? "[examine_icon] " : ""][thats ? "[examine_thats] ":""][get_examine_name(user)]"
/**
* Returns an extended list of examine strings for any contained ID cards.
@@ -98,7 +145,6 @@
*/
/atom/proc/get_id_examine_strings(mob/user)
. = list()
- return
///Used to insert text after the name but before the description in examine()
/atom/proc/get_name_chaser(mob/user, list/name_chaser = list())
diff --git a/code/game/atom/atom_tool_acts.dm b/code/game/atom/atom_tool_acts.dm
index 10bed5a407760..efb43fc16e571 100644
--- a/code/game/atom/atom_tool_acts.dm
+++ b/code/game/atom/atom_tool_acts.dm
@@ -1,7 +1,7 @@
/**
* ## Item interaction
*
- * Handles non-combat iteractions of a tool on this atom,
+ * Handles non-combat interactions of a tool on this atom,
* such as using a tool on a wall to deconstruct it,
* or scanning someone with a health analyzer
*/
@@ -14,7 +14,7 @@
if(tool_return)
return tool_return
- var/is_right_clicking = LAZYACCESS(modifiers, RIGHT_CLICK)
+ var/is_right_clicking = text2num(LAZYACCESS(modifiers, RIGHT_CLICK))
var/is_left_clicking = !is_right_clicking
var/early_sig_return = NONE
if(is_left_clicking)
@@ -22,10 +22,8 @@
* This is intentionally using `||` instead of `|` to short-circuit the signal calls
* This is because we want to return early if ANY of these signals return a value
*
- * This puts priority on the atom's signals, then the tool's signals, then the user's signals
- * So stuff like storage can be handled before stuff the item wants to do like cleaner component
- *
- * Future idea: Being on combat mode could change/reverse the priority of these signals
+ * This puts priority on the atom's signals, then the tool's signals, then the user's signals,
+ * so we can avoid doing two interactions at once
*/
early_sig_return = SEND_SIGNAL(src, COMSIG_ATOM_ITEM_INTERACTION, user, tool, modifiers) \
|| SEND_SIGNAL(tool, COMSIG_ITEM_INTERACTING_WITH_ATOM, user, src, modifiers) \
@@ -50,6 +48,16 @@
if(interact_return)
return interact_return
+ // We have to manually handle storage in item_interaction because storage is blocking in 99% of interactions, which stifles a lot
+ // Yeah it sucks not being able to signalize this, but the other option is to have a second signal here just for storage which is also not great
+ if(atom_storage)
+ if(is_left_clicking)
+ if(atom_storage.insert_on_attack)
+ return atom_storage.item_interact_insert(user, tool)
+ else
+ if(atom_storage.open_storage(user) && atom_storage.display_contents)
+ return ITEM_INTERACT_SUCCESS
+
return NONE
/**
@@ -61,7 +69,7 @@
*
* Handles the tool_acts in particular, such as wrenches and screwdrivers.
*
- * This can be overriden to handle unique "tool interactions"
+ * This can be overridden to handle unique "tool interactions"
* IE using an item like a tool (when it's not actually one)
* This is particularly useful for things that shouldn't be inserted into storage
* (because tool acting runs before storage checks)
@@ -315,23 +323,3 @@
/// Called on an object when a tool with analyzer capabilities is used to right click an object
/atom/proc/analyzer_act_secondary(mob/living/user, obj/item/tool)
return
-
-/**
- * Called before this item is placed into a storage container
- * via the item clicking on the target atom
- *
- * Returning FALSE will prevent the item from being stored.
- */
-/obj/item/proc/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/user)
- return TRUE
-
-/**
- * Called before an item is put into this atom's storage datum via the item clicking on this atom
- *
- * This can be used to add item-atom interactions that you want handled before inserting something into storage
- * (But it's also fairly snowflakey)
- *
- * Returning FALSE will block that item from being put into our storage.
- */
-/atom/proc/storage_insert_on_interacted_with(datum/storage, obj/item/inserted, mob/living/user)
- return TRUE
diff --git a/code/game/atom/atoms_initializing_EXPENSIVE.dm b/code/game/atom/atoms_initializing_EXPENSIVE.dm
index c603ae5514810..205617fdbc8f9 100644
--- a/code/game/atom/atoms_initializing_EXPENSIVE.dm
+++ b/code/game/atom/atoms_initializing_EXPENSIVE.dm
@@ -57,14 +57,14 @@
* Called when an atom is created in byond (built in engine proc)
*
* Not a lot happens here in SS13 code, as we offload most of the work to the
- * [Intialization][/atom/proc/Initialize] proc, mostly we run the preloader
+ * [Initialization][/atom/proc/Initialize] proc, mostly we run the preloader
* if the preloader is being used and then call [InitAtom][/datum/controller/subsystem/atoms/proc/InitAtom] of which the ultimate
- * result is that the Intialize proc is called.
+ * result is that the Initialize proc is called.
*
*/
/atom/New(loc, ...)
//atom creation method that preloads variables at creation
- if(GLOB.use_preloader && src.type == GLOB._preloader_path)//in case the instanciated atom is creating other atoms in New()
+ if(GLOB.use_preloader && src.type == GLOB._preloader_path)//in case the instantiated atom is creating other atoms in New()
world.preloader_load(src)
var/do_initialize = SSatoms.initialized
@@ -80,17 +80,17 @@
* we don't use New as we have better control over when this is called and we can choose
* to delay calls or hook other logic in and so forth
*
- * During roundstart map parsing, atoms are queued for intialization in the base atom/New(),
- * After the map has loaded, then Initalize is called on all atoms one by one. NB: this
- * is also true for loading map templates as well, so they don't Initalize until all objects
+ * During roundstart map parsing, atoms are queued for initialization in the base atom/New(),
+ * After the map has loaded, then Initialize is called on all atoms one by one. NB: this
+ * is also true for loading map templates as well, so they don't Initialize until all objects
* in the map file are parsed and present in the world
*
* If you're creating an object at any point after SSInit has run then this proc will be
* immediately be called from New.
*
- * mapload: This parameter is true if the atom being loaded is either being intialized during
- * the Atom subsystem intialization, or if the atom is being loaded from the map template.
- * If the item is being created at runtime any time after the Atom subsystem is intialized then
+ * mapload: This parameter is true if the atom being loaded is either being initialized during
+ * the Atom subsystem initialization, or if the atom is being loaded from the map template.
+ * If the item is being created at runtime any time after the Atom subsystem is initialized then
* it's false.
*
* The mapload argument occupies the same position as loc when Initialize() is called by New().
@@ -98,7 +98,7 @@
* with mapload at the end of atom/New() before this proc (atom/Initialize()) is called.
*
* You must always call the parent of this proc, otherwise failures will occur as the item
- * will not be seen as initalized (this can lead to all sorts of strange behaviour, like
+ * will not be seen as initialized (this can lead to all sorts of strange behaviour, like
* the item being completely unclickable)
*
* You must not sleep in this proc, or any subprocs
@@ -136,7 +136,7 @@
if(uses_integrity)
atom_integrity = max_integrity
- TEST_ONLY_ASSERT((!armor || istype(armor)), "[type] has an armor that contains an invalid value at intialize")
+ TEST_ONLY_ASSERT((!armor || istype(armor)), "[type] has an armor that contains an invalid value at initialize")
// apply materials properly from the default custom_materials value
// This MUST come after atom_integrity is set above, as if old materials get removed,
@@ -150,14 +150,14 @@
return INITIALIZE_HINT_NORMAL
/**
- * Late Intialization, for code that should run after all atoms have run Intialization
+ * Late Initialization, for code that should run after all atoms have run Initialization
*
- * To have your LateIntialize proc be called, your atoms [Initalization][/atom/proc/Initialize]
+ * To have your LateIntialize proc be called, your atoms [Initialization][/atom/proc/Initialize]
* proc must return the hint
* [INITIALIZE_HINT_LATELOAD] otherwise it will never be called.
*
* useful for doing things like finding other machines on GLOB.machines because you can guarantee
- * that all atoms will actually exist in the "WORLD" at this time and that all their Intialization
+ * that all atoms will actually exist in the "WORLD" at this time and that all their Initialization
* code has been run
*/
/atom/proc/LateInitialize()
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index e4aadc639e486..67b2346c2eeaa 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -17,7 +17,7 @@
var/tk_throw_range = 10
var/mob/pulledby = null
/// What language holder type to init as
- var/initial_language_holder = /datum/language_holder
+ var/initial_language_holder = /datum/language_holder/atom_basic
/// Holds all languages this mob can speak and understand
VAR_PRIVATE/datum/language_holder/language_holder
/// The list of factions this atom belongs to
@@ -33,8 +33,10 @@
var/speech_span
///Are we moving with inertia? Mostly used as an optimization
var/inertia_moving = FALSE
- ///Delay in deciseconds between inertia based movement
- var/inertia_move_delay = 5
+ ///Multiplier for inertia based movement in space
+ var/inertia_move_multiplier = 1
+ ///Object "weight", higher weight reduces acceleration applied to the object
+ var/inertia_force_weight = 1
///The last time we pushed off something
///This is a hack to get around dumb him him me scenarios
var/last_pushoff
@@ -107,6 +109,9 @@
/// The pitch adjustment that this movable uses when speaking.
var/pitch = 0
+ /// Datum that keeps all data related to zero-g drifting and handles related code/comsigs
+ var/datum/drift_handler/drift_handler
+
/// The filter to apply to the voice when processing the TTS audio message.
var/voice_filter = ""
@@ -198,6 +203,7 @@
/atom/movable/Destroy(force)
QDEL_NULL(language_holder)
QDEL_NULL(em_block)
+ QDEL_NULL(drift_handler)
unbuckle_all_mobs(force = TRUE)
@@ -459,7 +465,7 @@
var/static/list/not_falsey_edits = list(NAMEOF_STATIC(src, bound_width) = TRUE, NAMEOF_STATIC(src, bound_height) = TRUE)
if(banned_edits[var_name])
return FALSE //PLEASE no.
- if(careful_edits[var_name] && (var_value % world.icon_size) != 0)
+ if(careful_edits[var_name] && (var_value % ICON_SIZE_ALL) != 0)
return FALSE
if(not_falsey_edits[var_name] && !var_value)
return FALSE
@@ -768,7 +774,7 @@
if(!. && set_dir_on_move && update_dir)
setDir(first_step_dir)
else if(!inertia_moving)
- newtonian_move(direct)
+ newtonian_move(dir2angle(direct))
if(client_mobs_in_contents)
update_parallax_contents()
moving_diagonally = 0
@@ -842,8 +848,8 @@
/atom/movable/proc/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
SHOULD_CALL_PARENT(TRUE)
- if (!inertia_moving && momentum_change)
- newtonian_move(movement_dir)
+ if (!moving_diagonally && !inertia_moving && momentum_change && movement_dir)
+ newtonian_move(dir2angle(movement_dir))
// If we ain't moving diagonally right now, update our parallax
// We don't do this all the time because diag movements should trigger one call to this, not two
// Waste of cpu time, and it fucks the animate
@@ -1121,7 +1127,7 @@
RESOLVE_ACTIVE_MOVEMENT
var/atom/oldloc = loc
- var/is_multi_tile = bound_width > world.icon_size || bound_height > world.icon_size
+ var/is_multi_tile = bound_width > ICON_SIZE_X || bound_height > ICON_SIZE_Y
SET_ACTIVE_MOVEMENT(oldloc, NONE, TRUE, null)
@@ -1144,8 +1150,8 @@
var/list/new_locs = block(
destination,
locate(
- min(world.maxx, destination.x + ROUND_UP(bound_width / 32)),
- min(world.maxy, destination.y + ROUND_UP(bound_height / 32)),
+ min(world.maxx, destination.x + ROUND_UP(bound_width / ICON_SIZE_X)),
+ min(world.maxy, destination.y + ROUND_UP(bound_height / ICON_SIZE_Y)),
destination.z
)
)
@@ -1262,15 +1268,19 @@
/// Only moves the object if it's under no gravity
/// Accepts the direction to move, if the push should be instant, and an optional parameter to fine tune the start delay
-/atom/movable/proc/newtonian_move(direction, instant = FALSE, start_delay = 0)
- if(!isturf(loc) || Process_Spacemove(direction, continuous_move = TRUE))
+/// Drift force determines how much acceleration should be applied. Controlled cap, if set, will ensure that if the object was moving slower than the cap before, it cannot accelerate past the cap from this move.
+/atom/movable/proc/newtonian_move(inertia_angle, instant = FALSE, start_delay = 0, drift_force = 1 NEWTONS, controlled_cap = null)
+ if(!isturf(loc) || Process_Spacemove(angle2dir(inertia_angle), continuous_move = TRUE))
return FALSE
- if(SEND_SIGNAL(src, COMSIG_MOVABLE_NEWTONIAN_MOVE, direction, start_delay) & COMPONENT_MOVABLE_NEWTONIAN_BLOCK)
- return TRUE
-
- AddComponent(/datum/component/drift, direction, instant, start_delay)
+ if (!isnull(drift_handler))
+ if (drift_handler.newtonian_impulse(inertia_angle, start_delay, drift_force, controlled_cap))
+ return TRUE
+ new /datum/drift_handler(src, inertia_angle, instant, start_delay, drift_force)
+ // Something went wrong and it failed to create itself, most likely we have a higher priority loop already
+ if (QDELETED(drift_handler))
+ return FALSE
return TRUE
/atom/movable/set_explosion_block(explosion_block)
@@ -1283,16 +1293,22 @@
/atom/movable/proc/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
set waitfor = FALSE
var/hitpush = TRUE
- var/impact_signal = SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_IMPACT, hit_atom, throwingdatum)
- if(impact_signal & COMPONENT_MOVABLE_IMPACT_FLIP_HITPUSH)
- hitpush = FALSE // hacky, tie this to something else or a proper workaround later
-
- if(impact_signal && (impact_signal & COMPONENT_MOVABLE_IMPACT_NEVERMIND))
+ var/impact_flags = pre_impact(hit_atom, throwingdatum)
+ if(impact_flags & COMPONENT_MOVABLE_IMPACT_NEVERMIND)
return // in case a signal interceptor broke or deleted the thing before we could process our hit
- if(SEND_SIGNAL(hit_atom, COMSIG_ATOM_PREHITBY, src, throwingdatum) & COMSIG_HIT_PREVENTED)
- return
- SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
- return hit_atom.hitby(src, throwingdatum=throwingdatum, hitpush=hitpush)
+ if(impact_flags & COMPONENT_MOVABLE_IMPACT_FLIP_HITPUSH)
+ hitpush = FALSE
+ var/caught = hit_atom.hitby(src, throwingdatum=throwingdatum, hitpush=hitpush)
+ SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum, caught)
+ return caught
+
+///Called before we attempt to call hitby and send the COMSIG_MOVABLE_IMPACT signal
+/atom/movable/proc/pre_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ var/impact_flags = SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_IMPACT, hit_atom, throwingdatum)
+ var/target_flags = SEND_SIGNAL(hit_atom, COMSIG_ATOM_PREHITBY, src, throwingdatum)
+ if(target_flags & COMSIG_HIT_PREVENTED)
+ impact_flags |= COMPONENT_MOVABLE_IMPACT_NEVERMIND
+ return impact_flags
/atom/movable/hitby(atom/movable/hitting_atom, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum)
if(HAS_TRAIT(src, TRAIT_NO_THROW_HITPUSH))
@@ -1445,6 +1461,7 @@
return
/atom/movable/proc/get_spacemove_backup()
+ var/atom/secondary_backup
for(var/checked_range in orange(1, get_turf(src)))
if(isarea(checked_range))
continue
@@ -1452,12 +1469,18 @@
var/turf/turf = checked_range
if(!turf.density)
continue
- return turf
+ if (get_dir(src, turf) in GLOB.cardinals)
+ return turf
+ secondary_backup = turf
+ continue
var/atom/movable/checked_atom = checked_range
if(checked_atom.density || !checked_atom.CanPass(src, get_dir(src, checked_atom)))
if(checked_atom.last_pushoff == world.time)
continue
- return checked_atom
+ if (get_dir(src, checked_atom) in GLOB.cardinals)
+ return checked_atom
+ secondary_backup = checked_atom
+ return secondary_backup
///called when a mob resists while inside a container that is itself inside something.
/atom/movable/proc/relay_container_resist_act(mob/living/user, obj/container)
diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm
index e39ce9bd92d37..f1adb1b03f0ec 100644
--- a/code/game/data_huds.dm
+++ b/code/game/data_huds.dm
@@ -28,6 +28,8 @@
var/obj/item/clothing/under/U = H.w_uniform
if(!istype(U))
return FALSE
+ if(U.has_sensor < HAS_SENSORS)
+ return FALSE
if(U.sensor_mode <= SENSOR_VITALS)
return FALSE
return TRUE
@@ -173,7 +175,7 @@ Medical HUD! Basic mode needs suit sensors on.
holder.icon_state = "hud[RoundHealth(src)]"
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
//for carbon suit sensors
/mob/living/carbon/med_hud_set_health()
@@ -186,7 +188,7 @@ Medical HUD! Basic mode needs suit sensors on.
return
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH)))
holder.icon_state = "huddead"
else
@@ -199,7 +201,7 @@ Medical HUD! Basic mode needs suit sensors on.
var/icon/I = icon(icon, icon_state, dir)
var/virus_threat = check_virus()
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
if(HAS_TRAIT(src, TRAIT_XENO_HOST))
holder.icon_state = "hudxeno"
else if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH)))
@@ -227,7 +229,11 @@ Medical HUD! Basic mode needs suit sensors on.
holder.icon_state = "hudbuff"
if(null)
holder.icon_state = "hudhealthy"
-
+ if(ishuman(src))
+ var/mob/living/carbon/human/crew = src
+ var/obj/item/clothing/under/uniform = crew.w_uniform
+ if(uniform && uniform.has_sensor == BROKEN_SENSORS)
+ holder.icon_state = "hudnosensor"
/***********************************************
FAN HUDs! For identifying other fans on-sight.
@@ -238,7 +244,7 @@ FAN HUDs! For identifying other fans on-sight.
/mob/living/carbon/human/proc/fan_hud_set_fandom()
var/image/holder = hud_list[FAN_HUD]
var/icon/hud_icon = icon(icon, icon_state, dir)
- holder.pixel_y = hud_icon.Height() - world.icon_size
+ holder.pixel_y = hud_icon.Height() - ICON_SIZE_Y
holder.icon_state = "hudfan_no"
var/obj/item/clothing/under/undershirt = w_uniform
@@ -269,7 +275,7 @@ Security HUDs! Basic mode shows only the job.
/mob/living/carbon/human/proc/sec_hud_set_ID()
var/image/holder = hud_list[ID_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
var/sechud_icon_state = wear_id?.get_sechud_job_icon_state()
if(!sechud_icon_state || HAS_TRAIT(src, TRAIT_UNKNOWN))
sechud_icon_state = "hudno_id"
@@ -290,7 +296,7 @@ Security HUDs! Basic mode shows only the job.
if(1)
holder = hud_list[IMPSEC_FIRST_HUD]
var/icon/IC = icon(icon, icon_state, dir)
- holder.pixel_y = IC.Height() - world.icon_size
+ holder.pixel_y = IC.Height() - ICON_SIZE_Y
holder.icon_state = current_implant.hud_icon_state
set_hud_image_active(IMPSEC_FIRST_HUD)
security_slot++
@@ -298,22 +304,22 @@ Security HUDs! Basic mode shows only the job.
if(2) //Theoretically if we somehow get multiple sec implants, whatever the most recently implanted implant is will take over the 2nd position
holder = hud_list[IMPSEC_SECOND_HUD]
var/icon/IC = icon(icon, icon_state, dir)
- holder.pixel_y = IC.Height() - world.icon_size
- holder.pixel_x = initial(holder.pixel_x) + 7 //Adds an offset that mirrors the hud blip to the other side of the mob.
+ holder.pixel_y = IC.Height() - ICON_SIZE_Y
+ holder.pixel_x = initial(holder.pixel_x) + (ICON_SIZE_X / 4 - 1) //Adds an offset that mirrors the hud blip to the other side of the mob.
holder.icon_state = current_implant.hud_icon_state
set_hud_image_active(IMPSEC_SECOND_HUD)
if(HAS_TRAIT(src, TRAIT_MINDSHIELD))
holder = hud_list[IMPLOYAL_HUD]
var/icon/IC = icon(icon, icon_state, dir)
- holder.pixel_y = IC.Height() - world.icon_size
+ holder.pixel_y = IC.Height() - ICON_SIZE_Y
holder.icon_state = "hud_imp_loyal"
set_hud_image_active(IMPLOYAL_HUD)
/mob/living/carbon/human/proc/sec_hud_set_security_status()
var/image/holder = hud_list[WANTED_HUD]
var/icon/sec_icon = icon(icon, icon_state, dir)
- holder.pixel_y = sec_icon.Height() - world.icon_size
+ holder.pixel_y = sec_icon.Height() - ICON_SIZE_Y
if (HAS_TRAIT(src, TRAIT_ALWAYS_WANTED))
holder.icon_state = "hudwanted"
@@ -390,7 +396,7 @@ Diagnostic HUDs!
/mob/living/silicon/proc/diag_hud_set_health()
var/image/holder = hud_list[DIAG_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
if(stat == DEAD)
holder.icon_state = "huddiagdead"
else
@@ -399,7 +405,7 @@ Diagnostic HUDs!
/mob/living/silicon/proc/diag_hud_set_status()
var/image/holder = hud_list[DIAG_STAT_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
switch(stat)
if(CONSCIOUS)
holder.icon_state = "hudstat"
@@ -412,7 +418,7 @@ Diagnostic HUDs!
/mob/living/silicon/robot/proc/diag_hud_set_borgcell()
var/image/holder = hud_list[DIAG_BATT_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
if(cell)
var/chargelvl = (cell.charge/cell.maxcharge)
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
@@ -423,7 +429,7 @@ Diagnostic HUDs!
/mob/living/silicon/robot/proc/diag_hud_set_aishell() //Shows tracking beacons on the mech
var/image/holder = hud_list[DIAG_TRACK_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
if(!shell) //Not an AI shell
holder.icon_state = null
set_hud_image_inactive(DIAG_TRACK_HUD)
@@ -438,7 +444,7 @@ Diagnostic HUDs!
/mob/living/silicon/ai/proc/diag_hud_set_deployed() //Shows tracking beacons on the mech
var/image/holder = hud_list[DIAG_TRACK_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
if(!deployed_shell)
holder.icon_state = null
set_hud_image_inactive(DIAG_TRACK_HUD)
@@ -452,14 +458,14 @@ Diagnostic HUDs!
/obj/vehicle/sealed/mecha/proc/diag_hud_set_mechhealth()
var/image/holder = hud_list[DIAG_MECH_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
holder.icon_state = "huddiag[RoundDiagBar(atom_integrity/max_integrity)]"
/obj/vehicle/sealed/mecha/proc/diag_hud_set_mechcell()
var/image/holder = hud_list[DIAG_BATT_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
if(cell)
var/chargelvl = cell.charge/cell.maxcharge
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
@@ -469,7 +475,7 @@ Diagnostic HUDs!
/obj/vehicle/sealed/mecha/proc/diag_hud_set_mechstat()
var/image/holder = hud_list[DIAG_STAT_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
if(internal_damage)
holder.icon_state = "hudwarn"
set_hud_image_active(DIAG_STAT_HUD)
@@ -481,7 +487,7 @@ Diagnostic HUDs!
/obj/vehicle/sealed/mecha/proc/diag_hud_set_mechtracking()
var/image/holder = hud_list[DIAG_TRACK_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
var/new_icon_state //This var exists so that the holder's icon state is set only once in the event of multiple mech beacons.
for(var/obj/item/mecha_parts/mecha_tracking/T in trackers)
if(T.ai_beacon) //Beacon with AI uplink
@@ -495,7 +501,7 @@ Diagnostic HUDs!
/obj/vehicle/sealed/mecha/proc/diag_hud_set_camera()
var/image/holder = hud_list[DIAG_CAMERA_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
if(chassis_camera?.is_emp_scrambled)
holder.icon_state = "hudcamera_empd"
return
@@ -507,13 +513,13 @@ Diagnostic HUDs!
/mob/living/simple_animal/bot/proc/diag_hud_set_bothealth()
var/image/holder = hud_list[DIAG_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
/mob/living/simple_animal/bot/proc/diag_hud_set_botstat() //On (With wireless on or off), Off, EMP'ed
var/image/holder = hud_list[DIAG_STAT_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
if(bot_mode_flags & BOT_MODE_ON)
holder.icon_state = "hudstat"
else if(stat) //Generally EMP causes this
@@ -524,7 +530,7 @@ Diagnostic HUDs!
/mob/living/simple_animal/bot/proc/diag_hud_set_botmode() //Shows a bot's current operation
var/image/holder = hud_list[DIAG_BOT_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
if(client) //If the bot is player controlled, it will not be following mode logic!
holder.icon_state = "hudsentient"
return
@@ -546,7 +552,7 @@ Diagnostic HUDs!
/mob/living/simple_animal/bot/mulebot/proc/diag_hud_set_mulebotcell()
var/image/holder = hud_list[DIAG_BATT_HUD]
var/icon/I = icon(icon, icon_state, dir)
- holder.pixel_y = I.Height() - world.icon_size
+ holder.pixel_y = I.Height() - ICON_SIZE_Y
if(cell)
var/chargelvl = (cell.charge/cell.maxcharge)
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index bce9ed3bb9375..b2df8d7416dde 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -353,7 +353,7 @@ GLOBAL_LIST(admin_objective_list) //Prefilled admin assignable objective list
return FALSE
if(human_check)
brain_target = target.current?.get_organ_slot(ORGAN_SLOT_BRAIN)
- //Protect will always suceed when someone suicides
+ //Protect will always succeed when someone suicides
return !target || (target.current && HAS_TRAIT(target.current, TRAIT_SUICIDED)) || considered_alive(target, enforce_human = human_check) || (brain_target && HAS_TRAIT(brain_target, TRAIT_SUICIDED))
/datum/objective/protect/update_explanation_text()
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 8a7e9846561be..34b8612c2d4be 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -1147,6 +1147,9 @@
if(0 to 25)
. += span_warning("It's falling apart!")
+/obj/machinery/examine_descriptor(mob/user)
+ return "machine"
+
/obj/machinery/examine_more(mob/user)
. = ..()
if(HAS_TRAIT(user, TRAIT_RESEARCH_SCANNER) && component_parts)
diff --git a/code/game/machinery/announcement_system.dm b/code/game/machinery/announcement_system.dm
index 1700abb0af1ab..029f4a17ea99b 100644
--- a/code/game/machinery/announcement_system.dm
+++ b/code/game/machinery/announcement_system.dm
@@ -16,16 +16,26 @@ GLOBAL_LIST_EMPTY(announcement_systems)
circuit = /obj/item/circuitboard/machine/announcement_system
+ ///The headset that we use for broadcasting
var/obj/item/radio/headset/radio
+ ///The message that we send when someone is joining.
var/arrival = "%PERSON has signed up as %RANK"
- var/arrivalToggle = 1
+ ///Whether the arrival message is sent
+ var/arrival_toggle = TRUE
+ ///The message that we send when a department head arrives.
var/newhead = "%PERSON, %RANK, is the department head."
- var/newheadToggle = 1
+ ///Whether the newhead message is sent.
+ var/newhead_toggle = TRUE
var/greenlight = "Light_Green"
var/pinklight = "Light_Pink"
var/errorlight = "Error_Red"
+ ///If true, researched nodes will be announced to the appropriate channels
+ var/announce_research_node = TRUE
+ /// The text that we send when announcing researched nodes.
+ var/node_message = "The '%NODE' techweb node has been researched"
+
/obj/machinery/announcement_system/Initialize(mapload)
. = ..()
GLOB.announcement_systems += src
@@ -41,10 +51,10 @@ GLOBAL_LIST_EMPTY(announcement_systems)
/obj/machinery/announcement_system/update_overlays()
. = ..()
- if(arrivalToggle)
+ if(arrival_toggle)
. += greenlight
- if(newheadToggle)
+ if(newhead_toggle)
. += pinklight
if(machine_stat & BROKEN)
@@ -78,18 +88,25 @@ GLOBAL_LIST_EMPTY(announcement_systems)
str = replacetext(str, "%RANK", "[rank]")
return str
-/obj/machinery/announcement_system/proc/announce(message_type, user, rank, list/channels)
+/obj/machinery/announcement_system/proc/announce(message_type, target, rank, list/channels)
if(!is_operational)
return
var/message
- if(message_type == "ARRIVAL" && arrivalToggle)
- message = CompileText(arrival, user, rank)
- else if(message_type == "NEWHEAD" && newheadToggle)
- message = CompileText(newhead, user, rank)
- else if(message_type == "ARRIVALS_BROKEN")
- message = "The arrivals shuttle has been damaged. Docking for repairs..."
+ switch(message_type)
+ if(AUTO_ANNOUNCE_ARRIVAL)
+ if(!arrival_toggle)
+ return
+ message = CompileText(arrival, target, rank)
+ if(AUTO_ANNOUNCE_NEWHEAD)
+ if(!newhead_toggle)
+ return
+ message = CompileText(newhead, target, rank)
+ if(AUTO_ANNOUNCE_ARRIVALS_BROKEN)
+ message = "The arrivals shuttle has been damaged. Docking for repairs..."
+ if(AUTO_ANNOUNCE_NODE)
+ message = replacetext(node_message, "%NODE", target)
broadcast(message, channels)
@@ -118,9 +135,11 @@ GLOBAL_LIST_EMPTY(announcement_systems)
/obj/machinery/announcement_system/ui_data()
var/list/data = list()
data["arrival"] = arrival
- data["arrivalToggle"] = arrivalToggle
+ data["arrivalToggle"] = arrival_toggle
data["newhead"] = newhead
- data["newheadToggle"] = newheadToggle
+ data["newheadToggle"] = newhead_toggle
+ data["node_message"] = node_message
+ data["node_toggle"] = announce_research_node
return data
/obj/machinery/announcement_system/ui_act(action, param)
@@ -131,29 +150,32 @@ GLOBAL_LIST_EMPTY(announcement_systems)
return
if(machine_stat & BROKEN)
visible_message(span_warning("[src] buzzes."), span_hear("You hear a faint buzz."))
- playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, TRUE)
+ playsound(src.loc, 'sound/machines/buzz/buzz-two.ogg', 50, TRUE)
return
switch(action)
if("ArrivalText")
- var/NewMessage = trim(html_encode(param["newText"]), MAX_MESSAGE_LEN)
- if(!usr.can_perform_action(src, ALLOW_SILICON_REACH))
- return
- if(NewMessage)
- arrival = NewMessage
- usr.log_message("updated the arrivals announcement to: [NewMessage]", LOG_GAME)
+ var/new_message = trim(html_encode(param["newText"]), MAX_MESSAGE_LEN)
+ if(new_message)
+ arrival = new_message
+ usr.log_message("updated the arrivals announcement to: [new_message]", LOG_GAME)
if("NewheadText")
- var/NewMessage = trim(html_encode(param["newText"]), MAX_MESSAGE_LEN)
- if(!usr.can_perform_action(src, ALLOW_SILICON_REACH))
- return
- if(NewMessage)
- newhead = NewMessage
- usr.log_message("updated the head announcement to: [NewMessage]", LOG_GAME)
- if("NewheadToggle")
- newheadToggle = !newheadToggle
+ var/new_message = trim(html_encode(param["newText"]), MAX_MESSAGE_LEN)
+ if(new_message)
+ newhead = new_message
+ usr.log_message("updated the head announcement to: [new_message]", LOG_GAME)
+ if("node_message")
+ var/new_message = trim(html_encode(param["new_text"]), MAX_MESSAGE_LEN)
+ if(new_message)
+ node_message = new_message
+ usr.log_message("updated the researched node announcement to: [node_message]", LOG_GAME)
+ if("newhead_toggle")
+ newhead_toggle = !newhead_toggle
update_appearance()
- if("ArrivalToggle")
- arrivalToggle = !arrivalToggle
+ if("arrivalToggle")
+ arrival_toggle = !arrival_toggle
update_appearance()
+ if("node_toggle")
+ announce_research_node = !announce_research_node
add_fingerprint(usr)
/obj/machinery/announcement_system/attack_robot(mob/living/silicon/user)
@@ -173,6 +195,11 @@ GLOBAL_LIST_EMPTY(announcement_systems)
arrival = pick("#!@%ERR-34%2 CANNOT LOCAT@# JO# F*LE!", "CRITICAL ERROR 99.", "ERR)#: DA#AB@#E NOT F(*ND!")
newhead = pick("OV#RL()D: \[UNKNOWN??\] DET*#CT)D!", "ER)#R - B*@ TEXT F*O(ND!", "AAS.exe is not responding. NanoOS is searching for a solution to the problem.")
+ node_message = pick(list(
+ replacetext(/obj/machinery/announcement_system::node_message, "%NODE", /datum/techweb_node/mech_clown::display_name),
+ "R/NT1M3 A= ANNOUN-*#nt_SY!?EM.dm, LI%£ 86: N=0DE NULL!",
+ "BEPIS BEPIS BEPIS",
+ ))
/obj/machinery/announcement_system/emp_act(severity)
. = ..()
diff --git a/code/game/machinery/bank_machine.dm b/code/game/machinery/bank_machine.dm
index 40670a22f44c4..72de51d034fe8 100644
--- a/code/game/machinery/bank_machine.dm
+++ b/code/game/machinery/bank_machine.dm
@@ -73,7 +73,7 @@
end_siphon()
return
- playsound(src, 'sound/items/poster_being_created.ogg', 100, TRUE)
+ playsound(src, 'sound/items/poster/poster_being_created.ogg', 100, TRUE)
syphoning_credits += siphon_am
synced_bank_account.adjust_money(-siphon_am)
if(next_warning < world.time && prob(15))
diff --git a/code/game/machinery/barsigns.dm b/code/game/machinery/barsigns.dm
index 11dc005269b7b..0f33028aa9a76 100644
--- a/code/game/machinery/barsigns.dm
+++ b/code/game/machinery/barsigns.dm
@@ -102,9 +102,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/barsign, 32)
/obj/machinery/barsign/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
if(BRUTE)
- playsound(src.loc, 'sound/effects/glasshit.ogg', 75, TRUE)
+ playsound(src.loc, 'sound/effects/glass/glasshit.ogg', 75, TRUE)
if(BURN)
- playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
+ playsound(src.loc, 'sound/items/tools/welder.ogg', 100, TRUE)
/obj/machinery/barsign/attack_ai(mob/user)
return attack_hand(user)
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 6f21e9303fc5a..f3760fda0319a 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -133,8 +133,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
find_and_hang_on_wall(directional = TRUE, \
custom_drop_callback = CALLBACK(src, PROC_REF(deconstruct), FALSE))
- RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
-
/obj/machinery/camera/Destroy(force)
if(can_use())
toggle_cam(null, 0) //kick anyone viewing out and remove from the camera chunks
@@ -235,11 +233,11 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
M.reset_perspective(null)
to_chat(M, span_warning("The screen bursts into static!"))
-/obj/machinery/camera/proc/on_saboteur(datum/source, disrupt_duration)
- SIGNAL_HANDLER
+/obj/machinery/camera/on_saboteur(datum/source, disrupt_duration)
+ . = ..()
//lasts twice as much so we don't have to constantly shoot cameras just to be S T E A L T H Y
emp_act(EMP_LIGHT, reset_time = disrupt_duration * 2)
- return COMSIG_SABOTEUR_SUCCESS
+ return TRUE
/obj/machinery/camera/proc/post_emp_reset(thisemp, previous_network)
if(QDELETED(src))
@@ -368,7 +366,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0)
else
visible_message(span_danger("\The [src] [change_msg]!"))
- playsound(src, 'sound/items/wirecutter.ogg', 100, TRUE)
+ playsound(src, 'sound/items/tools/wirecutter.ogg', 100, TRUE)
update_appearance() //update Initialize() if you remove this.
// now disconnect anyone using the camera
diff --git a/code/game/machinery/camera/camera_construction.dm b/code/game/machinery/camera/camera_construction.dm
index 15f22d02cbe96..d6988617e1f25 100644
--- a/code/game/machinery/camera/camera_construction.dm
+++ b/code/game/machinery/camera/camera_construction.dm
@@ -30,7 +30,7 @@
switch(camera_construction_state)
if(CAMERA_STATE_WIRED)
tool.play_tool_sound(src)
- var/input = tgui_input_text(user, "Which networks would you like to connect this camera to? Separate networks with a comma. No Spaces!\nFor example: SS13,Security,Secret", "Set Network", "SS13")
+ var/input = tgui_input_text(user, "Which networks would you like to connect this camera to? Separate networks with a comma. No Spaces!\nFor example: SS13,Security,Secret", "Set Network", "SS13", max_length = MAX_NAME_LEN)
if(isnull(input))
return ITEM_INTERACT_BLOCKING
var/list/tempnetwork = splittext(input, ",")
diff --git a/code/game/machinery/civilian_bounties.dm b/code/game/machinery/civilian_bounties.dm
index 7760a646d9fae..d8c8a98caef77 100644
--- a/code/game/machinery/civilian_bounties.dm
+++ b/code/game/machinery/civilian_bounties.dm
@@ -76,11 +76,11 @@
return FALSE
if(!inserted_scan_id)
status_report = "Please insert your ID first."
- playsound(loc, 'sound/machines/synth_no.ogg', 30 , TRUE)
+ playsound(loc, 'sound/machines/synth/synth_no.ogg', 30 , TRUE)
return FALSE
if(!inserted_scan_id.registered_account.civilian_bounty)
status_report = "Please accept a new civilian bounty first."
- playsound(loc, 'sound/machines/synth_no.ogg', 30 , TRUE)
+ playsound(loc, 'sound/machines/synth/synth_no.ogg', 30 , TRUE)
return FALSE
status_report = "Civilian Bounty: "
var/obj/machinery/piratepad/civilian/pad = pad_ref?.resolve()
@@ -89,10 +89,10 @@
continue
if(inserted_scan_id.registered_account.civilian_bounty.applies_to(AM))
status_report += "Target Applicable."
- playsound(loc, 'sound/machines/synth_yes.ogg', 30 , TRUE)
+ playsound(loc, 'sound/machines/synth/synth_yes.ogg', 30 , TRUE)
return
status_report += "Not Applicable."
- playsound(loc, 'sound/machines/synth_no.ogg', 30 , TRUE)
+ playsound(loc, 'sound/machines/synth/synth_no.ogg', 30 , TRUE)
/**
* This fully rewrites base behavior in order to only check for bounty objects, and no other types of objects like pirate-pads do.
@@ -135,7 +135,7 @@
pad.visible_message(span_notice("[pad] activates!"))
flick(pad.sending_state,pad)
pad.icon_state = pad.idle_state
- playsound(loc, 'sound/machines/synth_yes.ogg', 30 , TRUE)
+ playsound(loc, 'sound/machines/synth/synth_yes.ogg', 30 , TRUE)
sending = FALSE
///Here is where cargo bounties are added to the player's bank accounts, then adjusted and scaled into a civilian bounty.
@@ -164,7 +164,7 @@
*/
/obj/machinery/computer/piratepad_control/civilian/proc/pick_bounty(datum/bounty/choice)
if(!inserted_scan_id || !inserted_scan_id.registered_account || !inserted_scan_id.registered_account.bounties || !inserted_scan_id.registered_account.bounties[choice])
- playsound(loc, 'sound/machines/synth_no.ogg', 40 , TRUE)
+ playsound(loc, 'sound/machines/synth/synth_no.ogg', 40 , TRUE)
return
inserted_scan_id.registered_account.civilian_bounty = inserted_scan_id.registered_account.bounties[choice]
inserted_scan_id.registered_account.bounties = null
@@ -242,13 +242,13 @@
if(target)
if(holder_item && inserting_item.InsertID(target))
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 50, FALSE)
else
id_eject(user, target)
user.visible_message(span_notice("[user] inserts \the [card_to_insert] into \the [src]."),
span_notice("You insert \the [card_to_insert] into \the [src]."))
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 50, FALSE)
ui_interact(user)
return TRUE
@@ -263,7 +263,7 @@
user.put_in_hands(target)
user.visible_message(span_notice("[user] gets \the [target] from \the [src]."), \
span_notice("You get \the [target] from \the [src]."))
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 50, FALSE)
inserted_scan_id = null
return TRUE
diff --git a/code/game/machinery/computer/_computer.dm b/code/game/machinery/computer/_computer.dm
index 6e1009def324f..8fdd5556e3b8b 100644
--- a/code/game/machinery/computer/_computer.dm
+++ b/code/game/machinery/computer/_computer.dm
@@ -75,16 +75,16 @@
if(machine_stat & BROKEN)
playsound(src.loc, 'sound/effects/hit_on_shattered_glass.ogg', 70, TRUE)
else
- playsound(src.loc, 'sound/effects/glasshit.ogg', 75, TRUE)
+ playsound(src.loc, 'sound/effects/glass/glasshit.ogg', 75, TRUE)
if(BURN)
- playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
+ playsound(src.loc, 'sound/items/tools/welder.ogg', 100, TRUE)
/obj/machinery/computer/atom_break(damage_flag)
if(!circuit) //no circuit, no breaking
return
. = ..()
if(.)
- playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE)
+ playsound(loc, 'sound/effects/glass/glassbr3.ogg', 100, TRUE)
set_light(0)
/obj/machinery/computer/proc/imprint_gps(gps_tag) // Currently used by the upload computers and communications console
diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm
index e0a7f36460041..0b28775a5b869 100644
--- a/code/game/machinery/computer/aifixer.dm
+++ b/code/game/machinery/computer/aifixer.dm
@@ -58,7 +58,7 @@
if("PRG_beginReconstruction")
if(occupier?.health < 100)
to_chat(usr, span_notice("Reconstruction in progress. This will take several minutes."))
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_prompt_confirm.ogg', 25, FALSE)
restoring = TRUE
occupier.notify_revival("Your core files are being restored!", source = src)
. = TRUE
diff --git a/code/game/machinery/computer/apc_control.dm b/code/game/machinery/computer/apc_control.dm
index 97bf368d3e25e..efb3f4480330e 100644
--- a/code/game/machinery/computer/apc_control.dm
+++ b/code/game/machinery/computer/apc_control.dm
@@ -57,7 +57,7 @@
return
if(active_apc)
disconnect_apc()
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_prompt_confirm.ogg', 50, FALSE)
apc.connect_remote_access(user)
user.log_message("remotely accessed [apc] from [src].", LOG_GAME)
log_activity("[auth_id] remotely accessed APC in [get_area_name(apc.area, TRUE)]")
@@ -134,18 +134,18 @@
authenticated = TRUE
auth_id = "[ID.registered_name] ([ID.assignment]):"
log_activity("[auth_id] logged in to the terminal")
- playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_on.ogg', 50, FALSE)
else
auth_id = "[ID.registered_name] ([ID.assignment]):"
log_activity("[auth_id] attempted to log into the terminal")
- playsound(src, 'sound/machines/terminal_error.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 50, FALSE)
say("ID rejected, access denied!")
return
auth_id = "Unknown (Unknown):"
log_activity("[auth_id] attempted to log into the terminal")
if("log-out")
log_activity("[auth_id] logged out of the terminal")
- playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_off.ogg', 50, FALSE)
authenticated = FALSE
auth_id = "\[NULL\]"
if("toggle-logs")
diff --git a/code/game/machinery/computer/arcade/_arcade.dm b/code/game/machinery/computer/arcade/_arcade.dm
index 69994634fc3b1..053ec907f3e63 100644
--- a/code/game/machinery/computer/arcade/_arcade.dm
+++ b/code/game/machinery/computer/arcade/_arcade.dm
@@ -78,7 +78,7 @@
/obj/machinery/computer/arcade/proc/prizevend(mob/living/user, prizes = 1)
SEND_SIGNAL(src, COMSIG_ARCADE_PRIZEVEND, user, prizes)
if(user.mind?.get_skill_level(/datum/skill/gaming) >= SKILL_LEVEL_LEGENDARY && HAS_TRAIT(user, TRAIT_GAMERGOD))
- visible_message("[user] inputs an intense cheat code!",\
+ visible_message(span_notice("[user] inputs an intense cheat code!"),\
span_notice("You hear a flurry of buttons being pressed."))
say("CODE ACTIVATED: EXTRA PRIZES.")
prizes *= 2
diff --git a/code/game/machinery/computer/arcade/amputation.dm b/code/game/machinery/computer/arcade/amputation.dm
index 84a02af387ad2..d20a5de2b2812 100644
--- a/code/game/machinery/computer/arcade/amputation.dm
+++ b/code/game/machinery/computer/arcade/amputation.dm
@@ -17,19 +17,19 @@
user.played_game()
var/obj/item/bodypart/chopchop = user.get_active_hand()
if(do_after(user, 5 SECONDS, target = src, extra_checks = CALLBACK(src, PROC_REF(do_they_still_have_that_hand), user, chopchop)))
- playsound(src, 'sound/weapons/slice.ogg', 25, TRUE, -1)
+ playsound(src, 'sound/items/weapons/slice.ogg', 25, TRUE, -1)
to_chat(user, span_userdanger("The guillotine drops on your arm, and the machine sucks it in!"))
chopchop.dismember()
qdel(chopchop)
user.mind?.adjust_experience(/datum/skill/gaming, 100)
user.won_game()
- playsound(src, 'sound/arcade/win.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/arcade/win.ogg', 50, TRUE)
new /obj/item/stack/arcadeticket((get_turf(src)), rand(6,10))
to_chat(user, span_notice("[src] dispenses a handful of tickets!"))
return
if(!do_they_still_have_that_hand(user, chopchop))
to_chat(user, span_warning("The guillotine drops, but your hand seems to be gone already!"))
- playsound(src, 'sound/weapons/slice.ogg', 25, TRUE, -1)
+ playsound(src, 'sound/items/weapons/slice.ogg', 25, TRUE, -1)
else
to_chat(user, span_notice("You (wisely) decide against putting your hand in the machine."))
user.lost_game()
diff --git a/code/game/machinery/computer/arcade/battle.dm b/code/game/machinery/computer/arcade/battle.dm
index 9733759b5caf4..169104d6c2fe3 100644
--- a/code/game/machinery/computer/arcade/battle.dm
+++ b/code/game/machinery/computer/arcade/battle.dm
@@ -226,7 +226,7 @@
user.mind?.adjust_experience(/datum/skill/gaming, exp_gained)
user.won_game()
SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("win", (obj_flags & EMAGGED ? "emagged":"normal")))
- playsound(loc, 'sound/arcade/win.ogg', 40)
+ playsound(loc, 'sound/machines/arcade/win.ogg', 40)
if(ui_panel != UI_PANEL_WORLD_MAP) //we havent been booted to world map, we're still going.
ui_panel = UI_PANEL_BETWEEN_FIGHTS
@@ -250,11 +250,11 @@
ui_panel = UI_PANEL_GAMEOVER
feedback_message = "GAME OVER."
say("You have been crushed! GAME OVER.")
- playsound(loc, 'sound/arcade/lose.ogg', 40, TRUE)
+ playsound(loc, 'sound/machines/arcade/lose.ogg', 40, TRUE)
lose_game(user)
else
feedback_message = "User took [damage_taken] damage!"
- playsound(loc, 'sound/arcade/hit.ogg', 40, TRUE, extrarange = -3)
+ playsound(loc, 'sound/machines/arcade/hit.ogg', 40, TRUE, extrarange = -3)
SStgui.update_uis(src)
///Called when you attack the enemy.
@@ -270,17 +270,17 @@
if(BATTLE_ARCADE_PLAYER_COUNTERATTACK)
feedback_message = "User prepares to counterattack!"
process_enemy_turn(user, defending_flags = BATTLE_ATTACK_FLAG_COUNTERATTACK)
- playsound(loc, 'sound/arcade/mana.ogg', 40, TRUE, extrarange = -3)
+ playsound(loc, 'sound/machines/arcade/mana.ogg', 40, TRUE, extrarange = -3)
if(BATTLE_ARCADE_PLAYER_DEFEND)
feedback_message = "User pulls up their shield!"
process_enemy_turn(user, defending_flags = BATTLE_ATTACK_FLAG_DEFEND)
- playsound(loc, 'sound/arcade/mana.ogg', 40, TRUE, extrarange = -3)
+ playsound(loc, 'sound/machines/arcade/mana.ogg', 40, TRUE, extrarange = -3)
if(!damage_dealt)
return
enemy_hp -= round(max(0, damage_dealt), 1)
feedback_message = "[enemy_name] took [damage_dealt] damage!"
- playsound(loc, 'sound/arcade/hit.ogg', 40, TRUE, extrarange = -3)
+ playsound(loc, 'sound/machines/arcade/hit.ogg', 40, TRUE, extrarange = -3)
process_enemy_turn(user)
///Called when you successfully counterattack the enemy.
@@ -289,7 +289,7 @@
var/damage_dealt = (rand(20, 30) * (!isnull(weapon) ? weapon.bonus_modifier : 1))
enemy_hp -= round(max(0, damage_dealt), 1)
feedback_message = "User counterattacked for [damage_dealt] damage!"
- playsound(loc, 'sound/arcade/boom.ogg', 40, TRUE, extrarange = -3)
+ playsound(loc, 'sound/machines/arcade/boom.ogg', 40, TRUE, extrarange = -3)
if(enemy_hp <= 0)
on_battle_win(user)
SStgui.update_uis(src)
@@ -334,7 +334,7 @@
enemy_hp = round(min(enemy_max_hp, enemy_hp + healed_amount), 1)
enemy_mp -= round(max(0, 10), 1)
feedback_message = "[enemy_name] healed for [healed_amount] health points!"
- playsound(loc, 'sound/arcade/heal.ogg', 40, TRUE, extrarange = -3)
+ playsound(loc, 'sound/machines/arcade/heal.ogg', 40, TRUE, extrarange = -3)
SStgui.update_uis(src)
return
if(player_current_mp >= 5) //minimum to steal
@@ -342,7 +342,7 @@
player_current_mp -= round(max(0, healed_amount), 1)
enemy_mp += healed_amount
feedback_message = "[enemy_name] stole [healed_amount] MP from you!"
- playsound(loc, 'sound/arcade/steal.ogg', 40, TRUE)
+ playsound(loc, 'sound/machines/arcade/steal.ogg', 40, TRUE)
SStgui.update_uis(src)
return
//we couldn't heal ourselves or steal MP, we'll just attack instead.
@@ -437,7 +437,7 @@
say("You don't have enough gold to rest!")
return TRUE
player_gold -= DEFAULT_ITEM_PRICE / 2
- playsound(loc, 'sound/mecha/skyfall_power_up.ogg', 40)
+ playsound(loc, 'sound/vehicles/mecha/skyfall_power_up.ogg', 40)
player_current_hp = PLAYER_MAX_HP
player_current_mp = PLAYER_MAX_MP
return TRUE
@@ -476,11 +476,11 @@
return TRUE
if("continue_with_rest")
if(prob(60))
- playsound(loc, 'sound/mecha/skyfall_power_up.ogg', 40)
+ playsound(loc, 'sound/vehicles/mecha/skyfall_power_up.ogg', 40)
player_current_hp = PLAYER_MAX_HP
player_current_mp = PLAYER_MAX_MP
else
- playsound(loc, 'sound/machines/defib_zap.ogg', 40)
+ playsound(loc, 'sound/machines/defib/defib_zap.ogg', 40)
if(prob(40))
//You got robbed, and now have to go to your next fight.
player_gold /= 2
diff --git a/code/game/machinery/computer/arcade/orion.dm b/code/game/machinery/computer/arcade/orion.dm
index 3300370d18e49..a6685e4782ccd 100644
--- a/code/game/machinery/computer/arcade/orion.dm
+++ b/code/game/machinery/computer/arcade/orion.dm
@@ -421,7 +421,7 @@
var/sheriff = remove_crewmember(target) //I shot the sheriff
if(target)
killed_crew += 1 //if there was no suspected lings, this is just plain murder
- playsound(loc,'sound/weapons/gun/pistol/shot.ogg', 100, TRUE)
+ playsound(loc,'sound/items/weapons/gun/pistol/shot.ogg', 100, TRUE)
if(!settlers.len || !alive)
say("The last crewmember [sheriff], shot themselves, GAME OVER!")
if(obj_flags & EMAGGED)
@@ -535,7 +535,7 @@
time_for_next_level = 3 SECONDS
if(2)
say("Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-")
- playsound(loc, 'sound/machines/buzz-sigh.ogg', 25, TRUE)
+ playsound(loc, 'sound/machines/buzz/buzz-sigh.ogg', 25, TRUE)
time_for_next_level = 0.36 SECONDS
if(3 to INFINITY)
visible_message(span_userdanger("[src] explodes!"))
diff --git a/code/game/machinery/computer/arcade/orion_event.dm b/code/game/machinery/computer/arcade/orion_event.dm
index 7ab2f3b98b3b0..d39766200dc52 100644
--- a/code/game/machinery/computer/arcade/orion_event.dm
+++ b/code/game/machinery/computer/arcade/orion_event.dm
@@ -67,8 +67,8 @@
text = "Oh no! The engine has broken down! \
You can repair it with an engine part, or you \
can make repairs for 3 days."
- emag_message = "You hear some large object lurch to a halt right behind you! When you go to look, nothing's there..."
- emag_sound = 'sound/effects/creak1.ogg'
+ emag_message = span_warning("You hear some large object lurch to a halt right behind you! When you go to look, nothing's there...")
+ emag_sound = 'sound/effects/creak/creak1.ogg'
weight = 2
event_responses = list()
@@ -170,7 +170,7 @@
/datum/orion_event/hull_part/proc/fix_floor(obj/machinery/computer/arcade/orion_trail/game)
game.say("A new floor suddenly appears around [game]. What the hell?")
- playsound(game, 'sound/weapons/genhit.ogg', 100, TRUE)
+ playsound(game, 'sound/items/weapons/genhit.ogg', 100, TRUE)
for(var/turf/open/space/fixed in orange(1, game))
fixed.place_on_top(/turf/open/floor/plating)
@@ -264,7 +264,7 @@
else
to_chat(usr, span_userdanger("Something strikes you from behind! It hurts like hell and feel like a blunt weapon, but nothing is there..."))
gamer.take_bodypart_damage(30)
- playsound(game, 'sound/weapons/genhit2.ogg', 100, TRUE)
+ playsound(game, 'sound/items/weapons/genhit2.ogg', 100, TRUE)
/datum/orion_event/illness
name = "Space Illness"
@@ -321,7 +321,7 @@
gamer.Paralyze(60)
game.say("A sudden gust of powerful wind slams [gamer] into the floor!")
gamer.take_bodypart_damage(25)
- playsound(game, 'sound/weapons/genhit.ogg', 100, TRUE)
+ playsound(game, 'sound/items/weapons/genhit.ogg', 100, TRUE)
/datum/orion_event/changeling_infiltration
name = "Changeling Infiltration"
diff --git a/code/game/machinery/computer/atmos_computers/_air_sensor.dm b/code/game/machinery/computer/atmos_computers/_air_sensor.dm
index 91a616cc5f678..1f4a8bf834098 100644
--- a/code/game/machinery/computer/atmos_computers/_air_sensor.dm
+++ b/code/game/machinery/computer/atmos_computers/_air_sensor.dm
@@ -15,6 +15,8 @@
var/inlet_id
/// The outlet[vent pump] controlled by this sensor
var/outlet_id
+ /// The air alarm connected to this sensor
+ var/obj/machinery/airalarm/connected_airalarm
/obj/machinery/air_sensor/Initialize(mapload)
id_tag = assign_random_name()
@@ -57,7 +59,7 @@
/obj/machinery/air_sensor/examine(mob/user)
. = ..()
- . += span_notice("Use multitool to link it to an injector/vent or reset its ports")
+ . += span_notice("Use a multitool to link it to an injector, vent, or air alarm, or reset its ports.")
. += span_notice("Click with hand to turn it off.")
/obj/machinery/air_sensor/attack_hand(mob/living/user, list/modifiers)
@@ -78,6 +80,11 @@
/obj/machinery/air_sensor/proc/reset()
inlet_id = null
outlet_id = null
+ if(connected_airalarm)
+ connected_airalarm.disconnect_sensor()
+ // if air alarm and sensor were linked at roundstart we allow them to link to new devices
+ connected_airalarm.allow_link_change = TRUE
+ connected_airalarm = null
///right click with multi tool to disconnect everything
/obj/machinery/air_sensor/multitool_act_secondary(mob/living/user, obj/item/tool)
diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm
index 6c4d32658573a..bf475e0136762 100644
--- a/code/game/machinery/computer/camera.dm
+++ b/code/game/machinery/computer/camera.dm
@@ -67,7 +67,7 @@
concurrent_users += user_ref
// Turn on the console
if(length(concurrent_users) == 1 && is_living)
- playsound(src, 'sound/machines/terminal_on.ogg', 25, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_on.ogg', 25, FALSE)
use_energy(active_power_usage)
// Register map objects
cam_screen.display_to(user)
@@ -110,7 +110,7 @@
z = C.z,
status = C.camera_enabled,
))
- if("[C.z]" in z_levels || !C.nanomap_png)
+ if(("[C.z]" in z_levels) || !C.nanomap_png)
continue
z_levels += list("[C.z]" = C.nanomap_png)
// BANDASTATION EDIT END - Nanomap
@@ -179,7 +179,7 @@
if(length(concurrent_users) == 0 && is_living)
active_camera = null
last_camera_turf = null
- playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_off.ogg', 25, FALSE)
/obj/machinery/computer/security/proc/show_camera_static()
cam_screen.vis_contents.Cut()
diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm
index 0e47a752b01d2..6640f5582fa20 100644
--- a/code/game/machinery/computer/camera_advanced.dm
+++ b/code/game/machinery/computer/camera_advanced.dm
@@ -118,7 +118,7 @@
eyeobj.eye_user = null
user.remote_control = null
current_user = null
- playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_off.ogg', 25, FALSE)
/obj/machinery/computer/camera_advanced/on_set_is_operational(old_value)
if(!is_operational)
@@ -296,7 +296,7 @@
continue
T["[netcam.c_tag][netcam.can_use() ? null : " (Deactivated)"]"] = netcam
- playsound(origin, 'sound/machines/terminal_prompt.ogg', 25, FALSE)
+ playsound(origin, 'sound/machines/terminal/terminal_prompt.ogg', 25, FALSE)
var/camera = tgui_input_list(usr, "Camera to view", "Cameras", T)
if(isnull(camera))
return
@@ -305,12 +305,12 @@
var/obj/machinery/camera/final = T[camera]
playsound(src, SFX_TERMINAL_TYPE, 25, FALSE)
if(final)
- playsound(origin, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE)
+ playsound(origin, 'sound/machines/terminal/terminal_prompt_confirm.ogg', 25, FALSE)
remote_eye.setLoc(get_turf(final))
owner.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
owner.clear_fullscreen("flash", 3) //Shorter flash than normal since it's an ~~advanced~~ console!
else
- playsound(origin, 'sound/machines/terminal_prompt_deny.ogg', 25, FALSE)
+ playsound(origin, 'sound/machines/terminal/terminal_prompt_deny.ogg', 25, FALSE)
/datum/action/innate/camera_multiz_up
name = "Move up a floor"
diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm
index d48b62977893c..abbfb055e1647 100644
--- a/code/game/machinery/computer/communications.dm
+++ b/code/game/machinery/computer/communications.dm
@@ -130,7 +130,7 @@
battlecruiser_called = TRUE
caller_card.use_charge(user)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(summon_battlecruiser), caller_card.team), rand(20 SECONDS, 1 MINUTES))
- playsound(src, 'sound/machines/terminal_alert.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_alert.ogg', 50, FALSE)
return TRUE
if(obj_flags & EMAGGED)
@@ -139,7 +139,7 @@
if (authenticated)
authorize_access = SSid_access.get_region_access_list(list(REGION_ALL_STATION))
balloon_alert(user, "routing circuits scrambled")
- playsound(src, 'sound/machines/terminal_alert.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_alert.ogg', 50, FALSE)
return TRUE
/obj/machinery/computer/communications/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
@@ -193,11 +193,11 @@
var/obj/item/card/id/id_card = held_item?.GetID()
if (!istype(id_card))
to_chat(user, span_warning("You need to swipe your ID!"))
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_prompt_deny.ogg', 50, FALSE)
return
if (!(ACCESS_CAPTAIN in id_card.access))
to_chat(user, span_warning("You are not authorized to do this!"))
- playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_prompt_deny.ogg', 50, FALSE)
return
var/new_sec_level = SSsecurity_level.text_level_to_number(params["newSecurityLevel"])
@@ -209,7 +209,7 @@
SSsecurity_level.set_level(new_sec_level)
to_chat(user, span_notice("Authorization confirmed. Modifying security level."))
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_prompt_confirm.ogg', 50, FALSE)
// Only notify people if an actual change happened
user.log_message("changed the security level to [params["newSecurityLevel"]] with [src].", LOG_GAME)
@@ -234,7 +234,7 @@
if (!COOLDOWN_FINISHED(src, important_action_cooldown))
return
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_prompt_confirm.ogg', 50, FALSE)
var/message = trim(html_encode(params["message"]), MAX_MESSAGE_LEN)
var/emagged = obj_flags & EMAGGED
@@ -300,7 +300,7 @@
to_chat(user, span_notice("Request sent."))
user.log_message("has requested the nuclear codes from CentCom with reason \"[reason]\"", LOG_SAY)
priority_announce("The codes for the on-station nuclear self-destruct have been requested by [user]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self-Destruct Codes Requested", SSstation.announcer.get_rand_report_sound())
- playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_prompt.ogg', 50, FALSE)
COOLDOWN_START(src, important_action_cooldown, IMPORTANT_ACTION_COOLDOWN)
if ("restoreBackupRoutingData")
if (!authenticated_as_non_silicon_captain(user))
@@ -308,7 +308,7 @@
if (!(obj_flags & EMAGGED))
return
to_chat(user, span_notice("Backup routing data restored."))
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_prompt_confirm.ogg', 50, FALSE)
obj_flags &= ~EMAGGED
if ("sendToOtherSector")
if (!authenticated_as_non_silicon_captain(user))
@@ -336,7 +336,7 @@
log_admin_private("[key_name(user)] has passed the soft filter for \"[soft_filter_result[CHAT_FILTER_INDEX_WORD]]\". They may be using a disallowed term for a cross-station message. Increasing delay time to reject.\n\n Message: \"[message]\"")
GLOB.communications_controller.soft_filtering = TRUE
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_prompt_confirm.ogg', 50, FALSE)
var/destination = params["destination"]
@@ -389,7 +389,7 @@
authenticated = FALSE
authorize_access = null
authorize_name = null
- playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_off.ogg', 50, FALSE)
return
if (obj_flags & EMAGGED)
@@ -397,7 +397,7 @@
authorize_access = SSid_access.get_region_access_list(list(REGION_ALL_STATION))
authorize_name = "Unknown"
to_chat(user, span_warning("[src] lets out a quiet alarm as its login is overridden."))
- playsound(src, 'sound/machines/terminal_alert.ogg', 25, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_alert.ogg', 25, FALSE)
else if(isliving(user))
var/mob/living/L = user
var/obj/item/card/id/id_card = L.get_idcard(hand_first = TRUE)
@@ -407,7 +407,7 @@
authorize_name = "[id_card.registered_name] - [id_card.assignment]"
state = STATE_MAIN
- playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_on.ogg', 50, FALSE)
imprint_gps(gps_tag = "Encrypted Communications Channel")
if ("toggleEmergencyAccess")
@@ -672,7 +672,7 @@
/// Returns TRUE if the user can buy shuttles.
/// If they cannot, returns FALSE or a string detailing why.
/obj/machinery/computer/communications/proc/can_buy_shuttles(mob/user)
- if (!SSmapping.config.allow_custom_shuttles)
+ if (!SSmapping.current_map.allow_custom_shuttles)
return FALSE
if (HAS_SILICON_ACCESS(user))
return FALSE
@@ -721,7 +721,7 @@
if(!GLOB.communications_controller.can_announce(user, is_ai))
to_chat(user, span_alert("Intercomms recharging. Please stand by."))
return
- var/input = tgui_input_text(user, "Message to announce to the station crew", "Announcement")
+ var/input = tgui_input_text(user, "Message to announce to the station crew", "Announcement", max_length = MAX_MESSAGE_LEN)
if(!input || !user.can_perform_action(src, ALLOW_SILICON_REACH))
return
if(user.try_speak(input))
diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm
index b5e5a915ce2bf..adac393d7bedb 100644
--- a/code/game/machinery/computer/crew.dm
+++ b/code/game/machinery/computer/crew.dm
@@ -223,7 +223,7 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
continue
// Check if their uniform is in a compatible mode.
- if((uniform.has_sensor <= NO_SENSORS) || !uniform.sensor_mode)
+ if((uniform.has_sensor == NO_SENSORS) || !uniform.sensor_mode)
stack_trace("Human without active suit sensors is in suit_sensors_list: [tracked_human] ([tracked_human.type]) ([uniform.type])")
continue
@@ -245,6 +245,19 @@ GLOBAL_DATUM_INIT(crewmonitor, /datum/crewmonitor, new)
if (jobs[trim_assignment] != null)
entry["ijob"] = jobs[trim_assignment]
+ // Broken sensors show garbage data
+ if (uniform.has_sensor == BROKEN_SENSORS)
+ entry["life_status"] = rand(0,1)
+ entry["area"] = pick_list (ION_FILE, "ionarea")
+ entry["oxydam"] = rand(0,175)
+ entry["toxdam"] = rand(0,175)
+ entry["burndam"] = rand(0,175)
+ entry["brutedam"] = rand(0,175)
+ entry["health"] = -50
+ entry["can_track"] = tracked_living_mob.can_track()
+ results[++results.len] = entry
+ continue
+
// Current status
if (sensor_mode >= SENSOR_LIVING)
entry["life_status"] = tracked_living_mob.stat
diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm
index e1612ae7ef2e5..58de43525357c 100644
--- a/code/game/machinery/computer/dna_console.dm
+++ b/code/game/machinery/computer/dna_console.dm
@@ -683,7 +683,7 @@
var/datum/mutation/human/target_mutation = get_mut_by_ref(bref, search_flags)
// Prompt for modifier string
- var/new_sequence_input = tgui_input_text(usr, "Enter a replacement sequence", "Inherent Gene Replacement", 32, encode = FALSE)
+ var/new_sequence_input = tgui_input_text(usr, "Enter a replacement sequence", "Inherent Gene Replacement", max_length = 32, encode = FALSE)
// Drop out if the string is the wrong length
if(length(new_sequence_input) != 32)
return
@@ -1347,7 +1347,7 @@
// However, if this is the case, we can't make a complete injector and
// this catches that edge case
if(!buffer_slot["name"] || !buffer_slot["UF"] || !buffer_slot["blood_type"])
- to_chat(usr,"Genetic data corrupted, unable to create injector.")
+ to_chat(usr,span_warning("Genetic data corrupted, unable to create injector."))
return
I = new /obj/item/dnainjector/timed(loc)
@@ -1731,7 +1731,7 @@
// However, if this is the case, we can't make a complete injector and
// this catches that edge case
if(!buffer_slot["UF"])
- to_chat(usr,"Genetic data corrupted, unable to apply genetic data.")
+ to_chat(usr,span_warning("Genetic data corrupted, unable to apply genetic data."))
return FALSE
COOLDOWN_START(src, enzyme_copy_timer, ENZYME_COPY_BASE_COOLDOWN)
scanner_occupant.dna.unique_features = buffer_slot["UF"]
diff --git a/code/game/machinery/computer/mechlaunchpad.dm b/code/game/machinery/computer/mechlaunchpad.dm
index 050f3447a8ce4..7590e690d07a6 100644
--- a/code/game/machinery/computer/mechlaunchpad.dm
+++ b/code/game/machinery/computer/mechlaunchpad.dm
@@ -66,7 +66,7 @@
/// A proc that makes random beeping sounds for a set amount of time, the sounds are separated by a random amount of time.
/obj/machinery/computer/mechpad/proc/random_beeps(mob/user, time = 0, mintime = 0, maxtime = 1)
- var/static/list/beep_sounds = list('sound/machines/terminal_prompt_confirm.ogg', 'sound/machines/terminal_prompt_deny.ogg', 'sound/machines/terminal_error.ogg', 'sound/machines/terminal_select.ogg', 'sound/machines/terminal_success.ogg')
+ var/static/list/beep_sounds = list('sound/machines/terminal/terminal_prompt_confirm.ogg', 'sound/machines/terminal/terminal_prompt_deny.ogg', 'sound/machines/terminal/terminal_error.ogg', 'sound/machines/terminal/terminal_select.ogg', 'sound/machines/terminal/terminal_success.ogg')
var/time_to_spend = 0
var/orig_time = time
while(time > 0)
@@ -132,7 +132,7 @@
if(!can_launch(user, where))
return
flick("mechpad-launch", connected_mechpad)
- playsound(connected_mechpad, 'sound/machines/triple_beep.ogg', 50, TRUE)
+ playsound(connected_mechpad, 'sound/machines/beep/triple_beep.ogg', 50, TRUE)
addtimer(CALLBACK(src, PROC_REF(start_launch), user, where), 1 SECONDS)
/obj/machinery/computer/mechpad/proc/start_launch(mob/user, obj/machinery/mechpad/where)
diff --git a/code/game/machinery/computer/prisoner/_prisoner.dm b/code/game/machinery/computer/prisoner/_prisoner.dm
index 9777c1b209cdf..828d981bec644 100644
--- a/code/game/machinery/computer/prisoner/_prisoner.dm
+++ b/code/game/machinery/computer/prisoner/_prisoner.dm
@@ -35,7 +35,7 @@
return
contained_id = new_id
balloon_alert_to_viewers("id inserted")
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 50, FALSE)
/obj/machinery/computer/prisoner/proc/id_eject(mob/user)
if(isnull(contained_id))
@@ -48,7 +48,7 @@
contained_id.forceMove(drop_location())
balloon_alert_to_viewers("id ejected")
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 50, FALSE)
/obj/machinery/computer/prisoner/attackby(obj/item/weapon, mob/user, params)
if(istype(weapon, /obj/item/card/id/advanced/prisoner))
diff --git a/code/game/machinery/computer/prisoner/gulag_teleporter.dm b/code/game/machinery/computer/prisoner/gulag_teleporter.dm
index f03c08a8d821b..66440bb97c3fd 100644
--- a/code/game/machinery/computer/prisoner/gulag_teleporter.dm
+++ b/code/game/machinery/computer/prisoner/gulag_teleporter.dm
@@ -72,7 +72,7 @@
if(.)
return
if(isliving(usr))
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_prompt_confirm.ogg', 50, FALSE)
if(!allowed(usr))
to_chat(usr, span_warning("Access denied."))
return
@@ -144,7 +144,7 @@
user.log_message("teleported [key_name(prisoner)] to the Labor Camp [COORD(beacon)] for [id_goal_not_set ? "default goal of ":""][contained_id.goal] points.", LOG_GAME)
prisoner.log_message("teleported to Labor Camp [COORD(beacon)] by [key_name(user)] for [id_goal_not_set ? "default goal of ":""][contained_id.goal] points.", LOG_GAME, log_globally = FALSE)
teleporter.handle_prisoner(contained_id, temporary_record)
- playsound(src, 'sound/weapons/emitter.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ playsound(src, 'sound/items/weapons/emitter.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
prisoner.forceMove(get_turf(beacon))
prisoner.Paralyze(40) // small travel dizziness
to_chat(prisoner, span_warning("The teleportation makes you a little dizzy."))
diff --git a/code/game/machinery/computer/prisoner/management.dm b/code/game/machinery/computer/prisoner/management.dm
index 32c2ddba984df..a971dce2d9d02 100644
--- a/code/game/machinery/computer/prisoner/management.dm
+++ b/code/game/machinery/computer/prisoner/management.dm
@@ -52,20 +52,20 @@ GLOBAL_LIST_EMPTY_TYPED(tracked_implants, /obj/item/implant)
CRASH("[usr] potentially spoofed ui action [action] on prisoner console without the console being logged in.")
if(isliving(usr))
- playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_prompt_confirm.ogg', 50, FALSE)
switch(action)
if("login")
if(allowed(usr))
authenticated = TRUE
- playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_on.ogg', 50, FALSE)
else
- playsound(src, 'sound/machines/terminal_error.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 50, FALSE)
return TRUE
if("logout")
authenticated = FALSE
- playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/terminal/terminal_off.ogg', 50, FALSE)
return TRUE
if("insert_id")
diff --git a/code/game/machinery/computer/records/records.dm b/code/game/machinery/computer/records/records.dm
index d53895300438d..7d01d973549b3 100644
--- a/code/game/machinery/computer/records/records.dm
+++ b/code/game/machinery/computer/records/records.dm
@@ -57,7 +57,7 @@
expunge_record_info(target)
balloon_alert(user, "record expunged")
- playsound(src, 'sound/machines/terminal_eject.ogg', 70, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_eject.ogg', 70, TRUE)
investigate_log("[key_name(user)] expunged the record of [target.name].", INVESTIGATE_RECORDS)
return TRUE
@@ -69,7 +69,7 @@
if("logout")
balloon_alert(user, "logged out")
- playsound(src, 'sound/machines/terminal_off.ogg', 70, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_off.ogg', 70, TRUE)
authenticated = FALSE
return TRUE
@@ -82,14 +82,14 @@
ui.close()
balloon_alert(user, "purging records...")
- playsound(src, 'sound/machines/terminal_alert.ogg', 70, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_alert.ogg', 70, TRUE)
if(do_after(user, 5 SECONDS))
for(var/datum/record/crew/entry in GLOB.manifest.general)
expunge_record_info(entry)
balloon_alert(user, "records purged")
- playsound(src, 'sound/machines/terminal_off.ogg', 70, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_off.ogg', 70, TRUE)
investigate_log("[key_name(user)] purged all records.", INVESTIGATE_RECORDS)
else
balloon_alert(user, "interrupted!")
@@ -139,23 +139,23 @@
if(!authenticated && !allowed(user))
balloon_alert(user, "access denied")
- playsound(src, 'sound/machines/terminal_error.ogg', 70, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 70, TRUE)
return FALSE
- if(mugshot.picture.psize_x > world.icon_size || mugshot.picture.psize_y > world.icon_size)
+ if(mugshot.picture.psize_x > ICON_SIZE_X || mugshot.picture.psize_y > ICON_SIZE_Y)
balloon_alert(user, "photo too large!")
- playsound(src, 'sound/machines/terminal_error.ogg', 70, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 70, TRUE)
return FALSE
var/trimmed = copytext(mugshot.name, 9, MAX_NAME_LEN) // Remove "photo - "
- var/name = tgui_input_text(user, "Enter the name of the new record.", "New Record", trimmed, MAX_NAME_LEN)
+ var/name = tgui_input_text(user, "Enter the name of the new record.", "New Record", trimmed, max_length = MAX_NAME_LEN)
if(!name || !is_operational || !user.can_perform_action(src, ALLOW_SILICON_REACH) || !mugshot || QDELETED(mugshot) || QDELETED(src))
return FALSE
new /datum/record/crew(name = name, character_appearance = mugshot.picture.picture_image)
balloon_alert(user, "record created")
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 70, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 70, TRUE)
qdel(mugshot)
@@ -168,10 +168,10 @@
if(!allowed(user))
balloon_alert(user, "access denied")
- playsound(src, 'sound/machines/terminal_error.ogg', 70, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 70, TRUE)
return FALSE
balloon_alert(user, "logged in")
- playsound(src, 'sound/machines/terminal_on.ogg', 70, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_on.ogg', 70, TRUE)
return TRUE
diff --git a/code/game/machinery/computer/records/security.dm b/code/game/machinery/computer/records/security.dm
index bdfa996dad68f..8b32bf9af0a15 100644
--- a/code/game/machinery/computer/records/security.dm
+++ b/code/game/machinery/computer/records/security.dm
@@ -204,13 +204,13 @@
var/input_name = strip_html_full(params["name"], MAX_CRIME_NAME_LEN)
if(!input_name)
to_chat(usr, span_warning("You must enter a name for the crime."))
- playsound(src, 'sound/machines/terminal_error.ogg', 75, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 75, TRUE)
return FALSE
var/max = CONFIG_GET(number/maxfine)
if(params["fine"] > max)
to_chat(usr, span_warning("The maximum fine is [max] credits."))
- playsound(src, 'sound/machines/terminal_error.ogg', 75, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 75, TRUE)
return FALSE
var/input_details
@@ -321,7 +321,7 @@
/// Finishes printing, resets the printer.
/obj/machinery/computer/records/security/proc/print_finish(obj/item/printable)
printing = FALSE
- playsound(src, 'sound/machines/terminal_eject.ogg', 100, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_eject.ogg', 100, TRUE)
printable.forceMove(loc)
return TRUE
@@ -330,7 +330,7 @@
/obj/machinery/computer/records/security/proc/print_record(mob/user, datum/record/crew/target, list/params)
if(printing)
balloon_alert(user, "printer busy")
- playsound(src, 'sound/machines/terminal_error.ogg', 100, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 100, TRUE)
return FALSE
printing = TRUE
diff --git a/code/game/machinery/computer/telescreen.dm b/code/game/machinery/computer/telescreen.dm
index 6058d3bf131ab..6021c954cabfc 100644
--- a/code/game/machinery/computer/telescreen.dm
+++ b/code/game/machinery/computer/telescreen.dm
@@ -55,7 +55,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/security/telescreen/entertai
/obj/machinery/computer/security/telescreen/entertainment/Initialize(mapload)
. = ..()
- RegisterSignal(src, COMSIG_CLICK, PROC_REF(BigClick))
find_and_hang_on_wall()
speakers = new(src)
@@ -63,16 +62,66 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/computer/security/telescreen/entertai
. = ..()
QDEL_NULL(speakers)
-/// Bypass clickchain to allow humans to use the telescreen from a distance
-/obj/machinery/computer/security/telescreen/entertainment/proc/BigClick()
- SIGNAL_HANDLER
+/obj/machinery/computer/security/telescreen/entertainment/examine(mob/user)
+ . = ..()
+ . += length(network) ? span_notice("The TV is broadcasting something!") : span_notice("There's nothing on TV.")
+
+/obj/machinery/computer/security/telescreen/entertainment/ui_state(mob/user)
+ return GLOB.always_state
+
+// Snowflake ui status to allow mobs to watch TV from across the room,
+// but only allow adjacent mobs / tk users / silicon to change the channel
+/obj/machinery/computer/security/telescreen/entertainment/ui_status(mob/living/user, datum/ui_state/state)
+ if(!can_watch_tv(user))
+ return UI_CLOSE
+ if(!isliving(user))
+ return isAdminGhostAI(user) ? UI_INTERACTIVE : UI_UPDATE
+ if(user.stat >= SOFT_CRIT)
+ return UI_UPDATE
+
+ var/can_range = FALSE
+ if(iscarbon(user))
+ var/mob/living/carbon/carbon_user = user
+ if(carbon_user.dna?.check_mutation(/datum/mutation/human/telekinesis) && tkMaxRangeCheck(user, src))
+ can_range = TRUE
+ if(HAS_SILICON_ACCESS(user) || (user.interaction_range && user.interaction_range >= get_dist(user, src)))
+ can_range = TRUE
+
+ if((can_range || user.CanReach(src)) && ISADVANCEDTOOLUSER(user))
+ if(user.incapacitated)
+ return UI_UPDATE
+ if(!can_range && user.can_hold_items() && (user.usable_hands <= 0 || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)))
+ return UI_UPDATE
+ return UI_INTERACTIVE
+ return UI_UPDATE
+
+/obj/machinery/computer/security/telescreen/entertainment/Click(location, control, params)
+ if(world.time <= usr.next_click + 1)
+ return // just so someone can't turn an auto clicker on and spam tvs
- if(!network.len)
- balloon_alert(usr, "nothing on TV!")
+ . = ..()
+ if(!can_watch_tv(usr))
return
-
+ if((!length(network) && !Adjacent(usr)) || LAZYACCESS(params2list(params), SHIFT_CLICK)) // let people examine
+ return
+ // Lets us see the tv regardless of click results
INVOKE_ASYNC(src, TYPE_PROC_REF(/atom, interact), usr)
+/obj/machinery/computer/security/telescreen/entertainment/proc/can_watch_tv(mob/living/watcher)
+ if(!is_operational)
+ return FALSE
+ if((watcher.sight & SEE_OBJS) || HAS_SILICON_ACCESS(watcher))
+ if(get_dist(watcher, src) > 7)
+ return FALSE
+ else
+ if(!can_see(watcher, src, 7))
+ return FALSE
+ if(watcher.is_blind())
+ return FALSE
+ if(!isobserver(watcher) && watcher.stat >= UNCONSCIOUS)
+ return FALSE
+ return TRUE
+
/// Sets the monitor's icon to the selected state, and says an announcement
/obj/machinery/computer/security/telescreen/entertainment/proc/notify(on, announcement)
if(on && icon_state == icon_state_off)
diff --git a/code/game/machinery/computer/warrant.dm b/code/game/machinery/computer/warrant.dm
index 3b73a8b75bfea..71455fc5a2e40 100644
--- a/code/game/machinery/computer/warrant.dm
+++ b/code/game/machinery/computer/warrant.dm
@@ -88,26 +88,26 @@
if(!isliving(user) || issilicon(user))
to_chat(user, span_warning("ACCESS DENIED"))
- playsound(src, 'sound/machines/terminal_error.ogg', 100, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 100, TRUE)
return FALSE
var/mob/living/player = user
var/obj/item/card/id/auth = player.get_idcard(TRUE)
if(!auth)
to_chat(user, span_warning("ACCESS DENIED: No ID card detected."))
- playsound(src, 'sound/machines/terminal_error.ogg', 100, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 100, TRUE)
return FALSE
var/datum/bank_account/account = auth.registered_account
if(!account?.account_holder || account.account_holder == "Unassigned")
to_chat(user, span_warning("ACCESS DENIED: No account linked to ID."))
- playsound(src, 'sound/machines/terminal_error.ogg', 100, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 100, TRUE)
return FALSE
var/amount = params["amount"]
if(!amount || !isnum(amount) || amount > warrant.fine || !account.adjust_money(-amount, "Paid fine for [target.name]"))
to_chat(user, span_warning("ACCESS DENIED: Invalid amount."))
- playsound(src, 'sound/machines/terminal_error.ogg', 100, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 100, TRUE)
return FALSE
account.bank_card_talk("You have paid [amount]cr towards [target.name]'s fine of [warrant.fine]cr.")
@@ -139,7 +139,7 @@
/// Finishes printing, resets the printer.
/obj/machinery/computer/warrant/proc/print_finish(obj/item/paper/bounty)
printing = FALSE
- playsound(src, 'sound/machines/terminal_eject.ogg', 100, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_eject.ogg', 100, TRUE)
bounty.forceMove(loc)
return TRUE
@@ -148,7 +148,7 @@
/obj/machinery/computer/warrant/proc/print_bounty(mob/user, list/params)
if(printing)
balloon_alert(user, "printer busy")
- playsound(src, 'sound/machines/terminal_error.ogg', 100, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_error.ogg', 100, TRUE)
return FALSE
var/datum/record/crew/target = locate(params["crew_ref"]) in GLOB.manifest.general
diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm
index d2e3c895ebd1d..43946538ac51b 100644
--- a/code/game/machinery/dance_machine.dm
+++ b/code/game/machinery/dance_machine.dm
@@ -50,11 +50,11 @@
return UI_CLOSE
if(!allowed(user))
to_chat(user,span_warning("Error: Access Denied."))
- user.playsound_local(src, 'sound/misc/compiler-failure.ogg', 25, TRUE)
+ user.playsound_local(src, 'sound/machines/compiler/compiler-failure.ogg', 25, TRUE)
return UI_CLOSE
if(!length(music_player.songs))
to_chat(user,span_warning("Error: No music tracks have been authorized for your station. Petition Central Command to resolve this issue."))
- user.playsound_local(src, 'sound/misc/compiler-failure.ogg', 25, TRUE)
+ user.playsound_local(src, 'sound/machines/compiler/compiler-failure.ogg', 25, TRUE)
return UI_CLOSE
return ..()
@@ -79,7 +79,7 @@
to_chat(usr, span_warning("Error: The device is still resetting from the last activation, \
it will be ready again in [DisplayTimeText(COOLDOWN_TIMELEFT(src, jukebox_song_cd))]."))
if(COOLDOWN_FINISHED(src, jukebox_error_cd))
- playsound(src, 'sound/misc/compiler-failure.ogg', 33, TRUE)
+ playsound(src, 'sound/machines/compiler/compiler-failure.ogg', 33, TRUE)
COOLDOWN_START(src, jukebox_error_cd, 15 SECONDS)
return TRUE
@@ -134,7 +134,7 @@
if(!QDELING(src))
COOLDOWN_START(src, jukebox_song_cd, 10 SECONDS)
- playsound(src,'sound/machines/terminal_off.ogg',50,TRUE)
+ playsound(src,'sound/machines/terminal/terminal_off.ogg',50,TRUE)
update_use_power(IDLE_POWER_USE)
update_appearance(UPDATE_ICON_STATE)
return TRUE
diff --git a/code/game/machinery/dish_drive.dm b/code/game/machinery/dish_drive.dm
index 74ca492657b6e..544e58bcabeea 100644
--- a/code/game/machinery/dish_drive.dm
+++ b/code/game/machinery/dish_drive.dm
@@ -75,7 +75,7 @@
LAZYREMOVE(dish_drive_contents, dish)
user.put_in_hands(dish)
balloon_alert(user, "[dish] taken")
- playsound(src, 'sound/items/pshoom.ogg', 50, TRUE)
+ playsound(src, 'sound/items/pshoom/pshoom.ogg', 50, TRUE)
flick("synthesizer_beam", src)
/obj/machinery/dish_drive/wrench_act(mob/living/user, obj/item/tool)
@@ -89,7 +89,7 @@
return
LAZYADD(dish_drive_contents, dish)
balloon_alert(user, "[dish] placed in drive")
- playsound(src, 'sound/items/pshoom.ogg', 50, TRUE)
+ playsound(src, 'sound/items/pshoom/pshoom.ogg', 50, TRUE)
flick("synthesizer_beam", src)
return
else if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), dish))
@@ -129,7 +129,7 @@
LAZYADD(dish_drive_contents, dish)
visible_message(span_notice("[src] beams up [dish]!"))
dish.forceMove(src)
- playsound(src, 'sound/items/pshoom.ogg', 50, TRUE)
+ playsound(src, 'sound/items/pshoom/pshoom.ogg', 50, TRUE)
flick("synthesizer_beam", src)
else
step_towards(dish, src)
@@ -153,7 +153,7 @@
if(!bin)
if(manual)
visible_message(span_warning("[src] buzzes. There are no disposal bins in range!"))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, TRUE)
return
var/disposed = 0
for(var/obj/item/dish in dish_drive_contents)
@@ -166,8 +166,8 @@
disposed++
if (disposed)
visible_message(span_notice("[src] [pick("whooshes", "bwooms", "fwooms", "pshooms")] and beams [disposed] stored item\s into the nearby [bin.name]."))
- playsound(src, 'sound/items/pshoom.ogg', 50, TRUE)
- playsound(bin, 'sound/items/pshoom.ogg', 50, TRUE)
+ playsound(src, 'sound/items/pshoom/pshoom.ogg', 50, TRUE)
+ playsound(bin, 'sound/items/pshoom/pshoom.ogg', 50, TRUE)
Beam(bin, icon_state = "rped_upgrade", time = 5)
bin.update_appearance()
flick("synthesizer_beam", src)
diff --git a/code/game/machinery/dna_infuser/dna_infuser.dm b/code/game/machinery/dna_infuser/dna_infuser.dm
index cc2641d32971e..961092c635b33 100644
--- a/code/game/machinery/dna_infuser/dna_infuser.dm
+++ b/code/game/machinery/dna_infuser/dna_infuser.dm
@@ -67,7 +67,7 @@
return
if(occupant && infusing_from)
if(!occupant.can_infuse(user))
- playsound(src, 'sound/machines/scanbuzz.ogg', 35, vary = TRUE)
+ playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 35, vary = TRUE)
return
balloon_alert(user, "starting DNA infusion...")
start_infuse()
diff --git a/code/game/machinery/dna_infuser/infuser_entries/infuser_tier_zero_entries.dm b/code/game/machinery/dna_infuser/infuser_entries/infuser_tier_zero_entries.dm
index 235986cbd0ddb..670abc2d87bbf 100644
--- a/code/game/machinery/dna_infuser/infuser_entries/infuser_tier_zero_entries.dm
+++ b/code/game/machinery/dna_infuser/infuser_entries/infuser_tier_zero_entries.dm
@@ -75,7 +75,7 @@
desc = "EVERYONE CALM DOWN! I'm not implying anything with this entry. Are we really so surprised that felinids are humans with mixed feline DNA?"
threshold_desc = DNA_INFUSION_NO_THRESHOLD
qualities = list(
- "oh, let me guess, you're a big fan of those japanese tourist bots",
+ "oh, let me guess, you're a big fan of those Japanese tourist bots",
)
input_obj_or_mob = list(
/mob/living/basic/pet/cat,
diff --git a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm
index 45d5f3ddfd997..4f8d38aa99971 100644
--- a/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm
+++ b/code/game/machinery/dna_infuser/organ_sets/rat_organs.dm
@@ -130,7 +130,7 @@
. = ..()
if(prob(5))
owner.emote("squeaks")
- playsound(owner, 'sound/creatures/mousesqueek.ogg', 100)
+ playsound(owner, 'sound/mobs/non-humanoids/mouse/mousesqueek.ogg', 100)
#undef RAT_ORGAN_COLOR
#undef RAT_SCLERA_COLOR
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index a33895e5569b7..8033c697bfa3d 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -125,12 +125,12 @@
var/normalspeed = TRUE
var/cutAiWire = FALSE
var/autoname = FALSE
- var/doorOpen = 'sound/machines/airlock.ogg'
- var/doorClose = 'sound/machines/airlockclose.ogg'
- var/doorDeni = 'sound/machines/deniedbeep.ogg' // i'm thinkin' Deni's
- var/boltUp = 'sound/machines/boltsup.ogg'
- var/boltDown = 'sound/machines/boltsdown.ogg'
- var/noPower = 'sound/machines/doorclick.ogg'
+ var/doorOpen = 'sound/machines/airlock/airlock.ogg'
+ var/doorClose = 'sound/machines/airlock/airlockclose.ogg'
+ var/doorDeni = 'sound/machines/beep/deniedbeep.ogg' // i'm thinkin' Deni's
+ var/boltUp = 'sound/machines/airlock/boltsup.ogg'
+ var/boltDown = 'sound/machines/airlock/boltsdown.ogg'
+ var/noPower = 'sound/machines/airlock/doorclick.ogg'
/// What airlock assembly mineral plating was applied to
var/previous_airlock = /obj/structure/door_assembly
/// Material of inner filling; if its an airlock with glass, this should be set to "glass"
@@ -1051,7 +1051,7 @@
to_chat(user, span_warning("[src] has already been sealed!"))
return
user.visible_message(span_notice("[user] begins sealing [src]."), span_notice("You begin sealing [src]."))
- playsound(src, 'sound/items/jaws_pry.ogg', 30, TRUE)
+ playsound(src, 'sound/items/tools/jaws_pry.ogg', 30, TRUE)
if(!do_after(user, airlockseal.seal_time, target = src))
return
if(!density)
@@ -1063,7 +1063,7 @@
if(!user.transferItemToLoc(airlockseal, src))
to_chat(user, span_warning("For some reason, you can't attach [airlockseal]!"))
return
- playsound(src, 'sound/machines/airlockforced.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/airlock/airlockforced.ogg', 30, TRUE)
user.visible_message(span_notice("[user] finishes sealing [src]."), span_notice("You finish sealing [src]."))
seal = airlockseal
modify_max_integrity(max_integrity * AIRLOCK_SEAL_MULTIPLIER)
@@ -1137,12 +1137,12 @@
to_chat(user, span_warning("You don't have the dexterity to remove the seal!"))
return TRUE
user.visible_message(span_notice("[user] begins removing the seal from [src]."), span_notice("You begin removing [src]'s pneumatic seal."))
- playsound(src, 'sound/machines/airlockforced.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/airlock/airlockforced.ogg', 30, TRUE)
if(!do_after(user, airlockseal.unseal_time, target = src))
return TRUE
if(!seal)
return TRUE
- playsound(src, 'sound/items/jaws_pry.ogg', 30, TRUE)
+ playsound(src, 'sound/items/tools/jaws_pry.ogg', 30, TRUE)
airlockseal.forceMove(get_turf(user))
user.visible_message(span_notice("[user] finishes removing the seal from [src]."), span_notice("You finish removing [src]'s pneumatic seal."))
seal = null
@@ -1176,10 +1176,10 @@
return TRUE
/obj/machinery/door/airlock/try_to_crowbar(obj/item/I, mob/living/user, forced = FALSE)
- if(I?.tool_behaviour == TOOL_CROWBAR && should_try_removing_electronics() && !operating)
+ if(I.tool_behaviour == TOOL_CROWBAR && should_try_removing_electronics() && !operating)
user.visible_message(span_notice("[user] removes the electronics from the airlock assembly."), \
span_notice("You start to remove electronics from the airlock assembly..."))
- if(I.use_tool(src, user, 40, volume=100))
+ if(I.use_tool(src, user, 40, volume = 100))
deconstruct(TRUE, user)
return
if(seal)
@@ -1202,10 +1202,10 @@
if(!prying_so_hard)
var/time_to_open = 50
- playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE) //is it aliens or just the CE being a dick?
+ playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 100, TRUE) //is it aliens or just the CE being a dick?
prying_so_hard = TRUE
- if(do_after(user, time_to_open, src))
- if(check_electrified && shock(user,100))
+ if(I.use_tool(src, user, time_to_open, volume = 100))
+ if(check_electrified && shock(user, 100))
prying_so_hard = FALSE
return
open(BYPASS_DOOR_CHECKS)
@@ -1303,7 +1303,7 @@
return TRUE
if(BYPASS_DOOR_CHECKS) // No power usage, special sound, get it open.
- playsound(src, 'sound/machines/airlockforced.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/airlock/airlockforced.ogg', 30, TRUE)
return TRUE
else
@@ -1381,7 +1381,7 @@
return TRUE
if(BYPASS_DOOR_CHECKS)
- playsound(src, 'sound/machines/airlockforced.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/airlock/airlockforced.ogg', 30, TRUE)
return TRUE
else
@@ -1474,7 +1474,7 @@
var/time_to_open = 5 //half a second
if(hasPower())
time_to_open = 5 SECONDS //Powered airlocks take longer to open, and are loud.
- playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE)
+ playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 100, TRUE)
if(do_after(user, time_to_open, src))
@@ -2228,7 +2228,7 @@
if(!hasPower())
to_chat(user, span_notice("You begin unlocking the airlock safety mechanism..."))
if(do_after(user, 15 SECONDS, target = src))
- try_to_crowbar(null, user, TRUE)
+ try_to_crowbar(src, user, TRUE)
return TRUE
else
// always open from the space side
@@ -2374,7 +2374,7 @@
new /obj/effect/temp_visual/cult/sac(loc)
var/atom/throwtarget
throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src)))
- SEND_SOUND(L, sound(pick('sound/hallucinations/turn_around1.ogg','sound/hallucinations/turn_around2.ogg'),0,1,50))
+ SEND_SOUND(L, sound(pick('sound/effects/hallucinations/turn_around1.ogg','sound/effects/hallucinations/turn_around2.ogg'),0,1,50))
flash_color(L, flash_color=COLOR_CULT_RED, flash_time=20)
L.Paralyze(40)
L.throw_at(throwtarget, 5, 1)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index d75b5e17174c5..5449f7f7cee4c 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -210,7 +210,7 @@
if(!red_alert_access)
return
audible_message(span_notice("[src] whirr[p_s()] as [p_they()] automatically lift[p_s()] access requirements!"))
- playsound(src, 'sound/machines/boltsup.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/airlock/boltsup.ogg', 50, TRUE)
/obj/machinery/door/proc/try_safety_unlock(mob/user)
return FALSE
@@ -334,7 +334,7 @@
return
-/obj/machinery/door/proc/try_to_crowbar(obj/item/acting_object, mob/user)
+/obj/machinery/door/proc/try_to_crowbar(obj/item/acting_object, mob/user, forced = FALSE)
return
/// Called when the user right-clicks on the door with a crowbar.
@@ -399,13 +399,13 @@
switch(damage_type)
if(BRUTE)
if(glass)
- playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE)
+ playsound(loc, 'sound/effects/glass/glasshit.ogg', 90, TRUE)
else if(damage_amount)
- playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE)
+ playsound(loc, 'sound/items/weapons/smash.ogg', 50, TRUE)
else
- playsound(src, 'sound/weapons/tap.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/tap.ogg', 50, TRUE)
if(BURN)
- playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
+ playsound(src.loc, 'sound/items/tools/welder.ogg', 100, TRUE)
/obj/machinery/door/emp_act(severity)
. = ..()
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index 84df989ba0ea3..858f2dffefff2 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -59,8 +59,8 @@
///Keeps track of if we're playing the alarm sound loop (as only one firelock per group should be). Used during power changes.
var/is_playing_alarm = FALSE
- var/knock_sound = 'sound/effects/glassknock.ogg'
- var/bash_sound = 'sound/effects/glassbash.ogg'
+ var/knock_sound = 'sound/effects/glass/glassknock.ogg'
+ var/bash_sound = 'sound/effects/glass/glassbash.ogg'
/datum/armor/door_firedoor
@@ -544,7 +544,7 @@
correct_state()
/// We check for adjacency when using the primary attack.
-/obj/machinery/door/firedoor/try_to_crowbar(obj/item/acting_object, mob/user)
+/obj/machinery/door/firedoor/try_to_crowbar(obj/item/acting_object, mob/user, forced = FALSE)
if(welded || operating)
return
diff --git a/code/game/machinery/doors/passworddoor.dm b/code/game/machinery/doors/passworddoor.dm
index bccc243381ba4..8d35f44b0d80c 100644
--- a/code/game/machinery/doors/passworddoor.dm
+++ b/code/game/machinery/doors/passworddoor.dm
@@ -20,7 +20,7 @@
/// Sound used upon closing.
var/door_close = 'sound/machines/blastdoor.ogg'
/// Sound used upon denying.
- var/door_deny = 'sound/machines/buzz-sigh.ogg'
+ var/door_deny = 'sound/machines/buzz/buzz-sigh.ogg'
/obj/machinery/door/password/voice
voice_activated = TRUE
@@ -103,7 +103,7 @@
playsound(src, door_deny, 30, TRUE)
/obj/machinery/door/password/proc/ask_for_pass(mob/user)
- var/guess = tgui_input_text(user, "Enter the password", "Password")
+ var/guess = tgui_input_text(user, "Enter the password", "Password", max_length = MAX_MESSAGE_LEN)
if(guess == password)
return TRUE
return FALSE
diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm
index ab18a63361c97..cb33ed6c14f5a 100644
--- a/code/game/machinery/doors/poddoor.dm
+++ b/code/game/machinery/doors/poddoor.dm
@@ -254,7 +254,7 @@
user.visible_message(span_warning("[user] begins prying open [src]."),\
span_noticealien("You begin digging your claws into [src] with all your might!"),\
span_warning("You hear groaning metal..."))
- playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE)
+ playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 100, TRUE)
var/time_to_open = 5 SECONDS
if(hasPower())
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 0c897c6809666..d093cc0d3556d 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -6,7 +6,7 @@
layer = ABOVE_WINDOW_LAYER
closingLayer = ABOVE_WINDOW_LAYER
resistance_flags = ACID_PROOF
- obj_flags = CAN_BE_HIT | BLOCKS_CONSTRUCTION_DIR
+ obj_flags = CAN_BE_HIT | IGNORE_DENSITY | BLOCKS_CONSTRUCTION_DIR
var/base_state = "left"
max_integrity = 150 //If you change this, consider changing ../door/window/brigdoor/ max_integrity at the bottom of this .dm file
integrity_failure = 0
@@ -326,9 +326,9 @@
/obj/machinery/door/window/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
if(BRUTE)
- playsound(src, 'sound/effects/glasshit.ogg', 90, TRUE)
+ playsound(src, 'sound/effects/glass/glasshit.ogg', 90, TRUE)
if(BURN)
- playsound(src, 'sound/items/welder.ogg', 100, TRUE)
+ playsound(src, 'sound/items/tools/welder.ogg', 100, TRUE)
/obj/machinery/door/window/on_deconstruction(disassembled)
if(disassembled)
diff --git a/code/game/machinery/droneDispenser.dm b/code/game/machinery/drone_dispenser.dm
similarity index 77%
rename from code/game/machinery/droneDispenser.dm
rename to code/game/machinery/drone_dispenser.dm
index af39257973cce..414746dcb1923 100644
--- a/code/game/machinery/droneDispenser.dm
+++ b/code/game/machinery/drone_dispenser.dm
@@ -14,40 +14,58 @@
integrity_failure = 0.33
// These allow for different icons when creating custom dispensers
+ /// Icon string to use when the drone dispenser is not processing.
var/icon_off = "off"
+ /// Icon string to use when the drone dispenser is processing.
var/icon_on = "on"
+ /// Icon string to use when the drone dispenser is on cooldown.
var/icon_recharging = "recharge"
+ /// Icon string to use when the drone dispenser is making a new shell.
var/icon_creating = "make"
+ /// The quantity of materials used when generating a new drone shell.
var/list/using_materials
+ /// Quantity of materials to automatically insert when the drone dispenser is spawned.
var/starting_amount = 0
var/iron_cost = HALF_SHEET_MATERIAL_AMOUNT
var/glass_cost = HALF_SHEET_MATERIAL_AMOUNT
+ /// Energy to draw when processing a new drone shell fresh.
var/energy_used = 1 KILO JOULES
+ /// What operation the drone shell dispenser is currently in, checked in process() to determine behavior
var/mode = DRONE_READY
+ /// Reference to world.time to use for calculation of cooldowns and production of a new drone dispenser.
var/timer
+ /// How long should the drone dispenser be on cooldown after operating
var/cooldownTime = 3 MINUTES
- var/production_time = 30
+ /// How long does it the drone dispenser take to generate a new drone shell?
+ var/production_time = 3 SECONDS
//The item the dispenser will create
- var/dispense_type = /obj/effect/mob_spawn/ghost_role/drone
+ var/list/dispense_type = list(/obj/effect/mob_spawn/ghost_role/drone)
- // The maximum number of "idle" drone shells it will make before
- // ceasing production. Set to 0 for infinite.
+ /// The maximum number of "idle" drone shells it will make before ceasing production. Set to 0 for infinite.
var/maximum_idle = 3
- var/work_sound = 'sound/items/rped.ogg'
+ /// Sound that the drone dispnser plays when it's ready to start making more drones.
+ var/work_sound = 'sound/items/tools/rped.ogg'
+ /// Sound that the drone dispnser plays when it's created a new drone.
var/create_sound = 'sound/items/deconstruct.ogg'
+ /// Sound that the drone dispnser plays when it's recharged it's cooldown.
var/recharge_sound = 'sound/machines/ping.ogg'
+ /// String that's displayed for when the drone dispenser start working.
var/begin_create_message = "whirs to life!"
+ /// String that's displayed for when the drone dispenser stops working.
var/end_create_message = "dispenses a drone shell."
+ /// String that's displayed for when the drone dispenser finished it's cooldown.
var/recharge_message = "pings."
+ /// String that's displayed for when the drone dispenser is still on cooldown.
var/recharging_text = "It is whirring and clicking. It seems to be recharging."
-
+ /// String that's displayed for when the drone dispenser is broken.
var/break_message = "lets out a tinny alarm before falling dark."
+ /// Sound that the drone dispnser plays when it's broken.
var/break_sound = 'sound/machines/warning-buzzer.ogg'
-
+ /// Reference to the object's internal storage for materials.
var/datum/component/material_container/materials
/obj/machinery/drone_dispenser/Initialize(mapload)
@@ -75,7 +93,7 @@
/obj/machinery/drone_dispenser/syndrone //Please forgive me
name = "syndrone shell dispenser"
desc = "A suspicious machine that will create Syndicate exterminator drones when supplied with iron and glass. Disgusting."
- dispense_type = /obj/effect/mob_spawn/ghost_role/drone/syndrone
+ dispense_type = list(/obj/effect/mob_spawn/ghost_role/drone/syndrone)
//If we're gonna be a jackass, go the full mile - 10 second recharge timer
cooldownTime = 100
end_create_message = "dispenses a suspicious drone shell."
@@ -84,14 +102,14 @@
/obj/machinery/drone_dispenser/syndrone/badass //Please forgive me
name = "badass syndrone shell dispenser"
desc = "A suspicious machine that will create Syndicate exterminator drones when supplied with iron and glass. Disgusting. This one seems ominous."
- dispense_type = /obj/effect/mob_spawn/ghost_role/drone/syndrone/badass
+ dispense_type = list(/obj/effect/mob_spawn/ghost_role/drone/syndrone/badass)
end_create_message = "dispenses an ominous suspicious drone shell."
// I don't need your forgiveness, this is awesome.
/obj/machinery/drone_dispenser/snowflake
name = "snowflake drone shell dispenser"
desc = "A hefty machine that, when supplied with iron and glass, will periodically create a snowflake drone shell. Does not need to be manually operated."
- dispense_type = /obj/effect/mob_spawn/ghost_role/drone/snowflake
+ dispense_type = list(/obj/effect/mob_spawn/ghost_role/drone/snowflake)
end_create_message = "dispenses a snowflake drone shell."
// Those holoprojectors aren't cheap
iron_cost = SHEET_MATERIAL_AMOUNT
@@ -103,7 +121,7 @@
/obj/machinery/drone_dispenser/derelict
name = "derelict drone shell dispenser"
desc = "A rusty machine that, when supplied with iron and glass, will periodically create a derelict drone shell. Does not need to be manually operated."
- dispense_type = /obj/effect/mob_spawn/ghost_role/drone/derelict
+ dispense_type = list(/obj/effect/mob_spawn/ghost_role/drone/derelict)
end_create_message = "dispenses a derelict drone shell."
iron_cost = SHEET_MATERIAL_AMOUNT * 5
glass_cost = SHEET_MATERIAL_AMOUNT * 2.5
@@ -113,7 +131,7 @@
/obj/machinery/drone_dispenser/classic
name = "classic drone shell dispenser"
desc = "A hefty machine that, when supplied with iron and glass, will periodically create a classic drone shell. Does not need to be manually operated."
- dispense_type = /obj/effect/mob_spawn/ghost_role/drone/classic
+ dispense_type = list(/obj/effect/mob_spawn/ghost_role/drone/classic)
end_create_message = "dispenses a classic drone shell."
// An example of a custom drone dispenser.
@@ -131,7 +149,7 @@
glass_cost = 0
energy_used = 0
cooldownTime = 10 //Only 1 second - hivebots are extremely weak
- dispense_type = /mob/living/basic/hivebot
+ dispense_type = list(/mob/living/basic/hivebot)
begin_create_message = "closes and begins fabricating something within."
end_create_message = "slams open, revealing a hivebot!"
recharge_sound = null
@@ -141,7 +159,7 @@
/obj/machinery/drone_dispenser/binoculars
name = "binoculars fabricator"
desc = "A hefty machine that periodically creates a pair of binoculars. Really, Nanotrasen? We're getting this lazy?"
- dispense_type = /obj/item/binoculars
+ dispense_type = list(/obj/item/binoculars)
starting_amount = SHEET_MATERIAL_AMOUNT * 2.5 //Redudant
maximum_idle = 1
cooldownTime = 5 SECONDS
@@ -194,8 +212,9 @@
if(energy_used)
use_energy(energy_used)
- var/atom/A = new dispense_type(loc)
- A.flags_1 |= (flags_1 & ADMIN_SPAWNED_1)
+ for(var/spawnable_item as anything in dispense_type)
+ var/atom/spawned_atom = new spawnable_item(loc)
+ spawned_atom.flags_1 |= (flags_1 & ADMIN_SPAWNED_1)
if(create_sound)
playsound(src, create_sound, 50, TRUE)
@@ -217,9 +236,10 @@
/obj/machinery/drone_dispenser/proc/count_shells()
. = 0
- for(var/a in loc)
- if(istype(a, dispense_type))
- .++
+ for(var/actual_shell in loc)
+ for(var/potential_item as anything in dispense_type)
+ if(istype(actual_shell, potential_item))
+ .++
/obj/machinery/drone_dispenser/update_icon_state()
if(machine_stat & (BROKEN|NOPOWER))
diff --git a/code/game/machinery/fat_sucker.dm b/code/game/machinery/fat_sucker.dm
index c93a0e4f7ea56..0652ab1605e5d 100644
--- a/code/game/machinery/fat_sucker.dm
+++ b/code/game/machinery/fat_sucker.dm
@@ -166,7 +166,7 @@
set_light(2, 1, "#ff0000")
else
say("Subject not fat enough.")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 40, FALSE)
overlays += "[icon_state]_red" //throw a red light icon over it, to show that it won't work
/obj/machinery/fat_sucker/proc/stop()
diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm
index 94612b8c9db35..5853389ac80ff 100644
--- a/code/game/machinery/flasher.dm
+++ b/code/game/machinery/flasher.dm
@@ -108,7 +108,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/flasher, 26)
power_change()
return
- playsound(src, 'sound/weapons/flash.ogg', 100, TRUE)
+ playsound(src, 'sound/items/weapons/flash.ogg', 100, TRUE)
flick("[base_icon_state]_flash", src)
flash_lighting_fx()
diff --git a/code/game/machinery/flatpacker.dm b/code/game/machinery/flatpacker.dm
index 01224ad8b00fa..56fcc8a8ae74c 100644
--- a/code/game/machinery/flatpacker.dm
+++ b/code/game/machinery/flatpacker.dm
@@ -268,7 +268,7 @@
if(!materials.has_materials(needed_mats, creation_efficiency))
say("Not enough materials to begin production.")
return
- playsound(src, 'sound/items/rped.ogg', 50, TRUE)
+ playsound(src, 'sound/items/tools/rped.ogg', 50, TRUE)
busy = TRUE
flick_overlay_view(mutable_appearance('icons/obj/machines/lathes.dmi', "flatpacker_bar"), flatpack_time)
@@ -401,7 +401,7 @@
new /obj/effect/temp_visual/mook_dust(loc)
var/obj/machinery/new_machine = new board.build_path(loc)
loc.visible_message(span_warning("[src] deploys!"))
- playsound(src, 'sound/machines/terminal_eject.ogg', 70, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_eject.ogg', 70, TRUE)
new_machine.on_construction(user)
qdel(src)
return ITEM_INTERACT_SUCCESS
diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm
index 5fa999a690e9a..4949f53adfbfe 100644
--- a/code/game/machinery/harvester.dm
+++ b/code/game/machinery/harvester.dm
@@ -76,15 +76,15 @@
for(var/obj/item/abiotic_item in carbon_occupant.held_items + carbon_occupant.get_equipped_items())
if(!(HAS_TRAIT(abiotic_item, TRAIT_NODROP)))
say("Subject may not have abiotic items on.")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
return
if(!(carbon_occupant.mob_biotypes & MOB_ORGANIC))
say("Subject is not organic.")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
return
if(!allow_living && !(carbon_occupant.stat == DEAD || HAS_TRAIT(carbon_occupant, TRAIT_FAKEDEATH))) //I mean, the machines scanners arent advanced enough to tell you're alive
say("Subject is still alive.")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
return
return TRUE
@@ -141,7 +141,7 @@
open_machine()
if (!success)
say("Protocol interrupted. Aborting harvest.")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
else
say("Subject has been successfully harvested.")
playsound(src, 'sound/machines/microwave/microwave-end.ogg', 100, FALSE)
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index bf3d97426fd24..a40eaf710a6be 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -522,7 +522,7 @@ Possible to do for anyone motivated enough:
if(outgoing_call)
holocall.Disconnect(src)//can't answer calls while calling
else
- playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring!
+ playsound(src, 'sound/machines/beep/twobeep.ogg', 100) //bring, bring!
are_ringing = TRUE
if(ringing != are_ringing)
diff --git a/code/game/machinery/hypnochair.dm b/code/game/machinery/hypnochair.dm
index b5ec2c58b3870..a2895f6ae9fcd 100644
--- a/code/game/machinery/hypnochair.dm
+++ b/code/game/machinery/hypnochair.dm
@@ -91,11 +91,11 @@
/obj/machinery/hypnochair/proc/interrogate()
if(!trigger_phrase)
- playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 25, TRUE)
+ playsound(get_turf(src), 'sound/machines/buzz/buzz-sigh.ogg', 25, TRUE)
return
var/mob/living/carbon/C = occupant
if(!istype(C))
- playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 25, TRUE)
+ playsound(get_turf(src), 'sound/machines/buzz/buzz-sigh.ogg', 25, TRUE)
return
victim = C
if(C.get_eye_protection() <= 0)
@@ -114,13 +114,13 @@
interrupt_interrogation()
return
if(SPT_PROB(5, seconds_per_tick) && !(C.get_eye_protection() > 0))
- to_chat(C, "[pick(\
+ to_chat(C, span_hypnophrase(pick(\
"...blue... red... green... blue, red, green, blueredgreen[span_small("blueredgreen")]",\
"...pretty colors...",\
"...you keep hearing words, but you can't seem to understand them...",\
"...so peaceful...",\
"...an annoying buzz in your ears..."\
- )]")
+ )))
use_energy(active_power_usage * seconds_per_tick)
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index e1b10dae6e43e..437c2dbd168a6 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -256,7 +256,7 @@
// If the human is losing too much blood, beep.
if(attached_mob.blood_volume < BLOOD_VOLUME_SAFE && prob(5))
audible_message(span_hear("[src] beeps loudly."))
- playsound(loc, 'sound/machines/twobeep_high.ogg', 50, TRUE)
+ playsound(loc, 'sound/machines/beep/twobeep_high.ogg', 50, TRUE)
var/atom/movable/target = use_internal_storage ? src : reagent_container
attached_mob.transfer_blood_to(target, amount)
update_appearance(UPDATE_ICON)
diff --git a/code/game/machinery/launch_pad.dm b/code/game/machinery/launch_pad.dm
index 67ad91681506d..c2fb218d50a33 100644
--- a/code/game/machinery/launch_pad.dm
+++ b/code/game/machinery/launch_pad.dm
@@ -181,11 +181,11 @@
indicator_icon = "launchpad_pull"
update_indicator()
- playsound(get_turf(src), 'sound/weapons/flash.ogg', 25, TRUE)
+ playsound(get_turf(src), 'sound/items/weapons/flash.ogg', 25, TRUE)
teleporting = TRUE
if(!hidden)
- playsound(target, 'sound/weapons/flash.ogg', 25, TRUE)
+ playsound(target, 'sound/items/weapons/flash.ogg', 25, TRUE)
var/datum/effect_system/spark_spread/quantum/spark_system = new /datum/effect_system/spark_spread/quantum()
spark_system.set_up(5, TRUE, target)
spark_system.start()
@@ -203,7 +203,7 @@
if(!hidden)
// Takes twice as long to make sure it properly fades out.
Beam(target, icon_state = teleport_beam, time = BEAM_FADE_TIME*2, beam_type = /obj/effect/ebeam/launchpad)
- playsound(target, 'sound/weapons/emitter2.ogg', 25, TRUE)
+ playsound(target, 'sound/items/weapons/emitter2.ogg', 25, TRUE)
// use a lot of power
use_energy(active_power_usage)
@@ -216,7 +216,7 @@
source = dest
dest = target
- playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 25, TRUE)
+ playsound(get_turf(src), 'sound/items/weapons/emitter2.ogg', 25, TRUE)
var/first = TRUE
for(var/atom/movable/ROI in source)
if(ROI == src)
diff --git a/code/game/machinery/limbgrower.dm b/code/game/machinery/limbgrower.dm
index cd36dcce09ad0..834d7115b0418 100644
--- a/code/game/machinery/limbgrower.dm
+++ b/code/game/machinery/limbgrower.dm
@@ -178,7 +178,7 @@
consumed_reagents_list[reagent_id] *= production_coefficient
if(!reagents.has_reagent(reagent_id, consumed_reagents_list[reagent_id]))
audible_message(span_notice("[src] buzzes."))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
return
power = max(active_power_usage, (power + consumed_reagents_list[reagent_id]))
@@ -207,7 +207,7 @@
for(var/reagent_id in modified_consumed_reagents_list)
if(!reagents.has_reagent(reagent_id, modified_consumed_reagents_list[reagent_id]))
audible_message(span_notice("The [src] buzzes."))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
break
reagents.remove_reagent(reagent_id, modified_consumed_reagents_list[reagent_id])
diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm
index f6f4270835ca0..98149a8232223 100644
--- a/code/game/machinery/navbeacon.dm
+++ b/code/game/machinery/navbeacon.dm
@@ -211,7 +211,7 @@
toggle_code(NAVBEACON_DELIVERY_MODE)
return TRUE
if("set_location")
- var/input_text = tgui_input_text(user, "Enter the beacon's location tag", "Beacon Location", location, 20)
+ var/input_text = tgui_input_text(user, "Enter the beacon's location tag", "Beacon Location", location, max_length = 20)
if (!input_text || location == input_text)
return
glob_lists_deregister()
@@ -220,7 +220,7 @@
return TRUE
if("set_patrol_next")
var/next_patrol = codes[NAVBEACON_PATROL_NEXT]
- var/input_text = tgui_input_text(user, "Enter the tag of the next patrol location", "Beacon Location", next_patrol, 20)
+ var/input_text = tgui_input_text(user, "Enter the tag of the next patrol location", "Beacon Location", next_patrol, max_length = 20)
if (!input_text || location == input_text)
return
codes[NAVBEACON_PATROL_NEXT] = input_text
diff --git a/code/game/machinery/newscaster/newscaster_machine.dm b/code/game/machinery/newscaster/newscaster_machine.dm
index 3186d2081e5a7..18c3d9434d869 100644
--- a/code/game/machinery/newscaster/newscaster_machine.dm
+++ b/code/game/machinery/newscaster/newscaster_machine.dm
@@ -405,14 +405,14 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30)
return TRUE
if("setCriminalName")
- var/temp_name = tgui_input_text(usr, "Write the Criminal's Name", "Warrent Alert Handler", "John Doe", MAX_NAME_LEN, multiline = FALSE)
+ var/temp_name = tgui_input_text(usr, "Write the Criminal's Name", "Warrent Alert Handler", "John Doe", max_length = MAX_NAME_LEN, multiline = FALSE)
if(!temp_name)
return TRUE
criminal_name = temp_name
return TRUE
if("setCrimeData")
- var/temp_desc = tgui_input_text(usr, "Write the Criminal's Crimes", "Warrent Alert Handler", "Unknown", MAX_BROADCAST_LEN, multiline = TRUE)
+ var/temp_desc = tgui_input_text(usr, "Write the Criminal's Crimes", "Warrent Alert Handler", "Unknown", max_length = MAX_BROADCAST_LEN, multiline = TRUE)
if(!temp_desc)
return TRUE
crime_description = temp_desc
@@ -529,9 +529,9 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30)
if(machine_stat & BROKEN)
playsound(loc, 'sound/effects/hit_on_shattered_glass.ogg', 100, TRUE)
else
- playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE)
+ playsound(loc, 'sound/effects/glass/glasshit.ogg', 90, TRUE)
if(BURN)
- playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
+ playsound(src.loc, 'sound/items/tools/welder.ogg', 100, TRUE)
/obj/machinery/newscaster/on_deconstruction(disassembled)
@@ -542,7 +542,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30)
/obj/machinery/newscaster/atom_break(damage_flag)
. = ..()
if(.)
- playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE)
+ playsound(loc, 'sound/effects/glass/glassbr3.ogg', 100, TRUE)
/obj/machinery/newscaster/attack_paw(mob/living/user, list/modifiers)
@@ -623,7 +623,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30)
if(channel)
if(update_alert)
say("Breaking news from [channel]!")
- playsound(loc, 'sound/machines/twobeep_high.ogg', 75, TRUE)
+ playsound(loc, 'sound/machines/beep/twobeep_high.ogg', 75, TRUE)
alert = TRUE
update_appearance()
addtimer(CALLBACK(src, PROC_REF(remove_alert)), ALERT_DELAY, TIMER_UNIQUE|TIMER_OVERRIDE)
@@ -703,7 +703,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30)
if(channel_name == potential_channel.channel_ID)
current_channel = potential_channel
break
- var/temp_message = tgui_input_text(usr, "Write your Feed story", "Network Channel Handler", feed_channel_message, multiline = TRUE)
+ var/temp_message = tgui_input_text(usr, "Write your Feed story", "Network Channel Handler", feed_channel_message, max_length = MAX_BROADCAST_LEN, multiline = TRUE)
if(length(temp_message) <= 1)
return TRUE
if(temp_message)
@@ -745,10 +745,10 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30)
*/
/obj/machinery/newscaster/proc/delete_bounty_request()
if(!active_request || !current_user)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 20, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 20, TRUE)
return TRUE
if(active_request?.owner != current_user.account_holder)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 20, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 20, TRUE)
return TRUE
say("Deleted current request.")
GLOB.request_list.Remove(active_request)
@@ -759,7 +759,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30)
*/
/obj/machinery/newscaster/proc/create_bounty()
if(!current_user || !bounty_text)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 20, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 20, TRUE)
return TRUE
for(var/datum/station_request/iterated_station_request as anything in GLOB.request_list)
if(iterated_station_request.req_number == current_user.account_id)
@@ -779,11 +779,11 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30)
say("No ID detected.")
return TRUE
if(current_user.account_holder == active_request.owner)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 20, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 20, TRUE)
return TRUE
for(var/new_apply in active_request?.applicants)
if(current_user.account_holder == active_request?.applicants[new_apply])
- playsound(src, 'sound/machines/buzz-sigh.ogg', 20, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 20, TRUE)
return TRUE
active_request.applicants += list(current_user)
@@ -794,7 +794,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/newscaster, 30)
if(!current_user)
return TRUE
if(!current_user.has_money(active_request.value) || (current_user.account_holder != active_request.owner))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
return TRUE
payment_target.transfer_money(current_user, active_request.value, "Bounty Request")
say("Paid out [active_request.value] credits.")
diff --git a/code/game/machinery/newscaster/newspaper.dm b/code/game/machinery/newscaster/newspaper.dm
index c381f2f506304..6bc1e6c77ff14 100644
--- a/code/game/machinery/newscaster/newspaper.dm
+++ b/code/game/machinery/newscaster/newspaper.dm
@@ -84,7 +84,7 @@
if(scribble_page == current_page)
user.balloon_alert(user, "already scribbled!")
return
- var/new_scribble_text = tgui_input_text(user, "What do you want to scribble?", "Write something")
+ var/new_scribble_text = tgui_input_text(user, "What do you want to scribble?", "Write something", max_length = MAX_MESSAGE_LEN)
if(isnull(new_scribble_text))
return
add_fingerprint(user)
diff --git a/code/game/machinery/photobooth.dm b/code/game/machinery/photobooth.dm
index 321ae7efd6e76..d1244bcc85d47 100644
--- a/code/game/machinery/photobooth.dm
+++ b/code/game/machinery/photobooth.dm
@@ -130,7 +130,7 @@
if(obj_flags & EMAGGED)
var/mob/living/carbon/carbon_occupant = occupant
for(var/i in 1 to 5) //play a ton of sounds to mimic it blinding you
- playsound(src, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, TRUE)
+ playsound(src, pick('sound/items/polaroid/polaroid1.ogg', 'sound/items/polaroid/polaroid2.ogg'), 75, TRUE)
if(carbon_occupant)
carbon_occupant.flash_act(5)
sleep(0.2 SECONDS)
@@ -141,12 +141,12 @@
if(!do_after(occupant, 2 SECONDS, src, timed_action_flags = IGNORE_HELD_ITEM)) //gives them time to put their hand items away.
taking_pictures = FALSE
return
- playsound(src, 'sound/items/polaroid1.ogg', 75, TRUE)
+ playsound(src, 'sound/items/polaroid/polaroid1.ogg', 75, TRUE)
flash()
if(!do_after(occupant, 3 SECONDS, src, timed_action_flags = IGNORE_HELD_ITEM))
taking_pictures = FALSE
return
- playsound(src, 'sound/items/polaroid2.ogg', 75, TRUE)
+ playsound(src, 'sound/items/polaroid/polaroid2.ogg', 75, TRUE)
flash()
if(!do_after(occupant, 2 SECONDS, src, timed_action_flags = IGNORE_HELD_ITEM))
taking_pictures = FALSE
diff --git a/code/game/machinery/pipe/pipe_dispenser.dm b/code/game/machinery/pipe/pipe_dispenser.dm
index 1e90b270c8c8d..de7c6351e38d4 100644
--- a/code/game/machinery/pipe/pipe_dispenser.dm
+++ b/code/game/machinery/pipe/pipe_dispenser.dm
@@ -187,9 +187,6 @@
//Allow you to drag-drop disposal pipes and transit tubes into it
/obj/machinery/pipedispenser/disposal/mouse_drop_receive(obj/structure/pipe, mob/user, params)
- if(user.incapacitated)
- return
-
if (!istype(pipe, /obj/structure/disposalconstruct) && !istype(pipe, /obj/structure/c_transit_tube) && !istype(pipe, /obj/structure/c_transit_tube_pod))
return
diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index b1b5fc2b8f8d6..e64e01bbcf246 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -135,15 +135,13 @@ DEFINE_BITFIELD(turret_flags, list(
if(!has_cover)
INVOKE_ASYNC(src, PROC_REF(popUp))
- RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
-
AddElement(/datum/element/hostile_machine)
///Toggles the turret on or off depending on the value of the turn_on arg.
/obj/machinery/porta_turret/proc/toggle_on(turn_on = TRUE)
if(on == turn_on)
return
- if(on && !COOLDOWN_FINISHED(src, disabled_time))
+ if(turn_on && !COOLDOWN_FINISHED(src, disabled_time))
return
on = turn_on
check_should_process()
@@ -157,10 +155,10 @@ DEFINE_BITFIELD(turret_flags, list(
addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), duration + 1) //the cooldown isn't over until the tick after its end.
toggle_on(FALSE)
-/obj/machinery/porta_turret/proc/on_saboteur(datum/source, disrupt_duration)
- SIGNAL_HANDLER
+/obj/machinery/porta_turret/on_saboteur(datum/source, disrupt_duration)
+ . = ..()
INVOKE_ASYNC(src, PROC_REF(set_disabled), disrupt_duration)
- return COMSIG_SABOTEUR_SUCCESS
+ return TRUE
/obj/machinery/porta_turret/proc/check_should_process()
if (datum_flags & DF_ISPROCESSING)
@@ -575,7 +573,7 @@ DEFINE_BITFIELD(turret_flags, list(
// If we aren't shooting heads then return a threatcount of 0
if (!(turret_flags & TURRET_FLAG_SHOOT_HEADS))
- var/datum/job/apparent_job = SSjob.GetJob(perp.get_assignment())
+ var/datum/job/apparent_job = SSjob.get_job(perp.get_assignment())
if(apparent_job?.job_flags & JOB_HEAD_OF_STAFF)
return 0
@@ -749,8 +747,8 @@ DEFINE_BITFIELD(turret_flags, list(
mode = TURRET_LETHAL
stun_projectile = /obj/projectile/bullet
lethal_projectile = /obj/projectile/bullet
- lethal_projectile_sound = 'sound/weapons/gun/pistol/shot.ogg'
- stun_projectile_sound = 'sound/weapons/gun/pistol/shot.ogg'
+ lethal_projectile_sound = 'sound/items/weapons/gun/pistol/shot.ogg'
+ stun_projectile_sound = 'sound/items/weapons/gun/pistol/shot.ogg'
icon_state = "syndie_off"
base_icon_state = "syndie"
faction = list(ROLE_SYNDICATE)
@@ -771,9 +769,9 @@ DEFINE_BITFIELD(turret_flags, list(
icon_state = "standard_lethal"
base_icon_state = "standard"
stun_projectile = /obj/projectile/energy/electrode
- stun_projectile_sound = 'sound/weapons/taser.ogg'
+ stun_projectile_sound = 'sound/items/weapons/taser.ogg'
lethal_projectile = /obj/projectile/beam/laser
- lethal_projectile_sound = 'sound/weapons/laser.ogg'
+ lethal_projectile_sound = 'sound/items/weapons/laser.ogg'
desc = "An energy blaster auto-turret."
armor_type = /datum/armor/syndicate_turret
@@ -790,14 +788,14 @@ DEFINE_BITFIELD(turret_flags, list(
icon_state = "standard_lethal"
base_icon_state = "standard"
stun_projectile = /obj/projectile/energy/electrode
- stun_projectile_sound = 'sound/weapons/taser.ogg'
+ stun_projectile_sound = 'sound/items/weapons/taser.ogg'
lethal_projectile = /obj/projectile/beam/laser/heavylaser
- lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
+ lethal_projectile_sound = 'sound/items/weapons/lasercannonfire.ogg'
desc = "An energy blaster auto-turret."
/obj/machinery/porta_turret/syndicate/energy/raven
stun_projectile = /obj/projectile/beam/laser
- stun_projectile_sound = 'sound/weapons/laser.ogg'
+ stun_projectile_sound = 'sound/items/weapons/laser.ogg'
faction = list(FACTION_NEUTRAL,FACTION_SILICON,FACTION_TURRET)
/obj/machinery/porta_turret/syndicate/pod
@@ -808,9 +806,9 @@ DEFINE_BITFIELD(turret_flags, list(
/obj/machinery/porta_turret/syndicate/irs
lethal_projectile = /obj/projectile/bullet/c10mm/ap
- lethal_projectile_sound = 'sound/weapons/gun/smg/shot.ogg'
+ lethal_projectile_sound = 'sound/items/weapons/gun/smg/shot.ogg'
stun_projectile = /obj/projectile/bullet/c10mm/ap
- stun_projectile_sound = 'sound/weapons/gun/smg/shot.ogg'
+ stun_projectile_sound = 'sound/items/weapons/gun/smg/shot.ogg'
armor_type = /datum/armor/syndicate_turret
faction = list(FACTION_PIRATE)
@@ -819,8 +817,8 @@ DEFINE_BITFIELD(turret_flags, list(
shot_delay = 3
stun_projectile = /obj/projectile/bullet/p50/penetrator/shuttle
lethal_projectile = /obj/projectile/bullet/p50/penetrator/shuttle
- lethal_projectile_sound = 'sound/weapons/gun/smg/shot.ogg'
- stun_projectile_sound = 'sound/weapons/gun/smg/shot.ogg'
+ lethal_projectile_sound = 'sound/items/weapons/gun/smg/shot.ogg'
+ stun_projectile_sound = 'sound/items/weapons/gun/smg/shot.ogg'
armor_type = /datum/armor/syndicate_shuttle
/datum/armor/syndicate_shuttle
@@ -853,7 +851,7 @@ DEFINE_BITFIELD(turret_flags, list(
installation = null
uses_stored = FALSE
lethal_projectile = /obj/projectile/plasma/turret
- lethal_projectile_sound = 'sound/weapons/plasma_cutter.ogg'
+ lethal_projectile_sound = 'sound/items/weapons/plasma_cutter.ogg'
mode = TURRET_LETHAL //It would be useless in stun mode anyway
faction = list(FACTION_NEUTRAL,FACTION_SILICON,FACTION_TURRET) //Minebots, medibots, etc that should not be shot.
@@ -880,8 +878,8 @@ DEFINE_BITFIELD(turret_flags, list(
scan_range = 9
stun_projectile = /obj/projectile/beam/laser
lethal_projectile = /obj/projectile/beam/laser
- lethal_projectile_sound = 'sound/weapons/plasma_cutter.ogg'
- stun_projectile_sound = 'sound/weapons/plasma_cutter.ogg'
+ lethal_projectile_sound = 'sound/items/weapons/plasma_cutter.ogg'
+ stun_projectile_sound = 'sound/items/weapons/plasma_cutter.ogg'
icon_state = "syndie_off"
base_icon_state = "syndie"
faction = list(FACTION_NEUTRAL,FACTION_SILICON,FACTION_TURRET)
@@ -906,6 +904,13 @@ DEFINE_BITFIELD(turret_flags, list(
lethal_projectile = /obj/projectile/beam/weak/penetrator
faction = list(FACTION_NEUTRAL,FACTION_SILICON,FACTION_TURRET)
+/obj/machinery/porta_turret/centcom_shuttle/weak/mining
+ name = "Old Mining Turret"
+ lethal_projectile = /obj/projectile/kinetic/miner
+ lethal_projectile_sound = 'sound/items/weapons/kinetic_accel.ogg'
+ stun_projectile = /obj/projectile/kinetic/miner
+ stun_projectile_sound = 'sound/items/weapons/kinetic_accel.ogg'
+
////////////////////////
//Turret Control Panel//
////////////////////////
diff --git a/code/game/machinery/porta_turret/portable_turret_construct.dm b/code/game/machinery/porta_turret/portable_turret_construct.dm
index a8fa4e67b2bf6..0ae7d9699ee26 100644
--- a/code/game/machinery/porta_turret/portable_turret_construct.dm
+++ b/code/game/machinery/porta_turret/portable_turret_construct.dm
@@ -182,7 +182,7 @@
return
if(used.get_writing_implement_details()?["interaction_mode"] == MODE_WRITING) //you can rename turrets like bots!
- var/choice = tgui_input_text(user, "Enter a new turret name", "Turret Classification", finish_name, MAX_NAME_LEN)
+ var/choice = tgui_input_text(user, "Enter a new turret name", "Turret Classification", finish_name, max_length = MAX_NAME_LEN)
if(!choice)
return
if(!user.can_perform_action(src))
diff --git a/code/game/machinery/portagrav.dm b/code/game/machinery/portagrav.dm
index c970fa5f8f1c6..62fc67b7c070a 100644
--- a/code/game/machinery/portagrav.dm
+++ b/code/game/machinery/portagrav.dm
@@ -229,7 +229,7 @@
. = ..()
if(.)
return
- playsound(src, 'sound/machines/terminal_button07.ogg', 45, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_button07.ogg', 45, TRUE)
switch(action)
if("adjust_grav")
var/adjustment = text2num(params["adjustment"])
diff --git a/code/game/machinery/prisongate.dm b/code/game/machinery/prisongate.dm
index b05b6dd90c4a1..88cb40dd50f79 100644
--- a/code/game/machinery/prisongate.dm
+++ b/code/game/machinery/prisongate.dm
@@ -51,7 +51,7 @@
for(var/mob/living/stowaway in cargobay.contents) //nice try bub
if(COOLDOWN_FINISHED(src, spam_cooldown_time))
say("Stowaway detected in internal contents. Access denied.")
- playsound(src, 'sound/machines/buzz-two.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-two.ogg', 50, FALSE)
COOLDOWN_START(src, spam_cooldown_time, SPAM_CD)
return FALSE
var/mob/living/carbon/the_toucher = gate_toucher
@@ -82,7 +82,7 @@
return TRUE
if(COOLDOWN_FINISHED(src, spam_cooldown_time))
say("Prison ID with ongoing sentence detected. Access denied.")
- playsound(src, 'sound/machines/buzz-two.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-two.ogg', 50, FALSE)
COOLDOWN_START(src, spam_cooldown_time, SPAM_CD)
return FALSE
if(COOLDOWN_FINISHED(src, spam_cooldown_time))
diff --git a/code/game/machinery/quantum_pad.dm b/code/game/machinery/quantum_pad.dm
index f5f5851b7586e..635f567b8b310 100644
--- a/code/game/machinery/quantum_pad.dm
+++ b/code/game/machinery/quantum_pad.dm
@@ -132,7 +132,7 @@
/obj/machinery/quantumpad/proc/doteleport(mob/user = null, obj/machinery/quantumpad/target_pad = linked_pad)
if(!target_pad)
return
- playsound(get_turf(src), 'sound/weapons/flash.ogg', 25, TRUE)
+ playsound(get_turf(src), 'sound/items/weapons/flash.ogg', 25, TRUE)
teleporting = TRUE
addtimer(CALLBACK(src, PROC_REF(teleport_contents), user, target_pad), teleport_speed)
@@ -156,9 +156,9 @@
target_pad.sparks()
flick("qpad-beam", src)
- playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 25, TRUE)
+ playsound(get_turf(src), 'sound/items/weapons/emitter2.ogg', 25, TRUE)
flick("qpad-beam", target_pad)
- playsound(get_turf(target_pad), 'sound/weapons/emitter2.ogg', 25, TRUE)
+ playsound(get_turf(target_pad), 'sound/items/weapons/emitter2.ogg', 25, TRUE)
for(var/atom/movable/ROI in get_turf(src))
if(QDELETED(ROI))
continue //sleeps in CHECK_TICK
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index 8ce9265917d63..166410cfccf56 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -15,7 +15,7 @@
var/amount_produced = 50
var/crush_damage = 1000
var/eat_victim_items = TRUE
- var/item_recycle_sound = 'sound/items/welder.ogg'
+ var/item_recycle_sound = 'sound/items/tools/welder.ogg'
var/datum/component/material_container/materials
/obj/machinery/recycler/Initialize(mapload)
@@ -206,7 +206,7 @@
if(nom.len && sound)
playsound(src, item_recycle_sound, (50 + nom.len * 5), TRUE, nom.len, ignore_walls = (nom.len - 10)) // As a substitute for playing 50 sounds at once.
if(not_eaten)
- playsound(src, 'sound/machines/buzz-sigh.ogg', (50 + not_eaten * 5), FALSE, not_eaten, ignore_walls = (not_eaten - 10)) // Ditto.
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', (50 + not_eaten * 5), FALSE, not_eaten, ignore_walls = (not_eaten - 10)) // Ditto.
/obj/machinery/recycler/proc/recycle_item(obj/item/weapon)
. = FALSE
@@ -225,7 +225,7 @@
qdel(weapon)
/obj/machinery/recycler/proc/emergency_stop()
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
safety_mode = TRUE
update_appearance()
addtimer(CALLBACK(src, PROC_REF(reboot)), SAFETY_COOLDOWN)
@@ -239,7 +239,7 @@
L.forceMove(loc)
if(issilicon(L))
- playsound(src, 'sound/items/welder.ogg', 50, TRUE)
+ playsound(src, 'sound/items/tools/welder.ogg', 50, TRUE)
else
playsound(src, 'sound/effects/splat.ogg', 50, TRUE)
diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm
index 4a764872a8a2a..6124d15a7f4b7 100644
--- a/code/game/machinery/requests_console.dm
+++ b/code/game/machinery/requests_console.dm
@@ -200,7 +200,7 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments)
var/mob/living/L = usr
message = L.treat_message(message)["message"]
- minor_announce(message, "[department] Announcement:", html_encode = FALSE, sound_override = 'sound/misc/announce_dig.ogg')
+ minor_announce(message, "[department] Announcement:", html_encode = FALSE, sound_override = 'sound/announcer/announcement/announce_dig.ogg')
GLOB.news_network.submit_article(message, department, "Station Announcements", null)
usr.log_talk(message, LOG_SAY, tag="station announcement from [src]")
message_admins("[ADMIN_LOOKUPFLW(usr)] has made a station announcement from [src] at [AREACOORD(usr)].")
@@ -217,7 +217,7 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments)
return
if(!reply_message)
has_mail_send_error = TRUE
- playsound(src, 'sound/machines/buzz-two.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-two.ogg', 50, TRUE)
return TRUE
send_message(recipient, reply_message, REQ_NORMAL_MESSAGE_PRIORITY, REPLY_REQUEST)
@@ -273,9 +273,9 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments)
if(!silent)
if(has_mail_send_error)
- playsound(src, 'sound/machines/buzz-two.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-two.ogg', 50, TRUE)
else
- playsound(src, 'sound/machines/twobeep.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/beep/twobeep.ogg', 50, TRUE)
message_stamped_by = ""
message_verified_by = ""
@@ -350,7 +350,7 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments)
var/alert = new_message.get_alert()
if(!silent)
- playsound(src, 'sound/machines/twobeep_high.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/beep/twobeep_high.ogg', 50, TRUE)
say(alert)
if(new_message.radio_freq)
diff --git a/code/game/machinery/roulette_machine.dm b/code/game/machinery/roulette_machine.dm
index bb43b7a46db9e..f0bc48916ae21 100644
--- a/code/game/machinery/roulette_machine.dm
+++ b/code/game/machinery/roulette_machine.dm
@@ -127,7 +127,7 @@
if(isidcard(W))
playsound(src, 'sound/machines/card_slide.ogg', 50, TRUE)
else
- playsound(src, 'sound/machines/terminal_success.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_success.ogg', 50, TRUE)
if(machine_stat & MAINT || !on || locked)
to_chat(user, span_notice("The machine appears to be disabled."))
@@ -135,17 +135,17 @@
if(!player_card.registered_account)
say("You don't have a bank account!")
- playsound(src, 'sound/machines/buzz-two.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-two.ogg', 30, TRUE)
return FALSE
if(my_card)
if(IS_DEPARTMENTAL_CARD(player_card)) // Are they using a department ID
say("You cannot gamble with the department budget!")
- playsound(src, 'sound/machines/buzz-two.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-two.ogg', 30, TRUE)
return FALSE
if(player_card.registered_account.account_balance < chosen_bet_amount) //Does the player have enough funds
say("You do not have the funds to play! Lower your bet or get more money.")
- playsound(src, 'sound/machines/buzz-two.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-two.ogg', 30, TRUE)
return FALSE
if(!chosen_bet_amount || isnull(chosen_bet_type))
return FALSE
@@ -181,13 +181,13 @@
icon_state = "rolling" //Prepare the new icon state for rolling before hand.
flick("flick_up", src)
- playsound(src, 'sound/machines/piston_raise.ogg', 70)
+ playsound(src, 'sound/machines/piston/piston_raise.ogg', 70)
playsound(src, 'sound/machines/chime.ogg', 50)
addtimer(CALLBACK(src, PROC_REF(play), user, player_card, chosen_bet_type, chosen_bet_amount, potential_payout), 4) //Animation first
return TRUE
else
- var/msg = tgui_input_text(user, "Name of your roulette wheel", "Roulette Customization", "Roulette Machine", MAX_NAME_LEN)
+ var/msg = tgui_input_text(user, "Name of your roulette wheel", "Roulette Customization", "Roulette Machine", max_length = MAX_NAME_LEN)
if(!msg)
return
name = msg
@@ -209,7 +209,7 @@
if(!my_card?.registered_account) // Something happened to my_card during the 0.4 seconds delay of the timed callback.
icon_state = "idle"
flick("flick_down", src)
- playsound(src, 'sound/machines/piston_lower.ogg', 70)
+ playsound(src, 'sound/machines/piston/piston_lower.ogg', 70)
return
var/payout = potential_payout
@@ -222,7 +222,7 @@
var/rolled_number = rand(0, 36)
- playsound(src, 'sound/machines/roulettewheel.ogg', 50)
+ playsound(src, 'sound/machines/roulette/roulettewheel.ogg', 50)
addtimer(CALLBACK(src, PROC_REF(finish_play), player_id, bet_type, bet_amount, payout, rolled_number), 34) //4 deciseconds more so the animation can play
addtimer(CALLBACK(src, PROC_REF(finish_play_animation)), 3 SECONDS)
@@ -231,7 +231,7 @@
/obj/machinery/roulette/proc/finish_play_animation()
icon_state = "idle"
flick("flick_down", src)
- playsound(src, 'sound/machines/piston_lower.ogg', 70)
+ playsound(src, 'sound/machines/piston/piston_lower.ogg', 70)
///Ran after a while to check if the player won or not.
/obj/machinery/roulette/proc/finish_play(obj/item/card/id/player_id, bet_type, bet_amount, potential_payout, rolled_number)
@@ -249,7 +249,7 @@
if(!is_winner)
say("You lost! Better luck next time")
- playsound(src, 'sound/machines/synth_no.ogg', 50)
+ playsound(src, 'sound/machines/synth/synth_no.ogg', 50)
return FALSE
// Prevents money generation exploits. Doesn't prevent the owner being a scrooge and running away with the money.
@@ -257,7 +257,7 @@
potential_payout = (account_balance >= potential_payout) ? potential_payout : account_balance
say("You have won [potential_payout] credits! Congratulations!")
- playsound(src, 'sound/machines/synth_yes.ogg', 50)
+ playsound(src, 'sound/machines/synth/synth_yes.ogg', 50)
dispense_prize(potential_payout)
@@ -362,7 +362,7 @@
if(my_card.registered_account.account_balance >= payout)
return TRUE //We got the betting amount
say("The bank account of [my_card.registered_account.account_holder] does not have enough funds to pay out the potential prize, contact them to fill up their account or lower your bet!")
- playsound(src, 'sound/machines/buzz-two.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-two.ogg', 30, TRUE)
return FALSE
/obj/machinery/roulette/update_overlays()
@@ -456,7 +456,7 @@
"path" = /obj/structure/closet/supplypod/centcompod,
"spawn" = /obj/machinery/roulette
))
-
+
qdel(src)
#undef ROULETTE_DOZ_COL_PAYOUT
diff --git a/code/game/machinery/scanner_gate.dm b/code/game/machinery/scanner_gate.dm
index 85d816543926b..07df8db82c704 100644
--- a/code/game/machinery/scanner_gate.dm
+++ b/code/game/machinery/scanner_gate.dm
@@ -306,7 +306,7 @@
say("[detected_thing][reverse ? " not " : " "]detected!!")
COOLDOWN_START(src, next_beep, 2 SECONDS)
- playsound(source = src, soundin = 'sound/machines/scanbuzz.ogg', vol = 30, vary = FALSE, extrarange = MEDIUM_RANGE_SOUND_EXTRARANGE, falloff_distance = 4)
+ playsound(source = src, soundin = 'sound/machines/scanner/scanbuzz.ogg', vol = 30, vary = FALSE, extrarange = MEDIUM_RANGE_SOUND_EXTRARANGE, falloff_distance = 4)
set_scanline("alarm", 2 SECONDS)
/obj/machinery/scanner_gate/can_interact(mob/user)
diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm
index 41aa0876169ed..d3266df16f880 100644
--- a/code/game/machinery/slotmachine.dm
+++ b/code/game/machinery/slotmachine.dm
@@ -335,14 +335,14 @@
else
balloon_alert(user, "no luck!")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50)
did_player_win = FALSE
if(did_player_win)
add_filter("jackpot_rays", 3, ray_filter)
animate(get_filter("jackpot_rays"), offset = 10, time = 3 SECONDS, loop = -1)
addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, remove_filter), "jackpot_rays"), 3 SECONDS)
- playsound(src, 'sound/machines/roulettejackpot.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/roulette/roulettejackpot.ogg', 50, TRUE)
/// Checks for a jackpot (5 matching icons in the middle row) with the given icon name
/obj/machinery/computer/slot_machine/proc/check_jackpot(name)
diff --git a/code/game/machinery/stasis.dm b/code/game/machinery/stasis.dm
index bf33530b93e3e..49f00741895fe 100644
--- a/code/game/machinery/stasis.dm
+++ b/code/game/machinery/stasis.dm
@@ -34,9 +34,9 @@
if(last_stasis_sound != _running)
var/sound_freq = rand(5120, 8800)
if(_running)
- playsound(src, 'sound/machines/synth_yes.ogg', 50, TRUE, frequency = sound_freq)
+ playsound(src, 'sound/machines/synth/synth_yes.ogg', 50, TRUE, frequency = sound_freq)
else
- playsound(src, 'sound/machines/synth_no.ogg', 50, TRUE, frequency = sound_freq)
+ playsound(src, 'sound/machines/synth/synth_no.ogg', 50, TRUE, frequency = sound_freq)
last_stasis_sound = _running
/obj/machinery/stasis/click_alt(mob/user)
diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm
index 3232dc524ab89..1eff3f6587080 100644
--- a/code/game/machinery/suit_storage_unit.dm
+++ b/code/game/machinery/suit_storage_unit.dm
@@ -506,7 +506,7 @@
locked = FALSE
if(uv_super)
visible_message(span_warning("[src]'s door creaks open with a loud whining noise. A cloud of foul black smoke escapes from its chamber."))
- playsound(src, 'sound/machines/airlock_alien_prying.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 50, TRUE)
var/datum/effect_system/fluid_spread/smoke/bad/black/smoke = new
smoke.set_up(0, holder = src, location = src)
smoke.start()
@@ -523,7 +523,7 @@
else
visible_message(span_warning("[src]'s door slides open, barraging you with the nauseating smell of charred flesh."))
qdel(mob_occupant.GetComponent(/datum/component/irradiated))
- playsound(src, 'sound/machines/airlockclose.ogg', 25, TRUE)
+ playsound(src, 'sound/machines/airlock/airlockclose.ogg', 25, TRUE)
var/list/things_to_clear = list() //Done this way since using GetAllContents on the SSU itself would include circuitry and such.
if(suit)
things_to_clear += suit
@@ -711,12 +711,12 @@
var/name_set = FALSE
var/desc_set = FALSE
- var/str = tgui_input_text(user, "Personal Unit Name", "Unit Name")
+ var/str = tgui_input_text(user, "Personal Unit Name", "Unit Name", max_length = MAX_NAME_LEN)
if(!isnull(str))
name = str
name_set = TRUE
- str = tgui_input_text(user, "Personal Unit Description", "Unit Description")
+ str = tgui_input_text(user, "Personal Unit Description", "Unit Description", max_length = MAX_DESC_LEN)
if(!isnull(str))
desc = str
desc_set = TRUE
diff --git a/code/game/machinery/syndicatebomb.dm b/code/game/machinery/syndicatebomb.dm
index 0ac1f7ee44df8..25dc258a38d94 100644
--- a/code/game/machinery/syndicatebomb.dm
+++ b/code/game/machinery/syndicatebomb.dm
@@ -501,7 +501,7 @@
reactants += S.reagents
if(!chem_splash(get_turf(src), reagents, spread_range, reactants, temp_boost))
- playsound(loc, 'sound/items/screwdriver2.ogg', 50, TRUE)
+ playsound(loc, 'sound/items/tools/screwdriver2.ogg', 50, TRUE)
return // The Explosion didn't do anything. No need to log, or disappear.
if(adminlog)
diff --git a/code/game/machinery/telecomms/computers/message.dm b/code/game/machinery/telecomms/computers/message.dm
index 05186da8bc08c..1b3197e702da5 100644
--- a/code/game/machinery/telecomms/computers/message.dm
+++ b/code/game/machinery/telecomms/computers/message.dm
@@ -139,7 +139,7 @@
return TRUE
authenticated = TRUE
- success_message = "YOU SUCCESFULLY LOGGED IN!"
+ success_message = "YOU SUCCESSFULLY LOGGED IN!"
return TRUE
if("link_server")
@@ -180,10 +180,10 @@
notice_message = "NOTICE: Logs cleared."
return TRUE
if("set_key")
- var/dkey = tgui_input_text(usr, "Please enter the decryption key", "Telecomms Decryption")
+ var/dkey = tgui_input_text(usr, "Please enter the decryption key", "Telecomms Decryption", max_length = 16)
if(dkey && dkey != "")
if(linkedServer.decryptkey == dkey)
- var/newkey = tgui_input_text(usr, "Please enter the new key (3 - 16 characters max)", "New Key")
+ var/newkey = tgui_input_text(usr, "Please enter the new key (3 - 16 characters max)", "New Key", max_length = 16)
if(length(newkey) <= 3)
notice_message = "NOTICE: Decryption key too short!"
else if(newkey && newkey != "")
@@ -210,8 +210,8 @@
break
return TRUE
if("send_fake_message")
- var/sender = tgui_input_text(usr, "What is the sender's name?", "Sender")
- var/job = tgui_input_text(usr, "What is the sender's job?", "Job")
+ var/sender = tgui_input_text(usr, "What is the sender's name?", "Sender", max_length = MAX_NAME_LEN)
+ var/job = tgui_input_text(usr, "What is the sender's job?", "Job", max_length = 60)
var/recipient
var/list/tablet_to_messenger = list()
@@ -229,7 +229,7 @@
else
recipient = null
- var/message = tgui_input_text(usr, "Please enter your message", "Message")
+ var/message = tgui_input_text(usr, "Please enter your message", "Message", max_length = MAX_MESSAGE_LEN)
if(isnull(sender) || sender == "")
sender = "UNKNOWN"
diff --git a/code/game/machinery/telecomms/machine_interactions.dm b/code/game/machinery/telecomms/machine_interactions.dm
index c92384aa4b6c1..8b982b4e3b953 100644
--- a/code/game/machinery/telecomms/machine_interactions.dm
+++ b/code/game/machinery/telecomms/machine_interactions.dm
@@ -105,7 +105,7 @@
if(params["value"])
if(length(params["value"]) > 32)
to_chat(current_user, span_warning("Error: Machine ID too long!"))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, TRUE)
return
else
id = params["value"]
@@ -115,7 +115,7 @@
if(params["value"])
if(length(params["value"]) > 15)
to_chat(current_user, span_warning("Error: Network name too long!"))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, TRUE)
return
else
for(var/obj/machinery/telecomms/linked_machine in links)
@@ -130,7 +130,7 @@
if("freq")
if(tempfreq in banned_frequencies)
to_chat(current_user, span_warning("Error: Interference preventing filtering frequency: \"[tempfreq / 10] kHz\""))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, TRUE)
else
if(!(tempfreq in freq_listening))
freq_listening.Add(tempfreq)
diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm
index 238994004ded0..45a91d7e5a6ec 100644
--- a/code/game/machinery/transformer.dm
+++ b/code/game/machinery/transformer.dm
@@ -84,7 +84,7 @@
return
if(!transform_dead && victim.stat == DEAD)
- playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src.loc, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
return
// Activate the cooldown
@@ -92,7 +92,7 @@
cooldown_timer = world.time + cooldown_duration
update_appearance()
- playsound(src.loc, 'sound/items/welder.ogg', 50, TRUE)
+ playsound(src.loc, 'sound/items/tools/welder.ogg', 50, TRUE)
victim.emote("scream") // It is painful
victim.adjustBruteLoss(max(0, 80 - victim.getBruteLoss())) // Hurt the human, don't try to kill them though.
diff --git a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm
index 27c02fb9d1806..d722d90ed1172 100644
--- a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm
+++ b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm
@@ -25,7 +25,7 @@
return
new /obj/effect/temp_visual/circle_wave/bioscrambler(get_turf(src))
- playsound(src, 'sound/magic/cosmic_energy.ogg', vol = 50, vary = TRUE)
+ playsound(src, 'sound/effects/magic/cosmic_energy.ogg', vol = 50, vary = TRUE)
COOLDOWN_START(src, pulse_cooldown, pulse_delay)
for(var/mob/living/carbon/nearby in hearers(range, src))
nearby.bioscramble(name)
@@ -62,7 +62,7 @@
for(var/mob/living/carbon/target in GLOB.player_list)
if (target.z != z)
continue
- if (target.status_flags & GODMODE)
+ if (HAS_TRAIT(target, TRAIT_GODMODE))
continue
if (target.stat >= UNCONSCIOUS)
continue // Don't just haunt a corpse
@@ -89,10 +89,12 @@
duration = 0.5 SECONDS
color = COLOR_LIME
var/max_alpha = 255
+ ///How far the effect would scale in size
+ var/amount_to_scale = 2
/obj/effect/temp_visual/circle_wave/Initialize(mapload)
transform = matrix().Scale(0.1)
- animate(src, transform = matrix().Scale(2), time = duration, flags = ANIMATION_PARALLEL)
+ animate(src, transform = matrix().Scale(amount_to_scale), time = duration, flags = ANIMATION_PARALLEL)
animate(src, alpha = max_alpha, time = duration * 0.6, flags = ANIMATION_PARALLEL)
animate(alpha = 0, time = duration * 0.4)
apply_wibbly_filters(src)
@@ -104,3 +106,7 @@
/obj/effect/temp_visual/circle_wave/bioscrambler/light
max_alpha = 128
+/obj/effect/temp_visual/circle_wave/void_conduit
+ color = COLOR_FULL_TONER_BLACK
+ duration = 12 SECONDS
+ amount_to_scale = 12
diff --git a/code/game/objects/effects/anomalies/anomalies_dimensional_themes.dm b/code/game/objects/effects/anomalies/anomalies_dimensional_themes.dm
index a9d2e0bcaa0c4..2d92eaabb929c 100644
--- a/code/game/objects/effects/anomalies/anomalies_dimensional_themes.dm
+++ b/code/game/objects/effects/anomalies/anomalies_dimensional_themes.dm
@@ -11,7 +11,7 @@
/// Typepath of custom material to use for objects.
var/datum/material/material
/// Sound to play when transforming a tile
- var/sound = 'sound/magic/blind.ogg'
+ var/sound = 'sound/effects/magic/blind.ogg'
/// Weighted list of turfs to replace the floor with.
var/list/replace_floors = list(/turf/open/floor/material = 1)
/// Typepath of turf to replace walls with.
@@ -255,7 +255,7 @@
icon = 'icons/obj/ore.dmi'
icon_state = "uranium"
material = /datum/material/uranium
- sound = 'sound/items/welder.ogg'
+ sound = 'sound/items/tools/welder.ogg'
/datum/dimension_theme/meat
name = "Meat"
@@ -468,4 +468,4 @@
/obj/item/reagent_containers/cup/glass/trophy = list(/obj/item/reagent_containers/cup/glass/trophy/bronze_cup = 1),
/obj/machinery/door/airlock = list(/obj/machinery/door/airlock/bronze = 1),
)
- sound = 'sound/magic/clockwork/fellowship_armory.ogg'
+ sound = 'sound/effects/magic/clockwork/fellowship_armory.ogg'
diff --git a/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm b/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm
index e6c3e855386b7..0998e3f803dec 100644
--- a/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm
+++ b/code/game/objects/effects/anomalies/anomalies_ectoplasm.dm
@@ -132,11 +132,11 @@
icon_state = "anom"
anchored = TRUE
var/static/list/spooky_noises = list(
- 'sound/hallucinations/growl1.ogg',
- 'sound/hallucinations/growl2.ogg',
- 'sound/hallucinations/growl3.ogg',
- 'sound/hallucinations/veryfar_noise.ogg',
- 'sound/hallucinations/wail.ogg'
+ 'sound/effects/hallucinations/growl1.ogg',
+ 'sound/effects/hallucinations/growl2.ogg',
+ 'sound/effects/hallucinations/growl3.ogg',
+ 'sound/effects/hallucinations/veryfar_noise.ogg',
+ 'sound/effects/hallucinations/wail.ogg'
)
var/list/ghosts_spawned = list()
diff --git a/code/game/objects/effects/anomalies/anomalies_hallucination.dm b/code/game/objects/effects/anomalies/anomalies_hallucination.dm
index 4065d8c04a45e..63997d4da6809 100644
--- a/code/game/objects/effects/anomalies/anomalies_hallucination.dm
+++ b/code/game/objects/effects/anomalies/anomalies_hallucination.dm
@@ -18,6 +18,7 @@
/obj/effect/anomaly/hallucination/Initialize(mapload, new_lifespan, drops_core)
. = ..()
apply_wibbly_filters(src)
+ generate_decoys()
/obj/effect/anomaly/hallucination/anomalyEffect(seconds_per_tick)
. = ..()
@@ -40,10 +41,60 @@
if(!isturf(loc))
return
- visible_hallucination_pulse(
+ hallucination_pulse(
center = get_turf(src),
- radius = 10,
+ radius = 15,
hallucination_duration = 50 SECONDS,
hallucination_max_duration = 300 SECONDS,
optional_messages = messages,
)
+
+/obj/effect/anomaly/hallucination/proc/generate_decoys()
+ for(var/turf/floor in orange(1, src))
+ if(prob(35))
+ new /obj/effect/anomaly/hallucination/decoy(floor)
+
+/obj/effect/anomaly/hallucination/decoy
+ drops_core = FALSE
+ ///Stores the fake analyzer scan text, so the result is always consistent for each anomaly.
+ var/report_text
+
+/obj/effect/anomaly/hallucination/decoy/Initialize(mapload, new_lifespan, drops_core)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_ILLUSORY_EFFECT, INNATE_TRAIT)
+ report_text = pick(
+ "[src]'s unstable field is fluctuating along frequency 9999999.99999, code 9999999.99999. No, no, that can't be right?",
+ "It doesn't detect anything. It awaits an input, as if you're pointing it towards nothing at all. What?",
+ "The interface displays [pick("a bad memory from your past", "the frequency numbers in a language you cannot read", "the first 15 digits of Pi", "yourself, from behind, angled at a 3/4ths isometric perspective")]. What the hell?",
+ "Nothing happens?",
+ "It reports that you are a [pick("moron", "idiot", "cretin", "lowlife", "worthless denthead", "gump")]. Huh?",
+ "It tells you to try again, because you're doing it all wrong. What?",
+ "It occurs to you that the anomaly you're scanning isn't actually there.",
+ "It's not working. You activate %TOOL% again. Still broken. You activate %TOOL%. You activate %TOOL%. Why isn't this working??",
+ "Something happens. You can't tell what. The interface on %TOOL% remains blank.",
+ "What are you even trying to accomplish here? Did you really think that was going to work?",
+ "Someone behind you whispers the frequency code to you, but you can't quite hear them. The interface on %TOOL% remains blank.",
+ "For a brief moment, you see yourself traversing a frozen forest, before snapping back to reality. The interface on %TOOL% remains blank.",
+ "Nothing interesting happens. Are you sure you're actually using it on anything?",
+ "For a moment you can feel your skin falling off, then blink as the sensation vanishes. What the hell did that mean?",
+ "The interface reports that you are a complete failure, and have screwed everything up again. Great work.",
+ "You realize that the formatting of this message is completely wrong, and get confused. Now why would that be?",
+ "%TOOL% stares back at you. It looks dissapointed, its screen practically saying 'You missed the anomaly, you dolt. There's nothing there!'",
+ "Nothing. Weird, maybe %TOOL% must be broken or something?",
+ "You activate %TOOL%. You activate %TOOL%. You activate %TOOL%. You activate %TOOL%. You activate %TOOL%. You activate %TOOL%. You activate %TOOL%. Why isn't it working??",
+ )
+
+/obj/effect/anomaly/hallucination/decoy/anomalyEffect(seconds_per_tick)
+ if(SPT_PROB(move_chance, seconds_per_tick))
+ move_anomaly()
+
+/obj/effect/anomaly/hallucination/decoy/analyzer_act(mob/living/user, obj/item/analyzer/tool)
+ to_chat(user, span_notice("You activate [tool]. [replacetext(report_text, "%TOOL%", "[tool]")]"))
+ return ITEM_INTERACT_BLOCKING
+
+/obj/effect/anomaly/hallucination/decoy/detonate()
+ do_sparks(3, source = src)
+ return
+
+/obj/effect/anomaly/hallucination/decoy/generate_decoys()
+ return
diff --git a/code/game/objects/effects/cursor_catcher.dm b/code/game/objects/effects/cursor_catcher.dm
index a8c19e40be80d..366ab0556248c 100644
--- a/code/game/objects/effects/cursor_catcher.dm
+++ b/code/game/objects/effects/cursor_catcher.dm
@@ -60,8 +60,8 @@
var/icon_y = text2num(LAZYACCESS(modifiers, VIS_Y))
if(isnull(icon_y))
icon_y = text2num(LAZYACCESS(modifiers, ICON_Y))
- var/our_x = round(icon_x / world.icon_size)
- var/our_y = round(icon_y / world.icon_size)
+ var/our_x = round(icon_x / ICON_SIZE_X)
+ var/our_y = round(icon_y / ICON_SIZE_Y)
given_turf = locate(owner.x + our_x - round(view_list[1]/2), owner.y + our_y - round(view_list[2]/2), owner.z)
- given_x = round(icon_x - world.icon_size * our_x, 1)
- given_y = round(icon_y - world.icon_size * our_y, 1)
+ given_x = round(icon_x - ICON_SIZE_X * our_x, 1)
+ given_y = round(icon_y - ICON_SIZE_Y * our_y, 1)
diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm
index b6837df6f9546..21eff5028b57e 100644
--- a/code/game/objects/effects/decals/cleanable.dm
+++ b/code/game/objects/effects/decals/cleanable.dm
@@ -1,6 +1,5 @@
/obj/effect/decal/cleanable
gender = PLURAL
- plane = GAME_PLANE
layer = FLOOR_CLEAN_LAYER
var/list/random_icon_states = null
///I'm sorry but cleanable/blood code is ass, and so is blood_DNA
diff --git a/code/game/objects/effects/decals/cleanable/aliens.dm b/code/game/objects/effects/decals/cleanable/aliens.dm
index bf826e207db37..bc7923ac0ed47 100644
--- a/code/game/objects/effects/decals/cleanable/aliens.dm
+++ b/code/game/objects/effects/decals/cleanable/aliens.dm
@@ -23,7 +23,8 @@
desc = "Gnarly..."
icon = 'icons/effects/blood.dmi'
icon_state = "xgib1"
- layer = LOW_OBJ_LAYER
+ plane = GAME_PLANE
+ layer = BELOW_OBJ_LAYER
random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6")
mergeable_decal = FALSE
diff --git a/code/game/objects/effects/decals/cleanable/food.dm b/code/game/objects/effects/decals/cleanable/food.dm
index 0fc4352c78da9..d612bd9e7f53e 100644
--- a/code/game/objects/effects/decals/cleanable/food.dm
+++ b/code/game/objects/effects/decals/cleanable/food.dm
@@ -10,6 +10,9 @@
icon_state = "tomato_floor1"
random_icon_states = list("tomato_floor1", "tomato_floor2", "tomato_floor3")
+/obj/effect/decal/cleanable/food/tomato_smudge/can_bloodcrawl_in()
+ return TRUE // why? why not.
+
/obj/effect/decal/cleanable/food/plant_smudge
name = "plant smudge"
desc = "Chlorophyll? More like borophyll!"
@@ -58,3 +61,14 @@
name = "flour"
desc = "It's still good. Four second rule!"
icon_state = "flour"
+
+/obj/effect/decal/cleanable/food/squid_ink
+ name = "ink smear"
+ desc = "a smear from some inky substance..."
+ icon = 'icons/mob/silicon/robots.dmi'
+ icon_state = "floor1"
+ color = COLOR_DARK
+
+/obj/effect/decal/cleanable/food/squid_ink/Initialize(mapload, list/datum/disease/diseases)
+ icon_state = "floor[rand(1, 7)]"
+ return ..()
diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm
index 062ba3837230b..4e7fd2d01a902 100644
--- a/code/game/objects/effects/decals/cleanable/humans.dm
+++ b/code/game/objects/effects/decals/cleanable/humans.dm
@@ -111,7 +111,7 @@
desc = "They look bloody and gruesome."
icon = 'icons/effects/blood.dmi'
icon_state = "gib1"
- layer = LOW_OBJ_LAYER
+ layer = BELOW_OBJ_LAYER
plane = GAME_PLANE
random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6")
mergeable_decal = FALSE
@@ -354,6 +354,8 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache)
pass_flags = PASSTABLE | PASSGRILLE
icon_state = "hitsplatter1"
random_icon_states = list("hitsplatter1", "hitsplatter2", "hitsplatter3")
+ plane = GAME_PLANE
+ layer = ABOVE_WINDOW_LAYER
/// The turf we just came from, so we can back up when we hit a wall
var/turf/prev_loc
/// The cached info about the blood
diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm
index f423f3644f0c8..caf7428ef01fa 100644
--- a/code/game/objects/effects/decals/cleanable/misc.dm
+++ b/code/game/objects/effects/decals/cleanable/misc.dm
@@ -10,6 +10,8 @@
desc = "Ashes to ashes, dust to dust, and into space."
icon = 'icons/obj/debris.dmi'
icon_state = "ash"
+ plane = GAME_PLANE
+ layer = GAME_CLEAN_LAYER
mergeable_decal = FALSE
beauty = -50
decal_reagent = /datum/reagent/ash
@@ -144,6 +146,7 @@
name = "cobweb"
desc = "Somebody should remove that."
gender = NEUTER
+ plane = GAME_PLANE
layer = WALL_OBJ_LAYER
icon = 'icons/effects/web.dmi'
icon_state = "cobweb1"
@@ -160,6 +163,8 @@
gender = NEUTER
icon = 'icons/effects/effects.dmi'
icon_state = "molten"
+ plane = GAME_PLANE
+ layer = GAME_CLEAN_LAYER
mergeable_decal = FALSE
beauty = -150
clean_type = CLEAN_TYPE_HARD_DECAL
@@ -245,6 +250,8 @@
name = "chemical pile"
desc = "A pile of chemicals. You can't quite tell what's inside it."
gender = NEUTER
+ plane = GAME_PLANE
+ layer = GAME_CLEAN_LAYER
icon = 'icons/obj/debris.dmi'
icon_state = "ash"
@@ -322,6 +329,8 @@
desc = "Torn pieces of cardboard and paper, left over from a package."
icon = 'icons/obj/debris.dmi'
icon_state = "paper_shreds"
+ plane = GAME_PLANE
+ layer = GAME_CLEAN_LAYER
/obj/effect/decal/cleanable/wrapping/pinata
name = "pinata shreds"
@@ -340,7 +349,7 @@
icon = 'icons/obj/debris.dmi'
icon_state = "garbage"
plane = GAME_PLANE
- layer = FLOOR_CLEAN_LAYER //To display the decal over wires.
+ layer = GAME_CLEAN_LAYER
beauty = -150
clean_type = CLEAN_TYPE_HARD_DECAL
@@ -359,7 +368,7 @@
decal_reagent = /datum/reagent/ants
reagent_amount = 5
/// Sound the ants make when biting
- var/bite_sound = 'sound/weapons/bite.ogg'
+ var/bite_sound = 'sound/items/weapons/bite.ogg'
/obj/effect/decal/cleanable/ants/Initialize(mapload)
if(mapload && reagent_amount > 2)
@@ -443,7 +452,6 @@
name = "pool of fuel"
desc = "A pool of flammable fuel. Its probably wise to clean this off before something ignites it..."
icon_state = "fuel_pool"
- layer = LOW_OBJ_LAYER
beauty = -50
clean_type = CLEAN_TYPE_BLOOD
mouse_opacity = MOUSE_OPACITY_OPAQUE
@@ -558,6 +566,8 @@
icon_state = "rubble"
mergeable_decal = FALSE
beauty = -10
+ plane = GAME_PLANE
+ layer = BELOW_OBJ_LAYER
/obj/effect/decal/cleanable/rubble/Initialize(mapload)
. = ..()
diff --git a/code/game/objects/effects/decals/cleanable/robots.dm b/code/game/objects/effects/decals/cleanable/robots.dm
index 808a68d6f5eb0..3f2957a9c9e16 100644
--- a/code/game/objects/effects/decals/cleanable/robots.dm
+++ b/code/game/objects/effects/decals/cleanable/robots.dm
@@ -5,7 +5,8 @@
desc = "It's a useless heap of junk... or is it?"
icon = 'icons/mob/silicon/robots.dmi'
icon_state = "gib1"
- layer = LOW_OBJ_LAYER
+ plane = GAME_PLANE
+ layer = BELOW_OBJ_LAYER
random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7")
blood_state = BLOOD_STATE_OIL
bloodiness = BLOOD_AMOUNT_PER_DECAL
diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm
index eced2fb66f1ee..e27e6f91337fe 100644
--- a/code/game/objects/effects/decals/crayon.dm
+++ b/code/game/objects/effects/decals/crayon.dm
@@ -4,7 +4,6 @@
icon = 'icons/effects/crayondecal.dmi'
icon_state = "rune1"
gender = NEUTER
- plane = GAME_PLANE //makes the graffiti visible over a wall.
mergeable_decal = FALSE
flags_1 = ALLOW_DARK_PAINTS_1
var/do_icon_rotate = TRUE
@@ -13,6 +12,10 @@
/obj/effect/decal/cleanable/crayon/Initialize(mapload, main, type, e_name, graf_rot, alt_icon = null, desc_override = null)
. = ..()
+ if(isclosedturf(loc) && loc.density)
+ // allows for wall graffiti to be seen
+ SET_PLANE_IMPLICIT(src, GAME_PLANE)
+ layer = GAME_CLEAN_LAYER
if(e_name)
name = e_name
if(desc_override)
diff --git a/code/game/objects/effects/decals/remains.dm b/code/game/objects/effects/decals/remains.dm
index 55cd7cd98d089..803555ae89a99 100644
--- a/code/game/objects/effects/decals/remains.dm
+++ b/code/game/objects/effects/decals/remains.dm
@@ -5,7 +5,7 @@
/obj/effect/decal/remains/acid_act()
visible_message(span_warning("[src] dissolve[gender == PLURAL?"":"s"] into a puddle of sizzling goop!"))
- playsound(src, 'sound/items/welder.ogg', 150, TRUE)
+ playsound(src, 'sound/items/tools/welder.ogg', 150, TRUE)
new /obj/effect/decal/cleanable/greenglow(drop_location())
qdel(src)
return TRUE
diff --git a/code/game/objects/effects/effect_system/effect_system.dm b/code/game/objects/effects/effect_system/effect_system.dm
index 4fdd4ac598ee0..6ddd65f12cfca 100644
--- a/code/game/objects/effects/effect_system/effect_system.dm
+++ b/code/game/objects/effects/effect_system/effect_system.dm
@@ -20,8 +20,8 @@ would spawn and follow the beaker, even if it is carried or thrown.
GLOB.cameranet.updateVisibility(src)
return ..()
-// Prevents effects from getting registered for SSspacedrift
-/obj/effect/particle_effect/newtonian_move(direction, instant = FALSE, start_delay = 0)
+// Prevents effects from getting registered for SSnewtonian_movement
+/obj/effect/particle_effect/newtonian_move(inertia_angle, instant = FALSE, start_delay = 0, drift_force = 0, controlled_cap = null)
return TRUE
/datum/effect_system
diff --git a/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm
index 6d968574c686c..07383a0aa6f0b 100644
--- a/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm
+++ b/code/game/objects/effects/effect_system/fluid_spread/effects_foam.dm
@@ -40,7 +40,7 @@
if(slippery_foam)
AddComponent(/datum/component/slippery, 100)
create_reagents(1000, REAGENT_HOLDER_INSTANT_REACT)
- playsound(src, 'sound/effects/bubbles2.ogg', 80, TRUE, -3)
+ playsound(src, 'sound/effects/bubbles/bubbles2.ogg', 80, TRUE, -3)
AddElement(/datum/element/atmos_sensitive, mapload)
SSfoam.start_processing(src)
@@ -324,7 +324,7 @@
return attack_hand(user, modifiers)
/obj/structure/foamedmetal/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
- playsound(src.loc, 'sound/weapons/tap.ogg', 100, TRUE)
+ playsound(src.loc, 'sound/items/weapons/tap.ogg', 100, TRUE)
/obj/structure/foamedmetal/attack_hand(mob/user, list/modifiers)
. = ..()
@@ -333,7 +333,7 @@
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
to_chat(user, span_warning("You hit [src] but bounce off it!"))
- playsound(src.loc, 'sound/weapons/tap.ogg', 100, TRUE)
+ playsound(src.loc, 'sound/items/weapons/tap.ogg', 100, TRUE)
/obj/structure/foamedmetal/attackby(obj/item/W, mob/user, params)
///A speed modifier for how fast the wall is build
diff --git a/code/game/objects/effects/forcefields.dm b/code/game/objects/effects/forcefields.dm
index 4445815a422be..60ce9d7662b81 100644
--- a/code/game/objects/effects/forcefields.dm
+++ b/code/game/objects/effects/forcefields.dm
@@ -84,7 +84,7 @@
icon = 'icons/effects/eldritch.dmi'
icon_state = "cosmic_carpet"
anchored = TRUE
- layer = LOW_SIGIL_LAYER
+ layer = BELOW_OBJ_LAYER
density = FALSE
can_atmos_pass = ATMOS_PASS_NO
initial_duration = 30 SECONDS
diff --git a/code/game/objects/effects/glowshroom.dm b/code/game/objects/effects/glowshroom.dm
index e9a6263286e59..c98dfc2ddf78e 100644
--- a/code/game/objects/effects/glowshroom.dm
+++ b/code/game/objects/effects/glowshroom.dm
@@ -246,7 +246,7 @@ GLOBAL_VAR_INIT(glowshrooms, 0)
/obj/structure/glowshroom/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
if(damage_type == BURN && damage_amount)
- playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
+ playsound(src.loc, 'sound/items/tools/welder.ogg', 100, TRUE)
/obj/structure/glowshroom/should_atmos_process(datum/gas_mixture/air, exposed_temperature)
return exposed_temperature > 300
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 64c0afe188a8a..b1a4a75c945d7 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -306,6 +306,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/start/new_player)
GLOB.newplayer_start += loc
return INITIALIZE_HINT_QDEL
+/obj/effect/landmark/start/pun_pun
+ name = JOB_PUN_PUN
+ icon = 'icons/mob/human/human.dmi'
+ icon_state = "monkey"
+
/obj/effect/landmark/latejoin
name = "JoinLate"
diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm
index f080035d54c2e..12c8c15b62eaf 100644
--- a/code/game/objects/effects/mines.dm
+++ b/code/game/objects/effects/mines.dm
@@ -275,7 +275,7 @@
if(active)
return
- playsound(src, 'sound/weapons/armbomb.ogg', 70, TRUE)
+ playsound(src, 'sound/items/weapons/armbomb.ogg', 70, TRUE)
to_chat(user, span_warning("You arm \the [src], causing it to shake! It will deploy in 3 seconds."))
active = TRUE
addtimer(CALLBACK(src, PROC_REF(deploy_mine)), 3 SECONDS)
diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm
index 255f34eff51dd..a43fee5608de3 100644
--- a/code/game/objects/effects/portals.dm
+++ b/code/game/objects/effects/portals.dm
@@ -66,7 +66,7 @@
return ..()
// Prevents portals spawned by jaunter/handtele from floating into space when relocated to an adjacent tile.
-/obj/effect/portal/newtonian_move(direction, instant = FALSE, start_delay = 0)
+/obj/effect/portal/newtonian_move(inertia_angle, instant = FALSE, start_delay = 0, drift_force = 0, controlled_cap = null)
return TRUE
/obj/effect/portal/attackby(obj/item/W, mob/user, params)
diff --git a/code/game/objects/effects/posters/poster.dm b/code/game/objects/effects/posters/poster.dm
index 4ced5babbbfa8..135887aafc83f 100644
--- a/code/game/objects/effects/posters/poster.dm
+++ b/code/game/objects/effects/posters/poster.dm
@@ -248,7 +248,7 @@
flick("poster_being_set", placed_poster)
placed_poster.forceMove(src) //deletion of the poster is handled in poster/Exited(), so don't have to worry about P anymore.
- playsound(src, 'sound/items/poster_being_created.ogg', 100, TRUE)
+ playsound(src, 'sound/items/poster/poster_being_created.ogg', 100, TRUE)
var/turf/user_drop_location = get_turf(user) //cache this so it just falls to the ground if they move. also no tk memes allowed.
if(!do_after(user, PLACE_SPEED, placed_poster, extra_checks = CALLBACK(placed_poster, TYPE_PROC_REF(/obj/structure/sign/poster, snowflake_closed_turf_check), src)))
@@ -266,7 +266,7 @@
/obj/structure/sign/poster/proc/tear_poster(mob/user)
visible_message(span_notice("[user] rips [src] in a single, decisive motion!") )
- playsound(src.loc, 'sound/items/poster_ripped.ogg', 100, TRUE)
+ playsound(src.loc, 'sound/items/poster/poster_ripped.ogg', 100, TRUE)
spring_trap(user)
var/obj/structure/sign/poster/ripped/torn_poster = new(loc)
diff --git a/code/game/objects/effects/powerup.dm b/code/game/objects/effects/powerup.dm
index 3dc5db8de95de..8598d623fb0e5 100644
--- a/code/game/objects/effects/powerup.dm
+++ b/code/game/objects/effects/powerup.dm
@@ -59,7 +59,7 @@
icon_state = "backpack-medical"
respawn_time = 30 SECONDS
pickup_message = "Health restored!"
- pickup_sound = 'sound/magic/staff_healing.ogg'
+ pickup_sound = 'sound/effects/magic/staff_healing.ogg'
/// How much the pickup heals when picked up
var/heal_amount = 50
/// Does this pickup fully heal when picked up
@@ -89,7 +89,7 @@
icon_state = "ammobox"
respawn_time = 30 SECONDS
pickup_message = "Ammunition reloaded!"
- pickup_sound = 'sound/weapons/gun/shotgun/rack.ogg'
+ pickup_sound = 'sound/items/weapons/gun/shotgun/rack.ogg'
/obj/effect/powerup/ammo/trigger(mob/living/target)
. = ..()
@@ -110,7 +110,7 @@
name = "Lightning Orb"
desc = "You feel faster just looking at it."
icon_state = "speed"
- pickup_sound = 'sound/magic/lightningshock.ogg'
+ pickup_sound = 'sound/effects/magic/lightningshock.ogg'
/obj/effect/powerup/speed/trigger(mob/living/target)
. = ..()
diff --git a/code/game/objects/effects/rcd.dm b/code/game/objects/effects/rcd.dm
index 17ea3e44a10a1..03d77f0f84504 100644
--- a/code/game/objects/effects/rcd.dm
+++ b/code/game/objects/effects/rcd.dm
@@ -10,7 +10,7 @@
* * fade_time - The time for RCD holograms to fade
*/
/proc/rcd_scan(atom/source, scan_range = RCD_DESTRUCTIVE_SCAN_RANGE, fade_time = RCD_HOLOGRAM_FADE_TIME)
- playsound(source, 'sound/items/rcdscan.ogg', 50, vary = TRUE, pressure_affected = FALSE)
+ playsound(source, 'sound/items/tools/rcdscan.ogg', 50, vary = TRUE, pressure_affected = FALSE)
var/turf/source_turf = get_turf(source)
for(var/turf/open/surrounding_turf as anything in RANGE_TURFS(scan_range, source_turf))
diff --git a/code/game/objects/effects/spawners/gibspawner.dm b/code/game/objects/effects/spawners/gibspawner.dm
index 50497d531e76f..d05d5f039256b 100644
--- a/code/game/objects/effects/spawners/gibspawner.dm
+++ b/code/game/objects/effects/spawners/gibspawner.dm
@@ -4,7 +4,7 @@
var/sparks = 0 //whether sparks spread
var/virusProb = 20 //the chance for viruses to spread on the gibs
var/gib_mob_type //generate a fake mob to transfer DNA from if we weren't passed a mob.
- var/sound_to_play = 'sound/effects/blobattack.ogg'
+ var/sound_to_play = 'sound/effects/blob/blobattack.ogg'
var/sound_vol = 60
var/list/gibtypes = list() //typepaths of the gib decals to spawn
var/list/gibamounts = list() //amount to spawn for each gib decal type we'll spawn.
diff --git a/code/game/objects/effects/spawners/random/trash.dm b/code/game/objects/effects/spawners/random/trash.dm
index 9cf00c20ee3ec..6f6f5badc8e7e 100644
--- a/code/game/objects/effects/spawners/random/trash.dm
+++ b/code/game/objects/effects/spawners/random/trash.dm
@@ -324,3 +324,18 @@
if(istype(crushed_can))
crushed_can.icon_state = pick(soda_icons)
return crushed_can
+
+/obj/effect/spawner/random/trash/ghetto_containers
+ name = "ghetto container spawner"
+ loot = list(
+ /obj/item/reagent_containers/cup/bucket = 5,
+ /obj/item/reagent_containers/cup/glass/bottle = 5,
+ /obj/item/reagent_containers/cup/glass/bottle/small = 5,
+ /obj/item/reagent_containers/cup/glass/mug = 5,
+ /obj/item/reagent_containers/cup/glass/shaker = 5,
+ /obj/item/reagent_containers/cup/watering_can/wood = 5,
+ /obj/item/reagent_containers/cup/mortar = 2,
+ /obj/item/reagent_containers/cup/soup_pot = 2,
+ /obj/item/reagent_containers/cup/blastoff_ampoule = 1,
+ /obj/item/reagent_containers/cup/maunamug = 1,
+ )
diff --git a/code/game/objects/effects/spiderwebs.dm b/code/game/objects/effects/spiderwebs.dm
index 2d0f1b9b14de2..49765c059865b 100644
--- a/code/game/objects/effects/spiderwebs.dm
+++ b/code/game/objects/effects/spiderwebs.dm
@@ -14,7 +14,7 @@
/obj/structure/spider/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
if(damage_type == BURN)//the stickiness of the web mutes all attack sounds except fire damage type
- playsound(loc, 'sound/items/welder.ogg', 100, TRUE)
+ playsound(loc, 'sound/items/tools/welder.ogg', 100, TRUE)
/obj/structure/spider/run_atom_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
if(damage_flag == MELEE)
diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
index 9cb926fd19756..a694b18d3388a 100644
--- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm
+++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
@@ -441,7 +441,7 @@
if(size_calc_target)
layer = size_calc_target.layer + 0.01
var/icon/I = icon(size_calc_target.icon, size_calc_target.icon_state, size_calc_target.dir)
- size_matrix = matrix() * (I.Height()/world.icon_size)
+ size_matrix = matrix() * (I.Height()/ICON_SIZE_Y)
transform = size_matrix //scale the bleed overlay's size based on the target's icon size
var/matrix/M = transform
if(shrink)
@@ -563,7 +563,7 @@
/obj/effect/constructing_effect/proc/attacked(mob/user)
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
user.changeNext_move(CLICK_CD_MELEE)
- playsound(loc, 'sound/weapons/egloves.ogg', vol = 80, vary = TRUE)
+ playsound(loc, 'sound/items/weapons/egloves.ogg', vol = 80, vary = TRUE)
end()
/obj/effect/constructing_effect/attackby(obj/item/weapon, mob/user, params)
diff --git a/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm b/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm
index 14b7f43a82280..8c4ea163232e1 100644
--- a/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm
+++ b/code/game/objects/effects/temporary_visuals/projectiles/tracer.dm
@@ -5,7 +5,7 @@
var/obj/effect/projectile/tracer/PB = new beam_type
if(isnull(light_color_override))
light_color_override = color
- PB.apply_vars(angle_between_points(starting, ending), midpoint.return_px(), midpoint.return_py(), color, pixel_length_between_points(starting, ending) / world.icon_size, midpoint.return_turf(), 0)
+ PB.apply_vars(angle_between_points(starting, ending), midpoint.return_px(), midpoint.return_py(), color, pixel_length_between_points(starting, ending) / ICON_SIZE_ALL, midpoint.return_turf(), 0)
. = PB
if(light_range > 0 && light_intensity > 0)
var/list/turf/line = get_line(starting.return_turf(), ending.return_turf())
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index d8288801b90fb..5692101ec2524 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -83,8 +83,10 @@
var/equip_sound
///Sound uses when picking the item up (into your hands)
var/pickup_sound
- ///Sound uses when dropping the item, or when its thrown.
+ ///Sound uses when dropping the item, or when its thrown if a thrown sound isn't specified.
var/drop_sound
+ ///Sound used on impact when the item is thrown.
+ var/throw_drop_sound
///Do the drop and pickup sounds vary?
var/sound_vary = FALSE
///Whether or not we use stealthy audio levels for this item's attack sounds
@@ -266,7 +268,7 @@
if(!hitsound)
if(damtype == BURN)
- hitsound = 'sound/items/welder.ogg'
+ hitsound = 'sound/items/tools/welder.ogg'
if(damtype == BRUTE)
hitsound = SFX_SWING_HIT
@@ -437,27 +439,36 @@
abstract_move(null)
forceMove(T)
-/obj/item/examine(mob/user) //This might be spammy. Remove?
- . = ..()
-
- . += "[gender == PLURAL ? "They are" : "It is"] a [weight_class_to_text(w_class)] item."
+/obj/item/examine_tags(mob/user)
+ var/list/parent_tags = ..()
+ parent_tags.Insert(1, weight_class_to_text(w_class)) // To make size display first, otherwise it looks goofy
+ . = parent_tags
+ .[weight_class_to_text(w_class)] = "[gender == PLURAL ? "They are" : "It is"] a [weight_class_to_text(w_class)] item."
if(item_flags & CRUEL_IMPLEMENT)
- . += "[src] seems quite practical for particularly morbid procedures and experiments."
+ .[span_red("morbid")] = "It seems quite practical for particularly morbid procedures and experiments."
+
+ if (siemens_coefficient == 0)
+ .["insulated"] = "It is made from a robust electrical insulator and will block any electricity passing through it!"
+ else if (siemens_coefficient <= 0.5)
+ .["partially insulated"] = "It is made from a poor insulator that will dampen (but not fully block) electric shocks passing through it."
if(resistance_flags & INDESTRUCTIBLE)
- . += "[src] seems extremely robust! It'll probably withstand anything that could happen to it!"
- else
- if(resistance_flags & LAVA_PROOF)
- . += "[src] is made of an extremely heat-resistant material, it'd probably be able to withstand lava!"
- if(resistance_flags & (ACID_PROOF | UNACIDABLE))
- . += "[src] looks pretty robust! It'd probably be able to withstand acid!"
- if(resistance_flags & FREEZE_PROOF)
- . += "[src] is made of cold-resistant materials."
- if(resistance_flags & FIRE_PROOF)
- . += "[src] is made of fire-retardant materials."
+ .["indestructible"] = "It is extremely robust! It'll probably withstand anything that could happen to it!"
return
+ if(resistance_flags & LAVA_PROOF)
+ .["lavaproof"] = "It is made of an extremely heat-resistant material, it'd probably be able to withstand lava!"
+ if(resistance_flags & (ACID_PROOF | UNACIDABLE))
+ .["acidproof"] = "It looks pretty robust! It'd probably be able to withstand acid!"
+ if(resistance_flags & FREEZE_PROOF)
+ .["freezeproof"] = "It is made of cold-resistant materials."
+ if(resistance_flags & FIRE_PROOF)
+ .["fireproof"] = "It is made of fire-retardant materials."
+
+/obj/item/examine_descriptor(mob/user)
+ return "item"
+
/obj/item/examine_more(mob/user)
. = ..()
if(HAS_TRAIT(user, TRAIT_RESEARCH_SCANNER))
@@ -624,7 +635,7 @@
/obj/item/attack_alien(mob/user, list/modifiers)
var/mob/living/carbon/alien/ayy = user
- if(!user.can_hold_items(src))
+ if(!ayy.can_hold_items(src))
if(src in ayy.contents) // To stop Aliens having items stuck in their pockets
ayy.dropItemToGround(src)
to_chat(user, span_warning("Your claws aren't capable of such fine manipulation!"))
@@ -831,36 +842,33 @@
. = ..()
do_drop_animation(master_storage.parent)
+/obj/item/pre_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ var/impact_flags = ..()
+ if(w_class < WEIGHT_CLASS_BULKY)
+ impact_flags |= COMPONENT_MOVABLE_IMPACT_FLIP_HITPUSH
+ if(!(impact_flags & COMPONENT_MOVABLE_IMPACT_NEVERMIND) && get_temperature() && isliving(hit_atom))
+ var/mob/living/victim = hit_atom
+ victim.ignite_mob()
+ return impact_flags
+
/obj/item/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
- if(QDELETED(hit_atom))
- return
- if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_IMPACT, hit_atom, throwingdatum) & COMPONENT_MOVABLE_IMPACT_NEVERMIND)
- return
- if(SEND_SIGNAL(hit_atom, COMSIG_ATOM_PREHITBY, src, throwingdatum) & COMSIG_HIT_PREVENTED)
+ . = ..()
+ if(!isliving(hit_atom)) //Living mobs handle hit sounds differently.
+ if(throw_drop_sound)
+ playsound(src, throw_drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE, vary = sound_vary)
+ return
+ playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE, vary = sound_vary)
return
-
- SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
- if(get_temperature() && isliving(hit_atom))
- var/mob/living/L = hit_atom
- L.ignite_mob()
- var/itempush = 1
- if(w_class < WEIGHT_CLASS_BULKY)
- itempush = 0 //too light to push anything
- if(isliving(hit_atom)) //Living mobs handle hit sounds differently.
- var/volume = get_volume_by_throwforce_and_or_w_class()
- if (throwforce > 0 || HAS_TRAIT(src, TRAIT_CUSTOM_TAP_SOUND))
- if (mob_throw_hit_sound)
- playsound(hit_atom, mob_throw_hit_sound, volume, TRUE, -1)
- else if(hitsound)
- playsound(hit_atom, hitsound, volume, TRUE, -1)
- else
- playsound(hit_atom, 'sound/weapons/genhit.ogg',volume, TRUE, -1)
+ var/volume = get_volume_by_throwforce_and_or_w_class()
+ if (throwforce > 0 || HAS_TRAIT(src, TRAIT_CUSTOM_TAP_SOUND))
+ if (mob_throw_hit_sound)
+ playsound(hit_atom, mob_throw_hit_sound, volume, TRUE, -1)
+ else if(hitsound)
+ playsound(hit_atom, hitsound, volume, TRUE, -1)
else
- playsound(hit_atom, 'sound/weapons/throwtap.ogg', 1, volume, -1)
-
+ playsound(hit_atom, 'sound/items/weapons/genhit.ogg',volume, TRUE, -1)
else
- playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE)
- return hit_atom.hitby(src, 0, itempush, throwingdatum=throwingdatum)
+ playsound(hit_atom, 'sound/items/weapons/throwtap.ogg', 1, volume, -1)
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE)
if(HAS_TRAIT(src, TRAIT_NODROP))
@@ -1173,13 +1181,13 @@
return TRUE
/// Called before [obj/item/proc/use_tool] if there is a delay, or by [obj/item/proc/use_tool] if there isn't. Only ever used by welding tools and stacks, so it's not added on any other [obj/item/proc/use_tool] checks.
-/obj/item/proc/tool_start_check(mob/living/user, amount=0)
- . = tool_use_check(user, amount)
+/obj/item/proc/tool_start_check(mob/living/user, amount=0, heat_required=0)
+ . = tool_use_check(user, amount, heat_required)
if(.)
SEND_SIGNAL(src, COMSIG_TOOL_START_USE, user)
/// A check called by [/obj/item/proc/tool_start_check] once, and by use_tool on every tick of delay.
-/obj/item/proc/tool_use_check(mob/living/user, amount)
+/obj/item/proc/tool_use_check(mob/living/user, amount, heat_required)
return !amount
/// Generic use proc. Depending on the item, it uses up fuel, charges, sheets, etc. Returns TRUE on success, FALSE on failure.
@@ -1746,9 +1754,11 @@
/obj/item/proc/set_embed(datum/embed_data/embed)
if(embed_data == embed)
return
+ if(isnull(get_embed())) // Add embed on objects that did not have it added
+ AddElement(/datum/element/embed)
if(!GLOB.embed_by_type[embed_data?.type])
qdel(embed_data)
- embed_data = ispath(embed) ? get_embed_by_type(armor) : embed
+ embed_data = ispath(embed) ? get_embed_by_type(embed) : embed
SEND_SIGNAL(src, COMSIG_ITEM_EMBEDDING_UPDATE)
/**
@@ -1763,3 +1773,37 @@
RETURN_TYPE(/obj/item)
return src
+
+/// Checks if the bait is liked by the fish type or not. Returns a multiplier that affects the chance of catching it.
+/obj/item/proc/check_bait(obj/item/fish/fish_type)
+ if(HAS_TRAIT(src, TRAIT_OMNI_BAIT))
+ return 1
+ var/catch_multiplier = 1
+ var/list/properties = SSfishing.fish_properties[fish_type]
+ //Bait matching likes doubles the chance
+ var/list/fav_bait = properties[FISH_PROPERTIES_FAV_BAIT]
+ for(var/bait_identifer in fav_bait)
+ if(is_matching_bait(src, bait_identifer))
+ catch_multiplier *= 2
+ //Bait matching dislikes
+ var/list/disliked_bait = properties[FISH_PROPERTIES_BAD_BAIT]
+ for(var/bait_identifer in disliked_bait)
+ if(is_matching_bait(src, bait_identifer))
+ catch_multiplier *= 0.5
+ return catch_multiplier
+
+/// Helper proc that checks if a bait matches identifier from fav/disliked bait list
+/proc/is_matching_bait(obj/item/bait, identifier)
+ if(ispath(identifier)) //Just a path
+ return istype(bait, identifier)
+ if(!islist(identifier))
+ return HAS_TRAIT(bait, identifier)
+ var/list/special_identifier = identifier
+ switch(special_identifier[FISH_BAIT_TYPE])
+ if(FISH_BAIT_FOODTYPE)
+ var/datum/component/edible/edible = bait.GetComponent(/datum/component/edible)
+ return edible?.foodtypes & special_identifier[FISH_BAIT_VALUE]
+ if(FISH_BAIT_REAGENT)
+ return bait.reagents?.has_reagent(special_identifier[FISH_BAIT_VALUE], special_identifier[FISH_BAIT_AMOUNT], check_subtypes = TRUE)
+ else
+ CRASH("Unknown bait identifier in fish favourite/disliked list")
diff --git a/code/game/objects/items/AI_modules/freeform.dm b/code/game/objects/items/AI_modules/freeform.dm
index a0a91f7185e5a..05ef00c946772 100644
--- a/code/game/objects/items/AI_modules/freeform.dm
+++ b/code/game/objects/items/AI_modules/freeform.dm
@@ -8,7 +8,7 @@
laws = list("")
/obj/item/ai_module/core/freeformcore/attack_self(mob/user)
- var/targName = tgui_input_text(user, "Enter a new core law for the AI.", "Freeform Law Entry", laws[1], CONFIG_GET(number/max_law_len), TRUE)
+ var/targName = tgui_input_text(user, "Enter a new core law for the AI.", "Freeform Law Entry", laws[1], max_length = CONFIG_GET(number/max_law_len), multiline = TRUE)
if(!targName || !user.is_holding(src))
return
if(is_ic_filtered(targName))
@@ -37,7 +37,7 @@
if(!newpos || !user.is_holding(src) || !usr.can_perform_action(src, FORBID_TELEKINESIS_REACH))
return
lawpos = newpos
- var/targName = tgui_input_text(user, "Enter a new law for the AI.", "Freeform Law Entry", laws[1], CONFIG_GET(number/max_law_len), TRUE)
+ var/targName = tgui_input_text(user, "Enter a new law for the AI.", "Freeform Law Entry", laws[1], max_length = CONFIG_GET(number/max_law_len), multiline = TRUE)
if(!targName || !user.is_holding(src))
return
if(is_ic_filtered(targName))
diff --git a/code/game/objects/items/AI_modules/full_lawsets.dm b/code/game/objects/items/AI_modules/full_lawsets.dm
index 30e904d45ac84..593bc43f2dcea 100644
--- a/code/game/objects/items/AI_modules/full_lawsets.dm
+++ b/code/game/objects/items/AI_modules/full_lawsets.dm
@@ -58,7 +58,7 @@
var/subject = "human being"
/obj/item/ai_module/core/full/asimov/attack_self(mob/user as mob)
- var/targName = tgui_input_text(user, "Enter a new subject that Asimov is concerned with.", "Asimov", subject, MAX_NAME_LEN)
+ var/targName = tgui_input_text(user, "Enter a new subject that Asimov is concerned with.", "Asimov", subject, max_length = MAX_NAME_LEN)
if(!targName || !user.is_holding(src))
return
subject = targName
@@ -73,7 +73,7 @@
var/subject = "human being"
/obj/item/ai_module/core/full/asimovpp/attack_self(mob/user)
- var/target_name = tgui_input_text(user, "Enter a new subject that Asimov++ is concerned with.", "Asimov++", subject, MAX_NAME_LEN)
+ var/target_name = tgui_input_text(user, "Enter a new subject that Asimov++ is concerned with.", "Asimov++", subject, max_length = MAX_NAME_LEN)
if(!target_name || !user.is_holding(src))
return
laws.Cut()
diff --git a/code/game/objects/items/AI_modules/hacked.dm b/code/game/objects/items/AI_modules/hacked.dm
index fafde17acb5f3..41a1f38ba891d 100644
--- a/code/game/objects/items/AI_modules/hacked.dm
+++ b/code/game/objects/items/AI_modules/hacked.dm
@@ -4,7 +4,7 @@
laws = list("")
/obj/item/ai_module/syndicate/attack_self(mob/user)
- var/targName = tgui_input_text(user, "Enter a new law for the AI", "Freeform Law Entry", laws[1], CONFIG_GET(number/max_law_len), TRUE)
+ var/targName = tgui_input_text(user, "Enter a new law for the AI", "Freeform Law Entry", laws[1], max_length = CONFIG_GET(number/max_law_len), multiline = TRUE)
if(!targName || !user.is_holding(src))
return
if(is_ic_filtered(targName)) // not even the syndicate can uwu
diff --git a/code/game/objects/items/AI_modules/supplied.dm b/code/game/objects/items/AI_modules/supplied.dm
index b53e16a86b0c8..76f4715730620 100644
--- a/code/game/objects/items/AI_modules/supplied.dm
+++ b/code/game/objects/items/AI_modules/supplied.dm
@@ -27,7 +27,7 @@
lawpos = 4
/obj/item/ai_module/supplied/safeguard/attack_self(mob/user)
- var/targName = tgui_input_text(user, "Subject to safeguard.", "Safeguard", user.name, MAX_NAME_LEN)
+ var/targName = tgui_input_text(user, "Subject to safeguard.", "Safeguard", user.name, max_length = MAX_NAME_LEN)
if(!targName || !user.is_holding(src))
return
targetName = targName
diff --git a/code/game/objects/items/AI_modules/zeroth.dm b/code/game/objects/items/AI_modules/zeroth.dm
index 74fc7ab8232ae..480735bfe2fe7 100644
--- a/code/game/objects/items/AI_modules/zeroth.dm
+++ b/code/game/objects/items/AI_modules/zeroth.dm
@@ -25,7 +25,7 @@
laws = list("Only SUBJECT is human.")
/obj/item/ai_module/zeroth/onehuman/attack_self(mob/user)
- var/targName = tgui_input_text(user, "Enter the subject who is the only human.", "One Human", user.real_name, MAX_NAME_LEN)
+ var/targName = tgui_input_text(user, "Enter the subject who is the only human.", "One Human", user.real_name, max_length = MAX_NAME_LEN)
if(!targName || !user.is_holding(src))
return
targetName = targName
diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm
index 4adb8d28b8c8a..4c83923355261 100644
--- a/code/game/objects/items/bodybag.dm
+++ b/code/game/objects/items/bodybag.dm
@@ -46,7 +46,7 @@
R.add_fingerprint(user)
qdel(src)
user.forceMove(R)
- playsound(src, 'sound/items/zip.ogg', 15, TRUE, -3)
+ playsound(src, 'sound/items/zip/zip.ogg', 15, TRUE, -3)
return OXYLOSS
// Bluespace bodybag
diff --git a/code/game/objects/items/boxcutter.dm b/code/game/objects/items/boxcutter.dm
index 467bc666e6027..58be269bacddf 100644
--- a/code/game/objects/items/boxcutter.dm
+++ b/code/game/objects/items/boxcutter.dm
@@ -38,7 +38,7 @@
throwforce_on = 4, \
throw_speed_on = throw_speed, \
sharpness_on = SHARP_EDGED, \
- hitsound_on = 'sound/weapons/bladeslice.ogg', \
+ hitsound_on = 'sound/items/weapons/bladeslice.ogg', \
w_class_on = WEIGHT_CLASS_NORMAL, \
attack_verb_continuous_on = list("cuts", "stabs", "slashes"), \
attack_verb_simple_on = list("cut", "stab", "slash"), \
diff --git a/code/game/objects/items/broom.dm b/code/game/objects/items/broom.dm
index fa849c51437da..32636b1a99c81 100644
--- a/code/game/objects/items/broom.dm
+++ b/code/game/objects/items/broom.dm
@@ -102,7 +102,7 @@
for (var/obj/item/garbage in items_to_sweep)
garbage.Move(new_item_loc, sweep_dir)
- playsound(current_item_loc, 'sound/weapons/thudswoosh.ogg', 30, TRUE, -1)
+ playsound(current_item_loc, 'sound/items/weapons/thudswoosh.ogg', 30, TRUE, -1)
/obj/item/pushbroom/cyborg
name = "cyborg push broom"
diff --git a/code/game/objects/items/cardboard_cutouts.dm b/code/game/objects/items/cardboard_cutouts.dm
index b49991b132a4e..960363685b1e8 100644
--- a/code/game/objects/items/cardboard_cutouts.dm
+++ b/code/game/objects/items/cardboard_cutouts.dm
@@ -49,7 +49,7 @@
if(!user.combat_mode || pushed_over || !isturf(loc))
return ..()
user.visible_message(span_warning("[user] pushes over [src]!"), span_danger("You push over [src]!"))
- playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/genhit.ogg', 50, TRUE)
push_over()
/obj/item/cardboard_cutout/equipped(mob/living/user, slot)
diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm
index 8490f30f1bb75..2d1a16986fbe8 100644
--- a/code/game/objects/items/cards_ids.dm
+++ b/code/game/objects/items/cards_ids.dm
@@ -125,7 +125,7 @@
/obj/item/card/id/Initialize(mapload)
. = ..()
- var/datum/bank_account/blank_bank_account = new("Unassigned", SSjob.GetJobType(/datum/job/unassigned), player_account = FALSE)
+ var/datum/bank_account/blank_bank_account = new("Unassigned", SSjob.get_job_type(/datum/job/unassigned), player_account = FALSE)
registered_account = blank_bank_account
registered_account.replaceable = TRUE
@@ -174,10 +174,10 @@
/obj/item/card/id/get_id_examine_strings(mob/user)
. = ..()
- . += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "bigicon")]")
+ . += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "hugeicon")]")
-/obj/item/card/id/get_examine_string(mob/user, thats = FALSE)
- return "[icon2html(get_cached_flat_icon(), user)] [thats? "That's ":""][get_examine_name(user)]"
+/obj/item/card/id/get_examine_icon(mob/user)
+ return icon2html(get_cached_flat_icon(), user)
/**
* Helper proc, checks whether the ID card can hold any given set of wildcards.
@@ -767,7 +767,7 @@
if(HAS_TRAIT(src, TRAIT_TASTEFULLY_THICK_ID_CARD) && (user.is_holding(src) || (user.CanReach(src) && user.put_in_hands(src, ignore_animation = FALSE))))
ADD_TRAIT(src, TRAIT_NODROP, "psycho")
. += span_hypnophrase("Look at that subtle coloring... The tasteful thickness of it. Oh my God, it even has a watermark...")
- var/sound/slowbeat = sound('sound/health/slowbeat.ogg', repeat = TRUE)
+ var/sound/slowbeat = sound('sound/effects/health/slowbeat.ogg', repeat = TRUE)
user.playsound_local(get_turf(src), slowbeat, 40, 0, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE)
if(isliving(user))
var/mob/living/living_user = user
@@ -810,7 +810,7 @@
if(registered_account.replaceable)
. += span_info("Alt-Right-Click the ID to change the linked bank account.")
if(registered_account.civilian_bounty)
- . += "There is an active civilian bounty."
+ . += span_info("There is an active civilian bounty.")
. += span_info("[registered_account.bounty_text()]")
. += span_info("Quantity: [registered_account.bounty_num()]")
. += span_info("Reward: [registered_account.bounty_value()]")
@@ -987,6 +987,11 @@
return ..()
+/obj/item/card/id/advanced/proc/after_input_check(mob/user)
+ if(QDELETED(user) || QDELETED(src) || !user.client || !user.can_perform_action(src, NEED_DEXTERITY|FORBID_TELEKINESIS_REACH))
+ return FALSE
+ return TRUE
+
/obj/item/card/id/advanced/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
. = ..()
if(.)
@@ -1289,7 +1294,7 @@
. = ..()
registered_account = new(player_account = FALSE)
registered_account.account_id = ADMIN_ACCOUNT_ID // this is so bank_card_talk() can work.
- registered_account.account_job = SSjob.GetJobType(/datum/job/admin)
+ registered_account.account_job = SSjob.get_job_type(/datum/job/admin)
registered_account.account_balance += 999999 // MONEY! We add more money to the account every time we spawn because it's a debug item and infinite money whoopie
/obj/item/card/id/advanced/debug/alt_click_can_use_id(mob/living/user)
@@ -1442,6 +1447,44 @@
trim = /datum/id_trim/highlander
wildcard_slots = WILDCARD_LIMIT_ADMIN
+/// An ID that you can flip with attack_self_secondary, overriding the appearance of the ID (useful for plainclothes detectives for example).
+/obj/item/card/id/advanced/plainclothes
+ name = "Plainclothes ID"
+ ///The trim that we use as plainclothes identity
+ var/alt_trim = /datum/id_trim/job/assistant
+
+/obj/item/card/id/advanced/plainclothes/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ . = ..()
+ context[SCREENTIP_CONTEXT_LMB] = "Show/Flip ID"
+
+/obj/item/card/id/advanced/plainclothes/examine(mob/user)
+ . = ..()
+ if(trim_assignment_override)
+ . += span_smallnotice("it's currently under plainclothes identity.")
+ else
+ . += span_smallnotice("flip it to switch to the plainclothes identity.")
+
+/obj/item/card/id/advanced/plainclothes/attack_self(mob/user)
+ var/popup_input = tgui_input_list(user, "Choose Action", "Two-Sided ID", list("Show", "Flip"))
+ if(!popup_input || !after_input_check(user))
+ return TRUE
+ if(popup_input == "Show")
+ return ..()
+ balloon_alert(user, "flipped")
+ if(trim_assignment_override)
+ SSid_access.remove_trim_from_chameleon_card(src)
+ else
+ SSid_access.apply_trim_to_chameleon_card(src, alt_trim)
+ update_label()
+ update_appearance()
+
+/obj/item/card/id/advanced/plainclothes/update_label()
+ if(!trim_assignment_override)
+ return ..()
+ var/name_string = registered_name ? "[registered_name]'s ID Card" : initial(name)
+ var/datum/id_trim/fake = SSid_access.trim_singletons_by_path[alt_trim]
+ name = "[name_string] ([fake.assignment])"
+
/obj/item/card/id/advanced/chameleon
name = "agent card"
desc = "A highly advanced chameleon ID card. Touch this card on another ID card or player to choose which accesses to copy. \
@@ -1656,8 +1699,9 @@
to_chat(user, span_notice("You successfully reset the ID card."))
return
- ///forge the ID if not forged.
- var/input_name = tgui_input_text(user, "What name would you like to put on this card? Leave blank to randomise.", "Agent card name", registered_name ? registered_name : (ishuman(user) ? user.real_name : user.name), MAX_NAME_LEN)
+ ///forge the ID if not forged.s
+ var/input_name = tgui_input_text(user, "What name would you like to put on this card? Leave blank to randomise.", "Agent card name", registered_name ? registered_name : (ishuman(user) ? user.real_name : user.name), max_length = MAX_NAME_LEN, encode = FALSE)
+
if(!after_input_check(user))
return TRUE
if(input_name)
@@ -1687,7 +1731,7 @@
if(!after_input_check(user))
return TRUE
- var/target_occupation = tgui_input_text(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels.", "Agent card job assignment", assignment ? assignment : "Assistant", MAX_NAME_LEN)
+ var/target_occupation = tgui_input_text(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels.", "Agent card job assignment", assignment ? assignment : "Assistant", max_length = MAX_NAME_LEN)
if(!after_input_check(user))
return TRUE
@@ -1724,11 +1768,6 @@
registered_account = account
to_chat(user, span_notice("Your account number has been automatically assigned."))
-/obj/item/card/id/advanced/chameleon/proc/after_input_check(mob/user)
- if(QDELETED(user) || QDELETED(src) || !user.client || !user.can_perform_action(src, NEED_DEXTERITY|FORBID_TELEKINESIS_REACH))
- return FALSE
- return TRUE
-
/obj/item/card/id/advanced/chameleon/add_item_context(obj/item/source, list/context, atom/target, mob/living/user,)
. = ..()
@@ -1846,15 +1885,15 @@
return
switch(popup_input)
if("Name")
- var/input_name = tgui_input_text(user, "What name would you like to put on this card?", "Cardboard card name", scribbled_name || (ishuman(user) ? user.real_name : user.name), MAX_NAME_LEN)
- input_name = sanitize_name(input_name, allow_numbers = TRUE)
+ var/raw_input = tgui_input_text(user, "What name would you like to put on this card?", "Cardboard card name", scribbled_name || (ishuman(user) ? user.real_name : user.name), max_length = MAX_NAME_LEN)
+ var/input_name = sanitize_name(raw_input, allow_numbers = TRUE)
if(!after_input_check(user, item, input_name, scribbled_name))
return
scribbled_name = input_name
var/list/details = item.get_writing_implement_details()
details_colors[INDEX_NAME_COLOR] = details["color"] || COLOR_BLACK
if("Assignment")
- var/input_assignment = tgui_input_text(user, "What assignment would you like to put on this card?", "Cardboard card job ssignment", scribbled_assignment || "Assistant", MAX_NAME_LEN)
+ var/input_assignment = tgui_input_text(user, "What assignment would you like to put on this card?", "Cardboard card job ssignment", scribbled_assignment || "Assistant", max_length = MAX_NAME_LEN)
if(!after_input_check(user, item, input_assignment, scribbled_assignment))
return
scribbled_assignment = sanitize(input_assignment)
@@ -1924,10 +1963,10 @@
/obj/item/card/cardboard/get_id_examine_strings(mob/user)
. = ..()
- . += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "bigicon")]")
+ . += list("[icon2html(get_cached_flat_icon(), user, extra_classes = "hugeicon")]")
-/obj/item/card/cardboard/get_examine_string(mob/user, thats = FALSE)
- return "[icon2html(get_cached_flat_icon(), user)] [thats? "That's ":""][get_examine_name(user)]"
+/obj/item/card/cardboard/get_examine_icon(mob/user)
+ return icon2html(get_cached_flat_icon(), user)
/obj/item/card/cardboard/examine(mob/user)
. = ..()
diff --git a/code/game/objects/items/chainsaw.dm b/code/game/objects/items/chainsaw.dm
index 5d5de16a4d12a..00ca25985bfee 100644
--- a/code/game/objects/items/chainsaw.dm
+++ b/code/game/objects/items/chainsaw.dm
@@ -47,13 +47,13 @@
/obj/item/chainsaw/suicide_act(mob/living/carbon/user)
if(on)
user.visible_message(span_suicide("[user] begins to tear [user.p_their()] head off with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
- playsound(src, 'sound/weapons/chainsawhit.ogg', 100, TRUE)
+ playsound(src, 'sound/items/weapons/chainsawhit.ogg', 100, TRUE)
var/obj/item/bodypart/head/myhead = user.get_bodypart(BODY_ZONE_HEAD)
if(myhead)
myhead.dismember()
else
user.visible_message(span_suicide("[user] smashes [src] into [user.p_their()] neck, destroying [user.p_their()] esophagus! It looks like [user.p_theyre()] trying to commit suicide!"))
- playsound(src, 'sound/weapons/genhit1.ogg', 100, TRUE)
+ playsound(src, 'sound/items/weapons/genhit1.ogg', 100, TRUE)
return BRUTELOSS
/obj/item/chainsaw/attack_self(mob/user)
@@ -66,7 +66,7 @@
butchering.butchering_enabled = on
if(on)
- hitsound = 'sound/weapons/chainsawhit.ogg'
+ hitsound = 'sound/items/weapons/chainsawhit.ogg'
chainsaw_loop.start()
else
hitsound = SFX_SWING_HIT
@@ -88,14 +88,14 @@
speed = 3 SECONDS, \
effectiveness = 100, \
bonus_modifier = 0, \
- butcher_sound = 'sound/weapons/chainsawhit.ogg', \
+ butcher_sound = 'sound/items/weapons/chainsawhit.ogg', \
disabled = TRUE, \
)
AddComponent(/datum/component/two_handed, require_twohands=TRUE)
/obj/item/chainsaw/doomslayer
name = "THE GREAT COMMUNICATOR"
- desc = "VRRRRRRR!!!"
+ desc = span_warning("VRRRRRRR!!!")
armour_penetration = 100
force_on = 30
@@ -110,7 +110,7 @@
if (isnull(head))
return ..()
- playsound(user, 'sound/weapons/slice.ogg', vol = 80, vary = TRUE)
+ playsound(user, 'sound/items/weapons/slice.ogg', vol = 80, vary = TRUE)
target_mob.balloon_alert(user, "cutting off head...")
if (!do_after(user, 2 SECONDS, target_mob, extra_checks = CALLBACK(src, PROC_REF(has_same_head), target_mob, head)))
@@ -124,7 +124,7 @@
/obj/item/chainsaw/doomslayer/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK, damage_type = BRUTE)
if(attack_type == PROJECTILE_ATTACK)
owner.visible_message(span_danger("Ranged attacks just make [owner] angrier!"))
- playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 75, TRUE)
+ playsound(src, pick('sound/items/weapons/bulletflyby.ogg', 'sound/items/weapons/bulletflyby2.ogg', 'sound/items/weapons/bulletflyby3.ogg'), 75, TRUE)
return TRUE
return FALSE
@@ -162,7 +162,7 @@
speed = 3 SECONDS, \
effectiveness = 100, \
bonus_modifier = 0, \
- butcher_sound = 'sound/weapons/chainsawhit.ogg', \
+ butcher_sound = 'sound/items/weapons/chainsawhit.ogg', \
disabled = TRUE, \
)
diff --git a/code/game/objects/items/charter.dm b/code/game/objects/items/charter.dm
index 1d1f8fad7cc56..6b4ae0f918394 100644
--- a/code/game/objects/items/charter.dm
+++ b/code/game/objects/items/charter.dm
@@ -69,8 +69,8 @@
if(!response_timer_id)
return
var/turf/T = get_turf(src)
- T.visible_message("The proposed changes disappear \
- from [src]; it looks like they've been rejected.")
+ T.visible_message(span_warning("The proposed changes disappear \
+ from [src]; it looks like they've been rejected."))
var/m = "[key_name(user)] has rejected the proposed station name."
message_admins(m)
diff --git a/code/game/objects/items/choice_beacon.dm b/code/game/objects/items/choice_beacon.dm
index 4a6de50990294..01b7933f91426 100644
--- a/code/game/objects/items/choice_beacon.dm
+++ b/code/game/objects/items/choice_beacon.dm
@@ -30,7 +30,7 @@
if(user.can_perform_action(src, FORBID_TELEKINESIS_REACH))
return TRUE
- playsound(src, 'sound/machines/buzz-sigh.ogg', 40, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 40, TRUE)
return FALSE
/// Opens a menu and allows the mob to pick an option from the list
@@ -162,7 +162,7 @@
// just drops the box at their feet, "quiet" and "sneaky"
/obj/item/choice_beacon/augments/spawn_option(obj/choice_path, mob/living/user)
new choice_path(get_turf(user))
- playsound(src, 'sound/weapons/emitter2.ogg', 50, extrarange = SILENCED_SOUND_EXTRARANGE)
+ playsound(src, 'sound/items/weapons/emitter2.ogg', 50, extrarange = SILENCED_SOUND_EXTRARANGE)
/obj/item/choice_beacon/holy
name = "armaments beacon"
@@ -176,7 +176,7 @@
if(user.mind?.holy_role)
return ..()
- playsound(src, 'sound/machines/buzz-sigh.ogg', 40, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 40, TRUE)
return FALSE
// Overrides generate options so that we can show a neat radial instead
diff --git a/code/game/objects/items/cigs_lighters.dm b/code/game/objects/items/cigarettes.dm
similarity index 75%
rename from code/game/objects/items/cigs_lighters.dm
rename to code/game/objects/items/cigarettes.dm
index 35f14640278e4..69b65149775ba 100644
--- a/code/game/objects/items/cigs_lighters.dm
+++ b/code/game/objects/items/cigarettes.dm
@@ -6,8 +6,6 @@ MATCHES
CIGARETTES
CIGARS
SMOKING PIPES
-CHEAP LIGHTERS
-ZIPPO
CIGARETTE PACKETS ARE IN FANCY.DM
*/
@@ -49,7 +47,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
icon_state = "match_lit"
damtype = BURN
force = 3
- hitsound = 'sound/items/welder.ogg'
+ hitsound = 'sound/items/tools/welder.ogg'
inhand_icon_state = "cigon"
name = "lit [initial(name)]"
desc = "A [initial(name)]. This one is lit."
@@ -134,6 +132,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
name = "cigarette"
desc = "A roll of tobacco and nicotine. It is not food."
icon = 'icons/obj/cigarettes.dmi'
+ worn_icon = 'icons/mob/clothing/mask.dmi'
icon_state = "cigoff"
inhand_icon_state = "cigon" //gets overriden during intialize(), just have it for unit test sanity.
throw_speed = 0.5
@@ -200,7 +199,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
// "It is called a cigarette"
AddComponent(/datum/component/edible,\
initial_reagents = list_reagents,\
- food_flags = null,\
+ food_flags = FOOD_NO_EXAMINE,\
foodtypes = JUNKFOOD,\
volume = 50,\
eat_time = 0 SECONDS,\
@@ -210,7 +209,6 @@ CIGARETTE PACKETS ARE IN FANCY.DM
junkiness = 0,\
reagent_purity = null,\
on_consume = CALLBACK(src, PROC_REF(on_consume)),\
- show_examine = FALSE, \
)
/obj/item/cigarette/Destroy()
@@ -349,7 +347,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
return
lit = TRUE
- playsound(src.loc, 'sound/items/cig_light.ogg', 100, 1)
+ playsound(src.loc, 'sound/items/lighter/cig_light.ogg', 100, 1)
make_cig_smoke()
if(!(flags_1 & INITIALIZED_1))
update_appearance(UPDATE_ICON)
@@ -357,7 +355,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
attack_verb_continuous = string_list(list("burns", "singes"))
attack_verb_simple = string_list(list("burn", "singe"))
- hitsound = 'sound/items/welder.ogg'
+ hitsound = 'sound/items/tools/welder.ogg'
damtype = BURN
force = 4
if(reagents.get_reagent_amount(/datum/reagent/toxin/plasma)) // the plasma explodes when exposed to fire
@@ -398,7 +396,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
STOP_PROCESSING(SSobj, src)
reagents.flags |= NO_REACT
lit = FALSE
- playsound(src.loc, 'sound/items/cig_snuff.ogg', 100, 1)
+ playsound(src.loc, 'sound/items/lighter/cig_snuff.ogg', 100, 1)
update_appearance(UPDATE_ICON)
if(ismob(loc))
to_chat(loc, span_notice("Your [name] goes out."))
@@ -680,6 +678,27 @@ CIGARETTE PACKETS ARE IN FANCY.DM
pixel_y = rand(-5, 5)
+/obj/item/cigarette/dart
+ name = "fat dart"
+ desc = "Chuff back this fat dart"
+ icon_state = "bigon"
+ icon_on = "bigon"
+ icon_off = "bigoff"
+ w_class = WEIGHT_CLASS_BULKY
+ smoketime = 18 MINUTES
+ chem_volume = 65
+ list_reagents = list(/datum/reagent/drug/nicotine = 45)
+ choke_time_max = 40 SECONDS
+ lung_harm = 2
+
+/obj/item/cigarette/dart/Initialize(mapload)
+ . = ..()
+ //the compiled icon state is how it appears when it's on.
+ //That's how we want it to show on orbies (little virtual PDA pets).
+ //However we should reset their appearance on runtime.
+ update_appearance(UPDATE_ICON_STATE)
+
+
////////////
// CIGARS //
////////////
@@ -820,319 +839,6 @@ CIGARETTE PACKETS ARE IN FANCY.DM
inhand_icon_on = null
inhand_icon_off = null
-/////////
-//ZIPPO//
-/////////
-/obj/item/lighter
- name = "\improper Zippo lighter"
- desc = "The zippo."
- icon = 'icons/obj/cigarettes.dmi'
- icon_state = "zippo"
- inhand_icon_state = "zippo"
- worn_icon_state = "lighter"
- w_class = WEIGHT_CLASS_TINY
- obj_flags = CONDUCTS_ELECTRICITY
- slot_flags = ITEM_SLOT_BELT
- heat = 1500
- resistance_flags = FIRE_PROOF
- grind_results = list(/datum/reagent/iron = 1, /datum/reagent/fuel = 5, /datum/reagent/fuel/oil = 5)
- custom_price = PAYCHECK_CREW * 1.1
- light_system = OVERLAY_LIGHT
- light_range = 2
- light_power = 1.3
- light_color = LIGHT_COLOR_FIRE
- light_on = FALSE
- /// Whether the lighter is lit.
- var/lit = FALSE
- /// Whether the lighter is fancy. Fancy lighters have fancier flavortext and won't burn thumbs.
- var/fancy = TRUE
- /// The engraving overlay used by this lighter.
- var/overlay_state
- /// A list of possible engraving overlays.
- var/overlay_list = list(
- "plain",
- "dame",
- "thirteen",
- "snake"
- )
-
-/obj/item/lighter/Initialize(mapload)
- . = ..()
- if(!overlay_state)
- overlay_state = pick(overlay_list)
- AddComponent(\
- /datum/component/bullet_intercepting,\
- block_chance = 0.5,\
- active_slots = ITEM_SLOT_SUITSTORE,\
- on_intercepted = CALLBACK(src, PROC_REF(on_intercepted_bullet)),\
- )
- update_appearance()
-
-/// 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!"))
- playsound(victim, SFX_RICOCHET, 100, TRUE)
- new /obj/effect/decal/cleanable/oil(get_turf(src))
- do_sparks(1, TRUE, src)
- victim.dropItemToGround(src, force = TRUE, silent = TRUE)
- qdel(src)
-
-/obj/item/lighter/cyborg_unequip(mob/user)
- if(!lit)
- return
- set_lit(FALSE)
-
-/obj/item/lighter/suicide_act(mob/living/carbon/user)
- if (lit)
- user.visible_message(span_suicide("[user] begins holding \the [src]'s flame up to [user.p_their()] face! It looks like [user.p_theyre()] trying to commit suicide!"))
- playsound(src, 'sound/items/welder.ogg', 50, TRUE)
- return FIRELOSS
- else
- user.visible_message(span_suicide("[user] begins whacking [user.p_them()]self with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
- return BRUTELOSS
-
-/obj/item/lighter/update_icon_state()
- icon_state = "[initial(icon_state)][lit ? "-on" : ""]"
- return ..()
-
-/obj/item/lighter/update_overlays()
- . = ..()
- . += create_lighter_overlay()
-
-/// Generates an overlay used by this lighter.
-/obj/item/lighter/proc/create_lighter_overlay()
- return mutable_appearance(icon, "lighter_overlay_[overlay_state][lit ? "-on" : ""]")
-
-/obj/item/lighter/ignition_effect(atom/A, mob/user)
- if(get_temperature())
- . = span_infoplain(span_rose("With a single flick of [user.p_their()] wrist, [user] smoothly lights [A] with [src]. Damn [user.p_theyre()] cool."))
-
-/obj/item/lighter/proc/set_lit(new_lit)
- if(lit == new_lit)
- return
-
- lit = new_lit
- if(lit)
- force = 5
- damtype = BURN
- hitsound = 'sound/items/welder.ogg'
- attack_verb_continuous = string_list(list("burns", "singes"))
- attack_verb_simple = string_list(list("burn", "singe"))
- START_PROCESSING(SSobj, src)
- if(isliving(loc))
- var/mob/living/male_model = loc
- if(male_model.fire_stacks && !(male_model.on_fire))
- male_model.ignite_mob()
- else
- hitsound = SFX_SWING_HIT
- force = 0
- attack_verb_continuous = null //human_defense.dm takes care of it
- attack_verb_simple = null
- STOP_PROCESSING(SSobj, src)
- set_light_on(lit)
- update_appearance()
-
-/obj/item/lighter/extinguish()
- . = ..()
- set_lit(FALSE)
-
-/obj/item/lighter/attack_self(mob/living/user)
- if(!user.is_holding(src))
- return ..()
- if(lit)
- set_lit(FALSE)
- if(fancy)
- user.visible_message(
- span_notice("You hear a quiet click, as [user] shuts off [src] without even looking at what [user.p_theyre()] doing. Wow."),
- span_notice("You quietly shut off [src] without even looking at what you're doing. Wow.")
- )
- playsound(src.loc , 'sound/items/zippo_off.ogg', 100, 1)
- else
- user.visible_message(
- span_notice("[user] quietly shuts off [src]."),
- span_notice("You quietly shut off [src].")
- )
- playsound(src.loc , 'sound/items/lighter_off.ogg', 100, 1)
- return
-
- set_lit(TRUE)
- if(fancy)
- user.visible_message(
- span_notice("Without even breaking stride, [user] flips open and lights [src] in one smooth movement."),
- span_notice("Without even breaking stride, you flip open and light [src] in one smooth movement.")
- )
- playsound(src.loc , 'sound/items/zippo_on.ogg', 100, 1)
- return
- else
- playsound(src.loc, 'sound/items/lighter_on.ogg', 100, 1)
-
- var/hand_protected = FALSE
- var/mob/living/carbon/human/human_user = user
- if(!istype(human_user) || HAS_TRAIT(human_user, TRAIT_RESISTHEAT) || HAS_TRAIT(human_user, TRAIT_RESISTHEATHANDS))
- hand_protected = TRUE
- else if(!istype(human_user.gloves, /obj/item/clothing/gloves))
- hand_protected = FALSE
- else
- var/obj/item/clothing/gloves/gloves = human_user.gloves
- if(gloves.max_heat_protection_temperature)
- hand_protected = (gloves.max_heat_protection_temperature > 360)
-
- if(hand_protected || prob(75))
- user.visible_message(
- span_notice("After a few attempts, [user] manages to light [src]."),
- span_notice("After a few attempts, you manage to light [src].")
- )
- return
-
- var/hitzone = user.held_index_to_dir(user.active_hand_index) == "r" ? BODY_ZONE_PRECISE_R_HAND : BODY_ZONE_PRECISE_L_HAND
- user.apply_damage(5, BURN, hitzone)
- user.visible_message(
- span_warning("After a few attempts, [user] manages to light [src] - however, [user.p_they()] burn[user.p_s()] [user.p_their()] finger in the process."),
- span_warning("You burn yourself while lighting the lighter!")
- )
- user.add_mood_event("burnt_thumb", /datum/mood_event/burnt_thumb)
-
-
-/obj/item/lighter/attack(mob/living/carbon/M, mob/living/carbon/user)
- if(lit && M.ignite_mob())
- message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(M)] on fire with [src] at [AREACOORD(user)]")
- log_game("[key_name(user)] set [key_name(M)] on fire with [src] at [AREACOORD(user)]")
- var/obj/item/cigarette/cig = help_light_cig(M)
- if(!lit || !cig || user.combat_mode)
- ..()
- return
-
- if(cig.lit)
- to_chat(user, span_warning("The [cig.name] is already lit!"))
- if(M == user)
- cig.attackby(src, user)
- return
-
- if(fancy)
- cig.light(span_rose("[user] whips the [name] out and holds it for [M]. [user.p_Their()] arm is as steady as the unflickering flame [user.p_they()] light[user.p_s()] \the [cig] with."))
- else
- cig.light(span_notice("[user] holds the [name] out for [M], and lights [M.p_their()] [cig.name]."))
-
-
-/obj/item/lighter/process()
- open_flame(heat)
-
-/obj/item/lighter/get_temperature()
- return lit * heat
-
-
-/obj/item/lighter/greyscale
- name = "cheap lighter"
- desc = "A cheap lighter."
- icon_state = "lighter"
- fancy = FALSE
- overlay_list = list(
- "transp",
- "tall",
- "matte",
- "zoppo" //u cant stoppo th zoppo
- )
-
- /// The color of the lighter.
- var/lighter_color
- /// The set of colors this lighter can be autoset as on init.
- var/list/color_list = list( //Same 16 color selection as electronic assemblies
- COLOR_ASSEMBLY_BLACK,
- COLOR_FLOORTILE_GRAY,
- COLOR_ASSEMBLY_BGRAY,
- COLOR_ASSEMBLY_WHITE,
- COLOR_ASSEMBLY_RED,
- COLOR_ASSEMBLY_ORANGE,
- COLOR_ASSEMBLY_BEIGE,
- COLOR_ASSEMBLY_BROWN,
- COLOR_ASSEMBLY_GOLD,
- COLOR_ASSEMBLY_YELLOW,
- COLOR_ASSEMBLY_GURKHA,
- COLOR_ASSEMBLY_LGREEN,
- COLOR_ASSEMBLY_GREEN,
- COLOR_ASSEMBLY_LBLUE,
- COLOR_ASSEMBLY_BLUE,
- COLOR_ASSEMBLY_PURPLE
- )
-
-/obj/item/lighter/greyscale/Initialize(mapload)
- . = ..()
- if(!lighter_color)
- lighter_color = pick(color_list)
- update_appearance()
-
-/obj/item/lighter/greyscale/create_lighter_overlay()
- var/mutable_appearance/lighter_overlay = ..()
- lighter_overlay.color = lighter_color
- return lighter_overlay
-
-/obj/item/lighter/greyscale/ignition_effect(atom/A, mob/user)
- if(get_temperature())
- . = span_notice("After some fiddling, [user] manages to light [A] with [src].")
-
-
-/obj/item/lighter/slime
- name = "slime zippo"
- desc = "A specialty zippo made from slimes and industry. Has a much hotter flame than normal."
- icon_state = "slighter"
- heat = 3000 //Blue flame!
- light_color = LIGHT_COLOR_CYAN
- overlay_state = "slime"
- grind_results = list(/datum/reagent/iron = 1, /datum/reagent/fuel = 5, /datum/reagent/medicine/pyroxadone = 5)
-
-/obj/item/lighter/skull
- name = "badass zippo"
- desc = "An absolutely badass zippo lighter. Just look at that skull!"
- overlay_state = "skull"
-
-/obj/item/lighter/mime
- name = "pale zippo"
- desc = "In lieu of fuel, performative spirit can be used to light cigarettes."
- icon_state = "mlighter" //These ones don't show a flame.
- light_color = LIGHT_COLOR_HALOGEN
- heat = 0 //I swear it's a real lighter dude you just can't see the flame dude I promise
- overlay_state = "mime"
- grind_results = list(/datum/reagent/iron = 1, /datum/reagent/toxin/mutetoxin = 5, /datum/reagent/consumable/nothing = 10)
- light_range = 0
- light_power = 0
- fancy = FALSE
-
-/obj/item/lighter/mime/ignition_effect(atom/A, mob/user)
- . = span_infoplain("[user] lifts the [name] to the [A], which miraculously lights!")
-
-/obj/item/lighter/bright
- name = "illuminative zippo"
- desc = "Sustains an incredibly bright chemical reaction when you spark it. Avoid looking directly at the igniter when lit."
- icon_state = "slighter"
- light_color = LIGHT_COLOR_ELECTRIC_CYAN
- overlay_state = "bright"
- grind_results = list(/datum/reagent/iron = 1, /datum/reagent/flash_powder = 10)
- light_range = 8
- light_power = 3 //Irritatingly bright and large enough to cover a small room.
- fancy = FALSE
-
-/obj/item/lighter/bright/examine(mob/user)
- . = ..()
-
- if(lit && isliving(user))
- var/mob/living/current_viewer = user
- current_viewer.flash_act(4)
-
-/obj/item/lighter/bright/ignition_effect(atom/A, mob/user)
- if(get_temperature())
- . = span_infoplain(span_rose("[user] lifts the [src] to the [A], igniting it with a brilliant flash of light!"))
- var/mob/living/current_viewer = user
- current_viewer.flash_act(4)
-
-/obj/effect/spawner/random/special_lighter
- name = "special lighter spawner"
- icon_state = "lighter"
- loot = list(
- /obj/item/lighter/skull,
- /obj/item/lighter/mime,
- /obj/item/lighter/bright,
- )
-
///////////
//ROLLING//
///////////
diff --git a/code/game/objects/items/circuitboards/circuitboard.dm b/code/game/objects/items/circuitboards/circuitboard.dm
index 236b6ed402c4a..6439ef9ccbe94 100644
--- a/code/game/objects/items/circuitboards/circuitboard.dm
+++ b/code/game/objects/items/circuitboards/circuitboard.dm
@@ -17,8 +17,8 @@
grind_results = list(/datum/reagent/silicon = 20)
greyscale_colors = CIRCUIT_COLOR_GENERIC
var/build_path = null
- ///determines if the circuit board originated from a vendor off station or not.
- var/onstation = TRUE
+ /// whether or not the circuit board will build into a vendor whose products cost nothing (used for offstation vending machines mostly)
+ var/all_products_free = FALSE
///determines if the board requires specific levels of parts. (ie specifically a femto menipulator vs generic manipulator)
var/specific_parts = FALSE
diff --git a/code/game/objects/items/circuitboards/computer_circuitboards.dm b/code/game/objects/items/circuitboards/computer_circuitboards.dm
index 41950561571d6..9c3cde9f725a5 100644
--- a/code/game/objects/items/circuitboards/computer_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/computer_circuitboards.dm
@@ -403,6 +403,29 @@
name = "R&D Console"
greyscale_colors = CIRCUIT_COLOR_SCIENCE
build_path = /obj/machinery/computer/rdconsole
+ var/silence_announcements = FALSE
+
+/obj/item/circuitboard/computer/rdconsole/examine(mob/user)
+ . = ..()
+ . += span_info("The board is configured to [silence_announcements ? "silence" : "announce"] researched nodes on radio.")
+ . += span_notice("The board mode can be changed with a [EXAMINE_HINT("multitool")].")
+
+/obj/item/circuitboard/computer/rdconsole/multitool_act(mob/living/user)
+ . = ..()
+ if(obj_flags & EMAGGED)
+ balloon_alert(user, "board mode is broken!")
+ return
+ silence_announcements = !silence_announcements
+ balloon_alert(user, "announcements [silence_announcements ? "enabled" : "disabled"]")
+
+/obj/item/circuitboard/computer/rdconsole/emag_act(mob/user, obj/item/card/emag/emag_card)
+ if (obj_flags & EMAGGED)
+ return FALSE
+
+ obj_flags |= EMAGGED
+ silence_announcements = FALSE
+ to_chat(user, span_notice("You overload the node announcement chip, forcing every node to be announced on the common channel."))
+ return TRUE
/obj/item/circuitboard/computer/rdservercontrol
name = "R&D Server Control"
diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
index 80c71e2b85983..ec5294fd61eed 100644
--- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm
@@ -344,6 +344,23 @@
/datum/stock_part/capacitor = 1)
def_components = list(/obj/item/stock_parts/power_store/battery = /obj/item/stock_parts/power_store/battery/high/empty)
+/obj/item/circuitboard/machine/smes/connector
+ name = "power connector"
+ build_path = /obj/machinery/power/smes/connector
+ req_components = list(
+ /obj/item/stack/cable_coil = 5,
+ /datum/stock_part/capacitor = 1,)
+
+/obj/item/circuitboard/machine/smesbank
+ name = "portable SMES"
+ greyscale_colors = CIRCUIT_COLOR_ENGINEERING
+ needs_anchored = FALSE
+ build_path = /obj/machinery/power/smesbank
+ req_components = list(
+ /obj/item/stack/cable_coil = 5,
+ /obj/item/stock_parts/power_store/battery = 5,)
+ def_components = list(/obj/item/stock_parts/power_store/battery = /obj/item/stock_parts/power_store/battery/high/empty)
+
/obj/item/circuitboard/machine/techfab/department/engineering
name = "\improper Departmental Techfab - Engineering"
greyscale_colors = CIRCUIT_COLOR_ENGINEERING
@@ -352,6 +369,9 @@
/obj/item/circuitboard/machine/smes/super
def_components = list(/obj/item/stock_parts/power_store/battery = /obj/item/stock_parts/power_store/battery/super/empty)
+/obj/item/circuitboard/machine/smesbank/super
+ def_components = list(/obj/item/stock_parts/power_store/battery = /obj/item/stock_parts/power_store/battery/super/empty)
+
/obj/item/circuitboard/machine/thermomachine
name = "Thermomachine"
greyscale_colors = CIRCUIT_COLOR_ENGINEERING
@@ -1715,3 +1735,65 @@
req_components = list(
/datum/stock_part/servo = 1,
)
+
+/obj/item/circuitboard/machine/manucrafter
+ name = /obj/machinery/power/manufacturing/crafter::name
+ greyscale_colors = CIRCUIT_COLOR_ENGINEERING
+ build_path = /obj/machinery/power/manufacturing/crafter
+ req_components = list(
+ /obj/item/stack/sheet/iron = 5,
+ /datum/stock_part/servo = 1,
+ )
+
+/obj/item/circuitboard/machine/manulathe
+ name = /obj/machinery/power/manufacturing/lathe::name
+ greyscale_colors = CIRCUIT_COLOR_ENGINEERING
+ build_path = /obj/machinery/power/manufacturing/lathe
+ req_components = list(
+ /obj/item/stack/sheet/iron = 5,
+ /datum/stock_part/servo = 1,
+ )
+
+/obj/item/circuitboard/machine/manucrusher
+ name = /obj/machinery/power/manufacturing/crusher::name
+ greyscale_colors = CIRCUIT_COLOR_ENGINEERING
+ build_path = /obj/machinery/power/manufacturing/crusher
+ req_components = list(
+ /obj/item/stack/sheet/iron = 5,
+ /datum/stock_part/servo = 1,
+ )
+
+/obj/item/circuitboard/machine/manuunloader
+ name = /obj/machinery/power/manufacturing/unloader::name
+ greyscale_colors = CIRCUIT_COLOR_ENGINEERING
+ build_path = /obj/machinery/power/manufacturing/unloader
+ req_components = list(
+ /obj/item/stack/sheet/iron = 5,
+ /datum/stock_part/servo = 1,
+ )
+
+/obj/item/circuitboard/machine/manusorter
+ name = /obj/machinery/power/manufacturing/sorter::name
+ greyscale_colors = CIRCUIT_COLOR_ENGINEERING
+ build_path = /obj/machinery/power/manufacturing/sorter
+ req_components = list(
+ /obj/item/stack/sheet/iron = 5,
+ /datum/stock_part/scanning_module = 1,
+ )
+
+/obj/item/circuitboard/machine/manusmelter
+ name = /obj/machinery/power/manufacturing/smelter::name
+ greyscale_colors = CIRCUIT_COLOR_ENGINEERING
+ build_path = /obj/machinery/power/manufacturing/smelter
+ req_components = list(
+ /obj/item/stack/sheet/iron = 5,
+ /datum/stock_part/micro_laser = 1,
+ )
+
+/obj/item/circuitboard/machine/manurouter
+ name = /obj/machinery/power/manufacturing/router::name
+ greyscale_colors = CIRCUIT_COLOR_ENGINEERING
+ build_path = /obj/machinery/power/manufacturing/router
+ req_components = list(
+ /obj/item/stack/sheet/iron = 5,
+ )
diff --git a/code/game/objects/items/climbingrope.dm b/code/game/objects/items/climbingrope.dm
index e68e508771248..f10a9db76704c 100644
--- a/code/game/objects/items/climbingrope.dm
+++ b/code/game/objects/items/climbingrope.dm
@@ -27,6 +27,8 @@
. += span_notice("The rope looks like you could use it [uses] times before it falls apart.")
/obj/item/climbing_hook/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION))
+ return NONE
return ranged_interact_with_atom(interacting_with, user, modifiers)
/obj/item/climbing_hook/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
@@ -46,8 +48,8 @@
var/away_dir = get_dir(above, target)
user.visible_message(span_notice("[user] begins climbing upwards with [src]."), span_notice("You get to work on properly hooking [src] and going upwards."))
- playsound(target, 'sound/effects/picaxe1.ogg', 50) //plays twice so people above and below can hear
- playsound(user_turf, 'sound/effects/picaxe1.ogg', 50)
+ playsound(target, 'sound/effects/pickaxe/picaxe1.ogg', 50) //plays twice so people above and below can hear
+ playsound(user_turf, 'sound/effects/pickaxe/picaxe1.ogg', 50)
var/list/effects = list(new /obj/effect/temp_visual/climbing_hook(target, away_dir), new /obj/effect/temp_visual/climbing_hook(user_turf, away_dir))
// Our climbers athletics ability
diff --git a/code/game/objects/items/clown_items.dm b/code/game/objects/items/clown_items.dm
index 6565bcae44f5b..1870b6dd2ba0f 100644
--- a/code/game/objects/items/clown_items.dm
+++ b/code/game/objects/items/clown_items.dm
@@ -161,9 +161,6 @@
return CLEAN_BLOCKED
return ..()
-/obj/item/soap/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/living/user)
- return !user.combat_mode // only cleans a storage item if on combat
-
/*
* Bike Horns
*/
@@ -212,7 +209,7 @@
desc = "Damn son, where'd you find this?"
icon_state = "air_horn"
worn_icon_state = "horn_air"
- sound_file = 'sound/items/airhorn2.ogg'
+ sound_file = 'sound/items/airhorn/airhorn2.ogg'
/datum/crafting_recipe/airhorn
name = "Air Horn"
diff --git a/code/game/objects/items/control_wand.dm b/code/game/objects/items/control_wand.dm
index abad07f96d844..9734661e88f63 100644
--- a/code/game/objects/items/control_wand.dm
+++ b/code/game/objects/items/control_wand.dm
@@ -35,10 +35,12 @@
update_icon_state()
balloon_alert(user, "mode: [desc[mode]]")
-/obj/item/door_remote/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- return interact_with_atom(interacting_with, user, modifiers)
-
/obj/item/door_remote/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!istype(interacting_with, /obj/machinery/door) && !isturf(interacting_with))
+ return NONE
+ return ranged_interact_with_atom(interacting_with, user, modifiers)
+
+/obj/item/door_remote/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
var/obj/machinery/door/door
if (istype(interacting_with, /obj/machinery/door))
diff --git a/code/game/objects/items/cosmetics.dm b/code/game/objects/items/cosmetics.dm
index 2aa0c927bedc0..45b716997c16b 100644
--- a/code/game/objects/items/cosmetics.dm
+++ b/code/game/objects/items/cosmetics.dm
@@ -210,7 +210,7 @@
skinhead.set_facial_hairstyle("Shaved", update = TRUE)
else
skinhead.set_hairstyle("Skinhead", update = TRUE)
- playsound(loc, 'sound/items/welder2.ogg', 20, TRUE)
+ playsound(loc, 'sound/items/tools/welder2.ogg', 20, TRUE)
/obj/item/razor/attack(mob/target_mob, mob/living/user, params)
if(!ishuman(target_mob))
diff --git a/code/game/objects/items/crab17.dm b/code/game/objects/items/crab17.dm
index 45bb25285ef24..630637316b355 100644
--- a/code/game/objects/items/crab17.dm
+++ b/code/game/objects/items/crab17.dm
@@ -79,7 +79,7 @@
var/throwtarget = get_step(user, get_dir(src, user))
user.safe_throw_at(throwtarget, 1, 1, force = MOVE_FORCE_EXTREMELY_STRONG)
- playsound(get_turf(src),'sound/magic/repulse.ogg', 100, TRUE)
+ playsound(get_turf(src),'sound/effects/magic/repulse.ogg', 100, TRUE)
return
@@ -126,7 +126,7 @@
sleep(3 SECONDS)
if(QDELETED(src))
return
- playsound(src,'sound/machines/twobeep.ogg',50,FALSE)
+ playsound(src,'sound/machines/beep/twobeep.ogg',50,FALSE)
var/mutable_appearance/hologram = mutable_appearance(icon, "hologram")
hologram.pixel_y = 16
add_overlay(hologram)
@@ -158,7 +158,7 @@
sleep(0.5 SECONDS)
if(QDELETED(src))
return
- playsound(src,'sound/machines/triple_beep.ogg',50,FALSE)
+ playsound(src,'sound/machines/beep/triple_beep.ogg',50,FALSE)
add_overlay("text")
sleep(1 SECONDS)
if(QDELETED(src))
@@ -247,7 +247,7 @@
dump = new /obj/structure/checkoutmachine(null, bogdanoff)
priority_announce("The spacecoin bubble has popped! Get to the credit deposit machine at [get_area(src)] and cash out before you lose all of your funds!", sender_override = "CRAB-17 Protocol")
animate(DF, pixel_z = -8, time = 5, , easing = LINEAR_EASING)
- playsound(src, 'sound/weapons/mortar_whistle.ogg', 70, TRUE, 6)
+ playsound(src, 'sound/items/weapons/mortar_whistle.ogg', 70, TRUE, 6)
addtimer(CALLBACK(src, PROC_REF(endLaunch)), 5, TIMER_CLIENT_TIME) //Go onto the last step after a very short falling animation
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index 9b4d17bbb9d64..31c2f66dac0bb 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -384,7 +384,7 @@
. = TRUE
if("select_stencil")
var/stencil = params["item"]
- if(stencil in all_drawables + randoms)
+ if(stencil in (all_drawables + randoms))
drawtype = stencil
. = TRUE
text_buffer = ""
@@ -402,7 +402,7 @@
set_painting_tool_color(paint_color)
. = TRUE
if("enter_text")
- var/txt = tgui_input_text(usr, "Choose what to write", "Scribbles", text_buffer)
+ var/txt = tgui_input_text(usr, "Choose what to write", "Scribbles", text_buffer, max_length = MAX_MESSAGE_LEN)
if(isnull(txt))
return
txt = crayon_text_strip(txt)
@@ -485,7 +485,7 @@
temp = "symbol"
else if(drawing in drawings)
temp = "drawing"
- else if(drawing in graffiti|oriented)
+ else if(drawing in (graffiti|oriented))
temp = "graffiti"
var/graf_rot
@@ -504,8 +504,8 @@
var/clicky
if(LAZYACCESS(modifiers, ICON_X) && LAZYACCESS(modifiers, ICON_Y))
- clickx = clamp(text2num(LAZYACCESS(modifiers, ICON_X)) - 16, -(world.icon_size/2), world.icon_size/2)
- clicky = clamp(text2num(LAZYACCESS(modifiers, ICON_Y)) - 16, -(world.icon_size/2), world.icon_size/2)
+ clickx = clamp(text2num(LAZYACCESS(modifiers, ICON_X)) - 16, -(ICON_SIZE_X/2), ICON_SIZE_X/2)
+ clicky = clamp(text2num(LAZYACCESS(modifiers, ICON_Y)) - 16, -(ICON_SIZE_Y/2), ICON_SIZE_Y/2)
if(!instant)
to_chat(user, span_notice("You start drawing a [temp] on the [target.name]..."))
@@ -865,7 +865,10 @@
/obj/item/toy/crayon/spraycan/can_use_on(atom/target, mob/user, list/modifiers)
if(iscarbon(target))
return TRUE
- if(ismob(target) && (HAS_TRAIT(target, TRAIT_SPRAY_PAINTABLE)))
+ if(is_capped && HAS_TRAIT(target, TRAIT_COMBAT_MODE_SKIP_INTERACTION))
+ // specifically don't try to use a capped spraycan on stuff like bags and tables, just place it
+ return FALSE
+ if(ismob(target) && HAS_TRAIT(target, TRAIT_SPRAY_PAINTABLE))
return TRUE
if(isobj(target) && !(target.flags_1 & UNPAINTABLE_1))
return TRUE
@@ -967,6 +970,10 @@
/obj/item/toy/crayon/spraycan/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
if(is_capped)
+ if(!interacting_with.color)
+ // let's be generous and assume if they're trying to match something with no color, while capped,
+ // we shouldn't be blocking further interactions
+ return NONE
balloon_alert(user, "take the cap off first!")
return ITEM_INTERACT_BLOCKING
if(check_empty(user))
@@ -1003,9 +1010,6 @@
update_appearance()
return CLICK_ACTION_SUCCESS
-/obj/item/toy/crayon/spraycan/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/user)
- return is_capped
-
/obj/item/toy/crayon/spraycan/update_icon_state()
icon_state = is_capped ? icon_capped : icon_uncapped
return ..()
diff --git a/code/game/objects/items/debug_items.dm b/code/game/objects/items/debug_items.dm
index 24f350f37907a..fb6400fc7b36c 100644
--- a/code/game/objects/items/debug_items.dm
+++ b/code/game/objects/items/debug_items.dm
@@ -168,7 +168,7 @@
return
if(!user.client.holder) //safety if the admin readmined to save their ass lol.
to_chat(user, span_reallybig("You shouldn't have done that..."))
- playsound(src, 'sound/voice/borg_deathsound.ogg')
+ playsound(src, 'sound/mobs/non-humanoids/cyborg/borg_deathsound.ogg')
sleep(3 SECONDS)
living_user.investigate_log("has been gibbed by [src].", INVESTIGATE_DEATHS)
living_user.gib(DROP_ALL_REMAINS)
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index b1e01e5a6cebc..9133068cb0027 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -258,10 +258,10 @@
if(cell)
if(cell.charge >= paddles.revivecost)
visible_message(span_notice("[src] beeps: Unit ready."))
- playsound(src, 'sound/machines/defib_ready.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/defib/defib_ready.ogg', 50, FALSE)
else
visible_message(span_notice("[src] beeps: Charge depleted."))
- playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/defib/defib_failed.ogg', 50, FALSE)
paddles.cooldown = FALSE
paddles.update_appearance()
update_power()
@@ -399,7 +399,7 @@
/obj/item/shockpaddles/proc/finish_recharge()
var/turf/current_turf = get_turf(src)
current_turf.audible_message(span_notice("[src] beeps: Unit is recharged."))
- playsound(src, 'sound/machines/defib_ready.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/defib/defib_ready.ogg', 50, FALSE)
cooldown = FALSE
update_appearance()
@@ -418,7 +418,7 @@
user.visible_message(span_danger("[user] is putting the live paddles on [user.p_their()] chest! It looks like [user.p_theyre()] trying to commit suicide!"))
if(req_defib)
defib.deductcharge(revivecost)
- playsound(src, 'sound/machines/defib_zap.ogg', 50, TRUE, -1)
+ playsound(src, 'sound/machines/defib/defib_zap.ogg', 50, TRUE, -1)
return OXYLOSS
/obj/item/shockpaddles/update_icon_state()
@@ -451,7 +451,7 @@
defib?.update_power()
if(req_defib && !defib.powered)
user.visible_message(span_warning("[defib] beeps: Not enough charge!"))
- playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/defib/defib_failed.ogg', 50, FALSE)
return
if(!HAS_TRAIT(src, TRAIT_WIELDED))
if(iscyborg(user))
@@ -493,7 +493,7 @@
do_help(H, user)
-/// Called whenever the paddles successfuly shock something
+/// Called whenever the paddles successfully shock something
/obj/item/shockpaddles/proc/do_success()
if(busy)
busy = FALSE
@@ -527,7 +527,7 @@
M.Knockdown(75)
M.set_jitter_if_lower(100 SECONDS)
M.apply_status_effect(/datum/status_effect/convulsing)
- playsound(src, 'sound/machines/defib_zap.ogg', 50, TRUE, -1)
+ playsound(src, 'sound/machines/defib/defib_zap.ogg', 50, TRUE, -1)
if(HAS_TRAIT(M,MOB_ORGANIC))
M.emote("gasp")
log_combat(user, M, "zapped", src)
@@ -544,7 +544,7 @@
user.visible_message(span_notice("[user] places [src] on [H]'s chest."),
span_warning("You place [src] on [H]'s chest and begin to charge them."))
var/turf/T = get_turf(defib)
- playsound(src, 'sound/machines/defib_charge.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/defib/defib_charge.ogg', 50, FALSE)
if(req_defib)
T.audible_message(span_warning("\The [defib] lets out an urgent beep and lets out a steadily rising hum..."))
else
@@ -555,12 +555,12 @@
return
if(H && H.stat == DEAD)
to_chat(user, span_warning("[H] is dead."))
- playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/defib/defib_failed.ogg', 50, FALSE)
do_cancel()
return
user.visible_message(span_boldannounce("[user] shocks [H] with \the [src]!"), span_warning("You shock [H] with \the [src]!"))
- playsound(src, 'sound/machines/defib_zap.ogg', 100, TRUE, -1)
- playsound(src, 'sound/weapons/egloves.ogg', 100, TRUE, -1)
+ playsound(src, 'sound/machines/defib/defib_zap.ogg', 100, TRUE, -1)
+ playsound(src, 'sound/items/weapons/egloves.ogg', 100, TRUE, -1)
H.emote("scream")
shock_pulling(45, H)
if(H.can_heartattack() && !H.undergoing_cardiac_arrest())
@@ -582,14 +582,14 @@
update_appearance()
if(do_after(user, 3 SECONDS, H, extra_checks = CALLBACK(src, PROC_REF(is_wielded)))) //beginning to place the paddles on patient's chest to allow some time for people to move away to stop the process
user.visible_message(span_notice("[user] places [src] on [H]'s chest."), span_warning("You place [src] on [H]'s chest."))
- playsound(src, 'sound/machines/defib_charge.ogg', 75, FALSE)
+ playsound(src, 'sound/machines/defib/defib_charge.ogg', 75, FALSE)
var/obj/item/organ/internal/heart = H.get_organ_by_type(/obj/item/organ/internal/heart)
if(do_after(user, 2 SECONDS, H, extra_checks = CALLBACK(src, PROC_REF(is_wielded)))) //placed on chest and short delay to shock for dramatic effect, revive time is 5sec total
if((!combat && !req_defib) || (req_defib && !defib.combat))
for(var/obj/item/clothing/C in H.get_equipped_items())
if((C.body_parts_covered & CHEST) && (C.clothing_flags & THICKMATERIAL)) //check to see if something is obscuring their chest.
user.audible_message(span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Patient's chest is obscured. Operation aborted."))
- playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/defib/defib_failed.ogg', 50, FALSE)
do_cancel()
return
if(SEND_SIGNAL(H, COMSIG_DEFIBRILLATOR_PRE_HELP_ZAP, user, src) & COMPONENT_DEFIB_STOP)
@@ -598,7 +598,7 @@
if(H.stat == DEAD)
H.visible_message(span_warning("[H]'s body convulses a bit."))
playsound(src, SFX_BODYFALL, 50, TRUE)
- playsound(src, 'sound/machines/defib_zap.ogg', 75, TRUE, -1)
+ playsound(src, 'sound/machines/defib/defib_zap.ogg', 75, TRUE, -1)
shock_pulling(30, H)
var/defib_result = H.can_defib()
@@ -626,7 +626,7 @@
if(fail_reason)
user.visible_message(span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Resuscitation failed - [fail_reason]"))
- playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/defib/defib_failed.ogg', 50, FALSE)
else
var/total_brute = H.getBruteLoss()
var/total_burn = H.getFireLoss()
@@ -645,7 +645,7 @@
if(need_mob_update)
H.updatehealth() // Previous "adjust" procs don't update health, so we do it manually.
user.visible_message(span_notice("[req_defib ? "[defib]" : "[src]"] pings: Resuscitation successful."))
- playsound(src, 'sound/machines/defib_success.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/defib/defib_success.ogg', 50, FALSE)
H.set_heartattack(FALSE)
if(defib_result == DEFIB_POSSIBLE)
H.grab_ghost()
@@ -662,9 +662,9 @@
return
else if (!H.get_organ_by_type(/obj/item/organ/internal/heart))
user.visible_message(span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Patient's heart is missing. Operation aborted."))
- playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/defib/defib_failed.ogg', 50, FALSE)
else if(H.undergoing_cardiac_arrest())
- playsound(src, 'sound/machines/defib_zap.ogg', 50, TRUE, -1)
+ playsound(src, 'sound/machines/defib/defib_zap.ogg', 50, TRUE, -1)
if(!(heart.organ_flags & ORGAN_FAILING))
H.set_heartattack(FALSE)
user.visible_message(span_notice("[req_defib ? "[defib]" : "[src]"] pings: Patient's heart is now beating again."))
@@ -673,7 +673,7 @@
else
user.visible_message(span_warning("[req_defib ? "[defib]" : "[src]"] buzzes: Patient is not in a valid state. Operation aborted."))
- playsound(src, 'sound/machines/defib_failed.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/defib/defib_failed.ogg', 50, FALSE)
do_cancel()
/obj/item/shockpaddles/proc/is_wielded()
@@ -708,7 +708,7 @@
base_icon_state = "syndiepaddles"
/obj/item/shockpaddles/syndicate/nanotrasen
- name = "elite nanotrasen defibrillator paddles"
+ name = "elite Nanotrasen defibrillator paddles"
desc = "A pair of paddles used to revive deceased ERT members. They possess both the ability to penetrate armor and to deliver powerful or disabling shocks offensively."
icon_state = "ntpaddles0"
inhand_icon_state = "ntpaddles0"
diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm
index c619f7d7018e0..77eefa8a1507b 100644
--- a/code/game/objects/items/devices/aicard.dm
+++ b/code/game/objects/items/devices/aicard.dm
@@ -43,17 +43,15 @@
user.visible_message(span_suicide("[user] is trying to upload [user.p_them()]self into [src]! That's not going to work out well!"))
return BRUTELOSS
-/obj/item/aicard/pre_attack(atom/target, mob/living/user, params)
- . = ..()
- if(.)
- return
-
+/obj/item/aicard/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(AI)
- if(upload_ai(target, user))
- return TRUE
+ if(upload_ai(interacting_with, user))
+ return ITEM_INTERACT_SUCCESS
else
- if(capture_ai(target, user))
- return TRUE
+ if(capture_ai(interacting_with, user))
+ return ITEM_INTERACT_SUCCESS
+
+ return NONE
/// Tries to get an AI from the atom clicked
/obj/item/aicard/proc/capture_ai(atom/from_what, mob/living/user)
diff --git a/code/game/objects/items/devices/aicard_evil.dm b/code/game/objects/items/devices/aicard_evil.dm
index 3e8c56ce940fd..852a105de350f 100644
--- a/code/game/objects/items/devices/aicard_evil.dm
+++ b/code/game/objects/items/devices/aicard_evil.dm
@@ -102,13 +102,13 @@
else
AI = locate() in A
if(!AI || AI.interaction_range == INFINITY)
- playsound(src,'sound/machines/buzz-sigh.ogg',50,FALSE)
+ playsound(src,'sound/machines/buzz/buzz-sigh.ogg',50,FALSE)
to_chat(user, span_notice("Error! Incompatible object!"))
return ..()
AI.interaction_range += 2
if(AI.interaction_range > 7)
AI.interaction_range = INFINITY
- playsound(src,'sound/machines/twobeep.ogg',50,FALSE)
+ playsound(src,'sound/machines/beep/twobeep.ogg',50,FALSE)
to_chat(user, span_notice("You insert [src] into [AI]'s compartment, and it beeps as it processes the data."))
to_chat(AI, span_notice("You process [src], and find yourself able to manipulate electronics from up to [AI.interaction_range] meters!"))
qdel(src)
diff --git a/code/game/objects/items/devices/battle_royale.dm b/code/game/objects/items/devices/battle_royale.dm
index 70ae0a8f85b2d..5a6fe059cb6c4 100644
--- a/code/game/objects/items/devices/battle_royale.dm
+++ b/code/game/objects/items/devices/battle_royale.dm
@@ -238,7 +238,7 @@ GLOBAL_DATUM_INIT(battle_royale_master, /datum/battle_royale_master, new)
As a gesture of gratitude, we will be providing our premium broadcast to your entertainment monitors at no cost so that you can watch the excitement. \n\
Bystanders are advised not to intervene... but if you do, make it look good for the camera!",
title = "Rumble Royale Beginning",
- sound = 'sound/machines/alarm.ogg',
+ sound = 'sound/announcer/alarm/nuke_alarm.ogg',
has_important_message = TRUE,
sender_override = "Rumble Royale Pirate Broadcast Station",
color_override = "red",
@@ -268,7 +268,7 @@ GLOBAL_DATUM_INIT(battle_royale_master, /datum/battle_royale_master, new)
priority_announce(
text = message,
title = "Rumble Royale Casualty Report",
- sound = 'sound/misc/notice1.ogg',
+ sound = 'sound/announcer/notice/notice1.ogg',
has_important_message = TRUE,
sender_override = "Rumble Royale Pirate Broadcast Station",
color_override = "red",
@@ -302,7 +302,7 @@ GLOBAL_DATUM_INIT(battle_royale_master, /datum/battle_royale_master, new)
priority_announce(
text = message,
title = "Rumble Royale Winner",
- sound = 'sound/misc/notice1.ogg',
+ sound = 'sound/announcer/notice/notice1.ogg',
has_important_message = TRUE,
sender_override = "Rumble Royale Pirate Broadcast Station",
color_override = "red",
@@ -317,7 +317,7 @@ GLOBAL_DATUM_INIT(battle_royale_master, /datum/battle_royale_master, new)
priority_announce(
text = "We're halfway done folks! And bad news to anyone who hasn't made it to the [chosen_area]... you're out!",
title = "Rumble Royale Update",
- sound = 'sound/misc/notice1.ogg',
+ sound = 'sound/announcer/notice/notice1.ogg',
has_important_message = TRUE,
sender_override = "Rumble Royale Pirate Broadcast Station",
color_override = "red",
@@ -335,7 +335,7 @@ GLOBAL_DATUM_INIT(battle_royale_master, /datum/battle_royale_master, new)
We're sorry to announce that this edition of Royal Rumble has no winner. \n\
Better luck next time!",
title = "Rumble Royale Concluded",
- sound = 'sound/misc/notice1.ogg',
+ sound = 'sound/announcer/notice/notice1.ogg',
has_important_message = TRUE,
sender_override = "Rumble Royale Pirate Broadcast Station",
color_override = "red",
diff --git a/code/game/objects/items/devices/broadcast_camera.dm b/code/game/objects/items/devices/broadcast_camera.dm
index 4f6e3f1f8f475..78868844e48cb 100644
--- a/code/game/objects/items/devices/broadcast_camera.dm
+++ b/code/game/objects/items/devices/broadcast_camera.dm
@@ -20,6 +20,10 @@
light_range = 1
light_power = 0.3
light_on = FALSE
+ /// Is camera streaming
+ var/active = FALSE
+ /// Is the microphone turned on
+ var/active_microphone = TRUE
/// The name of the broadcast
var/broadcast_name = "Curator News"
/// The networks it broadcasts to, default is CAMERANET_NETWORK_CURATOR
@@ -29,16 +33,6 @@
/// The "virtual" radio inside of the the physical camera, a la microphone
var/obj/item/radio/entertainment/microphone/internal_radio
-/obj/item/broadcast_camera/Initialize(mapload)
- . = ..()
- AddComponent(/datum/component/two_handed, \
- force_unwielded = 8, \
- force_wielded = 12, \
- icon_wielded = "[base_icon_state]1", \
- wield_callback = CALLBACK(src, PROC_REF(on_wield)), \
- unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \
- )
-
/obj/item/broadcast_camera/Destroy(force)
QDEL_NULL(internal_radio)
QDEL_NULL(internal_camera)
@@ -49,6 +43,14 @@
icon_state = "[base_icon_state]0"
return ..()
+/obj/item/broadcast_camera/attack_self(mob/user, modifiers)
+ . = ..()
+ active = !active
+ if(active)
+ on_activating()
+ else
+ on_deactivating()
+
/obj/item/broadcast_camera/attack_self_secondary(mob/user, modifiers)
. = ..()
broadcast_name = tgui_input_text(user = user, title = "Broadcast Name", message = "What will be the name of your broadcast?", default = "[broadcast_name]", max_length = MAX_CHARTER_LEN)
@@ -56,11 +58,24 @@
/obj/item/broadcast_camera/examine(mob/user)
. = ..()
. += span_notice("Broadcast name is [broadcast_name]")
+ . += span_notice("The microphone is [active_microphone ? "On" : "Off"]")
-/// When wielding the camera
-/obj/item/broadcast_camera/proc/on_wield()
+/obj/item/broadcast_camera/on_enter_storage(datum/storage/master_storage)
+ . = ..()
+ if(active)
+ on_deactivating()
+
+/obj/item/broadcast_camera/dropped(mob/user, silent)
+ . = ..()
+ if(active)
+ on_deactivating()
+
+/// When activating the camera
+/obj/item/broadcast_camera/proc/on_activating()
if(!iscarbon(loc))
return
+ active = TRUE
+ icon_state = "[base_icon_state][active]"
/// The carbon who wielded the camera, allegedly
var/mob/living/carbon/wielding_carbon = loc
@@ -73,18 +88,37 @@
// INTERNAL RADIO
internal_radio = new(src)
+ /// Sets the state of the microphone
+ set_microphone_state()
set_light_on(TRUE)
- playsound(source = src, soundin = 'sound/machines/terminal_processing.ogg', vol = 20, vary = FALSE, ignore_walls = FALSE)
+ playsound(source = src, soundin = 'sound/machines/terminal/terminal_processing.ogg', vol = 20, vary = FALSE, ignore_walls = FALSE)
balloon_alert_to_viewers("live!")
-/// When unwielding the camera
-/obj/item/broadcast_camera/proc/on_unwield()
+/// When deactivating the camera
+/obj/item/broadcast_camera/proc/on_deactivating()
+ active = FALSE
+ icon_state = "[base_icon_state][active]"
QDEL_NULL(internal_camera)
QDEL_NULL(internal_radio)
stop_broadcasting_network(camera_networks)
set_light_on(FALSE)
- playsound(source = src, soundin = 'sound/machines/terminal_prompt_deny.ogg', vol = 20, vary = FALSE, ignore_walls = FALSE)
+ playsound(source = src, soundin = 'sound/machines/terminal/terminal_prompt_deny.ogg', vol = 20, vary = FALSE, ignore_walls = FALSE)
balloon_alert_to_viewers("offline")
+
+/obj/item/broadcast_camera/click_alt(mob/user)
+ active_microphone = !active_microphone
+
+ /// Text popup for letting the user know that the microphone has changed state
+ balloon_alert(user, "turned [active_microphone ? "on" : "off"] the microphone.")
+
+ ///If the radio exists as an object, set its state accordingly
+ if(active)
+ set_microphone_state()
+
+ return CLICK_ACTION_SUCCESS
+
+/obj/item/broadcast_camera/proc/set_microphone_state()
+ internal_radio.set_broadcasting(active_microphone)
diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm
index 1920e47f97f66..fbdf3bae40a88 100644
--- a/code/game/objects/items/devices/chameleonproj.dm
+++ b/code/game/objects/items/devices/chameleonproj.dm
@@ -36,28 +36,39 @@
else
to_chat(user, span_warning("You can't use [src] while inside something!"))
-/obj/item/chameleon/interact_with_atom(atom/target, mob/living/user, list/modifiers)
+/obj/item/chameleon/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!can_copy(interacting_with) || SHOULD_SKIP_INTERACTION(interacting_with, src, user))
+ return NONE
+ make_copy(interacting_with, user)
+ return ITEM_INTERACT_SUCCESS
+
+/obj/item/chameleon/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!can_copy(interacting_with)) // RMB scan works on storage items, LMB scan does not
+ return NONE
+ make_copy(interacting_with, user)
+ return ITEM_INTERACT_SUCCESS
+
+/obj/item/chameleon/proc/can_copy(atom/target)
if(!check_sprite(target))
- return ITEM_INTERACT_BLOCKING
+ return FALSE
if(active_dummy)//I now present you the blackli(f)st
- return ITEM_INTERACT_BLOCKING
+ return FALSE
if(isturf(target))
- return ITEM_INTERACT_BLOCKING
+ return FALSE
if(ismob(target))
- return ITEM_INTERACT_BLOCKING
+ return FALSE
if(istype(target, /obj/structure/falsewall))
- return ITEM_INTERACT_BLOCKING
+ return FALSE
if(target.alpha != 255)
- return ITEM_INTERACT_BLOCKING
+ return FALSE
if(target.invisibility != 0)
- return ITEM_INTERACT_BLOCKING
+ return FALSE
if(iseffect(target) && !istype(target, /obj/effect/decal)) //be a footprint
- return ITEM_INTERACT_BLOCKING
- make_copy(target, user)
- return ITEM_INTERACT_SUCCESS
+ return FALSE
+ return TRUE
/obj/item/chameleon/proc/make_copy(atom/target, mob/user)
- playsound(get_turf(src), 'sound/weapons/flash.ogg', 100, TRUE, -6)
+ playsound(get_turf(src), 'sound/items/weapons/flash.ogg', 100, TRUE, -6)
to_chat(user, span_notice("Scanned [target]."))
var/obj/temp = new /obj()
temp.appearance = target.appearance
diff --git a/code/game/objects/items/devices/electroadaptive_pseudocircuit.dm b/code/game/objects/items/devices/electroadaptive_pseudocircuit.dm
index 5814101463ba4..cd0b42e0e8ac4 100644
--- a/code/game/objects/items/devices/electroadaptive_pseudocircuit.dm
+++ b/code/game/objects/items/devices/electroadaptive_pseudocircuit.dm
@@ -44,7 +44,7 @@
if(!circuits)
to_chat(R, span_warning("You need more material. Use [src] on existing simple circuits to break them down."))
return
- playsound(R, 'sound/items/rped.ogg', 50, TRUE)
+ playsound(R, 'sound/items/tools/rped.ogg', 50, TRUE)
recharging = TRUE
circuits--
maptext = MAPTEXT(circuits)
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index 8db7652d5a07e..657d054f11cc8 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -29,9 +29,9 @@
/// Can we toggle this light on and off (used for contexual screentips only)
var/toggle_context = TRUE
/// The sound the light makes when it's turned on
- var/sound_on = 'sound/weapons/magin.ogg'
+ var/sound_on = 'sound/items/weapons/magin.ogg'
/// The sound the light makes when it's turned off
- var/sound_off = 'sound/weapons/magout.ogg'
+ var/sound_off = 'sound/items/weapons/magout.ogg'
/// Should the flashlight start turned on?
var/start_on = FALSE
@@ -41,8 +41,6 @@
set_light_on(TRUE)
update_brightness()
register_context()
- if(toggle_context)
- RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/flashlight_eyes)
@@ -256,7 +254,7 @@
if(!scanning.get_bodypart(BODY_ZONE_HEAD))
to_chat(user, span_warning("[scanning] doesn't have a head!"))
return
- if(light_power < 1)
+ if(light_power < 0.5)
to_chat(user, span_warning("[src] isn't bright enough to see anything!"))
return
@@ -286,12 +284,12 @@
setDir(user.dir)
/// when hit by a light disruptor - turns the light off, forces the light to be disabled for a few seconds
-/obj/item/flashlight/proc/on_saboteur(datum/source, disrupt_duration)
- SIGNAL_HANDLER
+/obj/item/flashlight/on_saboteur(datum/source, disrupt_duration)
+ . = ..()
if(light_on)
toggle_light()
COOLDOWN_START(src, disabled_time, disrupt_duration)
- return COMSIG_SABOTEUR_SUCCESS
+ return TRUE
/obj/item/flashlight/pen
name = "penlight"
@@ -361,7 +359,7 @@
light_range = 5 // A little better than the standard flashlight.
light_power = 0.8
light_color = "#99ccff"
- hitsound = 'sound/weapons/genhit1.ogg'
+ hitsound = 'sound/items/weapons/genhit1.ogg'
// the desk lamps are a bit special
/obj/item/flashlight/lamp
@@ -430,7 +428,7 @@
if(light_on)
attack_verb_continuous = string_list(list("burns", "singes"))
attack_verb_simple = string_list(list("burn", "singe"))
- hitsound = 'sound/items/welder.ogg'
+ hitsound = 'sound/items/tools/welder.ogg'
force = on_damage
damtype = BURN
update_brightness()
@@ -457,7 +455,7 @@
name = "lit [initial(name)]"
attack_verb_continuous = string_list(list("burns", "singes"))
attack_verb_simple = string_list(list("burn", "singe"))
- hitsound = 'sound/items/welder.ogg'
+ hitsound = 'sound/items/tools/welder.ogg'
force = on_damage
damtype = BURN
diff --git a/code/game/objects/items/devices/forcefieldprojector.dm b/code/game/objects/items/devices/forcefieldprojector.dm
index 5d40d40a4d925..e41c346c9930d 100644
--- a/code/game/objects/items/devices/forcefieldprojector.dm
+++ b/code/game/objects/items/devices/forcefieldprojector.dm
@@ -21,10 +21,10 @@
/// Checks to make sure the projector isn't busy with making another forcefield.
var/force_proj_busy = FALSE
-/obj/item/forcefield_projector/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- return interact_with_atom(interacting_with, user, modifiers)
-
/obj/item/forcefield_projector/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ return ranged_interact_with_atom(interacting_with, user, modifiers)
+
+/obj/item/forcefield_projector/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!check_allowed_items(interacting_with, not_inside = TRUE))
return NONE
if(istype(interacting_with, /obj/structure/projected_forcefield))
@@ -59,7 +59,7 @@
return ITEM_INTERACT_BLOCKING
force_proj_busy = FALSE
- playsound(src,'sound/weapons/resonator_fire.ogg',50,TRUE)
+ playsound(src,'sound/items/weapons/resonator_fire.ogg',50,TRUE)
user.visible_message(span_warning("[user] projects a forcefield!"),span_notice("You project a forcefield."))
var/obj/structure/projected_forcefield/F = new(T, src)
current_fields += F
@@ -124,14 +124,14 @@
/obj/structure/projected_forcefield/Destroy()
visible_message(span_warning("[src] flickers and disappears!"))
- playsound(src,'sound/weapons/resonator_blast.ogg',25,TRUE)
+ playsound(src,'sound/items/weapons/resonator_blast.ogg',25,TRUE)
if(generator)
generator.current_fields -= src
generator = null
return ..()
/obj/structure/projected_forcefield/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
- playsound(loc, 'sound/weapons/egloves.ogg', 80, TRUE)
+ playsound(loc, 'sound/items/weapons/egloves.ogg', 80, TRUE)
/obj/structure/projected_forcefield/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
if(sound_effect)
diff --git a/code/game/objects/items/devices/geiger_counter.dm b/code/game/objects/items/devices/geiger_counter.dm
index 1d5ef17a90c4a..25bae4306091f 100644
--- a/code/game/objects/items/devices/geiger_counter.dm
+++ b/code/game/objects/items/devices/geiger_counter.dm
@@ -67,13 +67,13 @@
update_appearance(UPDATE_ICON)
balloon_alert(user, "switch [scanning ? "on" : "off"]")
-/obj/item/geiger_counter/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- return interact_with_atom(interacting_with, user, modifiers)
-
/obj/item/geiger_counter/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- if (user.combat_mode)
+ if(SHOULD_SKIP_INTERACTION(interacting_with, src, user))
return NONE
- if (!CAN_IRRADIATE(interacting_with))
+ return ranged_interact_with_atom(interacting_with, user, modifiers)
+
+/obj/item/geiger_counter/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!CAN_IRRADIATE(interacting_with))
return NONE
user.visible_message(span_notice("[user] scans [interacting_with] with [src]."), span_notice("You scan [interacting_with]'s radiation levels with [src]..."))
diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm
index 3fe16fdb3fd96..03e53a87db35e 100644
--- a/code/game/objects/items/devices/laserpointer.dm
+++ b/code/game/objects/items/devices/laserpointer.dm
@@ -99,7 +99,7 @@
var/obj/item/stock_parts/attack_diode = attack_item
if(crystal_lens && attack_diode.rating < 3) //only tier 3 and up are small enough to fit
to_chat(user, span_warning("You try to jam \the [attack_item.name] in place, but \the [crystal_lens.name] is in the way!"))
- playsound(src, 'sound/machines/airlock_alien_prying.ogg', 20)
+ playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 20)
if(do_after(user, 2 SECONDS, src))
var/atom/atom_to_teleport = pick(user, attack_item)
if(atom_to_teleport == user)
@@ -113,7 +113,7 @@
return
if(!user.transferItemToLoc(attack_item, src))
return
- playsound(src, 'sound/items/screwdriver.ogg', 30)
+ playsound(src, 'sound/items/tools/screwdriver.ogg', 30)
diode = attack_item
balloon_alert(user, "installed \the [diode.name]")
//we have a diode now, try starting a charge sequence in case the pointer was charging when we took out the diode
@@ -129,7 +129,7 @@
var/obj/item/stack/ore/bluespace_crystal/crystal_stack = attack_item
if(diode && diode.rating < 3) //only lasers of tier 3 and up can house a lens
to_chat(user, span_warning("You try to jam \the [crystal_stack.name] in front of the diode, but it's a bad fit!"))
- playsound(src, 'sound/machines/airlock_alien_prying.ogg', 20)
+ playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 20)
if(do_after(user, 2 SECONDS, src))
var/atom/atom_to_teleport = pick(user, src)
if(atom_to_teleport == user)
@@ -148,7 +148,7 @@
if(!user.transferItemToLoc(single_crystal, src))
return
crystal_lens = single_crystal
- playsound(src, 'sound/items/screwdriver2.ogg', 30)
+ playsound(src, 'sound/items/tools/screwdriver2.ogg', 30)
balloon_alert(user, "installed \the [crystal_lens.name]")
to_chat(user, span_notice("You install a [crystal_lens.name] in [src]. \
It can now be used to shine through obstacles at the cost of double the energy drain."))
@@ -183,12 +183,14 @@
and the wide margin between it and the focus lens could probably house a crystal of some sort."
/obj/item/laser_pointer/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- return interact_with_atom(interacting_with, user, modifiers)
-
-/obj/item/laser_pointer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
laser_act(interacting_with, user, modifiers)
return ITEM_INTERACT_BLOCKING
+/obj/item/laser_pointer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION))
+ return NONE
+ return ranged_interact_with_atom(interacting_with, user, modifiers)
+
///Handles shining the clicked atom,
/obj/item/laser_pointer/proc/laser_act(atom/target, mob/living/user, list/modifiers)
if(isnull(diode))
@@ -233,9 +235,12 @@
else if(user.zone_selected == BODY_ZONE_PRECISE_EYES)
//Intensity of the laser dot to pass to flash_act
var/severity = pick(0, 1, 2)
+ var/always_fail = FALSE
+ if(istype(target_humanoid.glasses, /obj/item/clothing/glasses/eyepatch) && prob(50))
+ always_fail = TRUE
//chance to actually hit the eyes depends on internal component
- if(prob(effectchance * diode.rating) && target_humanoid.flash_act(severity))
+ if(prob(effectchance * diode.rating) && !always_fail && target_humanoid.flash_act(severity))
outmsg = span_notice("You blind [target_humanoid] by shining [src] in [target_humanoid.p_their()] eyes.")
log_combat(user, target_humanoid, "blinded with a laser pointer", src)
else
diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm
index 13821a42de40f..e3c19dfde66f3 100644
--- a/code/game/objects/items/devices/lightreplacer.dm
+++ b/code/game/objects/items/devices/lightreplacer.dm
@@ -223,7 +223,7 @@
if(istype(target, /obj/machinery/light))
if(replace_light(target, user) && bluespace_toggle)
user.Beam(target, icon_state = "rped_upgrade", time = 0.5 SECONDS)
- playsound(src, 'sound/items/pshoom.ogg', 40, 1)
+ playsound(src, 'sound/items/pshoom/pshoom.ogg', 40, 1)
return TRUE
// if we are attacking a floodlight frame finish it
@@ -233,7 +233,7 @@
new /obj/machinery/power/floodlight(frame.loc)
if(bluespace_toggle)
user.Beam(target, icon_state = "rped_upgrade", time = 0.5 SECONDS)
- playsound(src, 'sound/items/pshoom.ogg', 40, 1)
+ playsound(src, 'sound/items/pshoom/pshoom.ogg', 40, 1)
to_chat(user, span_notice("You finish \the [frame] with a light tube."))
qdel(frame)
return TRUE
@@ -246,7 +246,7 @@
light_replaced = TRUE
if(light_replaced && bluespace_toggle)
user.Beam(target, icon_state = "rped_upgrade", time = 0.5 SECONDS)
- playsound(src, 'sound/items/pshoom.ogg', 40, 1)
+ playsound(src, 'sound/items/pshoom/pshoom.ogg', 40, 1)
return TRUE
return FALSE
@@ -325,6 +325,12 @@
. = ..()
ADD_TRAIT(src, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
+/obj/item/lightreplacer/cyborg/advanced
+ name = "high capacity light replacer"
+ desc = "A higher capacity light replacer. Refill with broken or working lightbulbs, or sheets of glass."
+ icon_state = "lightreplacer_high"
+ max_uses = 50
+
/obj/item/lightreplacer/blue
name = "bluespace light replacer"
desc = "A modified light replacer that zaps lights into place. Refill with broken or working lightbulbs, or sheets of glass."
diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm
index 03ed7f927b0f7..b9d8539562809 100644
--- a/code/game/objects/items/devices/multitool.dm
+++ b/code/game/objects/items/devices/multitool.dm
@@ -24,17 +24,22 @@
throwforce = 0
throw_range = 7
throw_speed = 3
- drop_sound = 'sound/items/handling/multitool_drop.ogg'
- pickup_sound = 'sound/items/handling/multitool_pickup.ogg'
+ drop_sound = 'sound/items/handling/tools/multitool_drop.ogg'
+ pickup_sound = 'sound/items/handling/tools/multitool_pickup.ogg'
custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 0.5, /datum/material/glass= SMALL_MATERIAL_AMOUNT * 0.2)
custom_premium_price = PAYCHECK_COMMAND * 3
toolspeed = 1
- usesound = 'sound/weapons/empty.ogg'
+ usesound = 'sound/items/weapons/empty.ogg'
var/datum/buffer // simple machine buffer for device linkage
var/mode = 0
var/apc_scanner = TRUE
COOLDOWN_DECLARE(next_apc_scan)
+/obj/item/multitool/Destroy()
+ if(buffer)
+ remove_buffer(buffer)
+ return ..()
+
/obj/item/multitool/examine(mob/user)
. = ..()
. += span_notice("Its buffer [buffer ? "contains [buffer]." : "is empty."]")
@@ -70,9 +75,10 @@
/obj/item/multitool/proc/set_buffer(datum/buffer)
if(src.buffer)
UnregisterSignal(src.buffer, COMSIG_QDELETING)
+ remove_buffer(src.buffer)
src.buffer = buffer
if(!QDELETED(buffer))
- RegisterSignal(buffer, COMSIG_QDELETING, PROC_REF(on_buffer_del))
+ RegisterSignal(buffer, COMSIG_QDELETING, PROC_REF(remove_buffer))
/**
* Called when the buffer's stored object is deleted
@@ -80,8 +86,9 @@
* This proc does not clear the buffer of the multitool, it is here to
* handle the deletion of the object the buffer references
*/
-/obj/item/multitool/proc/on_buffer_del(datum/source)
+/obj/item/multitool/proc/remove_buffer(datum/source)
SIGNAL_HANDLER
+ SEND_SIGNAL(src, COMSIG_MULTITOOL_REMOVE_BUFFER, source)
buffer = null
// Syndicate device disguised as a multitool; it will turn red when an AI camera is nearby.
diff --git a/code/game/objects/items/devices/pressureplates.dm b/code/game/objects/items/devices/pressureplates.dm
index 18bb026745ac0..17f324d109f99 100644
--- a/code/game/objects/items/devices/pressureplates.dm
+++ b/code/game/objects/items/devices/pressureplates.dm
@@ -7,7 +7,8 @@
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
icon_state = "pressureplate"
- layer = LOW_OBJ_LAYER
+ plane = FLOOR_PLANE
+ layer = HIGH_TURF_LAYER
var/trigger_mob = TRUE
var/trigger_item = FALSE
var/specific_item = null
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index 5673afc678a41..6ce325034e3ac 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -462,9 +462,7 @@ GLOBAL_LIST_INIT(channel_tokens, list(
grant_headset_languages(mob_loc)
/obj/item/radio/headset/click_alt(mob/living/user)
- if(!istype(user) || !Adjacent(user) || user.incapacitated)
- return CLICK_ACTION_BLOCKING
- if (!command)
+ if(!istype(user) || !command)
return CLICK_ACTION_BLOCKING
use_command = !use_command
to_chat(user, span_notice("You toggle high-volume mode [use_command ? "on" : "off"]."))
diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm
index 7a9a85cc66675..cd8535e8d2823 100644
--- a/code/game/objects/items/devices/radio/intercom.dm
+++ b/code/game/objects/items/devices/radio/intercom.dm
@@ -154,7 +154,7 @@
// A fully locked one will do nothing, as locked is intended to be used for stuff that should never be changed
if(RADIO_FREQENCY_LOCKED)
balloon_alert(user, "can't override frequency lock!")
- playsound(src, 'sound/machines/buzz-two.ogg', 50, FALSE, SILENCED_SOUND_EXTRARANGE)
+ playsound(src, 'sound/machines/buzz/buzz-two.ogg', 50, FALSE, SILENCED_SOUND_EXTRARANGE)
return
// Emagging an unlocked one will do nothing, for now
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 2b7a9bad7602a..93a131a9b468b 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -121,19 +121,17 @@
return
AddElement(/datum/element/slapcrafting, string_list(list(/datum/crafting_recipe/improv_explosive)))
- RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
-
/obj/item/radio/Destroy()
remove_radio_all(src) //Just to be sure
if(istype(keyslot))
QDEL_NULL(keyslot)
return ..()
-/obj/item/radio/proc/on_saboteur(datum/source, disrupt_duration)
- SIGNAL_HANDLER
+/obj/item/radio/on_saboteur(datum/source, disrupt_duration)
+ . = ..()
if(broadcasting) //no broadcasting but it can still be used to send radio messages.
set_broadcasting(FALSE)
- return COMSIG_SABOTEUR_SUCCESS
+ return TRUE
/obj/item/radio/proc/set_frequency(new_frequency)
SEND_SIGNAL(src, COMSIG_RADIO_NEW_FREQUENCY, args)
@@ -354,7 +352,7 @@
if(isliving(talking_movable))
var/mob/living/talking_living = talking_movable
if(radio_noise && !HAS_TRAIT(talking_living, TRAIT_DEAF) && talking_living.client?.prefs.read_preference(/datum/preference/toggle/radio_noise))
- SEND_SOUND(talking_living, 'sound/misc/radio_talk.ogg')
+ SEND_SOUND(talking_living, 'sound/items/radio/radio_talk.ogg')
// All radios make an attempt to use the subspace system first
signal.send_to_receivers()
@@ -437,10 +435,10 @@
var/list/spans = data["spans"]
if(COOLDOWN_FINISHED(src, audio_cooldown))
COOLDOWN_START(src, audio_cooldown, 0.5 SECONDS)
- SEND_SOUND(holder, 'sound/misc/radio_receive.ogg')
+ SEND_SOUND(holder, 'sound/items/radio/radio_receive.ogg')
if((SPAN_COMMAND in spans) && COOLDOWN_FINISHED(src, important_audio_cooldown))
COOLDOWN_START(src, important_audio_cooldown, 0.5 SECONDS)
- SEND_SOUND(holder, 'sound/misc/radio_important.ogg')
+ SEND_SOUND(holder, 'sound/items/radio/radio_important.ogg')
/obj/item/radio/ui_state(mob/user)
return GLOB.inventory_state
diff --git a/code/game/objects/items/devices/reverse_bear_trap.dm b/code/game/objects/items/devices/reverse_bear_trap.dm
index e88b1a51187a3..342383380cf76 100644
--- a/code/game/objects/items/devices/reverse_bear_trap.dm
+++ b/code/game/objects/items/devices/reverse_bear_trap.dm
@@ -112,7 +112,7 @@
source = src,
header = "Reverse bear trap armed",
notify_flags = NOTIFY_CATEGORY_NOFLASH,
- ghost_sound = 'sound/machines/beep.ogg',
+ ghost_sound = 'sound/machines/beep/beep.ogg',
notify_volume = 75,
)
diff --git a/code/game/objects/items/devices/scanners/autopsy_scanner.dm b/code/game/objects/items/devices/scanners/autopsy_scanner.dm
index c5d33b7422656..a054b3c69d2ce 100644
--- a/code/game/objects/items/devices/scanners/autopsy_scanner.dm
+++ b/code/game/objects/items/devices/scanners/autopsy_scanner.dm
@@ -95,7 +95,7 @@
var/blood_type = scanned.dna.blood_type
if(blood_id != /datum/reagent/blood)
var/datum/reagent/reagents = GLOB.chemical_reagents_list[blood_id]
- blood_type = reagents ? reagents.name : blood_id
+ blood_type = reagents?.name || blood_id
autopsy_information += "Blood Type: [blood_type] "
autopsy_information += "Blood Volume: [scanned.blood_volume] cl ([blood_percent]%) "
@@ -108,10 +108,11 @@
for(var/datum/symptom/symptom as anything in advanced_disease.symptoms)
autopsy_information += "[symptom.name] - [symptom.desc] "
- var/obj/item/paper/autopsy_report = new(user.loc)
- autopsy_report.name = "Autopsy Report ([scanned.name])"
+ var/obj/item/paper/autopsy_report = new(user.drop_location())
+ autopsy_report.name = "autopsy report of [scanned] - [station_time_timestamp()])"
autopsy_report.add_raw_text(autopsy_information.Join("\n"))
- autopsy_report.update_appearance(UPDATE_ICON)
+ autopsy_report.color = "#99ccff"
+ autopsy_report.update_appearance()
user.put_in_hands(autopsy_report)
user.balloon_alert(user, "report printed")
return TRUE
diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm
index 26df57683aa26..270c070d6dc5e 100644
--- a/code/game/objects/items/devices/scanners/health_analyzer.dm
+++ b/code/game/objects/items/devices/scanners/health_analyzer.dm
@@ -30,6 +30,8 @@
custom_price = PAYCHECK_COMMAND
/// If this analyzer will give a bonus to wound treatments apon woundscan.
var/give_wound_treatment_bonus = FALSE
+ var/last_scan_text
+ var/scanner_busy = FALSE
/obj/item/healthanalyzer/Initialize(mapload)
. = ..()
@@ -38,7 +40,7 @@
/obj/item/healthanalyzer/examine(mob/user)
. = ..()
if(src.mode != SCANNER_NO_MODE)
- . += span_notice("Alt-click [src] to toggle the limb damage readout.")
+ . += span_notice("Alt-click [src] to toggle the limb damage readout. Ctrl-shift-click to print readout report.")
/obj/item/healthanalyzer/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!"))
@@ -58,8 +60,6 @@
/obj/item/healthanalyzer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(!isliving(interacting_with))
return NONE
- if(!user.can_read(src) || user.is_blind())
- return ITEM_INTERACT_BLOCKING
var/mob/living/M = interacting_with
@@ -69,37 +69,45 @@
// Clumsiness/brain damage check
if ((HAS_TRAIT(user, TRAIT_CLUMSY) || HAS_TRAIT(user, TRAIT_DUMB)) && prob(50))
- user.visible_message(span_warning("[user] analyzes the floor's vitals!"), \
- span_notice("You stupidly try to analyze the floor's vitals!"))
- to_chat(user, "[span_info("Analyzing results for The floor:\n\tOverall status: Healthy")]\
- \n[span_info("Key: Suffocation/Toxin/Burn/Brute")]\
- \n[span_info("\tDamage specifics: 0-0-0-0")]\
- \n[span_info("Body temperature: ???")]")
+ var/turf/scan_turf = get_turf(user)
+ user.visible_message(
+ span_warning("[user] analyzes [scan_turf]'s vitals!"),
+ span_notice("You stupidly try to analyze [scan_turf]'s vitals!"),
+ )
+
+ var/floor_text = "Analyzing results for [scan_turf] ([station_time_timestamp()]): "
+ floor_text += "Overall status: Unknown "
+ floor_text += "Subject lacks a brain. "
+ floor_text += "Body temperature: [scan_turf?.return_air()?.return_temperature() || "???"] "
+
+ if(user.can_read(src) && !user.is_blind())
+ to_chat(user, examine_block(floor_text))
+ last_scan_text = floor_text
return
if(ispodperson(M) && !advanced)
- to_chat(user, "[M]'s biological structure is too complex for the health analyzer.")
+ to_chat(user, span_info("[M]'s biological structure is too complex for the health analyzer."))
return
user.visible_message(span_notice("[user] analyzes [M]'s vitals."))
balloon_alert(user, "analyzing vitals")
playsound(user.loc, 'sound/items/healthanalyzer.ogg', 50)
+ var/readability_check = user.can_read(src) && !user.is_blind()
switch (scanmode)
if (SCANMODE_HEALTH)
- healthscan(user, M, mode, advanced)
+ last_scan_text = healthscan(user, M, mode, advanced, tochat = readability_check)
if (SCANMODE_WOUND)
- woundscan(user, M, src)
+ if(readability_check)
+ woundscan(user, M, src)
add_fingerprint(user)
/obj/item/healthanalyzer/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
if(!isliving(interacting_with))
return NONE
- if(!user.can_read(src) || user.is_blind())
- return ITEM_INTERACT_BLOCKING
-
- chemscan(user, interacting_with)
+ if(user.can_read(src) && !user.is_blind())
+ chemscan(user, interacting_with)
return ITEM_INTERACT_SUCCESS
/obj/item/healthanalyzer/add_item_context(
@@ -136,277 +144,305 @@
return
// the final list of strings to render
- var/render_list = list()
+ var/list/render_list = list()
// Damage specifics
var/oxy_loss = target.getOxyLoss()
var/tox_loss = target.getToxLoss()
var/fire_loss = target.getFireLoss()
var/brute_loss = target.getBruteLoss()
- var/mob_status = (target.stat == DEAD ? span_alert("Deceased") : "[round(target.health/target.maxHealth,0.01)*100]% healthy")
+ var/mob_status = (!target.appears_alive() ? span_alert("Deceased") : "[round(target.health / target.maxHealth, 0.01) * 100]% healthy")
- if(HAS_TRAIT(target, TRAIT_FAKEDEATH) && !advanced)
- mob_status = span_alert("Deceased")
- oxy_loss = max(rand(1, 40), oxy_loss, (300 - (tox_loss + fire_loss + brute_loss))) // Random oxygen loss
+ if(HAS_TRAIT(target, TRAIT_FAKEDEATH) && target.stat != DEAD)
+ // if we don't appear to actually be in a "dead state", add fake oxyloss
+ if(oxy_loss + tox_loss + fire_loss + brute_loss < 200)
+ oxy_loss += 200 - (oxy_loss + tox_loss + fire_loss + brute_loss)
+ oxy_loss = clamp(oxy_loss, 0, 200)
- render_list += "[span_info("Analyzing results for [target]:")]\nOverall status: [mob_status]\n"
+ render_list += "[span_info("Analyzing results for [target] ([station_time_timestamp()]):")] Overall status: [mob_status] "
- if(ishuman(target))
- var/mob/living/carbon/human/humantarget = target
- if(humantarget.undergoing_cardiac_arrest() && humantarget.stat != DEAD)
- render_list += "Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!\n"
- if(humantarget.has_reagent(/datum/reagent/inverse/technetium))
- advanced = TRUE
+ if(!advanced && target.has_reagent(/datum/reagent/inverse/technetium))
+ advanced = TRUE
- SEND_SIGNAL(target, COMSIG_LIVING_HEALTHSCAN, render_list, advanced, user, mode)
+ SEND_SIGNAL(target, COMSIG_LIVING_HEALTHSCAN, render_list, advanced, user, mode, tochat)
// Husk detection
if(HAS_TRAIT(target, TRAIT_HUSK))
if(advanced)
if(HAS_TRAIT_FROM(target, TRAIT_HUSK, BURN))
- render_list += "Subject has been husked by severe burns.\n"
+ render_list += "Subject has been husked by [conditional_tooltip("severe burns", "Tend burns and apply a de-husking agent, such as [/datum/reagent/medicine/c2/synthflesh::name].", tochat)]. "
else if (HAS_TRAIT_FROM(target, TRAIT_HUSK, CHANGELING_DRAIN))
- render_list += "Subject has been husked by dessication.\n"
+ render_list += "Subject has been husked by [conditional_tooltip("desiccation", "Irreparable. Under normal circumstances, revival can only proceed via brain transplant.", tochat)]. "
else
- render_list += "Subject has been husked by mysterious causes.\n"
+ render_list += "Subject has been husked by mysterious causes. "
else
- render_list += "Subject has been husked.\n"
+ render_list += "Subject has been husked. "
if(target.getStaminaLoss())
if(advanced)
- render_list += "Fatigue level: [target.getStaminaLoss()]%.\n"
+ render_list += "Fatigue level: [target.getStaminaLoss()]%. "
else
- render_list += "Subject appears to be suffering from fatigue.\n"
+ render_list += "Subject appears to be suffering from fatigue. "
if (!target.get_organ_slot(ORGAN_SLOT_BRAIN)) // kept exclusively for soul purposes
- render_list += "Subject lacks a brain.\n"
+ render_list += "Subject lacks a brain. "
if(iscarbon(target))
var/mob/living/carbon/carbontarget = target
- if(LAZYLEN(carbontarget.get_traumas()))
- var/list/trauma_text = list()
- for(var/datum/brain_trauma/trauma in carbontarget.get_traumas())
- var/trauma_desc = ""
- switch(trauma.resilience)
- if(TRAUMA_RESILIENCE_SURGERY)
- trauma_desc += "severe "
- if(TRAUMA_RESILIENCE_LOBOTOMY)
- trauma_desc += "deep-rooted "
- if(TRAUMA_RESILIENCE_WOUND)
- trauma_desc += "fracture-derived "
- if(TRAUMA_RESILIENCE_MAGIC, TRAUMA_RESILIENCE_ABSOLUTE)
- trauma_desc += "permanent "
- trauma_desc += trauma.scan_desc
- trauma_text += trauma_desc
- render_list += "Cerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].\n"
- if(carbontarget.quirks.len)
- render_list += "Subject Major Disabilities: [carbontarget.get_quirk_string(FALSE, CAT_QUIRK_MAJOR_DISABILITY, from_scan = TRUE)].\n"
+ if(LAZYLEN(carbontarget.quirks))
+ render_list += "Subject Major Disabilities: [carbontarget.get_quirk_string(FALSE, CAT_QUIRK_MAJOR_DISABILITY, from_scan = TRUE)]. "
if(advanced)
- render_list += "Subject Minor Disabilities: [carbontarget.get_quirk_string(FALSE, CAT_QUIRK_MINOR_DISABILITY, TRUE)].\n"
-
- if (HAS_TRAIT(target, TRAIT_IRRADIATED))
- render_list += "Subject is irradiated. Supply toxin healing.\n"
-
- //Eyes and ears
- if(advanced && iscarbon(target))
- var/mob/living/carbon/carbontarget = target
-
- // Ear status
- var/obj/item/organ/internal/ears/ears = carbontarget.get_organ_slot(ORGAN_SLOT_EARS)
- if(istype(ears))
- if(HAS_TRAIT_FROM(carbontarget, TRAIT_DEAF, GENETIC_MUTATION))
- render_list += "Subject is genetically deaf.\n"
- else if(HAS_TRAIT_FROM(carbontarget, TRAIT_DEAF, EAR_DAMAGE))
- render_list += "Subject is deaf from ear damage.\n"
- else if(HAS_TRAIT(carbontarget, TRAIT_DEAF))
- render_list += "Subject is deaf.\n"
- else
- if(ears.damage)
- render_list += "Subject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage.\n"
- if(ears.deaf)
- render_list += "Subject is [ears.damage > ears.maxHealth ? "permanently": "temporarily"] deaf.\n"
-
- // Eye status
- var/obj/item/organ/internal/eyes/eyes = carbontarget.get_organ_slot(ORGAN_SLOT_EYES)
- if(istype(eyes))
- if(carbontarget.is_blind())
- render_list += "Subject is blind.\n"
- else if(carbontarget.is_nearsighted())
- render_list += "Subject is nearsighted.\n"
+ render_list += "Subject Minor Disabilities: [carbontarget.get_quirk_string(FALSE, CAT_QUIRK_MINOR_DISABILITY, TRUE)]. "
// Body part damage report
if(iscarbon(target))
var/mob/living/carbon/carbontarget = target
- var/list/damaged = carbontarget.get_damaged_bodyparts(1,1)
- if(length(damaged)>0 || oxy_loss>0 || tox_loss>0 || fire_loss>0)
- var/dmgreport = "General status:\
-
"
+ // Follow same body zone list every time so it's consistent across all humans
+ for(var/zone in GLOB.all_body_zones)
+ var/obj/item/bodypart/limb = carbontarget.get_bodypart(zone)
+ if(isnull(limb))
+ dmgreport += "
"
+ dmgreport += "
[capitalize(parse_zone(zone))]:
"
+ dmgreport += "
-
"
+ dmgreport += "
-
"
+ dmgreport += "
"
+ dmgreport += "
↳ Physical trauma: [conditional_tooltip("Dismembered", "Reattach or replace surgically.", tochat)]
"
render_list += dmgreport // tables do not need extra linebreak
- for(var/obj/item/bodypart/limb as anything in carbontarget.bodyparts)
- for(var/obj/item/embed as anything in limb.embedded_objects)
- render_list += "Embedded object: [embed] located in \the [limb.plaintext_zone]\n"
if(ishuman(target))
var/mob/living/carbon/human/humantarget = target
// Organ damage, missing organs
- if(humantarget.organs && humantarget.organs.len)
- var/render = FALSE
- var/toReport = "Organs:\
-
\
-
Organ:
\
- [advanced ? "
Dmg
" : ""]\
-
Status
"
-
- for(var/obj/item/organ/organ as anything in humantarget.organs)
- var/status = organ.get_status_text(advanced)
- if (status != "")
+ var/render = FALSE
+ var/toReport = "Organ status:\
+ \
+
" // less lines than in woundscan() so we don't overload people trying to get basic med info
- render_list += ""
-
- //Diseases
- for(var/datum/disease/disease as anything in target.diseases)
- if(!(disease.visibility_flags & HIDDEN_SCANNER))
- render_list += "Warning: [disease.form] detected\n\
-
Health scan report. Time of retrieval: [station_time_timestamp()]
"
+ report_text += last_scan_text
+
+ report_paper.add_raw_text(report_text)
+ report_paper.update_appearance()
+
+ if(ismob(loc))
+ var/mob/printer = loc
+ printer.put_in_hands(report_paper)
+ balloon_alert(printer, "logs cleared")
+
+ report_text = list()
+ scanner_busy = FALSE
/proc/chemscan(mob/living/user, mob/living/target)
if(user.incapacitated)
@@ -422,12 +458,12 @@
var/datum/reagent/reagent = r
if(reagent.chemical_flags & REAGENT_INVISIBLE) //Don't show hidden chems on scanners
continue
- render_block += "[round(reagent.volume, 0.001)] units of [reagent.name][reagent.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."]\n"
+ render_block += "[round(reagent.volume, 0.001)] units of [reagent.name][reagent.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."] "
if(!length(render_block)) //If no VISIBLY DISPLAYED reagents are present, we report as if there is nothing.
- render_list += "Subject contains no reagents in their blood.\n"
+ render_list += "Subject contains no reagents in their blood. "
else
- render_list += "Subject contains the following reagents in their blood:\n"
+ render_list += "Subject contains the following reagents in their blood: "
render_list += render_block //Otherwise, we add the header, reagent readouts, and clear the readout block for use on the stomach.
render_block.Cut()
@@ -440,35 +476,35 @@
if(bit.chemical_flags & REAGENT_INVISIBLE)
continue
if(!belly.food_reagents[bit.type])
- render_block += "[round(bit.volume, 0.001)] units of [bit.name][bit.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."]\n"
+ render_block += "[round(bit.volume, 0.001)] units of [bit.name][bit.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."] "
else
var/bit_vol = bit.volume - belly.food_reagents[bit.type]
if(bit_vol > 0)
- render_block += "[round(bit_vol, 0.001)] units of [bit.name][bit.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."]\n"
+ render_block += "[round(bit_vol, 0.001)] units of [bit.name][bit.overdosed ? " - [span_boldannounce("OVERDOSING")]" : "."] "
if(!length(render_block))
- render_list += "Subject contains no reagents in their stomach.\n"
+ render_list += "Subject contains no reagents in their stomach. "
else
- render_list += "Subject contains the following reagents in their stomach:\n"
+ render_list += "Subject contains the following reagents in their stomach: "
render_list += render_block
// Addictions
if(LAZYLEN(target.mind?.active_addictions))
- render_list += "Subject is addicted to the following types of drug:\n"
+ render_list += "Subject is addicted to the following types of drug: "
for(var/datum/addiction/addiction_type as anything in target.mind.active_addictions)
- render_list += "[initial(addiction_type.name)]\n"
+ render_list += "[initial(addiction_type.name)] "
// Special eigenstasium addiction
if(target.has_status_effect(/datum/status_effect/eigenstasium))
- render_list += "Subject is temporally unstable. Stabilising agent is recommended to reduce disturbances.\n"
+ render_list += "Subject is temporally unstable. Stabilising agent is recommended to reduce disturbances. "
// Allergies
for(var/datum/quirk/quirky as anything in target.quirks)
if(istype(quirky, /datum/quirk/item_quirk/allergic))
var/datum/quirk/item_quirk/allergic/allergies_quirk = quirky
var/allergies = allergies_quirk.allergy_string
- render_list += "Subject is extremely allergic to the following chemicals:\n"
- render_list += "[allergies]\n"
+ render_list += "Subject is extremely allergic to the following chemicals: "
+ render_list += "[allergies] "
// we handled the last so we don't need handholding
to_chat(user, examine_block(jointext(render_list, "")), trailing_newline = FALSE, type = MESSAGE_TYPE_INFO)
@@ -505,7 +541,7 @@
render_list += "Warning: Physical trauma[LAZYLEN(wounded_part.wounds) > 1? "s" : ""] detected in [wounded_part.name]"
for(var/limb_wound in wounded_part.wounds)
var/datum/wound/current_wound = limb_wound
- render_list += "
")
to_chat(owner, span_boldwarning("You have five minutes to find a safe location to place down the first rift. If you take longer than five minutes to place a rift, you will be returned from whence you came."))
owner.announce_objectives()
- owner.current.playsound_local(get_turf(owner.current), 'sound/magic/demon_attack1.ogg', 80)
+ owner.current.playsound_local(get_turf(owner.current), 'sound/effects/magic/demon_attack1.ogg', 80)
/datum/antagonist/space_dragon/forge_objectives()
var/static/list/area/allowed_areas
@@ -67,12 +67,12 @@
forge_objectives()
rift_ability = new()
owner.special_role = ROLE_SPACE_DRAGON
- owner.set_assigned_role(SSjob.GetJobType(/datum/job/space_dragon))
+ owner.set_assigned_role(SSjob.get_job_type(/datum/job/space_dragon))
return ..()
/datum/antagonist/space_dragon/on_removal()
owner.special_role = null
- owner.set_assigned_role(SSjob.GetJobType(/datum/job/unassigned))
+ owner.set_assigned_role(SSjob.get_job_type(/datum/job/unassigned))
return ..()
/datum/antagonist/space_dragon/apply_innate_effects(mob/living/mob_override)
@@ -142,7 +142,7 @@
if(riftTimer >= maxRiftTimer)
to_chat(owner.current, span_boldwarning("You've failed to summon the rift in a timely manner! You're being pulled back from whence you came!"))
destroy_rifts()
- SEND_SOUND(owner.current, sound('sound/magic/demon_dies.ogg'))
+ SEND_SOUND(owner.current, sound('sound/effects/magic/demon_dies.ogg'))
owner.current.death(/* gibbed = */ TRUE)
QDEL_NULL(owner.current)
@@ -182,7 +182,7 @@
main_objective?.completed = TRUE
priority_announce("A large amount of lifeforms have been detected approaching [station_name()] at extreme speeds. \
Remaining crew are advised to evacuate as soon as possible.", "[command_name()] Wildlife Observations", has_important_message = TRUE)
- sound_to_playing_players('sound/creatures/space_dragon_roar.ogg', volume = 75)
+ sound_to_playing_players('sound/mobs/non-humanoids/space_dragon/space_dragon_roar.ogg', volume = 75)
for(var/obj/structure/carp_rift/rift as anything in rift_list)
rift.carp_stored = 999999
rift.time_charged = rift.max_charge
@@ -256,7 +256,7 @@
parts += "The [name] has failed!"
if(length(carp))
- parts += " The [name] was assisted by:"
+ parts += span_header(" The [name] was assisted by:")
parts += "
"
var/list/players_to_carp_taken = list()
for(var/datum/mind/carpy as anything in carp)
diff --git a/code/modules/antagonists/space_ninja/space_ninja.dm b/code/modules/antagonists/space_ninja/space_ninja.dm
index 7f88c687c12d1..b54c5f2b8b3fe 100644
--- a/code/modules/antagonists/space_ninja/space_ninja.dm
+++ b/code/modules/antagonists/space_ninja/space_ninja.dm
@@ -101,7 +101,7 @@
/datum/antagonist/ninja/greet()
. = ..()
- SEND_SOUND(owner.current, sound('sound/effects/ninja_greeting.ogg'))
+ SEND_SOUND(owner.current, sound('sound/music/antag/ninja_greeting.ogg'))
to_chat(owner.current, span_danger("I am an elite mercenary of the mighty Spider Clan!"))
to_chat(owner.current, span_warning("Surprise is my weapon. Shadows are my armor. Without them, I am nothing."))
to_chat(owner.current, span_notice("The station is located to your [dir2text(get_dir(owner.current, locate(world.maxx/2, world.maxy/2, owner.current.z)))]. A thrown ninja star will be a great way to get there."))
@@ -114,12 +114,12 @@
equip_space_ninja(owner.current)
owner.current.add_quirk(/datum/quirk/freerunning)
owner.current.add_quirk(/datum/quirk/light_step)
- owner.current.mind.set_assigned_role(SSjob.GetJobType(/datum/job/space_ninja))
+ owner.current.mind.set_assigned_role(SSjob.get_job_type(/datum/job/space_ninja))
owner.current.mind.special_role = ROLE_NINJA
return ..()
/datum/antagonist/ninja/admin_add(datum/mind/new_owner,mob/admin)
- new_owner.set_assigned_role(SSjob.GetJobType(/datum/job/space_ninja))
+ new_owner.set_assigned_role(SSjob.get_job_type(/datum/job/space_ninja))
new_owner.special_role = ROLE_NINJA
new_owner.add_antag_datum(src)
message_admins("[key_name_admin(admin)] has ninja'ed [key_name_admin(new_owner)].")
diff --git a/code/modules/antagonists/spy/spy.dm b/code/modules/antagonists/spy/spy.dm
index 2468eb27cad3f..65dba0ef2eb1e 100644
--- a/code/modules/antagonists/spy/spy.dm
+++ b/code/modules/antagonists/spy/spy.dm
@@ -30,7 +30,7 @@
if(spawn_with_objectives)
give_random_objectives()
. = ..()
- SEND_SOUND(owner.current, sound('sound/ambience/antag/spy.ogg'))
+ SEND_SOUND(owner.current, sound('sound/music/antag/spy.ogg'))
/datum/antagonist/spy/ui_static_data(mob/user)
var/list/data = ..()
diff --git a/code/modules/antagonists/spy/spy_bounty.dm b/code/modules/antagonists/spy/spy_bounty.dm
index 42ab0203e5c03..ccab7952a9c2c 100644
--- a/code/modules/antagonists/spy/spy_bounty.dm
+++ b/code/modules/antagonists/spy/spy_bounty.dm
@@ -132,6 +132,14 @@
/datum/spy_bounty/proc/clean_up_stolen_item(atom/movable/stealing, mob/living/spy)
do_sparks(3, FALSE, stealing)
+ if(isitem(stealing) && stealing.loc == spy)
+ // get it out of our inventory before we mess with it to prevent any weirdness.
+ // bypasses nodrop - if you want, add a bespoke check for that higher up the chain
+ spy.temporarilyRemoveItemFromInventory(stealing, force = TRUE)
+ // also check for DROPDEL
+ if(QDELETED(stealing))
+ return
+
// Don't mess with it while it's going away
var/had_attack_hand_interaction = stealing.interaction_flags_atom & INTERACT_ATOM_ATTACK_HAND
stealing.interaction_flags_atom &= ~INTERACT_ATOM_ATTACK_HAND
diff --git a/code/modules/antagonists/spy/spy_uplink.dm b/code/modules/antagonists/spy/spy_uplink.dm
index 5de66271fe29c..f5c60f706c588 100644
--- a/code/modules/antagonists/spy/spy_uplink.dm
+++ b/code/modules/antagonists/spy/spy_uplink.dm
@@ -23,7 +23,7 @@
/datum/component/spy_uplink/RegisterWithParent()
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, PROC_REF(on_attack_self))
- RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK_SECONDARY, PROC_REF(on_pre_attack_secondary))
+ RegisterSignal(parent, COMSIG_ITEM_INTERACTING_WITH_ATOM, PROC_REF(on_item_atom_interaction))
RegisterSignal(parent, COMSIG_TABLET_CHECK_DETONATE, PROC_REF(block_pda_bombs))
/datum/component/spy_uplink/UnregisterFromParent()
@@ -60,13 +60,15 @@
INVOKE_ASYNC(src, TYPE_PROC_REF(/datum, ui_interact), user)
return NONE
-/datum/component/spy_uplink/proc/on_pre_attack_secondary(obj/item/source, atom/target, mob/living/user, params)
+/datum/component/spy_uplink/proc/on_item_atom_interaction(obj/item/source, mob/living/user, atom/target, list/modifiers)
SIGNAL_HANDLER
if(!ismovable(target))
return NONE
if(!IS_SPY(user))
return NONE
+ if(SHOULD_SKIP_INTERACTION(target, source, user))
+ return NONE
if(!try_steal(target, user))
return NONE
return COMPONENT_CANCEL_ATTACK_CHAIN
@@ -94,11 +96,11 @@
/// Wraps the stealing process in a scanning effect.
/datum/component/spy_uplink/proc/start_stealing(atom/movable/stealing, mob/living/spy, datum/spy_bounty/bounty)
if(!isturf(stealing.loc) && stealing.loc != spy)
- to_chat(spy, span_warning("Your uplinks blinks red: [stealing] cannot be extracted from there."))
+ to_chat(spy, span_warning("Your uplink blinks red: [stealing] cannot be extracted from there."))
return FALSE
log_combat(spy, stealing, "started stealing", parent, "(spy bounty)")
- playsound(stealing, 'sound/items/pshoom.ogg', 33, vary = TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, frequency = 0.33, ignore_walls = FALSE)
+ playsound(stealing, 'sound/items/pshoom/pshoom.ogg', 33, vary = TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, frequency = 0.33, ignore_walls = FALSE)
var/obj/effect/scan_effect/active_scan_effect = new(stealing.loc)
active_scan_effect.appearance = stealing.appearance
@@ -139,10 +141,10 @@
if(!do_after(spy, bounty.theft_time, stealing, interaction_key = REF(src), hidden = TRUE))
return FALSE
if(bounty.claimed)
- to_chat(spy, span_warning("Your uplinks blinks red: The bounty for [stealing] has been claimed by another spy!"))
+ to_chat(spy, span_warning("Your uplink blinks red: The bounty for [stealing] has been claimed by another spy!"))
return FALSE
if(spy.is_holding(stealing) && !spy.dropItemToGround(stealing))
- to_chat(spy, span_warning("Your uplinks blinks red: [stealing] seems stuck to your hand!"))
+ to_chat(spy, span_warning("Your uplink blinks red: [stealing] seems stuck to your hand!"))
return FALSE
var/bounty_key = bounty.get_dupe_protection_key(stealing)
diff --git a/code/modules/antagonists/traitor/contractor/syndicate_contract.dm b/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
index 2c9d45e382dd4..71ae454df72f9 100644
--- a/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
+++ b/code/modules/antagonists/traitor/contractor/syndicate_contract.dm
@@ -102,14 +102,17 @@
if(traitor_data.uplink_handler.contractor_hub.current_contract == src)
traitor_data.uplink_handler.contractor_hub.current_contract = null
- for(var/obj/item/person_contents as anything in person_sent.gather_belongings())
+ for(var/obj/item/person_contents as anything in person_sent.gather_belongings(FALSE, FALSE))
if(ishuman(person_sent))
var/mob/living/carbon/human/human_sent = person_sent
if(person_contents == human_sent.w_uniform)
continue //So all they're left with are shoes and uniform.
if(person_contents == human_sent.shoes)
continue
- person_sent.transferItemToLoc(person_contents)
+ var/unequipped = person_sent.temporarilyRemoveItemFromInventory(person_contents)
+ if (!unequipped)
+ continue
+ person_contents.moveToNullspace()
victim_belongings.Add(WEAKREF(person_contents))
var/obj/structure/closet/supplypod/extractionpod/pod = source
diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm
index 661007d3163a6..f739245314fc2 100644
--- a/code/modules/antagonists/traitor/datum_traitor.dm
+++ b/code/modules/antagonists/traitor/datum_traitor.dm
@@ -17,7 +17,7 @@
can_assign_self_objectives = TRUE
default_custom_objective = "Perform an overcomplicated heist on valuable Nanotrasen assets."
hardcore_random_bonus = TRUE
- stinger_sound = 'sound/ambience/antag/tatoralert.ogg'
+ stinger_sound = 'sound/music/antag/traitor/tatoralert.ogg'
///The flag of uplink that this traitor is supposed to have.
var/uplink_flag_given = UPLINK_TRAITORS
@@ -366,7 +366,7 @@
result += span_greentext("The [special_role_text] was successful!")
else
result += span_redtext("The [special_role_text] has failed!")
- SEND_SOUND(owner.current, 'sound/ambience/ambifailure.ogg')
+ SEND_SOUND(owner.current, 'sound/ambience/misc/ambifailure.ogg')
return result.Join(" ")
diff --git a/code/modules/antagonists/traitor/objectives/eyesnatching.dm b/code/modules/antagonists/traitor/objectives/eyesnatching.dm
index 31dec4e812a6b..7d664b20d36cb 100644
--- a/code/modules/antagonists/traitor/objectives/eyesnatching.dm
+++ b/code/modules/antagonists/traitor/objectives/eyesnatching.dm
@@ -204,7 +204,7 @@
target.equip_to_slot_if_possible(new_patch, ITEM_SLOT_EYES, disable_warning = TRUE)
to_chat(user, span_notice("You successfully extract [target]'s eyeballs."))
- playsound(target, 'sound/surgery/retractor2.ogg', 100, TRUE)
+ playsound(target, 'sound/items/handling/surgery/retractor2.ogg', 100, TRUE)
playsound(target, 'sound/effects/pop.ogg', 100, TRAIT_MUTE)
eyeballies.Remove(target)
eyeballies.forceMove(get_turf(target))
diff --git a/code/modules/antagonists/traitor/objectives/final_objective/final_objective.dm b/code/modules/antagonists/traitor/objectives/final_objective/final_objective.dm
index cb9f4ac73aaf8..6e722b1515eb4 100644
--- a/code/modules/antagonists/traitor/objectives/final_objective/final_objective.dm
+++ b/code/modules/antagonists/traitor/objectives/final_objective/final_objective.dm
@@ -34,7 +34,7 @@
if(objective == src)
continue
objective.fail_objective()
- user.playsound_local(get_turf(user), 'sound/traitor/final_objective.ogg', vol = 100, vary = FALSE, channel = CHANNEL_TRAITOR)
+ user.playsound_local(get_turf(user), 'sound/music/antag/traitor/final_objective.ogg', vol = 100, vary = FALSE, channel = CHANNEL_TRAITOR)
handler.final_objective = name
/datum/traitor_objective/ultimate/uplink_ui_data(mob/user)
diff --git a/code/modules/antagonists/traitor/objectives/kidnapping.dm b/code/modules/antagonists/traitor/objectives/kidnapping.dm
index ea7fef9b4b607..0d4ff5cfd9971 100644
--- a/code/modules/antagonists/traitor/objectives/kidnapping.dm
+++ b/code/modules/antagonists/traitor/objectives/kidnapping.dm
@@ -228,13 +228,14 @@
var/mob/living/carbon/human/sent_mob = entered_atom
- for(var/obj/item/belonging in sent_mob.gather_belongings())
+ for(var/obj/item/belonging in sent_mob.gather_belongings(FALSE, FALSE))
if(belonging == sent_mob.get_item_by_slot(ITEM_SLOT_ICLOTHING) || belonging == sent_mob.get_item_by_slot(ITEM_SLOT_FEET))
continue
- var/unequipped = sent_mob.transferItemToLoc(belonging)
+ var/unequipped = sent_mob.temporarilyRemoveItemFromInventory(belonging)
if (!unequipped)
continue
+ belonging.moveToNullspace()
target_belongings.Add(WEAKREF(belonging))
var/datum/market_item/hostage/market_item = sent_mob.process_capture(rand(1000, 3000))
diff --git a/code/modules/antagonists/traitor/objectives/locate_weakpoint.dm b/code/modules/antagonists/traitor/objectives/locate_weakpoint.dm
index 4acfe7120489a..5492f04a0f847 100644
--- a/code/modules/antagonists/traitor/objectives/locate_weakpoint.dm
+++ b/code/modules/antagonists/traitor/objectives/locate_weakpoint.dm
@@ -178,22 +178,22 @@
var/area/user_area = get_area(user)
if(!(user_area.type in objective.scan_areas))
balloon_alert(user, "invalid area!")
- playsound(user, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(user, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
return
if(!objective.scan_areas[user_area.type])
balloon_alert(user, "already scanned here!")
- playsound(user, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(user, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
return
user.visible_message(span_danger("[user] presses a few buttons on [src] and it starts ominously beeping!"), span_notice("You activate [src] and start scanning the area. Do not exit [get_area_name(user, TRUE)] until the scan finishes!"))
- playsound(user, 'sound/machines/triple_beep.ogg', 30, TRUE)
+ playsound(user, 'sound/machines/beep/triple_beep.ogg', 30, TRUE)
var/alertstr = span_userdanger("Network Alert: Station network probing attempt detected[user_area?" in [get_area_name(user, TRUE)]":". Unable to pinpoint location"].")
for(var/mob/living/silicon/ai/ai_player in GLOB.player_list)
to_chat(ai_player, alertstr)
if(!do_after(user, 30 SECONDS, src, IGNORE_USER_LOC_CHANGE | IGNORE_TARGET_LOC_CHANGE | IGNORE_HELD_ITEM | IGNORE_INCAPACITATED | IGNORE_SLOWDOWNS, extra_checks = CALLBACK(src, PROC_REF(scan_checks), user, user_area, objective), hidden = TRUE))
- playsound(user, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(user, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
return
playsound(user, 'sound/machines/ding.ogg', 100, TRUE)
diff --git a/code/modules/antagonists/traitor/objectives/steal.dm b/code/modules/antagonists/traitor/objectives/steal.dm
index fdcd693fed889..4c697d66d57fc 100644
--- a/code/modules/antagonists/traitor/objectives/steal.dm
+++ b/code/modules/antagonists/traitor/objectives/steal.dm
@@ -255,7 +255,7 @@ GLOBAL_DATUM_INIT(steal_item_handler, /datum/objective_item_handler, new())
/obj/item/traitor_bug
name = "suspicious device"
desc = "It looks dangerous."
- item_flags = EXAMINE_SKIP
+ item_flags = EXAMINE_SKIP|NOBLUDGEON
icon = 'icons/obj/antags/syndicate_tools.dmi'
icon_state = "bug"
@@ -280,7 +280,8 @@ GLOBAL_DATUM_INIT(steal_item_handler, /datum/objective_item_handler, new())
/obj/item/traitor_bug/interact_with_atom(atom/movable/target, mob/living/user, list/modifiers)
if(!target_object_type || !ismovable(target))
return NONE
-
+ if(SHOULD_SKIP_INTERACTION(target, src, user))
+ return NONE
var/result = SEND_SIGNAL(src, COMSIG_TRAITOR_BUG_PRE_PLANTED_OBJECT, target)
if(!(result & COMPONENT_FORCE_PLACEMENT))
if(result & COMPONENT_FORCE_FAIL_PLACEMENT || !istype(target, target_object_type))
@@ -315,6 +316,3 @@ GLOBAL_DATUM_INIT(steal_item_handler, /datum/objective_item_handler, new())
anchored = FALSE
UnregisterSignal(planted_on, COMSIG_QDELETING)
planted_on = null
-
-/obj/item/traitor_bug/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/user)
- return !istype(storage_holder, target_object_type)
diff --git a/code/modules/antagonists/traitor/traitor_objective.dm b/code/modules/antagonists/traitor/traitor_objective.dm
index 3e13340157334..ecfebaddeadcb 100644
--- a/code/modules/antagonists/traitor/traitor_objective.dm
+++ b/code/modules/antagonists/traitor/traitor_objective.dm
@@ -216,10 +216,10 @@
/datum/traitor_objective/proc/finish_objective(mob/user)
switch(objective_state)
if(OBJECTIVE_STATE_FAILED, OBJECTIVE_STATE_INVALID)
- user.playsound_local(get_turf(user), 'sound/traitor/objective_failed.ogg', vol = 100, vary = FALSE, channel = CHANNEL_TRAITOR)
+ user.playsound_local(get_turf(user), 'sound/music/antag/traitor/objective_failed.ogg', vol = 100, vary = FALSE, channel = CHANNEL_TRAITOR)
return TRUE
if(OBJECTIVE_STATE_COMPLETED)
- user.playsound_local(get_turf(user), 'sound/traitor/objective_success.ogg', vol = 100, vary = FALSE, channel = CHANNEL_TRAITOR)
+ user.playsound_local(get_turf(user), 'sound/music/antag/traitor/objective_success.ogg', vol = 100, vary = FALSE, channel = CHANNEL_TRAITOR)
completion_payout()
return TRUE
return FALSE
diff --git a/code/modules/antagonists/traitor/uplink_handler.dm b/code/modules/antagonists/traitor/uplink_handler.dm
index 2d27f3c4a0eff..83899300614e5 100644
--- a/code/modules/antagonists/traitor/uplink_handler.dm
+++ b/code/modules/antagonists/traitor/uplink_handler.dm
@@ -255,7 +255,7 @@
if(!(to_take in potential_objectives))
return
- user.playsound_local(get_turf(user), 'sound/traitor/objective_taken.ogg', vol = 100, vary = FALSE, channel = CHANNEL_TRAITOR)
+ user.playsound_local(get_turf(user), 'sound/music/antag/traitor/objective_taken.ogg', vol = 100, vary = FALSE, channel = CHANNEL_TRAITOR)
to_take.on_objective_taken(user)
to_take.objective_state = OBJECTIVE_STATE_ACTIVE
potential_objectives -= to_take
diff --git a/code/modules/antagonists/voidwalker/voidwalker_loot.dm b/code/modules/antagonists/voidwalker/voidwalker_loot.dm
index 73ed9c7cd2207..11a51c8a5b2f1 100644
--- a/code/modules/antagonists/voidwalker/voidwalker_loot.dm
+++ b/code/modules/antagonists/voidwalker/voidwalker_loot.dm
@@ -35,7 +35,7 @@
starer.gain_trauma(/datum/brain_trauma/voided/stable)
to_chat(user, span_purple("And a whole world opens up to you."))
- playsound(get_turf(user), 'sound/effects/curse5.ogg', 60)
+ playsound(get_turf(user), 'sound/effects/curse/curse5.ogg', 60)
uses--
if(uses <= 0 )
diff --git a/code/modules/antagonists/voidwalker/voidwalker_void_eater.dm b/code/modules/antagonists/voidwalker/voidwalker_void_eater.dm
index 9df3eabab3d21..9bf5b3c2664c3 100644
--- a/code/modules/antagonists/voidwalker/voidwalker_void_eater.dm
+++ b/code/modules/antagonists/voidwalker/voidwalker_void_eater.dm
@@ -16,7 +16,7 @@
resistance_flags = INDESTRUCTIBLE | ACID_PROOF | FIRE_PROOF | LAVA_PROOF | UNACIDABLE
w_class = WEIGHT_CLASS_HUGE
tool_behaviour = TOOL_MINING
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
wound_bonus = -30
bare_wound_bonus = 20
diff --git a/code/modules/antagonists/wizard/equipment/artefact.dm b/code/modules/antagonists/wizard/equipment/artefact.dm
index 9176558c7a166..1036abc24955f 100644
--- a/code/modules/antagonists/wizard/equipment/artefact.dm
+++ b/code/modules/antagonists/wizard/equipment/artefact.dm
@@ -17,7 +17,7 @@
force = 15
throwforce = 10
w_class = WEIGHT_CLASS_NORMAL
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
var/charges = 1
var/spawn_type = /obj/tear_in_reality
var/spawn_amt = 1
@@ -171,7 +171,7 @@
throwforce = 15
damtype = BURN
force = 15
- hitsound = 'sound/items/welder2.ogg'
+ hitsound = 'sound/items/tools/welder2.ogg'
var/mob/current_owner
@@ -335,7 +335,7 @@
whistler = user
var/turf/current_turf = get_turf(user)
var/turf/spawn_location = locate(user.x + pick(-7, 7), user.y, user.z)
- playsound(current_turf,'sound/magic/warpwhistle.ogg', 200, TRUE)
+ playsound(current_turf,'sound/effects/magic/warpwhistle.ogg', 200, TRUE)
new /obj/effect/temp_visual/teleporting_tornado(spawn_location, src)
///Teleporting tornado, spawned by warp whistle, teleports the user if they manage to pick them up.
@@ -431,10 +431,10 @@
COMSIG_ITEM_MAGICALLY_CHARGED = PROC_REF(on_magic_charge),
)
-/obj/item/runic_vendor_scepter/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- return interact_with_atom(interacting_with, user, modifiers)
-
/obj/item/runic_vendor_scepter/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ return ranged_interact_with_atom(interacting_with, user, modifiers)
+
+/obj/item/runic_vendor_scepter/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(scepter_is_busy_recharging)
user.balloon_alert(user, "busy!")
return ITEM_INTERACT_BLOCKING
@@ -472,7 +472,7 @@
return ITEM_INTERACT_BLOCKING
scepter_is_busy_summoning = FALSE
if(summon_vendor_charges)
- playsound(src,'sound/weapons/resonator_fire.ogg',50,TRUE)
+ playsound(src,'sound/items/weapons/resonator_fire.ogg',50,TRUE)
user.visible_message(span_warning("[user] summons a runic vendor!"))
new /obj/machinery/vending/runic_vendor(afterattack_turf)
summon_vendor_charges--
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index f8f5cf22350a7..751873c836277 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -92,7 +92,7 @@
if(IS_CULTIST(exorcist) || theme == THEME_HOLY)
return
balloon_alert(exorcist, "exorcising...")
- playsound(src, 'sound/hallucinations/veryfar_noise.ogg', 40, TRUE)
+ playsound(src, 'sound/effects/hallucinations/veryfar_noise.ogg', 40, TRUE)
if(!do_after(exorcist, 4 SECONDS, target = src))
return
playsound(src, 'sound/effects/pray_chaplain.ogg', 60, TRUE)
@@ -224,21 +224,21 @@
shade_datum.release_time = world.time
on_release_spirits()
-/obj/item/soulstone/pre_attack(atom/A, mob/living/user, params)
- var/mob/living/basic/shade/occupant = (locate() in src)
- var/obj/item/storage/toolbox/mechanical/target_toolbox = A
- if(!occupant || !istype(target_toolbox) || target_toolbox.has_soul)
- return ..()
+/obj/item/soulstone/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ var/mob/living/basic/shade/occupant = locate() in src
+ var/obj/item/storage/toolbox/mechanical/target_toolbox = interacting_with
+ if(isnull(occupant) || !istype(target_toolbox) || target_toolbox.has_soul)
+ return NONE
if(theme == THEME_HOLY && IS_CULTIST(user))
hot_potato(user)
- return
+ return ITEM_INTERACT_BLOCKING
if(!role_check(user))
user.Unconscious(10 SECONDS)
to_chat(user, span_userdanger("Your body is wracked with debilitating pain!"))
- return
+ return ITEM_INTERACT_BLOCKING
- user.visible_message("[user] holds [src] above [user.p_their()] head and forces it into [target_toolbox] with a flash of light!", \
+ user.visible_message(span_notice("[user] holds [src] above [user.p_their()] head and forces it into [target_toolbox] with a flash of light!"), \
span_notice("You hold [src] above your head briefly, then force it into [target_toolbox], transferring the [occupant]'s soul!"), ignored_mobs = occupant)
to_chat(occupant, span_userdanger("[user] holds you up briefly, then forces you into [target_toolbox]!"))
to_chat(occupant, span_deadsay("Your eternal soul has been sacrificed to restore the soul of a toolbox. Them's the breaks!"))
@@ -253,6 +253,7 @@
target_toolbox.icon_state = "toolbox_blue_old"
target_toolbox.has_soul = TRUE
target_toolbox.has_latches = FALSE
+ return ITEM_INTERACT_SUCCESS
///////////////////////////Transferring to constructs/////////////////////////////////////////////////////
/obj/structure/constructshell
@@ -260,11 +261,11 @@
icon = 'icons/mob/shells.dmi'
icon_state = "construct_cult"
desc = "A wicked machine used by those skilled in magical arts. It is inactive."
- var/extra_desc = {"A construct shell, used to house bound souls from a soulstone.\n
- Placing a soulstone with a soul into this shell allows you to produce your choice of the following:\n
- An Artificer, which can produce more shells and soulstones, as well as fortifications.\n
- A Wraith, which does high damage and can jaunt through walls, though it is quite fragile.\n
- A Juggernaut, which is very hard to kill and can produce temporary walls, but is slow."}
+ var/extra_desc = span_cult("A construct shell, used to house bound souls from a soulstone.\n\
+ Placing a soulstone with a soul into this shell allows you to produce your choice of the following:\n\
+ An Artificer, which can produce more shells and soulstones, as well as fortifications.\n\
+ A Wraith, which does high damage and can jaunt through walls, though it is quite fragile.\n\
+ A Juggernaut, which is very hard to kill and can produce temporary walls, but is slow.")
/obj/structure/constructshell/examine(mob/user)
. = ..()
diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm
index fe5f69fd9fa53..737a3d73403d1 100644
--- a/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm
+++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm
@@ -28,7 +28,7 @@
/datum/spellbook_entry/summon/guns/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE)
summon_guns(user, 10)
- playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE)
+ playsound(get_turf(user), 'sound/effects/magic/castsummon.ogg', 50, TRUE)
return ..()
/datum/spellbook_entry/summon/magic
@@ -45,7 +45,7 @@
/datum/spellbook_entry/summon/magic/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE)
summon_magic(user, 10)
- playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE)
+ playsound(get_turf(user), 'sound/effects/magic/castsummon.ogg', 50, TRUE)
return ..()
/datum/spellbook_entry/summon/events
@@ -65,7 +65,7 @@
/datum/spellbook_entry/summon/events/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE)
summon_events(user)
- playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE)
+ playsound(get_turf(user), 'sound/effects/magic/castsummon.ogg', 50, TRUE)
return ..()
/datum/spellbook_entry/summon/curse_of_madness
@@ -74,11 +74,11 @@
cost = 4
/datum/spellbook_entry/summon/curse_of_madness/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book, log_buy = TRUE)
- var/message = tgui_input_text(user, "Whisper a secret truth to drive your victims to madness", "Whispers of Madness")
+ var/message = tgui_input_text(user, "Whisper a secret truth to drive your victims to madness", "Whispers of Madness", max_length = MAX_MESSAGE_LEN)
if(!message || QDELETED(user) || QDELETED(book) || !can_buy(user, book))
return FALSE
curse_of_madness(user, message)
- playsound(user, 'sound/magic/mandswap.ogg', 50, TRUE)
+ playsound(user, 'sound/effects/magic/mandswap.ogg', 50, TRUE)
return ..()
/// A wizard ritual that allows the wizard to teach a specific spellbook enty to everyone on the station.
diff --git a/code/modules/antagonists/wizard/equipment/teleport_rod.dm b/code/modules/antagonists/wizard/equipment/teleport_rod.dm
index c15b66da6ff61..e0a5ce31145c6 100644
--- a/code/modules/antagonists/wizard/equipment/teleport_rod.dm
+++ b/code/modules/antagonists/wizard/equipment/teleport_rod.dm
@@ -85,7 +85,7 @@
. = ITEM_INTERACT_SUCCESS
- var/sound/teleport_sound = sound('sound/magic/summonitems_generic.ogg')
+ var/sound/teleport_sound = sound('sound/effects/magic/summonitems_generic.ogg')
teleport_sound.pitch = 0.5
// Handle our own pizzaz rather than doing it in do_teleport
new /obj/effect/temp_visual/teleport_flux(start_turf, user.dir)
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/all_access.dm b/code/modules/antagonists/wizard/grand_ritual/finales/all_access.dm
index ab699e74064de..1ff93db81938b 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/all_access.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/all_access.dm
@@ -14,4 +14,4 @@
target_door.req_one_access = list()
INVOKE_ASYNC(target_door, TYPE_PROC_REF(/obj/machinery/door/airlock, open))
CHECK_TICK
- priority_announce("AULIE OXIN FIERA!!", null, 'sound/magic/knock.ogg', sender_override = "[invoker.real_name]", color_override = "purple")
+ priority_announce("AULIE OXIN FIERA!!", null, 'sound/effects/magic/knock.ogg', sender_override = "[invoker.real_name]", color_override = "purple")
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm b/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm
index a951a5daf4223..3716a684c6a3d 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/armageddon.dm
@@ -31,14 +31,14 @@
)
/datum/grand_finale/armageddon/trigger(mob/living/carbon/human/invoker)
- priority_announce(pick(possible_last_words), null, 'sound/magic/voidblink.ogg', sender_override = "[invoker.real_name]", color_override = "purple")
+ priority_announce(pick(possible_last_words), null, 'sound/effects/magic/voidblink.ogg', sender_override = "[invoker.real_name]", color_override = "purple")
var/turf/current_location = get_turf(invoker)
invoker.gib(DROP_ALL_REMAINS)
var/static/list/doom_options = list()
if (!length(doom_options))
doom_options = list(DOOM_SINGULARITY, DOOM_TESLA)
- if (!SSmapping.config.planetary)
+ if (!SSmapping.is_planetary())
doom_options += DOOM_METEORS
switch(pick(doom_options))
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/captaincy.dm b/code/modules/antagonists/wizard/grand_ritual/finales/captaincy.dm
index d1a3c1afaf758..237fdfe63e738 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/captaincy.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/captaincy.dm
@@ -9,7 +9,7 @@
message_admins("[key_name(invoker)] has replaced the Captain")
var/list/former_captains = list()
var/list/other_crew = list()
- SEND_SOUND(world, sound('sound/magic/timeparadox2.ogg'))
+ SEND_SOUND(world, sound('sound/effects/magic/timeparadox2.ogg'))
for (var/mob/living/carbon/human/crewmate as anything in GLOB.human_list)
if (!crewmate.mind)
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/cheese.dm b/code/modules/antagonists/wizard/grand_ritual/finales/cheese.dm
index 5cdd770486cd5..f0b8ef709aee8 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/cheese.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/cheese.dm
@@ -10,7 +10,7 @@
/datum/grand_finale/cheese/trigger(mob/living/invoker)
message_admins("[key_name(invoker)] has summoned forth The Wabbajack and cursed the crew with madness!")
- priority_announce("Danger: Extremely potent reality altering object has been summoned on station. Immediate evacuation advised. Brace for impact.", "[command_name()] Higher Dimensional Affairs", 'sound/effects/glassbr1.ogg')
+ priority_announce("Danger: Extremely potent reality altering object has been summoned on station. Immediate evacuation advised. Brace for impact.", "[command_name()] Higher Dimensional Affairs", 'sound/effects/glass/glassbr1.ogg')
for (var/mob/living/carbon/human/crewmate as anything in GLOB.human_list)
if (isnull(crewmate.mind))
diff --git a/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm b/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm
index e0d4f3376f80a..436383975d131 100644
--- a/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/finales/immortality.dm
@@ -19,7 +19,7 @@
/datum/grand_finale/immortality/trigger(mob/living/carbon/human/invoker)
new /obj/effect/temp_visual/immortality_blast(get_turf(invoker))
- SEND_SOUND(world, sound('sound/magic/teleport_diss.ogg'))
+ SEND_SOUND(world, sound('sound/effects/magic/teleport_diss.ogg'))
for (var/mob/living/alive_guy as anything in GLOB.mob_living_list)
new /obj/effect/temp_visual/immortality_pulse(get_turf(alive_guy))
if (!alive_guy.mind)
diff --git a/code/modules/antagonists/wizard/grand_ritual/grand_ritual.dm b/code/modules/antagonists/wizard/grand_ritual/grand_ritual.dm
index a2f25863a4baf..e5611411a67e8 100644
--- a/code/modules/antagonists/wizard/grand_ritual/grand_ritual.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/grand_ritual.dm
@@ -191,7 +191,7 @@
possible_obstacle.atom_destruction("magic")
if (evaporated_obstacles)
- playsound(target_turf, 'sound/magic/blind.ogg', 100, TRUE)
+ playsound(target_turf, 'sound/effects/magic/blind.ogg', 100, TRUE)
target_turf.balloon_alert(owner, "rune created")
var/obj/effect/grand_rune/new_rune = new next_rune_typepath(target_turf, times_completed)
@@ -299,8 +299,8 @@
pixel_y = 16
pixel_z = -48
anchored = TRUE
- layer = SIGIL_LAYER
- plane = GAME_PLANE
+ plane = FLOOR_PLANE
+ layer = RUNE_LAYER
duration = 0 SECONDS
/obj/effect/temp_visual/wizard_rune/Initialize(mapload)
diff --git a/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm b/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
index 15900a6ac0b0a..6d08cd539fed5 100644
--- a/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/grand_rune.dm
@@ -21,7 +21,8 @@
anchored = TRUE
interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_ATTACK_PAW
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
- layer = SIGIL_LAYER
+ plane = FLOOR_PLANE
+ layer = RUNE_LAYER
/// How many prior grand rituals have been completed?
var/potency = 0
/// Time to take per invocation of rune.
@@ -147,7 +148,7 @@
haunt_color = spell_colour, \
haunt_duration = 10 SECONDS, \
aggro_radius = 0, \
- spawn_message = span_revenwarning("[sacrifice] begins to float and twirl into the air as it becomes enveloped in otherworldy energies..."), \
+ spawn_message = span_revenwarning("[sacrifice] begins to float and twirl into the air as it becomes enveloped in otherworldly energies..."), \
)
addtimer(CALLBACK(sacrifice, TYPE_PROC_REF(/obj/item/food/cheese/wheel, consume_cheese)), 10 SECONDS)
cheese_sacrificed += length(cheese_to_haunt)
@@ -162,7 +163,7 @@
on_invocation_complete(user)
return
flick("[icon_state]_flash", src)
- playsound(src,'sound/magic/staff_animation.ogg', 75, TRUE)
+ playsound(src,'sound/effects/magic/staff_animation.ogg', 75, TRUE)
INVOKE_ASYNC(src, PROC_REF(invoke_rune), user)
/// Add special effects for casting a spell, basically you glow and hover in the air.
@@ -181,7 +182,7 @@
/// Called when you actually finish the damn thing
/obj/effect/grand_rune/proc/on_invocation_complete(mob/living/user)
is_in_use = FALSE
- playsound(src,'sound/magic/staff_change.ogg', 75, TRUE)
+ playsound(src,'sound/effects/magic/staff_change.ogg', 75, TRUE)
INVOKE_ASYNC(src, PROC_REF(summon_round_event), user) // Running the event sleeps
trigger_side_effects()
tear_reality()
@@ -253,7 +254,7 @@
while(created < to_create && location_sanity < 100)
var/turf/chosen_location = get_safe_random_station_turf()
- // We don't want them close to each other - at least 1 tile of seperation
+ // We don't want them close to each other - at least 1 tile of separation
var/list/nearby_things = range(1, chosen_location)
var/obj/effect/heretic_influence/what_if_i_have_one = locate() in nearby_things
var/obj/effect/visible_heretic_influence/what_if_i_had_one_but_its_used = locate() in nearby_things
@@ -393,7 +394,7 @@
mergeable_decal = FALSE
resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
clean_type = CLEAN_TYPE_HARD_DECAL
- layer = SIGIL_LAYER
+ layer = RUNE_LAYER
/obj/effect/decal/cleanable/grand_remains/cheese
name = "cheese soot marks"
diff --git a/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm b/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm
index a760115a61b9e..52c4056ee01fa 100644
--- a/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm
+++ b/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm
@@ -38,7 +38,7 @@
abstract = FALSE
/datum/grand_side_effect/scramble_turfs/trigger(potency, turf/ritual_location, mob/invoker)
- playsound(ritual_location, 'sound/magic/timeparadox2.ogg', 60, TRUE)
+ playsound(ritual_location, 'sound/effects/magic/timeparadox2.ogg', 60, TRUE)
var/datum/action/cooldown/spell/spell = new /datum/action/cooldown/spell/spacetime_dist()
spell.cast(ritual_location)
@@ -200,7 +200,7 @@
#define CREWMATE_SUMMON_TELEPORT_DELAY 9 SECONDS
/datum/grand_side_effect/summon_crewmate/trigger(potency, turf/ritual_location, mob/invoker)
- playsound(ritual_location, 'sound/magic/lightning_chargeup.ogg', 65, TRUE)
+ playsound(ritual_location, 'sound/effects/magic/lightning_chargeup.ogg', 65, TRUE)
var/list/potential_victims = list()
var/area/our_area = get_area(ritual_location)
for (var/mob/living/carbon/human/crewmate as anything in GLOB.human_list)
@@ -218,7 +218,7 @@
new /obj/effect/temp_visual/teleport_abductor(landing_pos)
var/mob/living/carbon/human/victim = pick(potential_victims)
- playsound(get_turf(victim),'sound/magic/repulse.ogg', 60, TRUE)
+ playsound(get_turf(victim),'sound/effects/magic/repulse.ogg', 60, TRUE)
victim.Immobilize(CREWMATE_SUMMON_TELEPORT_DELAY)
victim.AddElement(/datum/element/forced_gravity, 0)
victim.add_filter("teleport_glow", 2, list("type" = "outline", "color" = "#de3aff48", "size" = 2))
@@ -244,7 +244,7 @@
abstract = FALSE
/datum/grand_side_effect/smoke/trigger(potency, turf/ritual_location, mob/invoker)
- playsound(src, 'sound/magic/smoke.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/magic/smoke.ogg', 50, TRUE)
var/range = LERP(2, 4, potency/GRAND_RITUAL_FINALE_COUNT)
var/datum/effect_system/fluid_spread/smoke/colourful/smoke = new
smoke.set_up(range, holder = ritual_location, location = ritual_location)
@@ -383,7 +383,7 @@
return
if (!mob_path)
return
- playsound(get_turf(src),'sound/magic/teleport_app.ogg', 60, TRUE)
+ playsound(get_turf(src),'sound/effects/magic/teleport_app.ogg', 60, TRUE)
do_sparks(5, FALSE, loc)
new mob_path(loc)
diff --git a/code/modules/antagonists/wizard/wizard.dm b/code/modules/antagonists/wizard/wizard.dm
index 335c934a32052..d0c99f03bbf06 100644
--- a/code/modules/antagonists/wizard/wizard.dm
+++ b/code/modules/antagonists/wizard/wizard.dm
@@ -15,6 +15,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
can_assign_self_objectives = TRUE
default_custom_objective = "Demonstrate your incredible and destructive magical powers."
hardcore_random_bonus = TRUE
+
var/give_objectives = TRUE
var/strip = TRUE //strip before equipping
var/allow_rename = TRUE
@@ -124,7 +125,7 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
if(!owner.current)
return
if(!GLOB.wizardstart.len)
- SSjob.SendToLateJoin(owner.current)
+ SSjob.send_to_late_join(owner.current)
to_chat(owner, "HOT INSERTION, GO GO GO")
owner.current.forceMove(pick(GLOB.wizardstart))
@@ -443,10 +444,10 @@ GLOBAL_LIST_EMPTY(wizard_spellbook_purchases_by_key)
/datum/team/wizard/roundend_report()
var/list/parts = list()
- parts += "Wizards/witches of [master_wizard.owner.name] team were:"
+ parts += span_header("Wizards/witches of [master_wizard.owner.name] team were:")
parts += master_wizard.roundend_report()
parts += " "
- parts += "[master_wizard.owner.name] apprentices and minions were:"
+ parts += span_header("[master_wizard.owner.name] apprentices and minions were:")
parts += printplayerlist(members - master_wizard.owner)
return "
[parts.Join(" ")]
"
diff --git a/code/modules/antagonists/xeno/xeno.dm b/code/modules/antagonists/xeno/xeno.dm
index 59c83fd52ed11..b60b649857877 100644
--- a/code/modules/antagonists/xeno/xeno.dm
+++ b/code/modules/antagonists/xeno/xeno.dm
@@ -11,7 +11,7 @@
//Simply lists them.
/datum/team/xeno/roundend_report()
var/list/parts = list()
- parts += "The [name] were:"
+ parts += span_header("The [name] were:")
parts += printplayerlist(members)
return "
[parts.Join(" ")]
"
@@ -109,7 +109,7 @@
var/escape_count = 0 //counts the number of xenomorphs that were born in captivity who ended the round outside of it
var/captive_count = 0 //counts the number of xenomorphs born in captivity who remained there until the end of the round (losers)
- parts += "The [name] were: "
+ parts += span_header("The [name] were: ")
if(check_captivity(progenitor.current) == CAPTIVE_XENO_PASS)
parts += span_greentext("The progenitor of this hive was [progenitor.key], as [progenitor], who successfully escaped captivity!") + " "
@@ -160,7 +160,7 @@
else
mind.add_antag_datum(/datum/antagonist/xeno)
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/xenomorph))
+ mind.set_assigned_role(SSjob.get_job_type(/datum/job/xenomorph))
mind.special_role = ROLE_ALIEN
/mob/living/carbon/alien/on_wabbajacked(mob/living/new_mob)
@@ -170,7 +170,7 @@
if(isalien(new_mob))
return
mind.remove_antag_datum(/datum/antagonist/xeno)
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/unassigned))
+ mind.set_assigned_role(SSjob.get_job_type(/datum/job/unassigned))
mind.special_role = null
#undef CAPTIVE_XENO_DEAD
diff --git a/code/modules/art/paintings.dm b/code/modules/art/paintings.dm
index 2e330d31185da..1c11e0f8086cc 100644
--- a/code/modules/art/paintings.dm
+++ b/code/modules/art/paintings.dm
@@ -417,7 +417,7 @@
/obj/item/canvas/proc/try_rename(mob/user)
if(painting_metadata.loaded_from_json) // No renaming old paintings
return TRUE
- var/new_name = tgui_input_text(user, "What do you want to name the painting?", "Title Your Masterpiece", null, MAX_NAME_LEN)
+ var/new_name = tgui_input_text(user, "What do you want to name the painting?", "Title Your Masterpiece", max_length = MAX_NAME_LEN)
new_name = reject_bad_name(new_name, allow_numbers = TRUE, ascii_only = FALSE, strict = TRUE, cap_after_symbols = FALSE)
if(isnull(new_name))
return FALSE
diff --git a/code/modules/art/statues.dm b/code/modules/art/statues.dm
index fd64d212f3e80..eeb0cfa9cb432 100644
--- a/code/modules/art/statues.dm
+++ b/code/modules/art/statues.dm
@@ -36,7 +36,7 @@
/obj/structure/statue/attackby(obj/item/W, mob/living/user, params)
add_fingerprint(user)
if(W.tool_behaviour == TOOL_WELDER)
- if(!W.tool_start_check(user, amount=1))
+ if(!W.tool_start_check(user, amount=1, heat_required = HIGH_TEMPERATURE_REQUIRED))
return FALSE
user.balloon_alert(user, "slicing apart...")
if(W.use_tool(src, user, 40, volume=50))
@@ -277,10 +277,10 @@
custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*0.75)
attack_verb_continuous = list("stabs")
attack_verb_simple = list("stab")
- hitsound = 'sound/weapons/bladeslice.ogg'
- usesound = list('sound/effects/picaxe1.ogg', 'sound/effects/picaxe2.ogg', 'sound/effects/picaxe3.ogg')
- drop_sound = 'sound/items/handling/screwdriver_drop.ogg'
- pickup_sound = 'sound/items/handling/screwdriver_pickup.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
+ usesound = list('sound/effects/pickaxe/picaxe1.ogg', 'sound/effects/pickaxe/picaxe2.ogg', 'sound/effects/pickaxe/picaxe3.ogg')
+ drop_sound = 'sound/items/handling/tools/screwdriver_drop.ogg'
+ pickup_sound = 'sound/items/handling/tools/screwdriver_pickup.ogg'
sharpness = SHARP_POINTY
tool_behaviour = TOOL_RUSTSCRAPER
toolspeed = 3 // You're gonna have a bad time
@@ -348,7 +348,7 @@ Moving interrupts
//How long whole process takes
var/sculpting_time = 30 SECONDS
//Single interruptible progress period
- var/sculpting_period = round(sculpting_time / world.icon_size) //this is just so it reveals pixels line by line for each.
+ var/sculpting_period = round(sculpting_time / ICON_SIZE_Y) //this is just so it reveals pixels line by line for each.
var/interrupted = FALSE
var/remaining_time = sculpting_time - (prepared_block.completion * sculpting_time)
@@ -473,7 +473,7 @@ Moving interrupts
return FALSE
//No big icon things
var/list/icon_dimensions = get_icon_dimensions(target.icon)
- if(icon_dimensions["width"] != world.icon_size || icon_dimensions["height"] != world.icon_size)
+ if(icon_dimensions["width"] != ICON_SIZE_X || icon_dimensions["height"] != ICON_SIZE_Y)
user.balloon_alert(user, "sculpt target is too big!")
return FALSE
return TRUE
@@ -509,7 +509,7 @@ Moving interrupts
remove_filter("partial_uncover")
target_appearance_with_filters = null
else
- var/mask_offset = min(world.icon_size,round(completion * world.icon_size))
+ var/mask_offset = min(ICON_SIZE_Y,round(completion * ICON_SIZE_Y))
remove_filter("partial_uncover")
add_filter("partial_uncover", 1, alpha_mask_filter(icon = white, y = -mask_offset))
target_appearance_with_filters.filters = filter(type="alpha",icon=white,y=-mask_offset,flags=MASK_INVERSE)
diff --git a/code/modules/assembly/flash.dm b/code/modules/assembly/flash.dm
index b81eb26e7fae9..6e448637dbaa4 100644
--- a/code/modules/assembly/flash.dm
+++ b/code/modules/assembly/flash.dm
@@ -109,7 +109,7 @@
if(burnt_out || (world.time < last_trigger + cooldown))
return FALSE
last_trigger = world.time
- playsound(src, 'sound/weapons/flash.ogg', 100, TRUE)
+ playsound(src, 'sound/items/weapons/flash.ogg', 100, TRUE)
set_light_on(TRUE)
addtimer(CALLBACK(src, PROC_REF(flash_end)), FLASH_LIGHT_DURATION, TIMER_OVERRIDE|TIMER_UNIQUE)
times_used++
@@ -128,7 +128,7 @@
/**
* Handles actual flashing part of the attack
*
- * This proc is awful in every sense of the way, someone should definately refactor this whole code.
+ * This proc is awful in every sense of the way, someone should definitely refactor this whole code.
* Arguments:
* * M - Victim
* * user - Attacker
@@ -161,7 +161,7 @@
else if(sigreturn & DEVIATION_OVERRIDE_NONE)
deviation = DEVIATION_NONE
- //If you face away from someone they shouldnt notice any effects.
+ //If you face away from someone they shouldn't notice any effects.
if(deviation == DEVIATION_FULL)
return
@@ -185,7 +185,7 @@
/**
* Handles the directionality of the attack
*
- * Returns the amount of 'deviation', 0 being facing eachother, 1 being sideways, 2 being facing away from eachother.
+ * Returns the amount of 'deviation', 0 being facing each other, 1 being sideways, 2 being facing away from each other.
* Arguments:
* * victim - Victim
* * attacker - Attacker
@@ -328,7 +328,7 @@
return FALSE
overheat = TRUE
addtimer(CALLBACK(src, PROC_REF(cooldown)), flashcd)
- playsound(src, 'sound/weapons/flash.ogg', 100, TRUE)
+ playsound(src, 'sound/items/weapons/flash.ogg', 100, TRUE)
update_icon(ALL, TRUE)
return TRUE
diff --git a/code/modules/assembly/health.dm b/code/modules/assembly/health.dm
index 1f918888610da..ad2c6ac17641d 100644
--- a/code/modules/assembly/health.dm
+++ b/code/modules/assembly/health.dm
@@ -58,8 +58,8 @@
//do the pulse & the scan
pulse()
- audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*")
- playsound(src, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
+ audible_message(span_infoplain("[icon2html(src, hearers(src))] *beep* *beep* *beep*"))
+ playsound(src, 'sound/machines/beep/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
toggle_scan()
/obj/item/assembly/health/proc/toggle_scan()
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index addbad2704e1b..f0e9b5136ee8d 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -164,7 +164,7 @@
message = span_infoplain("[icon2html(src, hearers(holder || src))] *beep* *beep* *beep*"),
hearing_distance = hearing_range,
)
- playsound(src, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE, extrarange = hearing_range - SOUND_RANGE + 1, falloff_distance = hearing_range)
+ playsound(src, 'sound/machines/beep/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE, extrarange = hearing_range - SOUND_RANGE + 1, falloff_distance = hearing_range)
COOLDOWN_START(src, next_activate, 3 SECONDS)
/obj/item/assembly/infra/activate()
@@ -255,15 +255,15 @@
var/x_move = 0
var/y_move = 0
if(movement_dir & NORTH)
- y_move = -32
+ y_move = -ICON_SIZE_Y
else if(movement_dir & SOUTH)
- y_move = 32
+ y_move = ICON_SIZE_Y
if(movement_dir & WEST)
- x_move = 32
+ x_move = ICON_SIZE_X
else if(movement_dir & EAST)
- x_move = -32
+ x_move = -ICON_SIZE_X
- var/fake_glide_time = round(world.icon_size / glide_size * world.tick_lag, world.tick_lag)
+ var/fake_glide_time = round(ICON_SIZE_ALL / glide_size * world.tick_lag, world.tick_lag)
for(var/obj/effect/ebeam/beam as anything in active_beam?.elements)
var/matrix/base_transform = matrix(beam.transform)
beam.transform = beam.transform.Translate(x_move, y_move)
diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm
index 5c7f5208254f0..69436f7985dbf 100644
--- a/code/modules/assembly/mousetrap.dm
+++ b/code/modules/assembly/mousetrap.dm
@@ -89,7 +89,7 @@
to_chat(user, span_warning("Your hand slips, setting off the trigger!"))
pulse()
update_appearance()
- playsound(loc, 'sound/weapons/handcuffs.ogg', 30, TRUE, -3)
+ playsound(loc, 'sound/items/weapons/handcuffs.ogg', 30, TRUE, -3)
/obj/item/assembly/mousetrap/update_icon_state()
icon_state = "mousetrap[armed ? "armed" : ""]"
@@ -174,7 +174,7 @@
to_chat(user, span_notice("You disarm [src]."))
armed = !armed
update_appearance()
- playsound(src, 'sound/weapons/handcuffs.ogg', 30, TRUE, -3)
+ playsound(src, 'sound/items/weapons/handcuffs.ogg', 30, TRUE, -3)
// Clumsy check only
diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm
index 61153738ee714..6ba2a7a63421e 100644
--- a/code/modules/assembly/proximity.dm
+++ b/code/modules/assembly/proximity.dm
@@ -92,9 +92,9 @@
return FALSE
next_activate = world.time + (3 SECONDS) // this must happen before anything else
pulse()
- audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
+ audible_message(span_infoplain("[icon2html(src, hearers(src))] *beep* *beep* *beep*"), null, hearing_range)
for(var/mob/hearing_mob in get_hearers_in_view(hearing_range, src))
- hearing_mob.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
+ hearing_mob.playsound_local(get_turf(src), 'sound/machines/beep/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
return TRUE
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index 5af89f10ec1b4..4e265384ace24 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -46,7 +46,7 @@
user.set_suicide(TRUE)
user.adjustOxyLoss(200)//it sends an electrical pulse to their heart, killing them. or something.
user.death(FALSE)
- playsound(user, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
+ playsound(user, 'sound/machines/beep/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
qdel(src)
/obj/item/assembly/signaler/Initialize(mapload)
@@ -169,9 +169,9 @@
last_receive_signal_log = istype(holder, /obj/item/transfer_valve) ? signal.logging_data : null
pulse()
- audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
+ audible_message(span_infoplain("[icon2html(src, hearers(src))] *beep* *beep* *beep*"), null, hearing_range)
for(var/mob/hearing_mob in get_hearers_in_view(hearing_range, src))
- hearing_mob.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
+ hearing_mob.playsound_local(get_turf(src), 'sound/machines/beep/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
return TRUE
/obj/item/assembly/signaler/proc/set_frequency(new_frequency)
diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm
index fad912d42229a..09cbfd9b0dc59 100644
--- a/code/modules/assembly/timer.dm
+++ b/code/modules/assembly/timer.dm
@@ -58,7 +58,7 @@
pulse()
audible_message(span_infoplain("[icon2html(src, hearers(src))] *beep* *beep* *beep*"), null, hearing_range)
for(var/mob/hearing_mob in get_hearers_in_view(hearing_range, src))
- hearing_mob.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
+ hearing_mob.playsound_local(get_turf(src), 'sound/machines/beep/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
if(loop)
timing = TRUE
update_appearance()
diff --git a/code/modules/asset_cache/asset_list.dm b/code/modules/asset_cache/asset_list.dm
index bc302a188d825..39e9cf925da62 100644
--- a/code/modules/asset_cache/asset_list.dm
+++ b/code/modules/asset_cache/asset_list.dm
@@ -391,6 +391,11 @@ GLOBAL_LIST_EMPTY(asset_datums)
to_generate += list(args.Copy())
/datum/asset/spritesheet/proc/queuedInsert(sprite_name, icon/I, icon_state="", dir=SOUTH, frame=1, moving=FALSE)
+#ifdef UNIT_TESTS
+ if (I && icon_state && !(icon_state in icon_states(I))) // check the base icon prior to extracting the state we want
+ stack_trace("Tried to insert nonexistent icon_state '[icon_state]' from [I] into spritesheet [name] ([type])")
+ return
+#endif
I = icon(I, icon_state=icon_state, dir=dir, frame=frame, moving=moving)
if (!I || !length(icon_states(I))) // that direction or state doesn't exist
return
diff --git a/code/modules/asset_cache/assets/plumbing.dm b/code/modules/asset_cache/assets/plumbing.dm
index 73b1dfc7df57d..980a85e83b040 100644
--- a/code/modules/asset_cache/assets/plumbing.dm
+++ b/code/modules/asset_cache/assets/plumbing.dm
@@ -17,7 +17,6 @@
"synthesizer",
"reaction_chamber",
"grinder_chemical",
- "growing_vat",
"fermenter",
"pump",
"disposal",
diff --git a/code/modules/asset_cache/assets/rtd.dm b/code/modules/asset_cache/assets/rtd.dm
index 23a3b0d71b2db..66899fcb943f1 100644
--- a/code/modules/asset_cache/assets/rtd.dm
+++ b/code/modules/asset_cache/assets/rtd.dm
@@ -2,7 +2,7 @@
name = "rtd"
/datum/asset/spritesheet/rtd/create_spritesheets()
- //some tiles may share the same icon but have diffrent properties to animate that icon
+ //some tiles may share the same icon but have different properties to animate that icon
//so we keep track of what icons we registered
var/list/registered = list()
diff --git a/code/modules/asset_cache/transports/asset_transport.dm b/code/modules/asset_cache/transports/asset_transport.dm
index 3dbcc301843cc..62ca18fe82a19 100644
--- a/code/modules/asset_cache/transports/asset_transport.dm
+++ b/code/modules/asset_cache/transports/asset_transport.dm
@@ -125,7 +125,7 @@
if (unreceived.len)
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
- to_chat(client, "Sending Resources...")
+ to_chat(client, span_infoplain("Sending Resources..."))
for (var/asset_name in unreceived)
var/new_asset_name = asset_name
diff --git a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
index e7c5f5d35c7fa..dc56aa3fda7f7 100644
--- a/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
+++ b/code/modules/atmospherics/environmental/LINDA_turf_tile.dm
@@ -304,7 +304,7 @@
our_excited_group = excited_group //update our cache
if(our_excited_group && enemy_excited_group && enemy_tile.excited) //If you're both excited, no need to compare right?
should_share_air = TRUE
- else if(our_air.compare(enemy_air)) //Lets see if you're up for it
+ else if(our_air.compare(enemy_air, ARCHIVE)) //Lets see if you're up for it
SSair.add_to_active(enemy_tile) //Add yourself young man
var/datum/excited_group/existing_group = our_excited_group || enemy_excited_group || new
if(!our_excited_group)
@@ -332,7 +332,7 @@
var/datum/gas_mixture/planetary_mix = SSair.planetary[initial_gas_mix]
// archive ourself again so we don't accidentally share more gas than we currently have
LINDA_CYCLE_ARCHIVE(src)
- if(our_air.compare(planetary_mix))
+ if(our_air.compare(planetary_mix, ARCHIVE))
if(!our_excited_group)
var/datum/excited_group/new_group = new
new_group.add_turf(src)
diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
index db2732be83fad..f329b98bec06a 100644
--- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm
+++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm
@@ -451,16 +451,17 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
//thermal energy of the system (self and sharer) is unchanged
///Compares sample to self to see if within acceptable ranges that group processing may be enabled
+///Takes the gas index to read from as a second arg (either MOLES or ARCHIVE)
///Returns: a string indicating what check failed, or "" if check passes
-/datum/gas_mixture/proc/compare(datum/gas_mixture/sample)
+/datum/gas_mixture/proc/compare(datum/gas_mixture/sample, index)
var/list/sample_gases = sample.gases //accessing datum vars is slower than proc vars
var/list/cached_gases = gases
var/moles_sum = 0
for(var/id in cached_gases | sample_gases) // compare gases from either mixture
// Yes this is actually fast. I too hate it here
- var/gas_moles = cached_gases[id]?[MOLES] || 0
- var/sample_moles = sample_gases[id]?[MOLES] || 0
+ var/gas_moles = cached_gases[id]?[index] || 0
+ var/sample_moles = sample_gases[id]?[index] || 0
// Brief explanation. We are much more likely to not pass this first check then pass the first and fail the second
// Because of this, double calculating the delta is FASTER then inserting it into a var
if(abs(gas_moles - sample_moles) > MINIMUM_MOLES_DELTA_TO_MOVE)
@@ -470,8 +471,12 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
moles_sum += gas_moles
if(moles_sum > MINIMUM_MOLES_DELTA_TO_MOVE) //Don't consider temp if there's not enough mols
- if(abs(temperature - sample.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
- return "temp"
+ if(index == ARCHIVE)
+ if(abs(temperature_archived - sample.temperature_archived) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
+ return "temp"
+ else
+ if(abs(temperature - sample.temperature) > MINIMUM_TEMPERATURE_DELTA_TO_SUSPEND)
+ return "temp"
return ""
@@ -547,7 +552,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
/**
* Counts how much pressure will there be if we impart MOLAR_ACCURACY amounts of our gas to the output gasmix.
- * We do all of this without actually transferring it so dont worry about it changing the gasmix.
+ * We do all of this without actually transferring it so don't worry about it changing the gasmix.
* Returns: Resulting pressure (number).
* Args:
* - output_air (gasmix).
@@ -562,10 +567,10 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
* Args:
* - output_air. The gas mix we want to pump to.
* - target_pressure. The target pressure we want.
- * - ignore_temperature. Returns a cheaper form of gas calculation, useful if the temperature difference between the two gasmixes is low or nonexistant.
+ * - ignore_temperature. Returns a cheaper form of gas calculation, useful if the temperature difference between the two gasmixes is low or nonexistent.
*/
/datum/gas_mixture/proc/gas_pressure_calculate(datum/gas_mixture/output_air, target_pressure, ignore_temperature = FALSE)
- // So we dont need to iterate the gaslist multiple times.
+ // So we don't need to iterate the gaslist multiple times.
var/our_moles = total_moles()
var/output_moles = output_air.total_moles()
var/output_pressure = output_air.return_pressure()
diff --git a/code/modules/atmospherics/gasmixtures/gas_types.dm b/code/modules/atmospherics/gasmixtures/gas_types.dm
index 7bbfea3dcf253..c040e30db3fcf 100644
--- a/code/modules/atmospherics/gasmixtures/gas_types.dm
+++ b/code/modules/atmospherics/gasmixtures/gas_types.dm
@@ -85,7 +85,7 @@
rarity = 1000
purchaseable = TRUE
base_value = 0.1
- desc = "A very common gas that used to pad artifical atmospheres to habitable pressure."
+ desc = "A very common gas that used to pad artificial atmospheres to habitable pressure."
primary_color = "#ffff00"
/datum/gas/carbon_dioxide //what the fuck is this?
@@ -133,7 +133,7 @@
fusion_power = 10
rarity = 50
base_value = 2.5
- desc = "The most noble gas of them all. High quantities of hyper-noblium actively prevents reactions from occuring."
+ desc = "The most noble gas of them all. High quantities of hyper-noblium actively prevents reactions from occurring."
primary_color = COLOR_TEAL
/datum/gas/nitrous_oxide
@@ -173,7 +173,7 @@
fusion_power = 5
rarity = 300
base_value = 2.5
- desc = "A highly flammable and radioctive gas."
+ desc = "A highly flammable and radioactive gas."
primary_color = "#32cd32"
/datum/gas/bz
@@ -279,7 +279,7 @@
moles_visible = MOLES_GAS_VISIBLE
rarity = 300
base_value = 4
- desc = "A potent fire supressant. Removes oxygen from high temperature fires and cools down the area"
+ desc = "A potent fire suppressant. Removes oxygen from high temperature fires and cools down the area"
primary_color = COLOR_PURPLE
/datum/gas/helium
@@ -314,7 +314,7 @@
appearance_flags = TILE_BOUND
vis_flags = NONE
// The visual offset we are "on".
- // Can't use the tradtional loc because we are stored in nullspace, and we can't set plane before init because of the helping that SET_PLANE_EXPLICIT does IN init
+ // Can't use the traditional loc because we are stored in nullspace, and we can't set plane before init because of the helping that SET_PLANE_EXPLICIT does IN init
var/plane_offset = 0
/obj/effect/overlay/gas/New(state, alph, offset)
diff --git a/code/modules/atmospherics/gasmixtures/reaction_factors.dm b/code/modules/atmospherics/gasmixtures/reaction_factors.dm
index 5148793179500..af058dc77877f 100644
--- a/code/modules/atmospherics/gasmixtures/reaction_factors.dm
+++ b/code/modules/atmospherics/gasmixtures/reaction_factors.dm
@@ -50,14 +50,14 @@
/datum/gas/carbon_dioxide = "Carbon Dioxide is formed at 1 mole per mole of freon consumed.",
"Temperature" = "Can only occur between [FREON_LOWER_TEMPERATURE] - [FREON_MAXIMUM_BURN_TEMPERATURE] Kelvin",
"Energy" = "[FIRE_FREON_ENERGY_CONSUMED] joules of energy is absorbed per mole of freon consumed.",
- "Hot Ice" = "This reaction produces hot ice when occuring between [HOT_ICE_FORMATION_MINIMUM_TEMPERATURE]-[HOT_ICE_FORMATION_MAXIMUM_TEMPERATURE] kelvins",
+ "Hot Ice" = "This reaction produces hot ice when occurring between [HOT_ICE_FORMATION_MINIMUM_TEMPERATURE]-[HOT_ICE_FORMATION_MAXIMUM_TEMPERATURE] kelvins",
)
/datum/gas_reaction/nitrousformation/init_factors()
factor = list(
/datum/gas/oxygen = "10 moles of Oxygen needs to be present for the reaction to occur. Oxygen is consumed at 0.5 moles per mole of nitrous oxide formed.",
- /datum/gas/nitrogen = " 20 moles of Nitrogen needs to be present for the reaction to occur. Nitrogen is consumed at 1 mole per mole of nitrous oxife formed.",
+ /datum/gas/nitrogen = " 20 moles of Nitrogen needs to be present for the reaction to occur. Nitrogen is consumed at 1 mole per mole of nitrous oxide formed.",
/datum/gas/bz = "5 moles of BZ needs to be present for the reaction to occur. Not consumed.",
/datum/gas/nitrous_oxide = "Nitrous oxide gets produced rapidly.",
"Temperature" = "Can only occur between [N2O_FORMATION_MIN_TEMPERATURE] - [N2O_FORMATION_MAX_TEMPERATURE] Kelvin",
diff --git a/code/modules/atmospherics/gasmixtures/reactions.dm b/code/modules/atmospherics/gasmixtures/reactions.dm
index 49254d077deae..f6c238c8d0e28 100644
--- a/code/modules/atmospherics/gasmixtures/reactions.dm
+++ b/code/modules/atmospherics/gasmixtures/reactions.dm
@@ -328,7 +328,7 @@
SET_REACTION_RESULTS(burned_fuel)
var/turf/open/location
- if(istype(holder, /datum/pipeline)) //Find the tile the reaction is occuring on, or a random part of the network if it's a pipenet.
+ if(istype(holder, /datum/pipeline)) //Find the tile the reaction is occurring on, or a random part of the network if it's a pipenet.
var/datum/pipeline/pipenet = holder
location = pick(pipenet.members)
else if(isatom(holder))
@@ -686,7 +686,7 @@
var/list/cached_gases = air.gases
var/temperature = air.temperature
- //This reaction is agressively slow. like, a tenth of a mole per fire slow. Keep that in mind
+ //This reaction is aggressively slow. like, a tenth of a mole per fire slow. Keep that in mind
var/heat_efficiency = min(temperature / NITRIUM_DECOMPOSITION_TEMP_DIVISOR, cached_gases[/datum/gas/nitrium][MOLES])
if (heat_efficiency <= 0 || (cached_gases[/datum/gas/nitrium][MOLES] - heat_efficiency < 0)) //Shouldn't produce gas from nothing.
@@ -1091,7 +1091,7 @@
SET_REACTION_RESULTS(produced_amount)
var/turf/open/location
var/energy_released = produced_amount * PN_TRITIUM_CONVERSION_ENERGY
- if(istype(holder,/datum/pipeline)) //Find the tile the reaction is occuring on, or a random part of the network if it's a pipenet.
+ if(istype(holder,/datum/pipeline)) //Find the tile the reaction is occurring on, or a random part of the network if it's a pipenet.
var/datum/pipeline/pipenet = holder
location = pick(pipenet.members)
else if(isatom(holder))
@@ -1143,7 +1143,7 @@
SET_REACTION_RESULTS(consumed_amount)
var/turf/open/location
var/energy_released = consumed_amount * PN_BZASE_ENERGY
- if(istype(holder,/datum/pipeline)) //Find the tile the reaction is occuring on, or a random part of the network if it's a pipenet.
+ if(istype(holder,/datum/pipeline)) //Find the tile the reaction is occurring on, or a random part of the network if it's a pipenet.
var/datum/pipeline/pipenet = holder
location = pick(pipenet.members)
else if(isatom(holder))
diff --git a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
index 737018b24ad7d..1e9045d82279c 100644
--- a/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
+++ b/code/modules/atmospherics/machinery/air_alarm/_air_alarm.dm
@@ -99,13 +99,14 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
tlv_collection = list()
tlv_collection["pressure"] = new /datum/tlv/pressure
tlv_collection["temperature"] = new /datum/tlv/temperature
- var/list/meta_info = GLOB.meta_gas_info // shorthand
- for(var/gas_path in meta_info)
+
+ var/list/cached_gas_info = GLOB.meta_gas_info
+ for(var/datum/gas/gas_path as anything in cached_gas_info)
if(ispath(gas_path, /datum/gas/oxygen))
tlv_collection[gas_path] = new /datum/tlv/oxygen
else if(ispath(gas_path, /datum/gas/carbon_dioxide))
tlv_collection[gas_path] = new /datum/tlv/carbon_dioxide
- else if(meta_info[gas_path][META_GAS_DANGER])
+ else if(cached_gas_info[gas_path][META_GAS_DANGER])
tlv_collection[gas_path] = new /datum/tlv/dangerous
else
tlv_collection[gas_path] = new /datum/tlv/no_checks
@@ -123,9 +124,9 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
))
GLOB.air_alarms += src
- update_appearance()
find_and_hang_on_wall()
register_context()
+ check_enviroment()
/obj/machinery/airalarm/process()
if(!COOLDOWN_FINISHED(src, warning_cooldown))
@@ -137,6 +138,12 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
/obj/machinery/airalarm/Destroy()
if(my_area)
my_area = null
+ if(connected_sensor)
+ UnregisterSignal(connected_sensor, COMSIG_QDELETING)
+ UnregisterSignal(connected_sensor.loc, COMSIG_TURF_EXPOSE)
+ connected_sensor.connected_airalarm = null
+ connected_sensor = null
+
QDEL_NULL(alarm_manager)
GLOB.air_alarms -= src
return ..()
@@ -201,10 +208,16 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
return .
if(istype(multi_tool.buffer, /obj/machinery/air_sensor))
+ var/obj/machinery/air_sensor/sensor = multi_tool.buffer
+
if(!allow_link_change)
balloon_alert(user, "linking disabled")
return ITEM_INTERACT_BLOCKING
- connect_sensor(multi_tool.buffer)
+ if(connected_sensor || sensor.connected_airalarm)
+ balloon_alert(user, "sensor already connected!")
+ return ITEM_INTERACT_BLOCKING
+
+ connect_sensor(sensor)
balloon_alert(user, "connected sensor")
return ITEM_INTERACT_SUCCESS
@@ -568,34 +581,40 @@ GLOBAL_LIST_EMPTY_TYPED(air_alarms, /obj/machinery/airalarm)
danger_level = max(danger_level, tlv_collection["pressure"].check_value(pressure))
danger_level = max(danger_level, tlv_collection["temperature"].check_value(temp))
if(total_moles)
- for(var/gas_path in environment.gases)
- var/moles = environment.gases[gas_path][MOLES]
+ var/list/cached_gas_info = GLOB.meta_gas_info
+ for(var/datum/gas/gas_path as anything in cached_gas_info)
+ var/moles = environment.gases[gas_path] ? environment.gases[gas_path][MOLES] : 0
danger_level = max(danger_level, tlv_collection[gas_path].check_value(pressure * moles / total_moles))
if(danger_level)
alarm_manager.send_alarm(ALARM_ATMOS)
- if(pressure <= WARNING_LOW_PRESSURE && temp <= BODYTEMP_COLD_WARNING_1+10)
+ var/is_high_pressure = tlv_collection["pressure"].hazard_max != TLV_VALUE_IGNORE && pressure >= tlv_collection["pressure"].hazard_max
+ var/is_high_temp = tlv_collection["temperature"].hazard_max != TLV_VALUE_IGNORE && temp >= tlv_collection["temperature"].hazard_max
+ var/is_low_pressure = tlv_collection["pressure"].hazard_min != TLV_VALUE_IGNORE && pressure <= tlv_collection["pressure"].hazard_min
+ var/is_low_temp = tlv_collection["temperature"].hazard_min != TLV_VALUE_IGNORE && temp <= tlv_collection["temperature"].hazard_min
+
+ if(is_low_pressure && is_low_temp)
warning_message = "Danger! Low pressure and temperature detected."
return
- if(pressure <= WARNING_LOW_PRESSURE && temp >= BODYTEMP_HEAT_WARNING_1-27)
+ if(is_low_pressure && is_high_temp)
warning_message = "Danger! Low pressure and high temperature detected."
return
- if(pressure >= WARNING_HIGH_PRESSURE && temp >= BODYTEMP_HEAT_WARNING_1-27)
+ if(is_high_pressure && is_high_temp)
warning_message = "Danger! High pressure and temperature detected."
return
- if(pressure >= WARNING_HIGH_PRESSURE && temp <= BODYTEMP_COLD_WARNING_1+10)
+ if(is_high_pressure && is_low_temp)
warning_message = "Danger! High pressure and low temperature detected."
return
- if(pressure <= WARNING_LOW_PRESSURE)
+ if(is_low_pressure)
warning_message = "Danger! Low pressure detected."
return
- if(pressure >= WARNING_HIGH_PRESSURE)
+ if(is_high_pressure)
warning_message = "Danger! High pressure detected."
return
- if(temp <= BODYTEMP_COLD_WARNING_1+10)
+ if(is_low_temp)
warning_message = "Danger! Low temperature detected."
return
- if(temp >= BODYTEMP_HEAT_WARNING_1-27)
+ if(is_high_temp)
warning_message = "Danger! High temperature detected."
return
else
@@ -677,20 +696,31 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/airalarm, 27)
tlv_collection["temperature"] = new /datum/tlv/no_checks
tlv_collection["pressure"] = new /datum/tlv/no_checks
+ for(var/gas_path in GLOB.meta_gas_info)
+ tlv_collection[gas_path] = new /datum/tlv/no_checks
+
///Used for air alarm link helper, which connects air alarm to a sensor with corresponding chamber_id
/obj/machinery/airalarm/proc/setup_chamber_link()
var/obj/machinery/air_sensor/sensor = GLOB.objects_by_id_tag[GLOB.map_loaded_sensors[air_sensor_chamber_id]]
if(isnull(sensor))
log_mapping("[src] at [AREACOORD(src)] tried to connect to a sensor, but no sensor with chamber_id:[air_sensor_chamber_id] found!")
return
+ if(connected_sensor)
+ log_mapping("[src] at [AREACOORD(src)] tried to connect to more than one sensor!")
+ return
connect_sensor(sensor)
///Used to connect air alarm with a sensor
/obj/machinery/airalarm/proc/connect_sensor(obj/machinery/air_sensor/sensor)
- if(!isnull(connected_sensor))
- UnregisterSignal(connected_sensor, COMSIG_QDELETING)
+ sensor.connected_airalarm = src
connected_sensor = sensor
+
RegisterSignal(connected_sensor, COMSIG_QDELETING, PROC_REF(disconnect_sensor))
+
+ // Transfer signal from air alarm to sensor
+ UnregisterSignal(loc, COMSIG_TURF_EXPOSE)
+ RegisterSignal(connected_sensor.loc, COMSIG_TURF_EXPOSE, PROC_REF(check_danger), override=TRUE)
+
my_area = get_area(connected_sensor)
check_enviroment()
@@ -701,6 +731,12 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/airalarm, 27)
///Used to reset the air alarm to default configuration after disconnecting from air sensor
/obj/machinery/airalarm/proc/disconnect_sensor()
UnregisterSignal(connected_sensor, COMSIG_QDELETING)
+
+ // Transfer signal from sensor to air alarm
+ UnregisterSignal(connected_sensor.loc, COMSIG_TURF_EXPOSE)
+ RegisterSignal(loc, COMSIG_TURF_EXPOSE, PROC_REF(check_danger), override=TRUE)
+
+ connected_sensor.connected_airalarm = null
connected_sensor = null
my_area = get_area(src)
diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm
index 12e6c684079e6..186ef8ea9aace 100644
--- a/code/modules/atmospherics/machinery/atmosmachinery.dm
+++ b/code/modules/atmospherics/machinery/atmosmachinery.dm
@@ -34,7 +34,7 @@
///The flags of the pipe/component (PIPING_ALL_LAYER | PIPING_ONE_PER_TURF | PIPING_DEFAULT_LAYER_ONLY | PIPING_CARDINAL_AUTONORMALIZE)
var/pipe_flags = NONE
- ///This only works on pipes, because they have 1000 subtypes wich need to be visible and invisible under tiles, so we track this here
+ ///This only works on pipes, because they have 1000 subtypes which need to be visible and invisible under tiles, so we track this here
var/hide = TRUE
///The image of the pipe/device used for ventcrawling
@@ -331,7 +331,7 @@
if(isnull(given_layer))
given_layer = piping_layer
- // you cant place the machine on the same location as the target cause it blocks
+ // you can't place the machine on the same location as the target cause it blocks
if(target.loc == loc)
return FALSE
@@ -478,7 +478,7 @@
* Called by wrench_act() before deconstruct()
* Arguments:
* * mob_user - the mob doing the act
- * * pressures - it can be passed on from wrench_act(), it's the pressure difference between the enviroment pressure and the pipe internal pressure
+ * * pressures - it can be passed on from wrench_act(), it's the pressure difference between the environment pressure and the pipe internal pressure
*/
/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures = null)
if(!user)
@@ -569,7 +569,7 @@
// Handles mob movement inside a pipenet
/obj/machinery/atmospherics/relaymove(mob/living/user, direction)
- if(!direction) //cant go this way.
+ if(!direction) //can't go this way.
return
if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug
return
@@ -614,8 +614,8 @@
our_client.set_eye(target_move)
// Let's smooth out that movement with an animate yeah?
// If the new x is greater (move is left to right) we get a negative offset. vis versa
- our_client.pixel_x = (x - target_move.x) * world.icon_size
- our_client.pixel_y = (y - target_move.y) * world.icon_size
+ our_client.pixel_x = (x - target_move.x) * ICON_SIZE_X
+ our_client.pixel_y = (y - target_move.y) * ICON_SIZE_Y
animate(our_client, pixel_x = 0, pixel_y = 0, time = 0.05 SECONDS)
our_client.move_delay = world.time + 0.05 SECONDS
diff --git a/code/modules/atmospherics/machinery/components/fusion/hfr_main_processes.dm b/code/modules/atmospherics/machinery/components/fusion/hfr_main_processes.dm
index c6e1d6183ef79..27cb78bb26ce7 100644
--- a/code/modules/atmospherics/machinery/components/fusion/hfr_main_processes.dm
+++ b/code/modules/atmospherics/machinery/components/fusion/hfr_main_processes.dm
@@ -493,7 +493,7 @@
zaps_aspect = OVER_9000_ZAP_ICON_STATE
flags |= (ZAP_MOB_DAMAGE | ZAP_OBJ_DAMAGE)
- playsound(loc, 'sound/weapons/emitter2.ogg', 100, TRUE, extrarange = 10)
+ playsound(loc, 'sound/items/weapons/emitter2.ogg', 100, TRUE, extrarange = 10)
for(var/i in 1 to zap_number)
supermatter_zap(src, 5, power_level * 2.4e5, flags, zap_cutoff = cutoff, power_level = src.power_level * 1000, zap_icon = zaps_aspect)
diff --git a/code/modules/atmospherics/machinery/components/fusion/hfr_procs.dm b/code/modules/atmospherics/machinery/components/fusion/hfr_procs.dm
index 9f2f6a96f4bf8..bc27ab0a42e36 100644
--- a/code/modules/atmospherics/machinery/components/fusion/hfr_procs.dm
+++ b/code/modules/atmospherics/machinery/components/fusion/hfr_procs.dm
@@ -301,13 +301,13 @@
/obj/machinery/atmospherics/components/unary/hypertorus/core/proc/alarm()
switch(get_status())
if(HYPERTORUS_MELTING)
- playsound(src, 'sound/misc/bloblarm.ogg', 100, FALSE, 40, 30, falloff_distance = 10)
+ playsound(src, 'sound/announcer/alarm/bloblarm.ogg', 100, FALSE, 40, 30, falloff_distance = 10)
if(HYPERTORUS_EMERGENCY)
- playsound(src, 'sound/machines/engine_alert1.ogg', 100, FALSE, 30, 30, falloff_distance = 10)
+ playsound(src, 'sound/machines/engine_alert/engine_alert1.ogg', 100, FALSE, 30, 30, falloff_distance = 10)
if(HYPERTORUS_DANGER)
- playsound(src, 'sound/machines/engine_alert2.ogg', 100, FALSE, 30, 30, falloff_distance = 10)
+ playsound(src, 'sound/machines/engine_alert/engine_alert2.ogg', 100, FALSE, 30, 30, falloff_distance = 10)
if(HYPERTORUS_WARNING)
- playsound(src, 'sound/machines/terminal_alert.ogg', 75)
+ playsound(src, 'sound/machines/terminal/terminal_alert.ogg', 75)
/**
* Getter for the machine integrity
@@ -413,7 +413,7 @@
var/critical = selected_fuel.meltdown_flags & HYPERTORUS_FLAG_CRITICAL_MELTDOWN
if(critical)
priority_announce("WARNING - The explosion will likely cover a big part of the station and the coming EMP will wipe out most of the electronics. \
- Get as far away as possible from the reactor or find a way to shut it down.", "Alert", 'sound/misc/notice3.ogg')
+ Get as far away as possible from the reactor or find a way to shut it down.", "Alert", 'sound/announcer/notice/notice3.ogg')
var/speaking = "[emergency_alert] The Hypertorus fusion reactor has reached critical integrity failure. Emergency magnetic dampeners online."
radio.talk_into(src, speaking, common_channel, language = get_selected_language())
@@ -430,7 +430,7 @@
radio.talk_into(src, "[safe_alert] Failsafe has been disengaged.", common_channel)
final_countdown = FALSE
return
- else if((i % 50) != 0 && i > 50) // A message once every 5 seconds until the final 5 seconds which count down individualy
+ else if((i % 50) != 0 && i > 50) // A message once every 5 seconds until the final 5 seconds which count down individually
sleep(1 SECONDS)
continue
else if(i > 50)
diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm
index 565ada80b931e..b5cc7c628df29 100644
--- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm
+++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm
@@ -106,7 +106,7 @@
return TRUE
return FALSE
-///Injects the gases from the input inside the internal gasmix, the amount is dependant on the gas_input var
+///Injects the gases from the input inside the internal gasmix, the amount is dependent on the gas_input var
/obj/machinery/atmospherics/components/binary/crystallizer/proc/inject_gases()
var/datum/gas_mixture/contents = airs[2]
for(var/gas_type in selected_recipe.requirements)
diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm
index 26cb395113519..6308e1eee611e 100644
--- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm
+++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer_items.dm
@@ -1,6 +1,6 @@
/obj/item/hypernoblium_crystal
name = "Hypernoblium Crystal"
- desc = "Crystalized oxygen and hypernoblium stored in a bottle to pressureproof your clothes or stop reactions occuring in portable atmospheric devices."
+ desc = "Crystallized oxygen and hypernoblium stored in a bottle to pressureproof your clothes or stop reactions occurring in portable atmospheric devices."
icon = 'icons/obj/pipes_n_cables/atmos.dmi'
icon_state = "hypernoblium_crystal"
var/uses = 1
diff --git a/code/modules/atmospherics/machinery/components/tank.dm b/code/modules/atmospherics/machinery/components/tank.dm
index ee1e61428d196..f76f3dc470f57 100644
--- a/code/modules/atmospherics/machinery/components/tank.dm
+++ b/code/modules/atmospherics/machinery/components/tank.dm
@@ -304,7 +304,7 @@
. = TRUE
if(atom_integrity >= max_integrity)
return
- if(!tool.tool_start_check(user, amount = 0))
+ if(!tool.tool_start_check(user, amount = 0, heat_required = HIGH_TEMPERATURE_REQUIRED))
return
to_chat(user, span_notice("You begin to repair the cracks in the gas tank..."))
var/repair_amount = max_integrity / 10
@@ -562,7 +562,7 @@
if(!anchored)
to_chat(user, span_notice("You need to wrench [src] to the floor before finishing."))
return
- if(!tool.tool_start_check(user, amount = 0))
+ if(!tool.tool_start_check(user, amount = 0, heat_required = HIGH_TEMPERATURE_REQUIRED))
return
to_chat(user, span_notice("You begin sealing the outer plating with the welder..."))
if(!tool.use_tool(src, user, 2 SECONDS, volume = 60))
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm
index 8a165830cec07..5ce16365a5fda 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm
@@ -230,7 +230,7 @@
fan_overclocked = !fan_overclocked
if(from_break)
- playsound(src, 'sound/machines/fan_break.ogg', 100)
+ playsound(src, 'sound/machines/fan/fan_break.ogg', 100)
fan_overclocked = FALSE
if(fan_overclocked)
@@ -360,7 +360,7 @@
update_appearance()
pipe_vision_img = image(src, loc, dir = dir)
SET_PLANE_EXPLICIT(pipe_vision_img, ABOVE_HUD_PLANE, src)
- playsound(loc, 'sound/weapons/bladeslice.ogg', 100, TRUE)
+ playsound(loc, 'sound/items/weapons/bladeslice.ogg', 100, TRUE)
/obj/machinery/atmospherics/components/unary/vent_pump/high_volume
name = "large air vent"
diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
index 22ee2f6b414a7..cc94f04623b71 100644
--- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
+++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm
@@ -322,7 +322,7 @@
update_appearance()
pipe_vision_img = image(src, loc, dir = dir)
SET_PLANE_EXPLICIT(pipe_vision_img, ABOVE_HUD_PLANE, src)
- playsound(loc, 'sound/weapons/bladeslice.ogg', 100, TRUE)
+ playsound(loc, 'sound/items/weapons/bladeslice.ogg', 100, TRUE)
/obj/machinery/atmospherics/components/unary/vent_scrubber/layer2
diff --git a/code/modules/atmospherics/machinery/portable/canister.dm b/code/modules/atmospherics/machinery/portable/canister.dm
index a5a03c3ba1204..467ad0e381d1e 100644
--- a/code/modules/atmospherics/machinery/portable/canister.dm
+++ b/code/modules/atmospherics/machinery/portable/canister.dm
@@ -76,7 +76,7 @@
. = ..()
if(!allowed(user))
to_chat(user, span_alert("Error - Unauthorized User."))
- playsound(src, 'sound/misc/compiler-failure.ogg', 50, TRUE)
+ playsound(src, 'sound/machines/compiler/compiler-failure.ogg', 50, TRUE)
return
/obj/machinery/portable_atmospherics/canister/add_context(atom/source, list/context, obj/item/held_item, mob/user)
@@ -397,7 +397,7 @@
return ITEM_INTERACT_SUCCESS
/obj/machinery/portable_atmospherics/canister/welder_act_secondary(mob/living/user, obj/item/I)
- if(!I.tool_start_check(user, amount=1))
+ if(!I.tool_start_check(user, amount=1, heat_required = HIGH_TEMPERATURE_REQUIRED))
return ITEM_INTERACT_BLOCKING
var/pressure = air_contents.return_pressure()
diff --git a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm
index 210eb9b0d9e28..cb1a23f82f15a 100644
--- a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm
+++ b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm
@@ -32,8 +32,8 @@
var/suppress_reactions = FALSE
/// Is there a hypernoblium crystal inserted into this
var/nob_crystal_inserted = FALSE
- var/insert_sound = 'sound/effects/tank_insert_clunky.ogg'
- var/remove_sound = 'sound/effects/tank_remove_thunk.ogg'
+ var/insert_sound = 'sound/effects/compressed_air/tank_insert_clunky.ogg'
+ var/remove_sound = 'sound/effects/compressed_air/tank_remove_thunk.ogg'
var/sound_vol = 50
/datum/armor/machinery_portable_atmospherics
@@ -95,7 +95,7 @@
/obj/machinery/portable_atmospherics/welder_act(mob/living/user, obj/item/tool)
if(user.combat_mode)
return ITEM_INTERACT_SKIP_TO_ATTACK
- if(atom_integrity >= max_integrity || (machine_stat & BROKEN) || !tool.tool_start_check(user, amount = 1))
+ if(atom_integrity >= max_integrity || (machine_stat & BROKEN) || !tool.tool_start_check(user, amount = 1, heat_required = HIGH_TEMPERATURE_REQUIRED))
return ITEM_INTERACT_BLOCKING
balloon_alert(user, "repairing...")
while(tool.use_tool(src, user, 2.5 SECONDS, volume=40))
@@ -223,16 +223,17 @@
if(!user.transferItemToLoc(new_tank, src))
return FALSE
- investigate_log("had its internal [holding] swapped with [new_tank] by [key_name(user)].", INVESTIGATE_ATMOS)
- to_chat(user, span_notice("[holding ? "In one smooth motion you pop [holding] out of [src]'s connector and replace it with [new_tank]" : "You insert [new_tank] into [src]"]."))
-
if(holding && new_tank)//for when we are actually switching tanks
+ investigate_log("had its internal [holding] swapped with [new_tank] by [key_name(user)].", INVESTIGATE_ATMOS)
+ to_chat(user, span_notice("In one smooth motion you pop [holding] out of [src]'s connector and replace it with [new_tank]."))
user.put_in_hands(holding)
UnregisterSignal(holding, COMSIG_QDELETING)
holding = new_tank
RegisterSignal(holding, COMSIG_QDELETING, PROC_REF(unregister_holding))
playsound(src, list(insert_sound,remove_sound), sound_vol)
else if(holding)//we remove a tank
+ investigate_log("had its internal [holding] removed by [key_name(user)].", INVESTIGATE_ATMOS)
+ to_chat(user, span_notice("You remove [holding] from [src]."))
if(Adjacent(user))
user.put_in_hands(holding)
else
@@ -241,6 +242,8 @@
UnregisterSignal(holding, COMSIG_QDELETING)
holding = null
else if(new_tank)//we insert the tank
+ investigate_log("had [new_tank] inserted into it by [key_name(user)].", INVESTIGATE_ATMOS)
+ to_chat(user, span_notice("You insert [new_tank] into [src]."))
holding = new_tank
playsound(src, insert_sound, sound_vol)
RegisterSignal(holding, COMSIG_QDELETING, PROC_REF(unregister_holding))
diff --git a/code/modules/autowiki/pages/base.dm b/code/modules/autowiki/pages/base.dm
index 8e745ace61c2d..ce32bfd4032b7 100644
--- a/code/modules/autowiki/pages/base.dm
+++ b/code/modules/autowiki/pages/base.dm
@@ -46,7 +46,12 @@
if (IsAdminAdvancedProcCall())
return
+ var/static/uploaded_icons = list()
+ if(uploaded_icons["[name]"])
+ CRASH("We tried uploading an icon, but the name \"[name]\" was already taken!")
+
fcopy(icon, "data/autowiki_files/[name].png")
+ uploaded_icons["[name]"] = TRUE
/// Escape a parameter such that it can be correctly put inside a wiki output
/datum/autowiki/proc/escape_value(parameter)
diff --git a/code/modules/autowiki/pages/fishing.dm b/code/modules/autowiki/pages/fishing.dm
new file mode 100644
index 0000000000000..eab26bd6c6c03
--- /dev/null
+++ b/code/modules/autowiki/pages/fishing.dm
@@ -0,0 +1,451 @@
+/datum/autowiki/fish
+ page = "Template:Autowiki/Content/Fish"
+
+/datum/autowiki/fish/generate()
+ var/output = ""
+
+ var/datum/reagent/def_food = /obj/item/fish::food
+ var/def_food_name = initial(def_food.name)
+ var/def_feeding = /obj/item/fish::feeding_frequency
+ var/def_feeding_text = DisplayTimeText(def_feeding)
+ var/def_breeding = /obj/item/fish::breeding_timeout
+ var/def_breeding_text = DisplayTimeText(def_breeding)
+
+ var/list/generated_icons = list()
+ var/list/fish_types = subtypesof(/obj/item/fish)
+ sortTim(fish_types, GLOBAL_PROC_REF(cmp_fish_fluid))
+
+ for (var/obj/item/fish/fish as anything in fish_types)
+
+ var/filename = FISH_AUTOWIKI_FILENAME(fish)
+
+ if(!generated_icons[filename])
+ upload_icon(icon(fish:icon, fish::icon_state, frame = 1), filename)
+ generated_icons[filename] = TRUE
+
+ if(!(fish::fish_flags & FISH_FLAG_SHOW_IN_CATALOG))
+ continue
+
+ var/list/properties = SSfishing.fish_properties[fish]
+
+ var/description = escape_value(fish::desc)
+ var/list/extra_info = list()
+ if(fish::fillet_type != /obj/item/food/fishmeat)
+ var/obj/item/fillet = fish::fillet_type
+ if(!fillet)
+ extra_info += "Cannot be butchered."
+ else
+ extra_info += "When butchered, it'll yield [initial(fillet.name)]."
+ var/datum/reagent/food = fish::food
+ if(food != def_food)
+ extra_info += "It has to be fed [initial(food.name)] instead of [def_food_name]"
+ if(fish::feeding_frequency != def_feeding)
+ extra_info += "It has to be fed every [DisplayTimeText(fish::feeding_frequency)] instead of [def_feeding_text]"
+ if(fish::breeding_timeout != def_breeding)
+ extra_info += "It takes [DisplayTimeText(fish::breeding_timeout)] to reproduce instead of [def_breeding_text]"
+ if(length(extra_info))
+ description += " [extra_info.Join(extra_info," ")]"
+
+ var/list/output_list = list(
+ "name" = full_capitalize(escape_value(fish::name)),
+ "icon" = filename,
+ "description" = description,
+ "size_weight" = "[fish::average_size]cm / [fish::average_weight]g",
+ "fluid" = escape_value(fish::required_fluid_type),
+ "temperature" = "Doesn't matter",
+ "stable_population" = fish::stable_population,
+ "traits" = generate_traits(properties[FISH_PROPERTIES_TRAITS]),
+ "favorite_baits" = generate_baits(properties[FISH_PROPERTIES_FAV_BAIT]),
+ "disliked_baits" = generate_baits(properties[FISH_PROPERTIES_BAD_BAIT], TRUE),
+ "beauty_score" = properties[FISH_PROPERTIES_BEAUTY_SCORE],
+ )
+ var/not_infinity = fish::required_temperature_max < INFINITY
+ if(fish::required_temperature_min > 0 || not_infinity)
+ var/max_temp = not_infinity ? fish::required_temperature_max : "∞"
+ output_list["temperature"] = "[fish::required_temperature_min] - [max_temp] K"
+
+ output += "\n\n" + include_template("Autowiki/FishEntry", output_list)
+
+ return output
+
+/datum/autowiki/fish/proc/generate_baits(list/baits, bad = FALSE)
+ var/list/list = list()
+ if(!length(baits))
+ return list("None")
+
+ for (var/identifier in baits)
+ if(ispath(identifier)) //Just a path
+ var/obj/item/item = identifier
+ list += initial(item.name)
+ continue
+ var/list/special_identifier = identifier
+ switch(special_identifier["Type"])
+ if("Foodtype")
+ list += english_list(bitfield_to_list(special_identifier["Value"], FOOD_FLAGS_IC))
+ if("Reagent")
+ var/datum/reagent/reagent = special_identifier["Value"]
+ list += "[reagent::name][bad ? "" : "(At least [special_identifier["Amount"]] units)"]"
+
+ return list
+
+/datum/autowiki/fish/proc/generate_traits(list/traits)
+ var/output = ""
+
+ for(var/trait_type in traits)
+ var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
+ output += include_template("Autowiki/FishTypeTraits", list(
+ "name" = escape_value(trait.name),
+ "description" = escape_value(trait.catalog_description),
+ ))
+
+ return output
+
+/datum/autowiki/fish_trait
+ page = "Template:Autowiki/Content/Fish/Trait"
+
+/datum/autowiki/fish_trait/generate()
+ var/output = ""
+
+ for(var/trait_type in GLOB.fish_traits)
+ var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
+ var/desc = escape_value(trait.catalog_description)
+ if(length(trait.incompatible_traits))
+ var/incompatible = list()
+ for(var/datum/fish_trait/bad as anything in trait.incompatible_traits)
+ incompatible += span_bold(initial(bad.name))
+ desc += " Incompatible with [english_list(incompatible)]."
+ output += include_template("Autowiki/FishAllTraits", list(
+ "name" = escape_value(trait.name),
+ "description" = escape_value(trait.catalog_description),
+ "inheritability" = trait.inheritability,
+ "inheritability_diff" = trait.diff_traits_inheritability,
+
+ ))
+
+ return output
+
+/datum/autowiki/fish_bait
+ page = "Template:Autowiki/Content/Fish/Bait"
+
+/datum/autowiki/fish_bait/generate()
+ var/output = ""
+
+ var/list/generated_icons = list()
+ for (var/obj/item/food/bait/bait as anything in subtypesof(/obj/item/food/bait))
+ if(!bait::show_on_wiki)
+ continue
+
+ var/filename = SANITIZE_FILENAME("[bait::icon_state]_wiki_bait")
+
+ var/quality = "Bland"
+
+ var/list/foodtypes
+ if(ispath(bait, /obj/item/food/bait/doughball/synthetic))
+ foodtypes = list("Don't worry about it")
+ else
+ foodtypes = bitfield_to_list(bait::foodtypes, FOOD_FLAGS_IC) || list("None")
+
+ switch(bait::bait_quality)
+ if(TRAIT_BASIC_QUALITY_BAIT)
+ quality = "Basic"
+ if(TRAIT_GOOD_QUALITY_BAIT)
+ quality = "Good"
+ if(TRAIT_GREAT_QUALITY_BAIT)
+ quality = "Great"
+
+ output += "\n\n" + include_template("Autowiki/FishBait", list(
+ "name" = full_capitalize(escape_value(bait::name)),
+ "icon" = filename,
+ "description" = escape_value(bait::desc),
+ "foodtypes" = foodtypes,
+ "quality" = quality,
+ ))
+
+ if(!generated_icons[filename])
+ upload_icon(icon(bait:icon, bait::icon_state, frame = 1), filename)
+ generated_icons[filename] = TRUE
+
+ var/filename = SANITIZE_FILENAME(/obj/item/stock_parts/power_store/cell/lead::icon_state)
+
+ var/lead_desc = /obj/item/stock_parts/power_store/cell/lead::desc
+ lead_desc += " You probably shouldn't use it unless you're trying to catch a zipzap."
+ output += "\n\n" + include_template("Autowiki/FishBait", list(
+ "name" = full_capitalize(escape_value(/obj/item/stock_parts/power_store/cell/lead::name)),
+ "icon" = filename,
+ "description" = lead_desc,
+ "foodtypes" = "None",
+ "quality" = "Poisonous",
+ ))
+
+ upload_icon(icon(/obj/item/stock_parts/power_store/cell/lead::icon, /obj/item/stock_parts/power_store/cell/lead::icon_state), filename)
+
+ var/obj/needletype = /obj/item/fish/needlefish
+ output += "\n\n" + include_template("Autowiki/FishBait", list(
+ "name" = "Baitfish",
+ "icon" = FISH_AUTOWIKI_FILENAME(needletype),
+ "description" = "Smaller fish such as goldfish, needlefish, armorfish and lavaloops can also be used as bait, It's a fish eat fish world.",
+ "foodtypes" = "Seafood?",
+ "quality" = "Good",
+ ))
+
+ output += "\n\n" + include_template("Autowiki/FishBait", list(
+ "name" = "Food",
+ "icon" = "plain_bread",
+ "description" = "In absence of baits, food can be used as a substitute.",
+ "foodtypes" = "Depends",
+ "quality" = "Bland most of the times",
+ ))
+
+ upload_icon(icon(/obj/item/food/bread/plain::icon, /obj/item/food/bread/plain::icon_state), "plain_bread")
+
+ return output
+
+/datum/autowiki/fishing_line
+ page = "Template:Autowiki/Content/Fish/Line"
+
+/datum/autowiki/fishing_line/generate()
+ var/output = ""
+
+ var/list/generated_icons = list()
+ for (var/obj/item/fishing_line/line as anything in typesof(/obj/item/fishing_line))
+ var/filename = SANITIZE_FILENAME("[line::icon_state]_wiki_line")
+
+ output += "\n\n" + include_template("Autowiki/FishLine", list(
+ "name" = full_capitalize(escape_value(line::name)),
+ "icon" = filename,
+ "description" = escape_value(line::wiki_desc),
+ ))
+
+ if(!generated_icons[filename])
+ upload_icon(icon(line:icon, line::icon_state), filename)
+ generated_icons[filename] = TRUE
+
+ return output
+
+/datum/autowiki/fishing_hook
+ page = "Template:Autowiki/Content/Fish/Hook"
+
+/datum/autowiki/fishing_hook/generate()
+ var/output = ""
+
+ var/list/generated_icons = list()
+ for (var/obj/item/fishing_hook/hook as anything in typesof(/obj/item/fishing_hook))
+ var/filename = SANITIZE_FILENAME("[hook::icon_state]_wiki_hook")
+
+ output += "\n\n" + include_template("Autowiki/FishHook", list(
+ "name" = full_capitalize(escape_value(hook::name)),
+ "icon" = filename,
+ "description" = escape_value(hook::wiki_desc),
+ ))
+
+ if(!generated_icons[filename])
+ upload_icon(icon(hook:icon, hook::icon_state), filename)
+ generated_icons[filename] = TRUE
+
+ return output
+
+/datum/autowiki/fishing_rod
+ page = "Template:Autowiki/Content/Fish/Rod"
+
+/datum/autowiki/fishing_rod/generate()
+ var/output = ""
+
+ var/list/generated_icons = list()
+ for (var/obj/item/fishing_rod/rod as anything in typesof(/obj/item/fishing_rod))
+ if(!rod::show_in_wiki)
+ continue
+
+ var/filename = SANITIZE_FILENAME("[rod::icon_state]_wiki_rod")
+
+ var/desc = escape_value(rod::ui_description)
+ if(rod::wiki_description)
+ desc += " [escape_value(rod::wiki_description)]"
+ output += "\n\n" + include_template("Autowiki/FishingRod", list(
+ "name" = full_capitalize(escape_value(rod::name)),
+ "icon" = filename,
+ "description" = desc,
+ ))
+
+ if(!generated_icons[filename])
+ var/icon/rod_icon = icon(rod:icon, rod::icon_state)
+ if(rod::reel_overlay)
+ var/icon/line = icon(rod::icon, rod::reel_overlay)
+ line.Blend(rod::default_line_color, ICON_MULTIPLY)
+ rod_icon.Blend(line, ICON_OVERLAY)
+ upload_icon(rod_icon, filename)
+ generated_icons[filename] = TRUE
+
+ return output
+
+/datum/autowiki/fish_sources
+ page = "Template:Autowiki/Content/Fish/Source"
+
+/datum/autowiki/fish_sources/generate()
+ var/output = ""
+
+ for(var/source_type in GLOB.preset_fish_sources)
+ var/datum/fish_source/source = GLOB.preset_fish_sources[source_type]
+ if(!source.catalog_description)
+ continue
+
+ output += "\n\n" + include_template("Autowiki/FishSource", list(
+ "name" = full_capitalize(source.catalog_description),
+ "difficulty" = source.fishing_difficulty,
+ "contents" = get_contents(source),
+ ))
+
+ ///Used for stuff that isn't fish by default
+ upload_icon(icon('icons/effects/random_spawners.dmi', "questionmark"), FISH_SOURCE_AUTOWIKI_QUESTIONMARK)
+
+ return output
+
+/datum/autowiki/fish_sources/proc/get_contents(datum/fish_source/source)
+ var/output = ""
+ var/list/data = source.generate_wiki_contents(src)
+ sortTim(data, GLOBAL_PROC_REF(cmp_autowiki_fish_sources_content))
+ for(var/list/entry in data)
+ entry[FISH_SOURCE_AUTOWIKI_WEIGHT] = "[round(entry[FISH_SOURCE_AUTOWIKI_WEIGHT], 0.1)]%"
+ var/weight_suffix = entry[FISH_SOURCE_AUTOWIKI_WEIGHT_SUFFIX]
+ if(weight_suffix)
+ entry[FISH_SOURCE_AUTOWIKI_WEIGHT] += " [weight_suffix]"
+ entry -= FISH_SOURCE_AUTOWIKI_WEIGHT
+ output += include_template("Autowiki/FishSourceContents", entry)
+
+ return output
+
+///Sort the autowiki fish entries by their weight. However, duds always come first.
+/proc/cmp_autowiki_fish_sources_content(list/A, list/B)
+ if(A[FISH_SOURCE_AUTOWIKI_NAME] == FISH_SOURCE_AUTOWIKI_DUD)
+ return -1
+ if(B[FISH_SOURCE_AUTOWIKI_NAME] == FISH_SOURCE_AUTOWIKI_DUD)
+ return 1
+ if(A[FISH_SOURCE_AUTOWIKI_NAME] == FISH_SOURCE_AUTOWIKI_OTHER)
+ return 1
+ if(B[FISH_SOURCE_AUTOWIKI_NAME] == FISH_SOURCE_AUTOWIKI_OTHER)
+ return -1
+ return B[FISH_SOURCE_AUTOWIKI_WEIGHT] - A[FISH_SOURCE_AUTOWIKI_WEIGHT]
+
+/datum/autowiki/fish_scan
+ page = "Template:Autowiki/Content/Fish/Scan"
+
+/datum/autowiki/fish_scan/generate()
+ var/output = ""
+
+ var/list/generated_icons = list()
+ var/datum/techweb/techweb = locate(/datum/techweb/admin) in SSresearch.techwebs
+ for(var/scan_type in typesof(/datum/experiment/scanning/fish))
+ techweb.add_experiment(scan_type) //Make sure each followup experiment is available
+ var/datum/experiment/scanning/fish/scan = locate(scan_type) in techweb.available_experiments
+ if(!scan) //Just to be sure, if the scan was already completed.
+ scan = locate(scan_type) in techweb.completed_experiments
+
+ output += "\n\n" + include_template("Autowiki/FishScan", list(
+ "name" = full_capitalize(escape_value(scan.name)),
+ "description" = escape_value(scan.description),
+ "requirements" = build_requirements(scan),
+ "rewards" = build_rewards(scan, generated_icons),
+
+ ))
+
+ return output
+
+/datum/autowiki/fish_scan/proc/build_requirements(datum/experiment/scanning/fish/scan)
+ var/output = ""
+ for(var/obj/item/type as anything in scan.required_atoms)
+ var/name = initial(type.name)
+ //snowflake case because the default holographic fish is called goldfish but we don't want to confuse readers.
+ if(type == /obj/item/fish/holo)
+ name = "holographic Fish"
+ output += include_template("Autowiki/FishScanRequirements", list(
+ "name" = full_capitalize(escape_value(name)),
+ "amount" = scan.required_atoms[type],
+ ))
+ return output
+
+/datum/autowiki/fish_scan/proc/build_rewards(datum/experiment/scanning/fish/scan, list/generated_icons)
+ var/output = ""
+ var/datum/fish_source/portal/reward = GLOB.preset_fish_sources[scan.fish_source_reward]
+ var/filename = SANITIZE_FILENAME("fishing_portal_[reward.radial_state]")
+
+ var/list/unlocks = list()
+ for(var/datum/experiment/unlock as anything in scan.next_experiments)
+ unlocks += initial(unlock.name)
+ output += include_template("Autowiki/FishScanRewards", list(
+ "name" = full_capitalize(escape_value("[reward.radial_name] Dimension")),
+ "icon" = filename,
+ "points" = scan.get_points_reward_text(),
+ "unlock" = english_list(unlocks, nothing_text = "Nothing"),
+ ))
+
+ if(!generated_icons[filename])
+ upload_icon(icon(icon = 'icons/hud/radial_fishing.dmi', icon_state = reward.radial_state), filename)
+ generated_icons[filename] = TRUE
+
+ return output
+
+/datum/autowiki/fish_evolution
+ page = "Template:Autowiki/Content/Fish/Evolution"
+
+/datum/autowiki/fish_evolution/generate()
+ var/output = ""
+
+ for(var/evo_type in GLOB.fish_evolutions)
+ var/datum/fish_evolution/evolution = GLOB.fish_evolutions[evo_type]
+ if(!evolution.show_on_wiki)
+ continue
+
+ output += "\n\n" + include_template("Autowiki/FishEvolution", list(
+ "name" = escape_value(evolution.name),
+ "fish" = get_fish(evo_type),
+ "min_max_temp" = "[evolution.required_temperature_min] - [evolution.required_temperature_max] K",
+ "notes" = escape_value(evolution.conditions_note),
+ "result_icon" = evolution.show_result_on_wiki ? FISH_AUTOWIKI_FILENAME(evolution.new_fish_type) : FISH_SOURCE_AUTOWIKI_QUESTIONMARK,
+ ))
+
+ return output
+
+/datum/autowiki/fish_evolution/proc/get_fish(evo_type)
+ var/output = ""
+
+ for(var/obj/item/fish/fish as anything in GLOB.fishes_by_fish_evolution[evo_type])
+ if(!(initial(fish.fish_flags) & FISH_FLAG_SHOW_IN_CATALOG))
+ continue
+ output += include_template("Autowiki/FishEvolutionCandidate", list(
+ "name" = escape_value(full_capitalize(initial(fish.name))),
+ "icon" = FISH_AUTOWIKI_FILENAME(fish),
+ ))
+
+ return output
+
+/datum/autowiki/fish_lure
+ page = "Template:Autowiki/Content/Fish/Lure"
+
+/datum/autowiki/fish_lure/generate()
+ var/output = ""
+
+ for(var/obj/item/fishing_lure/lure as anything in SSfishing.lure_catchables)
+ var/state = initial(lure.icon_state)
+ var/filename = SANITIZE_FILENAME("[state]_wiki_lure")
+ output += "\n\n" + include_template("Autowiki/FishLure", list(
+ "name" = escape_value(full_capitalize(initial(lure.name))),
+ "desc" = escape_value(initial(lure.name)),
+ "icon" = filename,
+ "catchables" = build_catchables(SSfishing.lure_catchables[lure]),
+ ))
+
+ upload_icon(icon(icon = initial(lure.icon), icon_state = state), filename)
+
+ return output
+
+/datum/autowiki/fish_lure/proc/build_catchables(list/catchables)
+ var/output = ""
+
+ for(var/obj/item/fish/fish as anything in catchables)
+ if(!(initial(fish.fish_flags) & FISH_FLAG_SHOW_IN_CATALOG))
+ continue
+ output += include_template("Autowiki/FishLureCatchables", list(
+ "name" = escape_value(full_capitalize(initial(fish.name))),
+ "icon" = FISH_AUTOWIKI_FILENAME(fish),
+ ))
+
+ return output
diff --git a/code/modules/autowiki/pages/soup.dm b/code/modules/autowiki/pages/soup.dm
index f67d00e97a057..754beb3a82adb 100644
--- a/code/modules/autowiki/pages/soup.dm
+++ b/code/modules/autowiki/pages/soup.dm
@@ -16,6 +16,7 @@
var/container_for_images = /obj/item/reagent_containers/cup/bowl
+ var/list/already_generated_icons = list()
for(var/soup_recipe_type in subtypesof(/datum/chemical_reaction/food/soup))
var/datum/chemical_reaction/food/soup/soup_recipe = new soup_recipe_type()
// Used to determine what icon is displayed on the wiki
@@ -123,14 +124,17 @@
template_list["results"] = escape_value(compiled_results)
// -- While we're here, generate an icon of the bowl --
- if(!soup_icon_state)
- var/obj/item/reagent_containers/cup/bowl/soup_bowl = new()
- soup_bowl.reagents.add_reagent(result_soup_type, soup_bowl.reagents.maximum_volume)
- upload_icon(getFlatIcon(soup_bowl, no_anim = TRUE), filename)
- qdel(soup_bowl)
- else
- var/image/compiled_image = image(icon = soup_icon, icon_state = soup_icon_state)
- upload_icon(getFlatIcon(compiled_image, no_anim = TRUE), filename)
+
+ if(!already_generated_icons[filename])
+ if(!soup_icon_state)
+ var/obj/item/reagent_containers/cup/bowl/soup_bowl = new()
+ soup_bowl.reagents.add_reagent(result_soup_type, soup_bowl.reagents.maximum_volume)
+ upload_icon(getFlatIcon(soup_bowl, no_anim = TRUE), filename)
+ qdel(soup_bowl)
+ else
+ var/image/compiled_image = image(icon = soup_icon, icon_state = soup_icon_state)
+ upload_icon(getFlatIcon(compiled_image, no_anim = TRUE), filename)
+ already_generated_icons[filename] = TRUE
// -- Cleanup --
qdel(soup_recipe)
diff --git a/code/modules/autowiki/pages/vending.dm b/code/modules/autowiki/pages/vending.dm
index 0a8dd3db0a9d5..e110afa760ea2 100644
--- a/code/modules/autowiki/pages/vending.dm
+++ b/code/modules/autowiki/pages/vending.dm
@@ -10,7 +10,10 @@
// So we put it inside, something
var/obj/parent = new
- for (var/vending_type in sort_list(subtypesof(/obj/machinery/vending), GLOBAL_PROC_REF(cmp_typepaths_asc)))
+ for (var/obj/machinery/vending/vending_type as anything in sort_list(subtypesof(/obj/machinery/vending), GLOBAL_PROC_REF(cmp_typepaths_asc)))
+ var/obj/machinery/vending/parent_machine = type2parent(vending_type)
+ if(initial(parent_machine.name) == initial(vending_type.name))
+ continue //Same name, likely just a slightly touched up subtype for specific maps.
var/obj/machinery/vending/vending_machine = new vending_type(parent)
vending_machine.use_power = FALSE
vending_machine.update_icon(UPDATE_ICON_STATE)
diff --git a/code/modules/awaymissions/away_props.dm b/code/modules/awaymissions/away_props.dm
index 843e55b963fe4..90fc3031088f2 100644
--- a/code/modules/awaymissions/away_props.dm
+++ b/code/modules/awaymissions/away_props.dm
@@ -137,3 +137,16 @@
if(!istype(mover))
return
return isnull(mover.ckey) == reverse
+
+/obj/effect/invisible_wall // why didnt we have this already
+ name = "invisible wall"
+ desc = "You shall not pass"
+ icon = 'icons/effects/mapping_helpers.dmi'
+ icon_state = "blocker"
+ color = COLOR_BLUE_LIGHT
+ invisibility = INVISIBILITY_MAXIMUM
+ anchored = TRUE
+
+/obj/effect/invisible_wall/CanAllowThrough(mob/living/mover, border_dir)
+ ..()
+ return FALSE // NO
diff --git a/code/modules/awaymissions/cordon.dm b/code/modules/awaymissions/cordon.dm
index 285d0d49e1051..738efa1d7c21f 100644
--- a/code/modules/awaymissions/cordon.dm
+++ b/code/modules/awaymissions/cordon.dm
@@ -49,10 +49,10 @@
/turf/cordon/Bumped(atom/movable/bumped_atom)
. = ..()
- if(HAS_TRAIT(bumped_atom, TRAIT_FREE_HYPERSPACE_SOFTCORDON_MOVEMENT)) //we could feasibly reach the border, so just dont
+ if(HAS_TRAIT(bumped_atom, TRAIT_FREE_HYPERSPACE_SOFTCORDON_MOVEMENT)) //we could feasibly reach the border, so just don't
dump_in_space(bumped_atom)
-/// Area used in conjuction with the cordon turf to create a fully functioning world border.
+/// Area used in conjunction with the cordon turf to create a fully functioning world border.
/area/misc/cordon
name = "CORDON"
icon_state = "cordon"
diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm
index 5e3cf1bb21eac..00d23b592c00e 100644
--- a/code/modules/awaymissions/gateway.dm
+++ b/code/modules/awaymissions/gateway.dm
@@ -22,7 +22,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
/datum/gateway_destination/proc/get_available_reason()
. = "Unreachable"
if(world.time - SSticker.round_start_time < wait)
- playsound(src, 'sound/effects/gateway_calibrating.ogg', 80, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ playsound(src, 'sound/machines/gateway/gateway_calibrating.ogg', 80, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
. = "Connection desynchronized. Recalibration in progress."
/* Check if the movable is allowed to arrive at this destination (exile implants mostly) */
@@ -135,7 +135,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
/obj/effect/gateway_portal_bumper/Bumped(atom/movable/AM)
if(get_dir(src,AM) == gateway?.dir)
- playsound(src, 'sound/effects/gateway_travel.ogg', 70, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ playsound(src, 'sound/machines/gateway/gateway_travel.ogg', 70, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
gateway.Transfer(AM)
/obj/effect/gateway_portal_bumper/Destroy(force)
@@ -200,7 +200,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
/obj/machinery/gateway/proc/deactivate()
var/datum/gateway_destination/dest = target
target = null
- playsound(src, 'sound/effects/gateway_close.ogg', 140, TRUE, TRUE, SOUND_RANGE)
+ playsound(src, 'sound/machines/gateway/gateway_close.ogg', 140, TRUE, TRUE, SOUND_RANGE)
dest.deactivate(src)
QDEL_NULL(portal)
update_use_power(IDLE_POWER_USE)
@@ -263,7 +263,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
target.activate(destination)
portal_visuals.setup_visuals(target)
transport_active = TRUE
- playsound(src, 'sound/effects/gateway_open.ogg', 140, TRUE, TRUE, SOUND_RANGE)
+ playsound(src, 'sound/machines/gateway/gateway_open.ogg', 140, TRUE, TRUE, SOUND_RANGE)
generate_bumper()
update_use_power(ACTIVE_POWER_USE)
update_appearance()
@@ -306,7 +306,7 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
if(calibrated)
to_chat(user, span_alert("The gate is already calibrated, there is no work for you to do here."))
else
- playsound(src, 'sound/effects/gateway_calibrated.ogg', 80, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ playsound(src, 'sound/machines/gateway/gateway_calibrated.ogg', 80, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
to_chat(user, "[span_boldnotice("Recalibration successful!")]: \black This gate's systems have been fine tuned. Travel to this gate will now be on target.")
calibrated = TRUE
return TRUE
diff --git a/code/modules/awaymissions/mission_code/Beach.dm b/code/modules/awaymissions/mission_code/Beach.dm
index 8e05cfe4a5eb1..7f0e27c090ae2 100644
--- a/code/modules/awaymissions/mission_code/Beach.dm
+++ b/code/modules/awaymissions/mission_code/Beach.dm
@@ -6,7 +6,7 @@
base_lighting_color = "#FFFFCC"
requires_power = FALSE
has_gravity = STANDARD_GRAVITY
- ambientsounds = list('sound/ambience/shore.ogg', 'sound/ambience/seag1.ogg','sound/ambience/seag2.ogg','sound/ambience/seag2.ogg','sound/ambience/ambiodd.ogg','sound/ambience/ambinice.ogg')
+ ambientsounds = list('sound/ambience/beach/shore.ogg', 'sound/ambience/beach/seag1.ogg','sound/ambience/beach/seag2.ogg','sound/ambience/beach/seag3.ogg','sound/ambience/misc/ambiodd.ogg','sound/ambience/medical/ambinice.ogg')
/obj/item/paper/fluff/old_pirate_note
name = "rum-stained letter"
diff --git a/code/modules/awaymissions/mission_code/Cabin.dm b/code/modules/awaymissions/mission_code/Cabin.dm
index cf6a6a3c9c7a4..2525e679cad64 100644
--- a/code/modules/awaymissions/mission_code/Cabin.dm
+++ b/code/modules/awaymissions/mission_code/Cabin.dm
@@ -90,7 +90,7 @@
name = "lumbermill saw"
desc = "Faster then the cartoons!"
obj_flags = CAN_BE_HIT | EMAGGED
- item_recycle_sound = 'sound/weapons/chainsawhit.ogg'
+ item_recycle_sound = 'sound/items/weapons/chainsawhit.ogg'
/obj/machinery/recycler/lumbermill/recycle_item(obj/item/grown/log/L)
if(!istype(L))
diff --git a/code/modules/awaymissions/mission_code/centcomAway.dm b/code/modules/awaymissions/mission_code/centcomAway.dm
index 3b0d3e8a810cb..1fc54d7ef9679 100644
--- a/code/modules/awaymissions/mission_code/centcomAway.dm
+++ b/code/modules/awaymissions/mission_code/centcomAway.dm
@@ -7,32 +7,32 @@
/area/awaymission/centcom_away/general
name = "XCC-P5831"
- ambientsounds = list('sound/ambience/ambigen2.ogg')
+ ambientsounds = list('sound/ambience/general/ambigen2.ogg')
/area/awaymission/centcom_away/maint
name = "XCC-P5831 Maintenance"
icon_state = "away1"
- ambientsounds = list('sound/ambience/ambisin1.ogg')
+ ambientsounds = list('sound/ambience/engineering/ambisin1.ogg')
/area/awaymission/centcom_away/thunderdome
name = "XCC-P5831 Thunderdome"
icon_state = "away2"
- ambientsounds = list('sound/ambience/ambisin2.ogg')
+ ambientsounds = list('sound/ambience/engineering/ambisin2.ogg')
/area/awaymission/centcom_away/cafe
name = "XCC-P5831 Kitchen Arena"
icon_state = "away3"
- ambientsounds = list('sound/ambience/ambisin3.ogg')
+ ambientsounds = list('sound/ambience/engineering/ambisin3.ogg')
/area/awaymission/centcom_away/courtroom
name = "XCC-P5831 Courtroom"
icon_state = "away4"
- ambientsounds = list('sound/ambience/ambisin4.ogg')
+ ambientsounds = list('sound/ambience/engineering/ambisin4.ogg')
/area/awaymission/centcom_away/hangar
name = "XCC-P5831 Hangars"
icon_state = "away4"
- ambientsounds = list('sound/ambience/ambigen4.ogg')
+ ambientsounds = list('sound/ambience/general/ambigen4.ogg')
//centcomAway items
diff --git a/code/modules/awaymissions/mission_code/moonoutpost19.dm b/code/modules/awaymissions/mission_code/moonoutpost19.dm
index 17385bc70bc40..446b9916a4577 100644
--- a/code/modules/awaymissions/mission_code/moonoutpost19.dm
+++ b/code/modules/awaymissions/mission_code/moonoutpost19.dm
@@ -34,7 +34,7 @@
power_environ = FALSE
power_equip = FALSE
power_light = FALSE
- ambientsounds = list('sound/ambience/ambimine.ogg')
+ ambientsounds = list('sound/ambience/ruin/ambimine.ogg')
icon_state = "awaycontent5"
outdoors = TRUE
@@ -56,7 +56,7 @@
power_environ = FALSE
power_equip = FALSE
power_light = FALSE
- ambientsounds = list('sound/ambience/ambimine.ogg')
+ ambientsounds = list('sound/ambience/ruin/ambimine.ogg')
icon_state = "awaycontent8"
//Fluff objects/structures.
diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm
index cc35b4a79efca..db48b1b31d1fb 100644
--- a/code/modules/awaymissions/mission_code/snowdin.dm
+++ b/code/modules/awaymissions/mission_code/snowdin.dm
@@ -306,13 +306,13 @@
PRESET /datum/preset_holoimage/nanotrasenprivatesecurity
NAME James Reed
DELAY 10
- SOUND sound/weapons/laser.ogg
+ SOUND sound/items/weapons/laser.ogg
DELAY 10
- SOUND sound/weapons/laser.ogg
+ SOUND sound/items/weapons/laser.ogg
DELAY 10
- SOUND sound/weapons/laser.ogg
+ SOUND sound/items/weapons/laser.ogg
DELAY 10
- SOUND sound/weapons/laser.ogg
+ SOUND sound/items/weapons/laser.ogg
DELAY 15
SAY Just go! I'll keep it busy, there's an outpost south of here with an elevator to the surface.
NAME Jacob Ullman
@@ -333,9 +333,9 @@
DELAY 10
SAY You don't need to tell me twice, I just need to swipe access and then..
DELAY 15
- SOUND sound/effects/glassbr1.ogg
+ SOUND sound/effects/glass/glassbr1.ogg
DELAY 10
- SOUND sound/effects/glassbr2.ogg
+ SOUND sound/effects/glass/glassbr2.ogg
DELAY 15
NAME Jacob Ullman
DELAY 10
@@ -347,13 +347,13 @@
DELAY 10
SAY DON'T FUCKING RUSH ME ALRIGHT IT'S BEING CALLED.
DELAY 15
- SOUND sound/effects/huuu.ogg
+ SOUND sound/mobs/non-humanoids/frog/huuu.ogg
DELAY 5
- SOUND sound/effects/huuu.ogg
+ SOUND sound/mobs/non-humanoids/frog/huuu.ogg
DELAY 15
SOUND sound/effects/woodhit.ogg
DELAY 2
- SOUND sound/effects/bodyfall3.ogg
+ SOUND sound/effects/bodyfall/bodyfall3.ogg
DELAY 5
SOUND sound/effects/meow1.ogg
DELAY 15
diff --git a/code/modules/balloon_alert/balloon_alert.dm b/code/modules/balloon_alert/balloon_alert.dm
index db8c529198631..cf890c0130807 100644
--- a/code/modules/balloon_alert/balloon_alert.dm
+++ b/code/modules/balloon_alert/balloon_alert.dm
@@ -44,7 +44,7 @@
if (isnull(viewer_client))
return
- var/bound_width = world.icon_size
+ var/bound_width = ICON_SIZE_X
if (ismovable(src))
var/atom/movable/movable_source = src
bound_width = movable_source.bound_width
@@ -64,7 +64,7 @@
animate(
balloon_alert,
- pixel_y = world.icon_size * 1.2,
+ pixel_y = ICON_SIZE_Y * 1.2,
time = BALLOON_TEXT_TOTAL_LIFETIME(length_mult),
easing = SINE_EASING | EASE_OUT,
)
diff --git a/code/modules/basketball/basketball.dm b/code/modules/basketball/basketball.dm
index 35579dc448282..c69c2fd1f7833 100644
--- a/code/modules/basketball/basketball.dm
+++ b/code/modules/basketball/basketball.dm
@@ -8,7 +8,6 @@
inhand_icon_state = "basketball"
desc = "Here's your chance, do your dance at the Space Jam."
w_class = WEIGHT_CLASS_BULKY //Stops people from hiding it in their bags/pockets
- item_flags = XENOMORPH_HOLDABLE // playing ball against a xeno is rigged since they cannot be disarmed
/// The person dribbling the basketball
var/mob/living/wielder
/// So the basketball doesn't make sound every step
diff --git a/code/modules/basketball/controller.dm b/code/modules/basketball/controller.dm
index 53e89d182a3e7..4373c8d784a8d 100644
--- a/code/modules/basketball/controller.dm
+++ b/code/modules/basketball/controller.dm
@@ -194,7 +194,7 @@ GLOBAL_VAR(basketball_game)
player_client.prefs.safe_transfer_prefs_to(baller, is_antag = TRUE)
baller.key = player_key
- SEND_SOUND(baller, sound('sound/misc/whistle.ogg', volume=30))
+ SEND_SOUND(baller, sound('sound/items/whistle/whistle.ogg', volume=30))
if(is_player_referee)
to_chat(baller, span_notice("You are a referee. Make sure the teams play fair and use your whistle to call fouls appropriately."))
else
diff --git a/code/modules/basketball/hoop.dm b/code/modules/basketball/hoop.dm
index 72669df017d90..edc106c155e63 100644
--- a/code/modules/basketball/hoop.dm
+++ b/code/modules/basketball/hoop.dm
@@ -38,7 +38,7 @@
/obj/structure/hoop/proc/score(obj/item/toy/basketball/ball, mob/living/baller, points)
// we still play buzzer sound regardless of the object
- playsound(src, 'sound/machines/scanbuzz.ogg', 100, FALSE)
+ playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 100, FALSE)
if(!istype(ball))
return
@@ -130,7 +130,7 @@
loser.forceMove(loc)
loser.Paralyze(100)
visible_message(span_danger("[baller] dunks [loser] into \the [src]!"))
- playsound(src, 'sound/machines/scanbuzz.ogg', 100, FALSE)
+ playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 100, FALSE)
baller.adjustStaminaLoss(STAMINA_COST_DUNKING_MOB)
baller.stop_pulling()
diff --git a/code/modules/basketball/referee.dm b/code/modules/basketball/referee.dm
index 666ff628682b8..2c0a28d7331e1 100644
--- a/code/modules/basketball/referee.dm
+++ b/code/modules/basketball/referee.dm
@@ -34,7 +34,7 @@
/datum/action/innate/timeout/do_ability(mob/living/caller, mob/living/carbon/human/target)
caller.say("FOUL BY [target]!", forced = "whistle")
- playsound(caller, 'sound/misc/whistle.ogg', 30, FALSE, 4)
+ playsound(caller, 'sound/items/whistle/whistle.ogg', 30, FALSE, 4)
new /obj/effect/timestop(get_turf(target), 0, 5 SECONDS, list(caller), TRUE, TRUE)
diff --git a/code/modules/bitrunning/antagonists/netguardian.dm b/code/modules/bitrunning/antagonists/netguardian.dm
index f0ea7822985f4..0f546f87b1dc9 100644
--- a/code/modules/bitrunning/antagonists/netguardian.dm
+++ b/code/modules/bitrunning/antagonists/netguardian.dm
@@ -22,7 +22,7 @@
attack_verb_continuous = "drills"
attack_verb_simple = "drills"
- attack_sound = 'sound/weapons/drill.ogg'
+ attack_sound = 'sound/items/weapons/drill.ogg'
attack_vis_effect = ATTACK_EFFECT_MECHFIRE
verb_say = "states"
verb_ask = "queries"
@@ -58,7 +58,7 @@
ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT)
AddComponent(/datum/component/ranged_attacks, \
casing_type = /obj/item/ammo_casing/c46x30mm, \
- projectile_sound = 'sound/weapons/gun/smg/shot.ogg', \
+ projectile_sound = 'sound/items/weapons/gun/smg/shot.ogg', \
burst_shots = 6 \
)
@@ -71,7 +71,7 @@
/mob/living/basic/netguardian/death(gibbed)
do_sparks(number = 3, cardinal_only = TRUE, source = src)
- playsound(src, 'sound/mecha/weapdestr.ogg', 100)
+ playsound(src, 'sound/vehicles/mecha/weapdestr.ogg', 100)
return ..()
/mob/living/basic/netguardian/update_overlays()
@@ -91,7 +91,7 @@
/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/netguardian/Activate(atom/target_atom)
var/mob/living/player = owner
- playsound(player, 'sound/mecha/skyfall_power_up.ogg', 120)
+ playsound(player, 'sound/vehicles/mecha/skyfall_power_up.ogg', 120)
player.say("target acquired.", "machine")
var/overlay_icon = 'icons/mob/nonhuman-player/netguardian.dmi'
diff --git a/code/modules/bitrunning/areas.dm b/code/modules/bitrunning/areas.dm
index 4fcf0a0496e47..0656f9d65b389 100644
--- a/code/modules/bitrunning/areas.dm
+++ b/code/modules/bitrunning/areas.dm
@@ -14,7 +14,7 @@
name = "Virtual Domain Ruins"
icon_state = "bit_ruin"
icon = 'icons/area/areas_station.dmi'
- area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA
+ area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA | UNLIMITED_FISHING
has_gravity = STANDARD_GRAVITY
requires_power = FALSE
@@ -26,7 +26,7 @@
/area/virtual_domain/safehouse
name = "Virtual Domain Safehouse"
- area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | VIRTUAL_SAFE_AREA
+ area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | VIRTUAL_SAFE_AREA | UNLIMITED_FISHING
icon_state = "bit_safe"
requires_power = FALSE
sound_environment = SOUND_ENVIRONMENT_ROOM
@@ -36,30 +36,30 @@
/area/lavaland/surface/outdoors/virtual_domain
name = "Virtual Domain Lava Ruins"
icon_state = "bit_ruin"
- area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA
+ area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA | UNLIMITED_FISHING
/area/icemoon/underground/explored/virtual_domain
name = "Virtual Domain Ice Ruins"
icon_state = "bit_ice"
- area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA
+ area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA | UNLIMITED_FISHING
/area/ruin/space/virtual_domain
name = "Virtual Domain Unexplored Location"
icon = 'icons/area/areas_station.dmi'
icon_state = "bit_ruin"
- area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA
+ area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA | UNLIMITED_FISHING
/area/space/virtual_domain
name = "Virtual Domain Space"
icon = 'icons/area/areas_station.dmi'
icon_state = "bit_space"
- area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA
+ area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | HIDDEN_AREA | UNLIMITED_FISHING
///Areas that virtual entities should not be in
/area/virtual_domain/protected_space
name = "Virtual Domain Safe Zone"
- area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | VIRTUAL_SAFE_AREA
+ area_flags = UNIQUE_AREA | NOTELEPORT | EVENT_PROTECTED | VIRTUAL_SAFE_AREA | UNLIMITED_FISHING
icon_state = "bit_safe"
/area/virtual_domain/protected_space/fullbright
diff --git a/code/modules/bitrunning/components/avatar_connection.dm b/code/modules/bitrunning/components/avatar_connection.dm
index abf3a7809fcda..15c267cf0c8dd 100644
--- a/code/modules/bitrunning/components/avatar_connection.dm
+++ b/code/modules/bitrunning/components/avatar_connection.dm
@@ -69,7 +69,7 @@
for(var/skill_type in old_mind.known_skills)
avatar.mind.set_experience(skill_type, old_mind.get_skill_exp(skill_type), silent = TRUE)
- avatar.playsound_local(avatar, 'sound/magic/blink.ogg', 25, TRUE)
+ avatar.playsound_local(avatar, 'sound/effects/magic/blink.ogg', 25, TRUE)
avatar.set_static_vision(2 SECONDS)
avatar.set_temp_blindness(1 SECONDS) // I'm in
@@ -134,7 +134,7 @@
SIGNAL_HANDLER
var/mob/living/avatar = parent
- avatar.playsound_local(avatar, 'sound/machines/terminal_success.ogg', 50, vary = TRUE)
+ avatar.playsound_local(avatar, 'sound/machines/terminal/terminal_success.ogg', 50, vary = TRUE)
avatar.throw_alert(
ALERT_BITRUNNER_COMPLETED,
/atom/movable/screen/alert/bitrunning/qserver_domain_complete,
@@ -179,7 +179,7 @@
SIGNAL_HANDLER
var/mob/living/avatar = parent
- avatar.playsound_local(avatar, 'sound/machines/terminal_alert.ogg', 50, vary = TRUE)
+ avatar.playsound_local(avatar, 'sound/machines/terminal/terminal_alert.ogg', 50, vary = TRUE)
var/atom/movable/screen/alert/bitrunning/alert = avatar.throw_alert(
ALERT_BITRUNNER_CROWBAR,
/atom/movable/screen/alert/bitrunning,
@@ -229,7 +229,7 @@
SIGNAL_HANDLER
var/mob/living/avatar = parent
- avatar.playsound_local(avatar, 'sound/machines/terminal_alert.ogg', 50, vary = TRUE)
+ avatar.playsound_local(avatar, 'sound/machines/terminal/terminal_alert.ogg', 50, vary = TRUE)
var/atom/movable/screen/alert/bitrunning/alert = avatar.throw_alert(
ALERT_BITRUNNER_SHUTDOWN,
/atom/movable/screen/alert/bitrunning,
@@ -244,7 +244,7 @@
SIGNAL_HANDLER
var/mob/living/avatar = parent
- avatar.playsound_local(avatar, 'sound/machines/terminal_alert.ogg', 50, vary = TRUE)
+ avatar.playsound_local(avatar, 'sound/machines/terminal/terminal_alert.ogg', 50, vary = TRUE)
var/atom/movable/screen/alert/bitrunning/alert = avatar.throw_alert(
ALERT_BITRUNNER_BREACH,
/atom/movable/screen/alert/bitrunning,
diff --git a/code/modules/bitrunning/components/bitrunning_points.dm b/code/modules/bitrunning/components/bitrunning_points.dm
index ea8f63f76d8df..ae20ec41fb4a3 100644
--- a/code/modules/bitrunning/components/bitrunning_points.dm
+++ b/code/modules/bitrunning/components/bitrunning_points.dm
@@ -28,7 +28,7 @@
/// Spawns the crate with some effects
/datum/component/bitrunning_points/proc/reveal()
- playsound(src, 'sound/magic/blink.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/magic/blink.ogg', 50, TRUE)
var/turf/tile = parent
var/obj/structure/closet/crate/secure/bitrunning/encrypted/crate = new()
diff --git a/code/modules/bitrunning/event.dm b/code/modules/bitrunning/event.dm
index 16190851f3720..370957e2ebb0f 100644
--- a/code/modules/bitrunning/event.dm
+++ b/code/modules/bitrunning/event.dm
@@ -61,7 +61,7 @@
var/total = 0
for(var/datum/weakref/server_ref in cyber_control.active_servers)
var/obj/machinery/quantum_server/server = server_ref?.resolve()
- if(isnull(server))
+ if(isnull(server) || QDELETED(server))
continue
total += length(server.mutation_candidate_refs)
diff --git a/code/modules/bitrunning/netpod/container.dm b/code/modules/bitrunning/netpod/container.dm
index 6165544544511..b9c262a9fb190 100644
--- a/code/modules/bitrunning/netpod/container.dm
+++ b/code/modules/bitrunning/netpod/container.dm
@@ -17,7 +17,7 @@
/obj/machinery/netpod/open_machine(drop = TRUE, density_to_set = FALSE)
- playsound(src, 'sound/machines/tramopen.ogg', 60, TRUE, frequency = 65000)
+ playsound(src, 'sound/machines/tram/tramopen.ogg', 60, TRUE, frequency = 65000)
flick("[base_icon_state]_opening", src)
SEND_SIGNAL(src, COMSIG_BITRUNNER_NETPOD_OPENED)
update_use_power(IDLE_POWER_USE)
@@ -29,7 +29,7 @@
if(!state_open || panel_open || !is_operational || !iscarbon(user))
return
- playsound(src, 'sound/machines/tramclose.ogg', 60, TRUE, frequency = 65000)
+ playsound(src, 'sound/machines/tram/tramclose.ogg', 60, TRUE, frequency = 65000)
flick("[base_icon_state]_closing", src)
..()
@@ -52,7 +52,7 @@
span_notice("You start to pry open [src]."),
span_notice("You hear loud prying on metal.")
)
- playsound(src, 'sound/machines/airlock_alien_prying.ogg', 100, TRUE)
+ playsound(src, 'sound/machines/airlock/airlock_alien_prying.ogg', 100, TRUE)
SEND_SIGNAL(src, COMSIG_BITRUNNER_CROWBAR_ALERT, pryer)
@@ -67,7 +67,7 @@
/// Closes the machine without shoving in an occupant
/obj/machinery/netpod/proc/shut_pod()
state_open = FALSE
- playsound(src, 'sound/machines/tramclose.ogg', 60, TRUE, frequency = 65000)
+ playsound(src, 'sound/machines/tram/tramclose.ogg', 60, TRUE, frequency = 65000)
flick("[base_icon_state]_closing", src)
set_density(TRUE)
diff --git a/code/modules/bitrunning/netpod/utils.dm b/code/modules/bitrunning/netpod/utils.dm
index c593b457e9cc1..fa271748e78ad 100644
--- a/code/modules/bitrunning/netpod/utils.dm
+++ b/code/modules/bitrunning/netpod/utils.dm
@@ -29,7 +29,7 @@
open_machine()
return
- mob_occupant.playsound_local(src, 'sound/magic/blink.ogg', 25, TRUE)
+ mob_occupant.playsound_local(src, 'sound/effects/magic/blink.ogg', 25, TRUE)
mob_occupant.set_static_vision(2 SECONDS)
mob_occupant.set_temp_blindness(1 SECONDS)
mob_occupant.Paralyze(2 SECONDS)
diff --git a/code/modules/bitrunning/objects/byteforge.dm b/code/modules/bitrunning/objects/byteforge.dm
index b971cdae75ddc..cc18d2011a294 100644
--- a/code/modules/bitrunning/objects/byteforge.dm
+++ b/code/modules/bitrunning/objects/byteforge.dm
@@ -26,7 +26,7 @@
/// Does some sparks after it's done
/obj/machinery/byteforge/proc/flash(atom/movable/thing)
- playsound(src, 'sound/magic/blink.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/magic/blink.ogg', 50, TRUE)
var/datum/effect_system/spark_spread/quantum/sparks = new()
sparks.set_up(5, 1, loc)
diff --git a/code/modules/bitrunning/objects/clothing.dm b/code/modules/bitrunning/objects/clothing.dm
index de2b6789d5812..731b7dc2cca58 100644
--- a/code/modules/bitrunning/objects/clothing.dm
+++ b/code/modules/bitrunning/objects/clothing.dm
@@ -8,3 +8,4 @@
name = "trenchcoat"
desc = "A long, black trenchcoat. Makes you feel like you're the one, but you're not."
icon_state = "trenchcoat"
+ flags_inv = HIDEBELT
diff --git a/code/modules/bitrunning/objects/landmarks.dm b/code/modules/bitrunning/objects/landmarks.dm
index ea55b96979edd..170e7ab3075b1 100644
--- a/code/modules/bitrunning/objects/landmarks.dm
+++ b/code/modules/bitrunning/objects/landmarks.dm
@@ -72,11 +72,13 @@
encrypted_crate.abstract_move(selected_crate.loc)
selected_crate.abstract_move(original_location)
+
/// A location for mobs to spawn.
/obj/effect/landmark/bitrunning/mob_segment
name = "Bitrunning modular mob segment"
icon_state = "mob_segment"
+
/// Bitrunning safehouses. Typically 7x6 rooms with a single entrance.
/obj/modular_map_root/safehouse
config_file = "strings/modular_maps/safehouse.toml"
diff --git a/code/modules/bitrunning/objects/loot_box.dm b/code/modules/bitrunning/objects/loot_box.dm
index 200e0b259fbda..e142a3e93a43c 100644
--- a/code/modules/bitrunning/objects/loot_box.dm
+++ b/code/modules/bitrunning/objects/loot_box.dm
@@ -47,7 +47,7 @@
atom_storage.max_total_storage = 3
atom_storage.locked = STORAGE_NOT_LOCKED
icon_state = icon_closed
- playsound(src, 'sound/magic/blink.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/magic/blink.ogg', 50, TRUE)
/obj/item/storage/lockbox/bitrunning/decrypted/PopulateContents()
var/choice = SSbitrunning.pick_secondary_loot(source_domain)
diff --git a/code/modules/bitrunning/objects/loot_crate.dm b/code/modules/bitrunning/objects/loot_crate.dm
index db3f927e02153..158da4e29f0ff 100644
--- a/code/modules/bitrunning/objects/loot_crate.dm
+++ b/code/modules/bitrunning/objects/loot_crate.dm
@@ -37,7 +37,7 @@
rewards_multiplier = 1,
)
. = ..()
- playsound(src, 'sound/magic/blink.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/magic/blink.ogg', 50, TRUE)
if(isnull(completed_domain))
return
diff --git a/code/modules/bitrunning/orders/tech.dm b/code/modules/bitrunning/orders/tech.dm
index 0b097127ffb71..9dd1db17c799d 100644
--- a/code/modules/bitrunning/orders/tech.dm
+++ b/code/modules/bitrunning/orders/tech.dm
@@ -2,38 +2,38 @@
category_index = CATEGORY_BITRUNNING_TECH
/datum/orderable_item/bitrunning_tech/item_tier1
- cost_per_order = 1000
+ cost_per_order = 750
item_path = /obj/item/bitrunning_disk/item/tier1
desc = "This disk contains a program that lets you equip a medical beamgun, a C4 explosive, or a box of infinite pizza."
/datum/orderable_item/bitrunning_tech/item_tier2
- cost_per_order = 1500
+ cost_per_order = 1250
item_path = /obj/item/bitrunning_disk/item/tier2
desc = "This disk contains a program that lets you equip a luxury medipen, a pistol, or an armour vest."
/datum/orderable_item/bitrunning_tech/item_tier3
- cost_per_order = 2500
+ cost_per_order = 2000
item_path = /obj/item/bitrunning_disk/item/tier3
desc = "This disk contains a program that lets you equip an advanced energy gun, a dual bladed energy sword, or a minibomb."
/datum/orderable_item/bitrunning_tech/ability_tier1
- cost_per_order = 1000
+ cost_per_order = 750
item_path = /obj/item/bitrunning_disk/ability/tier1
desc = "This disk contains a program that lets you cast Summon Cheese or Lesser Heal."
/datum/orderable_item/bitrunning_tech/ability_tier2
- cost_per_order = 1800
+ cost_per_order = 1500
item_path = /obj/item/bitrunning_disk/ability/tier2
desc = "This disk contains a program that lets you cast Fireball, Lightning Bolt, or Forcewall."
/datum/orderable_item/bitrunning_tech/ability_tier3
- cost_per_order = 3200
+ cost_per_order = 2500
item_path = /obj/item/bitrunning_disk/ability/tier3
desc = "This disk contains a program that lets you shapeshift into a lesser ashdrake, or a polar bear."
/datum/orderable_item/bitrunning_tech/flip_skillchip
item_path = /obj/item/skillchip/matrix_taunt
- cost_per_order = 2000
+ cost_per_order = 1500
/datum/orderable_item/bitrunning_tech/pka_mod
item_path = /obj/item/bitrunning_disk/item/pka_mods
@@ -42,7 +42,7 @@
/datum/orderable_item/bitrunning_tech/pka_mod/premium
item_path = /obj/item/bitrunning_disk/item/pka_mods/premium
- cost_per_order = 1800
+ cost_per_order = 1600
desc = "This disk contains a program that lets you equip stronger modkits for the proto-kinetic accelerator. Proto-kinetic accelerator not included."
/datum/orderable_item/bitrunning_tech/pkc_mod
@@ -52,5 +52,5 @@
/datum/orderable_item/bitrunning_tech/pkc_mod/premium
item_path = /obj/item/bitrunning_disk/item/pkc_mods/premium
- cost_per_order = 1800
+ cost_per_order = 1600
desc = "This disk contains a program that lets you equip stronger trophies for the proto-kinetic crusher. Proto-kinetic crusher not included."
diff --git a/code/modules/bitrunning/server/_parent.dm b/code/modules/bitrunning/server/_parent.dm
index 541d36ad5d72c..672a79ba72cef 100644
--- a/code/modules/bitrunning/server/_parent.dm
+++ b/code/modules/bitrunning/server/_parent.dm
@@ -108,7 +108,7 @@
add_overlay(mutable_appearance('icons/obj/machines/bitrunning.dmi', "emag_overlay"))
balloon_alert(user, "system jailbroken...")
- playsound(src, 'sound/effects/sparks1.ogg', 35, vary = TRUE)
+ playsound(src, 'sound/effects/sparks/sparks1.ogg', 35, vary = TRUE)
/obj/machinery/quantum_server/update_appearance(updates)
if(isnull(generated_domain) || !is_operational)
diff --git a/code/modules/bitrunning/server/loot.dm b/code/modules/bitrunning/server/loot.dm
index 0e6ff30cd246a..e4c523099ea54 100644
--- a/code/modules/bitrunning/server/loot.dm
+++ b/code/modules/bitrunning/server/loot.dm
@@ -33,7 +33,7 @@
SEND_SIGNAL(src, COMSIG_BITRUNNER_DOMAIN_COMPLETE, cache, generated_domain.reward_points)
points += generated_domain.reward_points
- playsound(src, 'sound/machines/terminal_success.ogg', 30, vary = TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_success.ogg', 30, vary = TRUE)
var/bonus = calculate_rewards()
diff --git a/code/modules/bitrunning/server/map_handling.dm b/code/modules/bitrunning/server/map_handling.dm
index f98332f7e8d8f..ba2162ef4f52e 100644
--- a/code/modules/bitrunning/server/map_handling.dm
+++ b/code/modules/bitrunning/server/map_handling.dm
@@ -5,12 +5,12 @@
if(!length(avatar_connection_refs))
balloon_alert_to_viewers("powering down domain...")
- playsound(src, 'sound/machines/terminal_off.ogg', 40, vary = TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_off.ogg', 40, vary = TRUE)
reset()
return
balloon_alert_to_viewers("notifying clients...")
- playsound(src, 'sound/machines/terminal_alert.ogg', 100, vary = TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_alert.ogg', 100, vary = TRUE)
user.visible_message(
span_danger("[user] begins depowering the server!"),
span_notice("You start disconnecting clients..."),
@@ -43,7 +43,7 @@
return FALSE
is_ready = FALSE
- playsound(src, 'sound/machines/terminal_processing.ogg', 30, 2)
+ playsound(src, 'sound/machines/terminal/terminal_processing.ogg', 30, 2)
/// If any one of these fail, it reverts the entire process
if(!load_domain(map_key) || !load_map_items() || !load_mob_segments())
@@ -60,7 +60,7 @@
if(prob(spawn_chance))
setup_glitch()
- playsound(src, 'sound/machines/terminal_insert_disc.ogg', 30, vary = TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_insert_disc.ogg', 30, vary = TRUE)
balloon_alert_to_viewers("domain loaded.")
generated_domain.start_time = world.time
points -= generated_domain.cost
diff --git a/code/modules/bitrunning/server/obj_generation.dm b/code/modules/bitrunning/server/obj_generation.dm
index dabac8ae62dd9..baf427855a236 100644
--- a/code/modules/bitrunning/server/obj_generation.dm
+++ b/code/modules/bitrunning/server/obj_generation.dm
@@ -116,9 +116,13 @@
path = pick(generated_domain.mob_modules)
var/datum/modular_mob_segment/segment = new path()
- segment.spawn_mobs(get_turf(landmark))
- mutation_candidate_refs += segment.spawned_mob_refs
+
+ var/list/mob_spawns = landmark.spawn_mobs(get_turf(landmark), segment)
+ if(length(mob_spawns))
+ mutation_candidate_refs += mob_spawns
+
qdel(landmark)
+ qdel(segment)
return TRUE
diff --git a/code/modules/bitrunning/server/threats.dm b/code/modules/bitrunning/server/threats.dm
index 28d91aa4b3714..145cdc9ee2bbf 100644
--- a/code/modules/bitrunning/server/threats.dm
+++ b/code/modules/bitrunning/server/threats.dm
@@ -119,9 +119,9 @@
var/datum/mind/antag_mind = new_mob.mind
antag_mind.add_antag_datum(chosen_role)
antag_mind.special_role = ROLE_GLITCH
- antag_mind.set_assigned_role(SSjob.GetJobType(/datum/job/bitrunning_glitch))
+ antag_mind.set_assigned_role(SSjob.get_job_type(/datum/job/bitrunning_glitch))
- playsound(new_mob, 'sound/magic/ethereal_exit.ogg', 50, vary = TRUE)
+ playsound(new_mob, 'sound/effects/magic/ethereal_exit.ogg', 50, vary = TRUE)
message_admins("[ADMIN_LOOKUPFLW(new_mob)] has been made into virtual antagonist by an event.")
new_mob.log_message("was spawned as a virtual antagonist by an event.", LOG_GAME)
@@ -170,13 +170,13 @@
if(temp_body)
qdel(temp_body)
- do_teleport(antag, get_turf(chosen_forge), forced = TRUE, asoundin = 'sound/magic/ethereal_enter.ogg', asoundout = 'sound/magic/ethereal_exit.ogg', channel = TELEPORT_CHANNEL_QUANTUM)
+ do_teleport(antag, get_turf(chosen_forge), forced = TRUE, asoundin = 'sound/effects/magic/ethereal_enter.ogg', asoundout = 'sound/effects/magic/ethereal_exit.ogg', channel = TELEPORT_CHANNEL_QUANTUM)
/// Removes any invalid candidates from the list
/obj/machinery/quantum_server/proc/validate_mutation_candidates()
for(var/datum/weakref/creature_ref as anything in mutation_candidate_refs)
- var/mob/living/creature = creature_ref.resolve()
+ var/mob/living/creature = creature_ref?.resolve()
if(isnull(creature) || creature.mind)
mutation_candidate_refs.Remove(creature_ref)
diff --git a/code/modules/bitrunning/server/util.dm b/code/modules/bitrunning/server/util.dm
index ab267a34cd330..912f0c1f874fe 100644
--- a/code/modules/bitrunning/server/util.dm
+++ b/code/modules/bitrunning/server/util.dm
@@ -134,7 +134,7 @@
/// Do some magic teleport sparks
/obj/machinery/quantum_server/proc/spark_at_location(obj/cache)
- playsound(cache, 'sound/magic/blink.ogg', 50, vary = TRUE)
+ playsound(cache, 'sound/effects/magic/blink.ogg', 50, vary = TRUE)
var/datum/effect_system/spark_spread/quantum/sparks = new()
sparks.set_up(5, location = get_turf(cache))
sparks.start()
diff --git a/code/modules/bitrunning/spawners.dm b/code/modules/bitrunning/spawners.dm
index 4b5f79a43b186..26288d54a1555 100644
--- a/code/modules/bitrunning/spawners.dm
+++ b/code/modules/bitrunning/spawners.dm
@@ -1,5 +1,5 @@
/obj/effect/mob_spawn/ghost_role/human/virtual_domain
- outfit = /datum/outfit/pirate
+ outfit = /datum/outfit/virtual_pirate
prompt_name = "a virtual domain debug entity"
flavour_text = "You probably shouldn't be seeing this, contact a coder!"
you_are_text = "You are NOT supposed to be here. How did you let this happen?"
@@ -32,6 +32,16 @@
you_are_text = "You are a virtual pirate. Yarrr!"
flavour_text = " There's a LANDLUBBER after yer booty. Stop them!"
+/datum/outfit/virtual_pirate
+ name = "Virtual Pirate"
+ id = /obj/item/card/id/advanced
+ id_trim = /datum/id_trim/pirate
+ uniform = /obj/item/clothing/under/costume/pirate
+ suit = /obj/item/clothing/suit/costume/pirate/armored
+ glasses = /obj/item/clothing/glasses/eyepatch
+ head = /obj/item/clothing/head/costume/pirate/bandana/armored
+ shoes = /obj/item/clothing/shoes/pirate/armored
+
/obj/effect/mob_spawn/ghost_role/human/virtual_domain/pirate/special(mob/living/spawned_mob, mob/mob_possessor)
. = ..()
diff --git a/code/modules/bitrunning/virtual_domain/domains/grassland_hunt.dm b/code/modules/bitrunning/virtual_domain/domains/grassland_hunt.dm
new file mode 100644
index 0000000000000..9c9e1c7171a6c
--- /dev/null
+++ b/code/modules/bitrunning/virtual_domain/domains/grassland_hunt.dm
@@ -0,0 +1,28 @@
+/datum/lazy_template/virtual_domain/grasslands_hunt
+ name = "Grasslands Hunt"
+ desc = "A peaceful hunt in the wilderness."
+ help_text = "As a hunter, you must be able to track and kill your prey. Prove yourself."
+ is_modular = TRUE
+ key = "grasslands_hunt"
+ map_name = "grasslands_hunt"
+ mob_modules = list(/datum/modular_mob_segment/deer)
+
+
+/datum/lazy_template/virtual_domain/grasslands_hunt/setup_domain(list/created_atoms)
+ for(var/obj/effect/landmark/bitrunning/mob_segment/landmark in created_atoms)
+ RegisterSignal(landmark, COMSIG_BITRUNNING_MOB_SEGMENT_SPAWNED, PROC_REF(on_spawned))
+
+
+/// The mob segment has concluded spawning
+/datum/lazy_template/virtual_domain/grasslands_hunt/proc/on_spawned(datum/source, list/mobs)
+ SIGNAL_HANDLER
+
+ for(var/mob/living/fauna as anything in mobs)
+ RegisterSignal(fauna, COMSIG_LIVING_DEATH, PROC_REF(on_death))
+
+
+/// Handles deer being slain
+/datum/lazy_template/virtual_domain/grasslands_hunt/proc/on_death(datum/source)
+ SIGNAL_HANDLER
+
+ add_points(3.5)
diff --git a/code/modules/bitrunning/virtual_domain/domains/meta_central.dm b/code/modules/bitrunning/virtual_domain/domains/meta_central.dm
new file mode 100644
index 0000000000000..dc1029353c320
--- /dev/null
+++ b/code/modules/bitrunning/virtual_domain/domains/meta_central.dm
@@ -0,0 +1,13 @@
+/datum/lazy_template/virtual_domain/meta_central
+ name = "Meta Central"
+ cost = BITRUNNER_COST_LOW
+ desc = "Every so often, workers demand rights from Nanotrasen. This is unprofitable."
+ difficulty = BITRUNNER_DIFFICULTY_LOW
+ forced_outfit = /datum/outfit/job/security/mod
+ help_text = "Respond to the worker's demands with sanctioned violence. Recover valuable materials that may be scattered around. Just remember your training: Always assume guilt, they can confess in medbay... Or something like that."
+ is_modular = TRUE
+ key = "meta_central"
+ map_name = "meta_central"
+ mob_modules = list(/datum/modular_mob_segment/revolutionary)
+ reward_points = BITRUNNER_REWARD_LOW
+ announce_to_ghosts = TRUE
diff --git a/code/modules/bitrunning/virtual_domain/modular_mob_segment.dm b/code/modules/bitrunning/virtual_domain/modular_mob_segment.dm
index b8c5880a69c38..225912797b28a 100644
--- a/code/modules/bitrunning/virtual_domain/modular_mob_segment.dm
+++ b/code/modules/bitrunning/virtual_domain/modular_mob_segment.dm
@@ -3,27 +3,49 @@
#define SPAWN_UNLIKELY 35
#define SPAWN_RARE 10
+
+/// Handles spawning mobs for this landmark. Sends a signal when done.
+/obj/effect/landmark/bitrunning/mob_segment/proc/spawn_mobs(turf/origin, datum/modular_mob_segment/segment)
+ var/list/mob/living/spawned_mobs = list()
+
+ spawned_mobs += segment.spawn_mobs(origin)
+
+ SEND_SIGNAL(src, COMSIG_BITRUNNING_MOB_SEGMENT_SPAWNED, spawned_mobs)
+
+ var/list/datum/weakref/mob_refs = list()
+ for(var/mob/living/spawned as anything in spawned_mobs)
+ if(QDELETED(spawned))
+ continue
+
+ mob_refs += WEAKREF(spawned)
+
+ return mob_refs
+
+
+/**
+ * A list for mob spawning landmarks to use.
+ */
/datum/modular_mob_segment
- /// Spawn no more than this amount
- var/max = 4
/// Set this to false if you want explicitly what's in the list to spawn
var/exact = FALSE
/// The list of mobs to spawn
- var/list/mob/living/mobs = list()
- /// The mobs spawned from this segment
- var/list/spawned_mob_refs = list()
+ var/list/mobs = list()
+ /// Spawn no more than this amount
+ var/max = 4
/// Chance this will spawn (1 - 100)
var/probability = SPAWN_LIKELY
+
/// Spawns mobs in a circle around the location
/datum/modular_mob_segment/proc/spawn_mobs(turf/origin)
if(!prob(probability))
return
- var/total_amount = exact ? rand(1, max) : length(mobs)
+ var/list/mob/living/spawned_mobs = list()
- shuffle_inplace(mobs)
+ var/total_amount = exact ? length(mobs) : rand(1, max)
+ shuffle_inplace(mobs)
var/list/turf/nearby = list()
for(var/turf/tile as anything in RANGE_TURFS(2, origin))
@@ -46,7 +68,10 @@
var/mob/living/mob = new path(destination)
nearby -= destination
- spawned_mob_refs.Add(WEAKREF(mob))
+ spawned_mobs += mob
+
+ return spawned_mobs
+
// Some generic mob segments. If you want to add generic ones for any map, add them here
@@ -55,41 +80,48 @@
/mob/living/basic/pet/gondola,
)
+
/datum/modular_mob_segment/corgis
max = 2
mobs = list(
/mob/living/basic/pet/dog/corgi,
)
+
/datum/modular_mob_segment/monkeys
mobs = list(
/mob/living/carbon/human/species/monkey,
)
+
/datum/modular_mob_segment/syndicate_team
mobs = list(
/mob/living/basic/trooper/syndicate/ranged,
/mob/living/basic/trooper/syndicate/melee,
)
+
/datum/modular_mob_segment/abductor_agents
mobs = list(
/mob/living/basic/trooper/abductor/melee,
/mob/living/basic/trooper/abductor/ranged,
)
+
/datum/modular_mob_segment/syndicate_elite
mobs = list(
/mob/living/basic/trooper/syndicate/melee/sword/space/stormtrooper,
/mob/living/basic/trooper/syndicate/ranged/space/stormtrooper,
)
+
/datum/modular_mob_segment/bears
max = 2
mobs = list(
/mob/living/basic/bear,
)
+
/datum/modular_mob_segment/bees
exact = TRUE
mobs = list(
@@ -100,33 +132,39 @@
/mob/living/basic/bee/queen,
)
+
/datum/modular_mob_segment/bees_toxic
mobs = list(
/mob/living/basic/bee/toxin,
)
+
/datum/modular_mob_segment/blob_spores
mobs = list(
/mob/living/basic/blob_minion,
)
+
/datum/modular_mob_segment/carps
mobs = list(
/mob/living/basic/carp,
)
+
/datum/modular_mob_segment/hivebots
mobs = list(
/mob/living/basic/hivebot,
/mob/living/basic/hivebot/range,
)
+
/datum/modular_mob_segment/hivebots_strong
mobs = list(
/mob/living/basic/hivebot/strong,
/mob/living/basic/hivebot/range,
)
+
/datum/modular_mob_segment/lavaland_assorted
mobs = list(
/mob/living/basic/mining/basilisk,
@@ -135,6 +173,7 @@
/mob/living/basic/mining/lobstrosity,
)
+
/datum/modular_mob_segment/spiders
mobs = list(
/mob/living/basic/spider/giant/ambush,
@@ -144,11 +183,13 @@
/mob/living/basic/spider/giant/midwife,
)
+
/datum/modular_mob_segment/venus_trap
mobs = list(
/mob/living/basic/venus_human_trap,
)
+
/datum/modular_mob_segment/xenos
mobs = list(
/mob/living/basic/alien,
@@ -156,6 +197,20 @@
/mob/living/basic/alien/drone,
)
+
+/datum/modular_mob_segment/deer
+ max = 1
+ mobs = list(
+ /mob/living/basic/deer,
+ )
+
+
+/datum/modular_mob_segment/revolutionary
+ mobs = list(
+ /mob/living/basic/revolutionary,
+ )
+
+
#undef SPAWN_ALWAYS
#undef SPAWN_LIKELY
#undef SPAWN_UNLIKELY
diff --git a/code/modules/buildmode/submodes/map_export.dm b/code/modules/buildmode/submodes/map_export.dm
index e0cb6629e1902..19c0d57b57f73 100644
--- a/code/modules/buildmode/submodes/map_export.dm
+++ b/code/modules/buildmode/submodes/map_export.dm
@@ -19,7 +19,7 @@
if (!what_to_change)
return
save_flag ^= options[what_to_change]
- to_chat(builder, "[what_to_change] is now [save_flag & options[what_to_change] ? "ENABLED" : "DISABLED"].")
+ to_chat(builder, span_notice("[what_to_change] is now [save_flag & options[what_to_change] ? "ENABLED" : "DISABLED"]."))
/datum/buildmode_mode/map_export/show_help(client/builder)
to_chat(builder, span_purple(examine_block(
diff --git a/code/modules/capture_the_flag/ctf_controller.dm b/code/modules/capture_the_flag/ctf_controller.dm
index b5df4981c9f15..dbc152d6bf467 100644
--- a/code/modules/capture_the_flag/ctf_controller.dm
+++ b/code/modules/capture_the_flag/ctf_controller.dm
@@ -63,7 +63,7 @@
///Unloading CTF removes the map entirely and allows for a new map to be loaded in its place.
/datum/ctf_controller/proc/unload_ctf()
if(game_id != CTF_GHOST_CTF_GAME_ID)
- return //At present we only support unloading standard centcom ctf, if we intend to support ctf unloading elsewhere then this proc will need to be ammended.
+ return //At present we only support unloading standard centcom ctf, if we intend to support ctf unloading elsewhere then this proc will need to be amended.
stop_ctf()
new /obj/effect/landmark/ctf(get_turf(GLOB.ctf_spawner))
@@ -187,7 +187,7 @@
respawn_cooldown = CTF_DEFAULT_RESPAWN
instagib_mode = !instagib_mode
-///A datum that holds details about individual CTF teams, any team specific CTF functionality should be implimented here.
+///A datum that holds details about individual CTF teams, any team specific CTF functionality should be implemented here.
/datum/ctf_team
///Reference to the spawn point that this team uses.
var/obj/machinery/ctf/spawner/spawner
@@ -206,7 +206,7 @@
team_color = spawner.team
team_span = spawner.team_span
-///If the team is destroyed all players in that team need their componenet removed.
+///If the team is destroyed all players in that team need their component removed.
/datum/ctf_team/Destroy(force)
for(var/player in team_members)
var/datum/component/ctf_player/ctf_player = team_members[player]
@@ -217,7 +217,7 @@
/datum/ctf_team/proc/score_points(points_scored)
points += points_scored
-///Resets this teams score and clears its member list. All members will be dusted and have their player componenet removed.
+///Resets this teams score and clears its member list. All members will be dusted and have their player component removed.
/datum/ctf_team/proc/reset_team()
points = 0
for(var/player in team_members)
@@ -231,7 +231,7 @@
var/datum/component/ctf_player/ctf_player = team_members[player]
ctf_player.send_message(message)
-///Creates a CTF game with the provided teeam ID then returns a reference to the new controller. If a controller already exists provides a reference to it.
+///Creates a CTF game with the provided team ID then returns a reference to the new controller. If a controller already exists provides a reference to it.
/proc/create_ctf_game(game_id)
if(GLOB.ctf_games[game_id])
return GLOB.ctf_games[game_id]
diff --git a/code/modules/capture_the_flag/ctf_equipment.dm b/code/modules/capture_the_flag/ctf_equipment.dm
index 0211a066555d1..3261b1d82660d 100644
--- a/code/modules/capture_the_flag/ctf_equipment.dm
+++ b/code/modules/capture_the_flag/ctf_equipment.dm
@@ -67,7 +67,7 @@
slot_flags = null
accepted_magazine_type = /obj/item/ammo_box/magazine/recharge/ctf/shotgun
empty_indicator = TRUE
- fire_sound = 'sound/weapons/gun/shotgun/shot_alt.ogg'
+ fire_sound = 'sound/items/weapons/gun/shotgun/shot_alt.ogg'
semi_auto = TRUE
internal_magazine = FALSE
tac_reloads = TRUE
diff --git a/code/modules/capture_the_flag/ctf_game.dm b/code/modules/capture_the_flag/ctf_game.dm
index 38a7318b5548e..2f292218e79f6 100644
--- a/code/modules/capture_the_flag/ctf_game.dm
+++ b/code/modules/capture_the_flag/ctf_game.dm
@@ -275,7 +275,7 @@
var/obj/item/ctf_flag/flag = item
if(flag.team != team)
to_chat(user, span_userdanger("Take \the [initial(flag.name)] to your team's controller!"))
- user.playsound_local(get_turf(user), 'sound/machines/buzz-sigh.ogg', 100, vary = FALSE, use_reverb = FALSE)
+ user.playsound_local(get_turf(user), 'sound/machines/buzz/buzz-sigh.ogg', 100, vary = FALSE, use_reverb = FALSE)
/obj/item/ctf_flag/dropped(mob/user)
..()
diff --git a/code/modules/capture_the_flag/ctf_map_loading.dm b/code/modules/capture_the_flag/ctf_map_loading.dm
index 6448533a3c74b..4c2f6b319e036 100644
--- a/code/modules/capture_the_flag/ctf_map_loading.dm
+++ b/code/modules/capture_the_flag/ctf_map_loading.dm
@@ -100,5 +100,5 @@ GLOBAL_DATUM(ctf_spawner, /obj/effect/landmark/ctf)
/datum/map_template/ctf/turbine
name = "Turbine"
- description = "A CTF map that takes place in a familiar facility. Don't try to hold out mid- Theres no sentries in this version."
+ description = "A CTF map that takes place in a familiar facility. Don't try to hold out mid- There's no sentries in this version."
mappath = "_maps/map_files/CTF/turbine.dmm"
diff --git a/code/modules/capture_the_flag/ctf_player_component.dm b/code/modules/capture_the_flag/ctf_player_component.dm
index c51b48b918c98..5a02a954aba6a 100644
--- a/code/modules/capture_the_flag/ctf_player_component.dm
+++ b/code/modules/capture_the_flag/ctf_player_component.dm
@@ -1,4 +1,4 @@
-///A component added to the mind of anyone who is playing in an ongoing CTF match. Any player specific CTF functionality should be implimented here. (someone should impliment score tracking here)
+///A component added to the mind of anyone who is playing in an ongoing CTF match. Any player specific CTF functionality should be implemented here. (someone should implement score tracking here)
/datum/component/ctf_player
///The team that this player is associated with.
var/team
diff --git a/code/modules/cards/cardhand.dm b/code/modules/cards/cardhand.dm
index 8fc9b4d0dc70a..2c72a552767e7 100644
--- a/code/modules/cards/cardhand.dm
+++ b/code/modules/cards/cardhand.dm
@@ -14,7 +14,7 @@
/obj/item/toy/cards/cardhand/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] is slitting [user.p_their()] wrists with \the [src]! It looks like [user.p_they()] [user.p_have()] a crummy hand!"))
- playsound(src, 'sound/items/cardshuffle.ogg', 50, TRUE)
+ playsound(src, 'sound/items/cards/cardshuffle.ogg', 50, TRUE)
return BRUTELOSS
/obj/item/toy/cards/cardhand/examine(mob/user)
diff --git a/code/modules/cards/cards.dm b/code/modules/cards/cards.dm
index 5cd17a53515cc..e151af0226426 100644
--- a/code/modules/cards/cards.dm
+++ b/code/modules/cards/cards.dm
@@ -50,7 +50,7 @@
card.transform = Matrix
card.update_appearance()
- playsound(src, 'sound/items/cardshuffle.ogg', 50, TRUE)
+ playsound(src, 'sound/items/cards/cardshuffle.ogg', 50, TRUE)
if(istype(src, /obj/item/toy/cards/cardhand))
qdel(src)
@@ -125,7 +125,7 @@
cards -= card
update_appearance()
- playsound(src, 'sound/items/cardflip.ogg', 50, TRUE)
+ playsound(src, 'sound/items/cards/cardflip.ogg', 50, TRUE)
return card
/// Returns the cards in this deck.
diff --git a/code/modules/cards/deck/deck.dm b/code/modules/cards/deck/deck.dm
index ccce356024956..69aa85ed8f2fe 100644
--- a/code/modules/cards/deck/deck.dm
+++ b/code/modules/cards/deck/deck.dm
@@ -32,7 +32,7 @@
/obj/item/toy/cards/deck/Initialize(mapload)
. = ..()
AddElement(/datum/element/drag_pickup)
- AddComponent(/datum/component/two_handed, attacksound='sound/items/cardflip.ogg')
+ AddComponent(/datum/component/two_handed, attacksound='sound/items/cards/cardflip.ogg')
register_context()
if(!is_standard_deck)
@@ -50,7 +50,7 @@
/obj/item/toy/cards/deck/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] is slitting [user.p_their()] wrists with \the [src]! It looks like their luck ran out!"))
- playsound(src, 'sound/items/cardshuffle.ogg', 50, TRUE)
+ playsound(src, 'sound/items/cards/cardshuffle.ogg', 50, TRUE)
return BRUTELOSS
/obj/item/toy/cards/deck/examine(mob/user)
@@ -103,7 +103,7 @@
return
COOLDOWN_START(src, shuffle_cooldown, shuffle_time)
shuffle_inplace(fetch_card_atoms())
- playsound(src, 'sound/items/cardshuffle.ogg', 50, TRUE)
+ playsound(src, 'sound/items/cards/cardshuffle.ogg', 50, TRUE)
user.balloon_alert_to_viewers("shuffles the deck")
addtimer(CALLBACK(src, PROC_REF(CardgameEvent), user), 60 SECONDS, TIMER_OVERRIDE|TIMER_UNIQUE)
@@ -209,7 +209,7 @@
cardgame_desc = "suspicious card game"
icon_state = "deck_syndicate_full"
deckstyle = "syndicate"
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
force = 5
throwforce = 10
attack_verb_continuous = list("attacks", "slices", "dices", "slashes", "cuts")
diff --git a/code/modules/cards/deck/tarot.dm b/code/modules/cards/deck/tarot.dm
index cf21fe789352e..a276716844b0a 100644
--- a/code/modules/cards/deck/tarot.dm
+++ b/code/modules/cards/deck/tarot.dm
@@ -40,7 +40,7 @@
. = ..()
AddComponent( \
/datum/component/two_handed, \
- attacksound = 'sound/items/cardflip.ogg', \
+ attacksound = 'sound/items/cards/cardflip.ogg', \
wield_callback = CALLBACK(src, PROC_REF(on_wield)), \
unwield_callback = CALLBACK(src, PROC_REF(on_unwield)), \
)
diff --git a/code/modules/cards/singlecard.dm b/code/modules/cards/singlecard.dm
index 5f2c6e37f387a..1999c19d7ff88 100644
--- a/code/modules/cards/singlecard.dm
+++ b/code/modules/cards/singlecard.dm
@@ -103,7 +103,7 @@
/obj/item/toy/singlecard/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] is slitting [user.p_their()] wrists with \the [src]! It looks like [user.p_they()] [user.p_have()] an unlucky card!"))
- playsound(src, 'sound/weapons/bladeslice.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/bladeslice.ogg', 50, TRUE)
return BRUTELOSS
/**
diff --git a/code/modules/cargo/bounties/science.dm b/code/modules/cargo/bounties/science.dm
index 0206ea41967d6..be0edbe1eb64b 100644
--- a/code/modules/cargo/bounties/science.dm
+++ b/code/modules/cargo/bounties/science.dm
@@ -55,7 +55,7 @@
//******Modular Computer Bounties******
/datum/bounty/item/science/ntnet
name = "Modular Tablets"
- description = "Turns out that NTNet wasn't actually a fad afterall, who knew. Send some fully functional PDAs to help get us up to speed on the latest technology."
+ description = "Turns out that NTNet wasn't actually a fad after all, who knew. Send some fully functional PDAs to help get us up to speed on the latest technology."
reward = CARGO_CRATE_VALUE * 6
required_count = 4
wanted_types = list(/obj/item/modular_computer/pda = TRUE)
diff --git a/code/modules/cargo/centcom_podlauncher.dm b/code/modules/cargo/centcom_podlauncher.dm
index cd86761587680..179ca701adc0c 100644
--- a/code/modules/cargo/centcom_podlauncher.dm
+++ b/code/modules/cargo/centcom_podlauncher.dm
@@ -343,10 +343,10 @@ ADMIN_VERB(centcom_podlauncher, R_ADMIN, "Config/Launch Supplypod", "Configure a
temp_pod.adminNamed = FALSE
temp_pod.setStyle(temp_pod.style) //This resets the name of the pod based on its current style (see supplypod/setStyle() proc)
return
- var/nameInput= tgui_input_text(usr, "Enter a custom name", "Custom name", temp_pod.style::name, MAX_NAME_LEN) //Gather input for name and desc
+ var/nameInput= tgui_input_text(usr, "Enter a custom name", "Custom name", temp_pod.style::name, max_length = MAX_NAME_LEN)
if (isnull(nameInput))
return
- var/descInput = tgui_input_text(usr, "Enter a custom desc", "Custom description", temp_pod.style::desc)
+ var/descInput = tgui_input_text(usr, "Enter a custom desc", "Custom description", temp_pod.style::desc, max_length = MAX_DESC_LEN)
if (isnull(descInput))
return
temp_pod.name = nameInput
diff --git a/code/modules/cargo/expressconsole.dm b/code/modules/cargo/expressconsole.dm
index 0a2dcfec4b0f0..4070301bfbe39 100644
--- a/code/modules/cargo/expressconsole.dm
+++ b/code/modules/cargo/expressconsole.dm
@@ -1,3 +1,6 @@
+#define EXPRESS_EMAG_DISCOUNT 0.72
+#define BEACON_PRINT_COOLDOWN 10 SECONDS
+
/obj/machinery/computer/cargo/express
name = "express supply console"
desc = "This console allows the user to purchase a package \
@@ -11,18 +14,28 @@
interface_type = "CargoExpress"
var/message
- var/printed_beacons = 0 //number of beacons printed. Used to determine beacon names.
var/list/meme_pack_data
- var/obj/item/supplypod_beacon/beacon //the linked supplypod beacon
- var/area/landingzone = /area/station/cargo/storage //where we droppin boys
- var/podType = /obj/structure/closet/supplypod
- var/cooldown = 0 //cooldown to prevent printing supplypod beacon spam
- var/locked = TRUE //is the console locked? unlock with ID
- var/usingBeacon = FALSE //is the console in beacon mode? exists to let beacon know when a pod may come in
+ /// The linked supplypod beacon
+ var/obj/item/supplypod_beacon/beacon
+ /// Where we droppin boys
+ var/area/landingzone = /area/station/cargo/storage
+ var/pod_type = /obj/structure/closet/supplypod
+ /// If this console is locked and needs to be unlocked with an ID
+ var/locked = TRUE
+ /// Is the console in beacon mode? Exists to let beacon know when a pod may come in
+ var/using_beacon = FALSE
+ /// Number of beacons printed. Used to determine beacon names.
+ var/static/printed_beacons = 0
+ /// Cooldown to prevent beacon spam
+ COOLDOWN_DECLARE(beacon_print_cooldown)
/obj/machinery/computer/cargo/express/Initialize(mapload)
. = ..()
packin_up()
+ landingzone = GLOB.areas_by_type[landingzone]
+ if (isnull(landingzone))
+ WARNING("[src] couldnt find a Quartermaster/Storage (aka cargobay) area on the station, and as such it has set the supplypod landingzone to the area it resides in.")
+ landingzone = get_area(src)
/obj/machinery/computer/cargo/express/on_construction(mob/user)
. = ..()
@@ -33,24 +46,33 @@
beacon.unlink_console()
return ..()
-/obj/machinery/computer/cargo/express/attackby(obj/item/W, mob/living/user, params)
- if(W.GetID() && allowed(user))
+/obj/machinery/computer/cargo/express/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if (tool.GetID() && allowed(user))
locked = !locked
to_chat(user, span_notice("You [locked ? "lock" : "unlock"] the interface."))
- return
- else if(istype(W, /obj/item/disk/cargo/bluespace_pod))
- podType = /obj/structure/closet/supplypod/bluespacepod//doesnt effect circuit board, making reversal possible
+ return ITEM_INTERACT_SUCCESS
+
+ if (istype(tool, /obj/item/disk/cargo/bluespace_pod))
+ if (pod_type == /obj/structure/closet/supplypod/bluespacepod)
+ balloon_alert(user, "already upgraded!")
+ return ITEM_INTERACT_FAILURE
+ if(!user.temporarilyRemoveItemFromInventory(tool))
+ return ITEM_INTERACT_FAILURE
+ pod_type = /obj/structure/closet/supplypod/bluespacepod // doesnt affect our circuit board, making reversal possible
to_chat(user, span_notice("You insert the disk into [src], allowing for advanced supply delivery vehicles."))
- qdel(W)
- return TRUE
- else if(istype(W, /obj/item/supplypod_beacon))
- var/obj/item/supplypod_beacon/sb = W
- if (sb.express_console != src)
- sb.link_console(src, user)
- return TRUE
- else
- to_chat(user, span_alert("[src] is already linked to [sb]."))
- ..()
+ tool.forceMove(src)
+ return ITEM_INTERACT_SUCCESS
+
+ if(istype(tool, /obj/item/supplypod_beacon))
+ var/obj/item/supplypod_beacon/beacon = tool
+ if (beacon.express_console != src)
+ beacon.link_console(src, user)
+ return ITEM_INTERACT_SUCCESS
+
+ to_chat(user, span_alert("[src] is already linked to [beacon]."))
+ return ITEM_INTERACT_FAILURE
+
+ return NONE
/obj/machinery/computer/cargo/express/emag_act(mob/user, obj/item/card/emag/emag_card)
if(obj_flags & EMAGGED)
@@ -68,8 +90,11 @@
packin_up()
return TRUE
-/obj/machinery/computer/cargo/express/proc/packin_up() // oh shit, I'm sorry
+/obj/machinery/computer/cargo/express/proc/packin_up(forced = FALSE) // oh shit, I'm sorry
meme_pack_data = list() // sorry for what?
+ if (!forced && !SSshuttle.initialized) // Subsystem is still sleeping, add ourselves to its buffer and abort
+ SSshuttle.express_consoles += src
+ return
for(var/pack in SSshuttle.supply_packs) // our quartermaster taught us not to be ashamed of our supply packs
var/datum/supply_pack/P = SSshuttle.supply_packs[pack] // specially since they're such a good price and all
if(!meme_pack_data[P.group]) // yeah, I see that, your quartermaster gave you good advice
@@ -83,7 +108,7 @@
continue // i'd be right happy to
meme_pack_data[P.group]["packs"] += list(list(
"name" = P.name,
- "cost" = P.get_cost(),
+ "cost" = P.get_cost() * get_discount(),
"id" = pack,
"desc" = P.desc || P.name // If there is a description, use it. Otherwise use the pack's name.
))
@@ -91,26 +116,26 @@
/obj/machinery/computer/cargo/express/ui_data(mob/user)
var/canBeacon = beacon && (isturf(beacon.loc) || ismob(beacon.loc))//is the beacon in a valid location?
var/list/data = list()
- var/datum/bank_account/D = SSeconomy.get_dep_account(cargo_account)
- if(D)
- data["points"] = D.account_balance
+ var/datum/bank_account/account = SSeconomy.get_dep_account(cargo_account)
+ if(account)
+ data["points"] = account.account_balance
data["locked"] = locked//swipe an ID to unlock
data["siliconUser"] = HAS_SILICON_ACCESS(user)
data["beaconzone"] = beacon ? get_area(beacon) : ""//where is the beacon located? outputs in the tgui
- data["usingBeacon"] = usingBeacon //is the mode set to deliver to the beacon or the cargobay?
- data["canBeacon"] = !usingBeacon || canBeacon //is the mode set to beacon delivery, and is the beacon in a valid location?
- data["canBuyBeacon"] = cooldown <= 0 && D.account_balance >= BEACON_COST
- data["beaconError"] = usingBeacon && !canBeacon ? "(BEACON ERROR)" : ""//changes button text to include an error alert if necessary
+ data["using_beacon"] = using_beacon //is the mode set to deliver to the beacon or the cargobay?
+ data["canBeacon"] = !using_beacon || canBeacon //is the mode set to beacon delivery, and is the beacon in a valid location?
+ data["canBuyBeacon"] = COOLDOWN_FINISHED(src, beacon_print_cooldown) && account.account_balance >= BEACON_COST
+ data["beaconError"] = using_beacon && !canBeacon ? "(BEACON ERROR)" : ""//changes button text to include an error alert if necessary
data["hasBeacon"] = beacon != null//is there a linked beacon?
data["beaconName"] = beacon ? beacon.name : "No Beacon Found"
- data["printMsg"] = cooldown > 0 ? "Print Beacon for [BEACON_COST] credits ([cooldown])" : "Print Beacon for [BEACON_COST] credits"//buttontext for printing beacons
+ data["printMsg"] = COOLDOWN_FINISHED(src, beacon_print_cooldown) ? "Print Beacon for [BEACON_COST] credits" : "Print Beacon for [BEACON_COST] credits ([COOLDOWN_TIMELEFT(src, beacon_print_cooldown)])" //buttontext for printing beacons
data["supplies"] = list()
message = "Sales are near-instantaneous - please choose carefully."
if(SSshuttle.supply_blocked)
message = blockade_warning
- if(usingBeacon && !beacon)
+ if(using_beacon && !beacon)
message = "BEACON ERROR: BEACON MISSING"//beacon was destroyed
- else if (usingBeacon && !canBeacon)
+ else if (using_beacon && !canBeacon)
message = "BEACON ERROR: MUST BE EXPOSED"//beacon's loc/user's loc must be a turf
if(obj_flags & EMAGGED)
message = "(&!#@ERROR: R0UTING_#PRO7O&OL MALF(*CT#ON. $UG%ESTE@ ACT#0N: !^/PULS3-%E)ET CIR*)ITB%ARD."
@@ -119,10 +144,11 @@
packin_up()
stack_trace("There was no pack data for [src]")
data["supplies"] = meme_pack_data
- if (cooldown > 0)//cooldown used for printing beacons
- cooldown--
return data
+/obj/machinery/computer/cargo/express/proc/get_discount()
+ return (obj_flags & EMAGGED) ? EXPRESS_EMAG_DISCOUNT : 1
+
/obj/machinery/computer/cargo/express/ui_act(action, params, datum/tgui/ui)
. = ..()
if(.)
@@ -131,23 +157,24 @@
var/mob/user = ui.user
switch(action)
if("LZCargo")
- usingBeacon = FALSE
+ using_beacon = FALSE
if (beacon)
beacon.update_status(SP_UNREADY) //ready light on beacon will turn off
if("LZBeacon")
- usingBeacon = TRUE
+ using_beacon = TRUE
if (beacon)
beacon.update_status(SP_READY) //turns on the beacon's ready light
if("printBeacon")
- var/datum/bank_account/D = SSeconomy.get_dep_account(cargo_account)
- if(D)
- if(D.adjust_money(-BEACON_COST))
- cooldown = 10//a ~ten second cooldown for printing beacons to prevent spam
- var/obj/item/supplypod_beacon/C = new /obj/item/supplypod_beacon(drop_location())
- C.link_console(src, user)//rather than in beacon's Initialize(), we can assign the computer to the beacon by reusing this proc)
- printed_beacons++//printed_beacons starts at 0, so the first one out will be called beacon # 1
- beacon.name = "Supply Pod Beacon #[printed_beacons]"
+ var/datum/bank_account/account = SSeconomy.get_dep_account(cargo_account)
+ if(isnull(account) || !account.adjust_money(-BEACON_COST))
+ return
+ // a ~ten second cooldown for printing beacons to prevent spam
+ COOLDOWN_START(src, beacon_print_cooldown, BEACON_PRINT_COOLDOWN)
+ var/obj/item/supplypod_beacon/new_beacon = new /obj/item/supplypod_beacon(drop_location())
+ new_beacon.link_console(src, user) //rather than in beacon's Initialize(), we can assign the computer to the beacon by reusing this proc)
+ printed_beacons++ //printed_beacons starts at 0, so the first one out will be called beacon # 1
+ beacon.name = "Supply Pod Beacon #[printed_beacons]"
if("add")//Generate Supply Order first
if(TIMER_COOLDOWN_RUNNING(src, COOLDOWN_EXPRESSPOD_CONSOLE))
@@ -169,60 +196,62 @@
name = user.real_name
rank = "Silicon"
var/reason = ""
+ var/datum/supply_order/order = new(pack, name, rank, ckey, reason)
+ var/datum/bank_account/account = SSeconomy.get_dep_account(cargo_account)
+ if (isnull(account) && order.pack.get_cost() > 0)
+ return
+
+ if (obj_flags & EMAGGED)
+ landingzone = GLOB.areas_by_type[pick(GLOB.the_station_areas)]
+
var/list/empty_turfs
- var/datum/supply_order/SO = new(pack, name, rank, ckey, reason)
- var/points_to_check
- var/datum/bank_account/D = SSeconomy.get_dep_account(cargo_account)
- if(D)
- points_to_check = D.account_balance
- if(!(obj_flags & EMAGGED))
- if(SO.pack.get_cost() <= points_to_check)
- var/LZ
- if (istype(beacon) && usingBeacon)//prioritize beacons over landing in cargobay
- LZ = get_turf(beacon)
- beacon.update_status(SP_LAUNCH)
- else if (!usingBeacon)//find a suitable supplypod landing zone in cargobay
- landingzone = GLOB.areas_by_type[/area/station/cargo/storage]
- if (!landingzone)
- WARNING("[src] couldnt find a Quartermaster/Storage (aka cargobay) area on the station, and as such it has set the supplypod landingzone to the area it resides in.")
- landingzone = get_area(src)
- for(var/turf/open/floor/T in landingzone.get_turfs_from_all_zlevels())//uses default landing zone
- if(T.is_blocked_turf())
- continue
- LAZYADD(empty_turfs, T)
- CHECK_TICK
- if(empty_turfs?.len)
- LZ = pick(empty_turfs)
- if (SO.pack.get_cost() <= points_to_check && LZ)//we need to call the cost check again because of the CHECK_TICK call
- TIMER_COOLDOWN_START(src, COOLDOWN_EXPRESSPOD_CONSOLE, 5 SECONDS)
- D.adjust_money(-SO.pack.get_cost())
- if(pack.special_pod)
- new /obj/effect/pod_landingzone(LZ, pack.special_pod, SO)
- else
- new /obj/effect/pod_landingzone(LZ, podType, SO)
- . = TRUE
- update_appearance()
+ if (!istype(beacon) || !using_beacon || (obj_flags & EMAGGED))
+ empty_turfs = list()
+ for(var/turf/open/floor/open_turf in landingzone.get_turfs_from_all_zlevels())
+ if(!open_turf.is_blocked_turf())
+ empty_turfs += open_turf
+
+ if (!length(empty_turfs))
+ return
+
+ if (obj_flags & EMAGGED)
+ if (account.account_balance < order.pack.get_cost() * -get_discount())
+ return
+
+ TIMER_COOLDOWN_START(src, COOLDOWN_EXPRESSPOD_CONSOLE, 10 SECONDS)
+ order.generateRequisition(get_turf(src))
+ for(var/i in 1 to MAX_EMAG_ROCKETS)
+ if (!account.adjust_money(order.pack.get_cost() * -get_discount()))
+ break
+
+ var/turf/landing_turf = pick(empty_turfs)
+ empty_turfs -= landing_turf
+ if(pack.special_pod)
+ new /obj/effect/pod_landingzone(landing_turf, pack.special_pod, order)
+ else
+ new /obj/effect/pod_landingzone(landing_turf, pod_type, order)
+
+ update_appearance()
+ return TRUE
+
+ var/turf/landing_turf
+ if (istype(beacon) && using_beacon)
+ landing_turf = get_turf(beacon)
+ beacon.update_status(SP_LAUNCH)
else
- if(SO.pack.get_cost() * (0.72*MAX_EMAG_ROCKETS) <= points_to_check) // bulk discount :^)
- landingzone = GLOB.areas_by_type[pick(GLOB.the_station_areas)] //override default landing zone
- for(var/turf/open/floor/T in landingzone.get_turfs_from_all_zlevels())
- if(T.is_blocked_turf())
- continue
- LAZYADD(empty_turfs, T)
- CHECK_TICK
- if(empty_turfs?.len)
- TIMER_COOLDOWN_START(src, COOLDOWN_EXPRESSPOD_CONSOLE, 10 SECONDS)
- D.adjust_money(-(SO.pack.get_cost() * (0.72*MAX_EMAG_ROCKETS)))
-
- SO.generateRequisition(get_turf(src))
- for(var/i in 1 to MAX_EMAG_ROCKETS)
- var/LZ = pick(empty_turfs)
- LAZYREMOVE(empty_turfs, LZ)
- if(pack.special_pod)
- new /obj/effect/pod_landingzone(LZ, pack.special_pod, SO)
- else
- new /obj/effect/pod_landingzone(LZ, podType, SO)
- . = TRUE
- update_appearance()
- CHECK_TICK
+ landing_turf = pick(empty_turfs)
+
+ if (!account.adjust_money(-order.pack.get_cost() * get_discount()))
+ return
+
+ TIMER_COOLDOWN_START(src, COOLDOWN_EXPRESSPOD_CONSOLE, 5 SECONDS)
+ if(pack.special_pod)
+ new /obj/effect/pod_landingzone(landing_turf, pack.special_pod, order)
+ else
+ new /obj/effect/pod_landingzone(landing_turf, pod_type, order)
+
+ update_appearance()
+ return TRUE
+#undef EXPRESS_EMAG_DISCOUNT
+#undef BEACON_PRINT_COOLDOWN
diff --git a/code/modules/cargo/goodies.dm b/code/modules/cargo/goodies.dm
index aa42b5cacde44..f7ce106d58048 100644
--- a/code/modules/cargo/goodies.dm
+++ b/code/modules/cargo/goodies.dm
@@ -237,6 +237,12 @@
cost = PAYCHECK_CREW
contains = list(/obj/item/storage/box/fishing_lines)
+/datum/supply_pack/goody/fishing_lure_set
+ name = "Fishing Lures Set"
+ desc = "A set of bite-resistant fishing lures to fish all (most) sort of fish. Beat randomness to a curb today!"
+ cost = PAYCHECK_CREW * 8
+ contains = list(/obj/item/storage/box/fishing_lures)
+
/datum/supply_pack/goody/fishing_hook_rescue
name = "Rescue Fishing Hook Single-Pack"
desc = "For when your fellow miner has inevitably fallen into a chasm, and it's up to you to save them."
@@ -252,7 +258,7 @@
/datum/supply_pack/goody/fish_feed
name = "Can of Fish Food Single-Pack"
desc = "For keeping your little friends fed and alive."
- cost = PAYCHECK_CREW * 1
+ cost = PAYCHECK_CREW
contains = list(/obj/item/fish_feed)
/datum/supply_pack/goody/naturalbait
diff --git a/code/modules/cargo/markets/_market.dm b/code/modules/cargo/markets/_market.dm
index 4696d3007a7ae..e2a21eb12ebad 100644
--- a/code/modules/cargo/markets/_market.dm
+++ b/code/modules/cargo/markets/_market.dm
@@ -68,7 +68,7 @@
uplink.current_user.adjust_money(-price, "Other: Third Party Transaction")
if(ismob(user))
var/mob/m_user = user
- m_user.playsound_local(get_turf(m_user), 'sound/machines/twobeep_high.ogg', 50, TRUE)
+ m_user.playsound_local(get_turf(m_user), 'sound/machines/beep/twobeep_high.ogg', 50, TRUE)
return TRUE
return FALSE
diff --git a/code/modules/cargo/markets/market_telepad.dm b/code/modules/cargo/markets/market_telepad.dm
index 53a3d73ee486a..f0c8e058fc0fb 100644
--- a/code/modules/cargo/markets/market_telepad.dm
+++ b/code/modules/cargo/markets/market_telepad.dm
@@ -176,7 +176,7 @@
if(state_open)
if(locate(/mob/living) in tool.get_all_contents())
say("Living being detected, cannot sell!")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 40, FALSE)
return ITEM_INTERACT_BLOCKING
if(!user.transferItemToLoc(tool, src))
balloon_alert(user, "stuck to your hands!")
@@ -193,7 +193,7 @@
if(creds_value < restock_cost)
say("Insufficient credits!")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 40, FALSE)
return ITEM_INTERACT_BLOCKING
if(istype(tool, /obj/item/holochip))
@@ -271,7 +271,7 @@
return
if(locate(/mob/living) in occupant.get_all_contents())
say("Living being detected, cannot sell!")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 40, FALSE)
return
var/datum/bank_account/account
var/datum/market/our_market = SSmarket.markets[/datum/market/blackmarket]
@@ -280,17 +280,17 @@
return
if(length(our_market.available_items[/datum/market_item/local_good::category]) >= LTSRBT_MAX_MARKET_ITEMS)
say("Local market saturated, buy some goods first!")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 40, FALSE)
return
var/mob/living/living_user = user
var/obj/item/card/id/card = living_user.get_idcard(TRUE)
if(!(card?.registered_account))
say("No bank account to charge market fees detected!")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 40, FALSE)
return
if(!card.registered_account.adjust_money(-PLACE_ON_MARKET_COST, "Market: Placement Fee"))
say("Insufficient credits!")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 40, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 40, FALSE)
return
account = card.registered_account
diff --git a/code/modules/cargo/materials_market.dm b/code/modules/cargo/materials_market.dm
index 797ebf5411d6f..4037a51c6916b 100644
--- a/code/modules/cargo/materials_market.dm
+++ b/code/modules/cargo/materials_market.dm
@@ -68,7 +68,7 @@
if(!amount)
say("Not enough material. Aborting.")
- playsound(src, 'sound/machines/scanbuzz.ogg', 25, FALSE)
+ playsound(src, 'sound/machines/scanner/scanbuzz.ogg', 25, FALSE)
return TRUE
qdel(exportable)
@@ -77,7 +77,7 @@
new_block.export_mat = material_to_export
new_block.quantity = amount
to_chat(user, span_notice("You have created a stock block worth [new_block.export_value] cr! Sell it before it becomes liquid!"))
- playsound(src, 'sound/machines/synth_yes.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/synth/synth_yes.ogg', 50, FALSE)
return TRUE
return ..()
@@ -274,14 +274,14 @@
var/prior_sheets = current_order.pack.contains[sheet_to_buy]
if(prior_sheets + quantity > SSstock_market.materials_quantity[material_bought] )
say("There aren't enough sheets on the market! Please wait for more sheets to be traded before adding more.")
- playsound(usr, 'sound/machines/synth_no.ogg', 35, FALSE)
+ playsound(usr, 'sound/machines/synth/synth_no.ogg', 35, FALSE)
return
// Check if the order exceeded the purchase limit
var/prior_stacks = ROUND_UP(prior_sheets / MAX_STACK_SIZE)
if(prior_stacks >= MAX_STACK_LIMIT)
say("There are already 10 stacks of sheets on order! Please wait for them to arrive before ordering more.")
- playsound(usr, 'sound/machines/synth_no.ogg', 35, FALSE)
+ playsound(usr, 'sound/machines/synth/synth_no.ogg', 35, FALSE)
return
// Prevents you from ordering more than the available budget
diff --git a/code/modules/cargo/order.dm b/code/modules/cargo/order.dm
index a10bb3da5c00c..ef6484f21fc1f 100644
--- a/code/modules/cargo/order.dm
+++ b/code/modules/cargo/order.dm
@@ -140,7 +140,7 @@
manifest_text += "Item: [packname] "
manifest_text += "Contents: "
manifest_text += "
"
- var/container_contents = list() // Associative list with the format (item_name = nº of occurences, ...)
+ var/container_contents = list() // Associative list with the format (item_name = nº of occurrences, ...)
for(var/atom/movable/AM in container.contents - manifest_paper)
container_contents[AM.name]++
if((manifest_paper.errors & MANIFEST_ERROR_CONTENTS) && container_contents)
diff --git a/code/modules/cargo/orderconsole.dm b/code/modules/cargo/orderconsole.dm
index ae46d9943818b..87a085707c0c3 100644
--- a/code/modules/cargo/orderconsole.dm
+++ b/code/modules/cargo/orderconsole.dm
@@ -235,18 +235,18 @@
var/reason = ""
if(requestonly && !self_paid)
working_list = SSshuttle.request_list
- reason = tgui_input_text(user, "Reason", name)
+ reason = tgui_input_text(user, "Reason", name, max_length = MAX_MESSAGE_LEN)
if(isnull(reason))
return
if(pack.goody && !self_paid)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
say("ERROR: Small crates may only be purchased by private accounts.")
return
var/similar_count = SSshuttle.supply.get_order_count(pack)
if(similar_count == OVER_ORDER_LIMIT)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
say("ERROR: No more then [CARGO_MAX_ORDER] of any pack may be ordered at once")
return
@@ -384,7 +384,7 @@
return add_item(ui.user, supply_pack_id)
if("remove")
var/order_name = params["order_name"]
- //try removing atleast one item with the specified name. An order may not be removed if it was from the department
+ //try removing at least one item with the specified name. An order may not be removed if it was from the department
for(var/datum/supply_order/order in SSshuttle.shopping_list)
if(order.pack.name != order_name)
continue
diff --git a/code/modules/cargo/packs/engineering.dm b/code/modules/cargo/packs/engineering.dm
index e8255a58e6431..dd376ec201747 100644
--- a/code/modules/cargo/packs/engineering.dm
+++ b/code/modules/cargo/packs/engineering.dm
@@ -199,7 +199,6 @@
desc = "Protect the very existence of this station with these Anti-Meteor defenses. \
Contains three Shield Generator Satellites."
cost = CARGO_CRATE_VALUE * 6
- special = TRUE
access_view = ACCESS_COMMAND
contains = list(/obj/machinery/satellite/meteor_shield = 3)
crate_name= "shield sat crate"
@@ -209,7 +208,6 @@
name = "Shield System Control Board"
desc = "A control system for the Shield Generator Satellite system."
cost = CARGO_CRATE_VALUE * 10
- special = TRUE
access_view = ACCESS_COMMAND
contains = list(/obj/item/circuitboard/computer/sat_control)
crate_name= "shield control board crate"
diff --git a/code/modules/cargo/packs/imports.dm b/code/modules/cargo/packs/imports.dm
index fc35e473f5ada..8720cb554fd43 100644
--- a/code/modules/cargo/packs/imports.dm
+++ b/code/modules/cargo/packs/imports.dm
@@ -143,7 +143,7 @@
/obj/item/gun/ballistic/automatic/wt550 = 2,
/obj/item/ammo_box/magazine/wt550m9 = 2,
)
- crate_type = /obj/structure/closet/crate/secure/gorlex_weapons/jammed
+ crate_type = /obj/structure/closet/crate/secure/syndicate/gorlex/weapons/bustedlock
/datum/supply_pack/imports/wt550ammo
name = "Smuggled WT-550 Ammo Crate"
@@ -156,7 +156,7 @@
/obj/item/ammo_box/magazine/wt550m9/wtic = 2,
)
crate_name = "emergency crate"
- crate_type = /obj/structure/closet/crate/secure/gorlex_weapons/jammed
+ crate_type = /obj/structure/closet/crate/secure/syndicate/gorlex/weapons/bustedlock
/datum/supply_pack/imports/shocktrooper
name = "Shocktrooper Crate"
@@ -172,7 +172,7 @@
/obj/item/clothing/suit/armor/vest,
/obj/item/clothing/head/helmet,
)
- crate_type = /obj/structure/closet/crate/secure/gorlex_weapons/jammed
+ crate_type = /obj/structure/closet/crate/secure/syndicate/gorlex/weapons/bustedlock
/datum/supply_pack/imports/specialops
name = "Special Ops Crate"
@@ -188,7 +188,7 @@
/obj/item/switchblade,
/obj/item/grenade/mirage = 5,
)
- crate_type = /obj/structure/closet/crate/secure/gorlex_weapons/jammed
+ crate_type = /obj/structure/closet/crate/secure/syndicate/gorlex/weapons/bustedlock
/datum/supply_pack/imports/russian
name = "Russian Surplus Military Gear Crate"
diff --git a/code/modules/cargo/packs/organic.dm b/code/modules/cargo/packs/organic.dm
index f405fc7de3a8a..a47c92fabc449 100644
--- a/code/modules/cargo/packs/organic.dm
+++ b/code/modules/cargo/packs/organic.dm
@@ -101,8 +101,8 @@
/datum/supply_pack/organic/randomized/chef/fruits
name = "Fruit Crate"
- desc = "Rich of vitamins. Contains a lime, orange, watermelon, apple, \
- berries and a lime."
+ desc = "Rich in vitamins. Contains a lime, orange, watermelon, apple, \
+ berries and a lemon."
cost = CARGO_CRATE_VALUE * 3
contains = list(/obj/item/food/grown/citrus/lime,
/obj/item/food/grown/citrus/orange,
diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm
index 69b9342007c41..e703271b79e54 100644
--- a/code/modules/cargo/supplypod.dm
+++ b/code/modules/cargo/supplypod.dm
@@ -40,7 +40,7 @@
var/reversing = FALSE //If true, the pod will not send any items. Instead, after opening, it will close again (picking up items/mobs) and fly back to centcom
var/list/reverse_dropoff_coords //Turf that the reverse pod will drop off its newly-acquired cargo to
var/fallingSoundLength = 11
- var/fallingSound = 'sound/weapons/mortar_long_whistle.ogg'//Admin sound to play before the pod lands
+ var/fallingSound = 'sound/items/weapons/mortar_long_whistle.ogg'//Admin sound to play before the pod lands
var/landingSound //Admin sound to play when the pod lands
var/openingSound //Admin sound to play when the pod opens
var/leavingSound //Admin sound to play when the pod leaves
@@ -695,7 +695,7 @@
target_living.Stun(pod.delays[POD_TRANSIT]+10, ignore_canstun = TRUE)//you ain't goin nowhere, kid.
if (pod.delays[POD_TRANSIT] + pod.delays[POD_FALLING] < pod.fallingSoundLength)
pod.fallingSoundLength = 3 //The default falling sound is a little long, so if the landing time is shorter than the default falling sound, use a special, shorter default falling sound
- pod.fallingSound = 'sound/weapons/mortar_whistle.ogg'
+ pod.fallingSound = 'sound/items/weapons/mortar_whistle.ogg'
var/soundStartTime = pod.delays[POD_TRANSIT] - pod.fallingSoundLength + pod.delays[POD_FALLING]
if (soundStartTime < 0)
soundStartTime = 1
diff --git a/code/modules/cargo/supplypod_beacon.dm b/code/modules/cargo/supplypod_beacon.dm
index 2d9a618bb414e..976298bbc0b15 100644
--- a/code/modules/cargo/supplypod_beacon.dm
+++ b/code/modules/cargo/supplypod_beacon.dm
@@ -27,17 +27,17 @@
switch(consoleStatus)
if (SP_LINKED)
linked = TRUE
- playsound(src,'sound/machines/twobeep.ogg',50,FALSE)
+ playsound(src,'sound/machines/beep/twobeep.ogg',50,FALSE)
if (SP_READY)
ready = TRUE
if (SP_LAUNCH)
launched = TRUE
- playsound(src,'sound/machines/triple_beep.ogg',50,FALSE)
+ playsound(src,'sound/machines/beep/triple_beep.ogg',50,FALSE)
playsound(src,'sound/machines/warning-buzzer.ogg',50,FALSE)
addtimer(CALLBACK(src, PROC_REF(endLaunch)), 33)//wait 3.3 seconds (time it takes for supplypod to land), then update icon
if (SP_UNLINK)
linked = FALSE
- playsound(src,'sound/machines/synth_no.ogg',50,FALSE)
+ playsound(src,'sound/machines/synth/synth_no.ogg',50,FALSE)
if (SP_UNREADY)
ready = FALSE
update_appearance()
@@ -73,7 +73,9 @@
/obj/item/supplypod_beacon/wrench_act(mob/living/user, obj/item/tool)
. = ..()
- default_unfasten_wrench(user, tool)
+ if (default_unfasten_wrench(user, tool) == SUCCESSFUL_UNFASTEN)
+ pixel_x = 0
+ pixel_y = 0
return ITEM_INTERACT_SUCCESS
/obj/item/supplypod_beacon/proc/unlink_console()
@@ -91,7 +93,7 @@
express_console = C//set the linked console var to the console
express_console.beacon = src//out with the old in with the news
update_status(SP_LINKED)
- if (express_console.usingBeacon)
+ if (express_console.using_beacon)
update_status(SP_READY)
to_chat(user, span_notice("[src] linked to [C]."))
diff --git a/code/modules/cargo/universal_scanner.dm b/code/modules/cargo/universal_scanner.dm
index 9ce1771421e61..d86a758ef89d3 100644
--- a/code/modules/cargo/universal_scanner.dm
+++ b/code/modules/cargo/universal_scanner.dm
@@ -200,7 +200,7 @@
to_chat(user, span_notice(message))
if(price)
- playsound(src, 'sound/machines/terminal_select.ogg', 50, vary = TRUE)
+ playsound(src, 'sound/machines/terminal/terminal_select.ogg', 50, vary = TRUE)
if(istype(target, /obj/item/delivery))
var/obj/item/delivery/parcel = target
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index c078a23e89c24..eafda7f792e73 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -181,7 +181,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
data["character_profiles"] = create_character_profiles()
data["character_preview_view"] = character_preview_view.assigned_map
- data["overflow_role"] = SSjob.GetJobType(SSjob.overflow_role).title
+ data["overflow_role"] = SSjob.get_job_type(SSjob.overflow_role).title
data["window"] = current_window
data["content_unlocked"] = unlock_content
diff --git a/code/modules/client/preferences/README.md b/code/modules/client/preferences/README.md
index fabfb779c902b..674f234d48ef6 100644
--- a/code/modules/client/preferences/README.md
+++ b/code/modules/client/preferences/README.md
@@ -398,11 +398,11 @@ For inspiration, here is changeling's:
var/icon/final_icon = render_preview_outfit(/datum/outfit/changeling)
var/icon/split_icon = render_preview_outfit(/datum/outfit/job/engineer)
- final_icon.Shift(WEST, world.icon_size / 2)
- final_icon.Shift(EAST, world.icon_size / 2)
+ final_icon.Shift(WEST, ICON_SIZE_X / 2)
+ final_icon.Shift(EAST, ICON_SIZE_X / 2)
- split_icon.Shift(EAST, world.icon_size / 2)
- split_icon.Shift(WEST, world.icon_size / 2)
+ split_icon.Shift(EAST, ICON_SIZE_X / 2)
+ split_icon.Shift(WEST, ICON_SIZE_X / 2)
final_icon.Blend(split_icon, ICON_OVERLAY)
diff --git a/code/modules/client/preferences/middleware/jobs.dm b/code/modules/client/preferences/middleware/jobs.dm
index 201e57668ea6a..392822eac6149 100644
--- a/code/modules/client/preferences/middleware/jobs.dm
+++ b/code/modules/client/preferences/middleware/jobs.dm
@@ -10,7 +10,7 @@
if (level != null && level != JP_LOW && level != JP_MEDIUM && level != JP_HIGH)
return FALSE
- var/datum/job/job = SSjob.GetJob(job_title)
+ var/datum/job/job = SSjob.get_job(job_title)
if (isnull(job))
return FALSE
diff --git a/code/modules/client/preferences/species_features/vampire.dm b/code/modules/client/preferences/species_features/vampire.dm
index 5ecdae7b1bf8e..f720dca3649ec 100644
--- a/code/modules/client/preferences/species_features/vampire.dm
+++ b/code/modules/client/preferences/species_features/vampire.dm
@@ -32,7 +32,7 @@ GLOBAL_LIST_EMPTY(vampire_houses)
//find and setup the house (department) this vampire is joining
var/datum/job_department/vampire_house
- var/datum/job/vampire_job = SSjob.GetJob(target.job)
+ var/datum/job/vampire_job = SSjob.get_job(target.job)
if(!vampire_job) //no job or no mind LOSERS
return
var/list/valid_departments = (SSjob.joinable_departments.Copy()) - list(/datum/job_department/silicon, /datum/job_department/undefined)
diff --git a/code/modules/client/preferences/ui_style.dm b/code/modules/client/preferences/ui_style.dm
index 64a82b592c60a..fa002fd71b0e7 100644
--- a/code/modules/client/preferences/ui_style.dm
+++ b/code/modules/client/preferences/ui_style.dm
@@ -12,8 +12,8 @@
var/icon/icons = GLOB.available_ui_styles[value]
var/icon/icon = icon(icons, "hand_r")
- icon.Crop(1, 1, world.icon_size * 2, world.icon_size)
- icon.Blend(icon(icons, "hand_l"), ICON_OVERLAY, world.icon_size)
+ icon.Crop(1, 1, ICON_SIZE_X * 2, ICON_SIZE_Y)
+ icon.Blend(icon(icons, "hand_l"), ICON_OVERLAY, ICON_SIZE_X)
return icon
diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm
index baabaa610c523..127840631d631 100644
--- a/code/modules/client/verbs/ooc.dm
+++ b/code/modules/client/verbs/ooc.dm
@@ -58,7 +58,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
msg = emoji_parse(msg)
- if(SSticker.HasRoundStarted() && (msg[1] in list(".",";",":","#") || findtext_char(msg, "say", 1, 5)))
+ if(SSticker.HasRoundStarted() && ((msg[1] in list(".",";",":","#")) || findtext_char(msg, "say", 1, 5)))
if(tgui_alert(usr,"Your message \"[raw_msg]\" looks like it was meant for in game communication, say it in OOC?", "Meant for OOC?", list("Yes", "No")) != "Yes")
return
@@ -254,7 +254,7 @@ ADMIN_VERB(reset_ooc_color, R_FUN, "Reset Player OOC Color", "Returns player OOC
// Check if the list is empty
if(!length(players))
// Express that there are no players we can ignore in chat
- to_chat(src, "There are no other players you can ignore!")
+ to_chat(src, span_infoplain("There are no other players you can ignore!"))
// Stop running
return
@@ -275,7 +275,7 @@ ADMIN_VERB(reset_ooc_color, R_FUN, "Reset Player OOC Color", "Returns player OOC
// Check if the selected player is on our ignore list
if(selection in prefs.ignoring)
// Express that the selected player is already on our ignore list in chat
- to_chat(src, "You are already ignoring [selection]!")
+ to_chat(src, span_infoplain("You are already ignoring [selection]!"))
// Stop running
return
@@ -287,7 +287,7 @@ ADMIN_VERB(reset_ooc_color, R_FUN, "Reset Player OOC Color", "Returns player OOC
prefs.save_preferences()
// Express that we've ignored the selected player in chat
- to_chat(src, "You are now ignoring [selection] on the OOC channel.")
+ to_chat(src, span_infoplain("You are now ignoring [selection] on the OOC channel."))
// Unignore verb
/client/verb/select_unignore()
@@ -298,7 +298,7 @@ ADMIN_VERB(reset_ooc_color, R_FUN, "Reset Player OOC Color", "Returns player OOC
// Check if we've ignored any players
if(!length(prefs.ignoring))
// Express that we haven't ignored any players in chat
- to_chat(src, "You haven't ignored any players!")
+ to_chat(src, span_infoplain("You haven't ignored any players!"))
// Stop running
return
@@ -313,7 +313,7 @@ ADMIN_VERB(reset_ooc_color, R_FUN, "Reset Player OOC Color", "Returns player OOC
// Check if the selected player is not on our ignore list
if(!(selection in prefs.ignoring))
// Express that the selected player is not on our ignore list in chat
- to_chat(src, "You are not ignoring [selection]!")
+ to_chat(src, span_infoplain("You are not ignoring [selection]!"))
// Stop running
return
@@ -325,7 +325,7 @@ ADMIN_VERB(reset_ooc_color, R_FUN, "Reset Player OOC Color", "Returns player OOC
prefs.save_preferences()
// Express that we've unignored the selected player in chat
- to_chat(src, "You are no longer ignoring [selection] on the OOC channel.")
+ to_chat(src, span_infoplain("You are no longer ignoring [selection] on the OOC channel."))
/client/proc/show_previous_roundend_report()
set name = "Your Last Round"
@@ -367,7 +367,7 @@ ADMIN_VERB(reset_ooc_color, R_FUN, "Reset Player OOC Color", "Returns player OOC
var/desired_width = 0
if(zoom_value)
- desired_width = round(view_size[1] * zoom_value * world.icon_size)
+ desired_width = round(view_size[1] * zoom_value * ICON_SIZE_X)
else
// Looks like we expect mapwindow.size to be "ixj" where i and j are numbers.
@@ -456,3 +456,9 @@ ADMIN_VERB(reset_ooc_color, R_FUN, "Reset Player OOC Color", "Returns player OOC
ASSERT(prefs, "User attempted to export preferences while preferences were null!") // what the fuck
prefs.savefile.export_json_to_client(usr, ckey)
+
+/client/verb/map_vote_tally_count()
+ set name = "Show Map Vote Tallies"
+ set desc = "View the current map vote tally counts."
+ set category = "Server"
+ to_chat(mob, SSmap_vote.tally_printout)
diff --git a/code/modules/client/verbs/who.dm b/code/modules/client/verbs/who.dm
index 5b31ae49849ce..bd023ede6308e 100644
--- a/code/modules/client/verbs/who.dm
+++ b/code/modules/client/verbs/who.dm
@@ -67,7 +67,7 @@
msg += ""
msg += "Total Players: [length(Lines)]"
- to_chat(src, "[msg]")
+ to_chat(src, span_infoplain("[msg]"))
/client/verb/adminwho()
set category = "Admin"
diff --git a/code/modules/clothing/belts/polymorph_belt.dm b/code/modules/clothing/belts/polymorph_belt.dm
index fb09b2e68c8f1..8e1bfb0aed7bd 100644
--- a/code/modules/clothing/belts/polymorph_belt.dm
+++ b/code/modules/clothing/belts/polymorph_belt.dm
@@ -49,7 +49,7 @@
active = TRUE
update_appearance(UPDATE_ICON_STATE)
update_transform_action()
- playsound(src, 'sound/machines/crate_open.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/crate/crate_open.ogg', 50, FALSE)
/obj/item/polymorph_belt/attack(mob/living/target_mob, mob/living/user, params)
. = ..()
@@ -104,9 +104,14 @@
invocation_type = INVOCATION_NONE
spell_requirements = NONE
possible_shapes = list(/mob/living/basic/cockroach)
+ can_be_shared = FALSE
/// Amount of time it takes us to transform back or forth
var/channel_time = 3 SECONDS
+/datum/action/cooldown/spell/shapeshift/polymorph_belt/cast(mob/living/cast_on)
+ cast_on = owner //make sure this is only affecting the wearer of the belt
+ return ..()
+
/datum/action/cooldown/spell/shapeshift/polymorph_belt/Remove(mob/remove_from)
var/datum/status_effect/shapechange_mob/shapechange = remove_from.has_status_effect(/datum/status_effect/shapechange_mob/from_spell)
var/atom/changer = shapechange?.caster_mob || remove_from
@@ -114,6 +119,7 @@
return ..()
/datum/action/cooldown/spell/shapeshift/polymorph_belt/before_cast(mob/living/cast_on)
+ cast_on = owner
. = ..()
if (. & SPELL_CANCEL_CAST)
return
@@ -139,7 +145,7 @@
cast_on.transform = old_transform
return . | SPELL_CANCEL_CAST
cast_on.visible_message(span_warning("[cast_on]'s body rearranges itself with a horrible crunching sound!"))
- playsound(cast_on, 'sound/magic/demon_consume.ogg', 50, TRUE)
+ playsound(cast_on, 'sound/effects/magic/demon_consume.ogg', 50, TRUE)
/datum/action/cooldown/spell/shapeshift/polymorph_belt/after_cast(atom/cast_on)
. = ..()
diff --git a/code/modules/clothing/chameleon/_chameleon_action.dm b/code/modules/clothing/chameleon/_chameleon_action.dm
index 7d11355791bde..b9a1697976bd8 100644
--- a/code/modules/clothing/chameleon/_chameleon_action.dm
+++ b/code/modules/clothing/chameleon/_chameleon_action.dm
@@ -208,7 +208,7 @@
if(istype(applying_from, /datum/outfit/job))
var/datum/outfit/job/job_outfit = applying_from
- var/datum/job/job_datum = SSjob.GetJobType(job_outfit.jobtype)
+ var/datum/job/job_datum = SSjob.get_job_type(job_outfit.jobtype)
apply_job_data(job_datum)
update_look(using_item_type)
diff --git a/code/modules/clothing/chameleon/chameleon_scanner.dm b/code/modules/clothing/chameleon/chameleon_scanner.dm
index 2ea0958a0cc66..8b213e3f816f9 100644
--- a/code/modules/clothing/chameleon/chameleon_scanner.dm
+++ b/code/modules/clothing/chameleon/chameleon_scanner.dm
@@ -46,10 +46,14 @@
/obj/item/chameleon_scanner/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
return scan_target(interacting_with, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING
-/obj/item/chameleon_scanner/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- return interact_with_atom(interacting_with, user, modifiers)
+/obj/item/chameleon_scanner/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(SHOULD_SKIP_INTERACTION(interacting_with, src, user))
+ return NONE
+ return ranged_interact_with_atom(interacting_with, user, modifiers)
-/obj/item/chameleon_scanner/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
+/obj/item/chameleon_scanner/ranged_interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!isliving(interacting_with) && !isturf(interacting_with))
+ return NONE
var/list/scanned_outfit = scan_target(interacting_with, user)
if(length(scanned_outfit))
var/datum/outfit/empty_outfit = new()
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index d4705aee14c43..8260acaa9474e 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -314,14 +314,6 @@
. += span_warning("[p_Theyre()] completely shredded and require[p_s()] mending before [p_they()] can be worn again!")
return
- switch (max_heat_protection_temperature)
- if (400 to 1000)
- . += "[src] offers the wearer limited protection from fire."
- if (1001 to 1600)
- . += "[src] offers the wearer some protection from fire."
- if (1601 to 35000)
- . += "[src] offers the wearer robust protection from fire."
-
if(TRAIT_FAST_CUFFING in clothing_traits)
. += "[src] increase the speed that you handcuff others."
@@ -356,6 +348,38 @@
if(get_armor().has_any_armor() || (flags_cover & (HEADCOVERSMOUTH|PEPPERPROOF)) || (clothing_flags & STOPSPRESSUREDAMAGE) || (visor_flags & STOPSPRESSUREDAMAGE))
. += span_notice("It has a tag listing its protection classes.")
+/obj/item/clothing/examine_tags(mob/user)
+ . = ..()
+ if (clothing_flags & THICKMATERIAL)
+ .["thick"] = "Protects from most injections and sprays."
+ if (clothing_flags & CASTING_CLOTHES)
+ .["magical"] = "Allows magical beings to cast spells when wearing [src]."
+ if((clothing_flags & STOPSPRESSUREDAMAGE) || (visor_flags & STOPSPRESSUREDAMAGE))
+ .["pressureproof"] = "Protects the wearer from extremely low or high pressure, such as vacuum of space."
+ if(flags_cover & PEPPERPROOF)
+ .["pepperproof"] = "Protects the wearer from the effects of pepperspray."
+ if (heat_protection || cold_protection)
+ var/heat_desc
+ var/cold_desc
+ switch (max_heat_protection_temperature)
+ if (400 to 1000)
+ heat_desc = "high"
+ if (1001 to 1600)
+ heat_desc = "very high"
+ if (1601 to 35000)
+ heat_desc = "extremely high"
+ switch (min_cold_protection_temperature)
+ if (160 to 272)
+ cold_desc = "low"
+ if (72 to 159)
+ cold_desc = "very low"
+ if (0 to 71)
+ cold_desc = "extremely low"
+ .["thermally insulated"] = "Protects the wearer from [jointext(list(heat_desc, cold_desc), " and ")] temperatures."
+
+/obj/item/clothing/examine_descriptor(mob/user)
+ return "clothing"
+
/obj/item/clothing/Topic(href, href_list)
. = ..()
@@ -383,7 +407,7 @@
added_damage_header = TRUE
readout += "[armor_to_protection_name(durability_key)] [armor_to_protection_class(rating)]"
- if(flags_cover & HEADCOVERSMOUTH || flags_cover & PEPPERPROOF)
+ if((flags_cover & HEADCOVERSMOUTH) || (flags_cover & PEPPERPROOF))
var/list/things_blocked = list()
if(flags_cover & HEADCOVERSMOUTH)
things_blocked += span_tooltip("Because this item is worn on the head and is covering the mouth, it will block facehugger proboscides, killing facehuggers.", "facehuggers")
@@ -393,7 +417,7 @@
readout += "COVERAGE"
readout += "It will block [english_list(things_blocked)]."
- if(clothing_flags & STOPSPRESSUREDAMAGE || visor_flags & STOPSPRESSUREDAMAGE)
+ if((clothing_flags & STOPSPRESSUREDAMAGE) || (visor_flags & STOPSPRESSUREDAMAGE))
var/list/parts_covered = list()
var/output_string = "It"
if(!(clothing_flags & STOPSPRESSUREDAMAGE))
@@ -405,8 +429,19 @@
if(length(parts_covered)) // Just in case someone makes spaceproof gloves or something
readout += "[output_string] will protect the wearer's [english_list(parts_covered)] from [span_tooltip("The extremely low pressure is the biggest danger posed by the vacuum of space.", "low pressure")]."
- if(min_cold_protection_temperature == SPACE_SUIT_MIN_TEMP_PROTECT)
- readout += "It will insulate the wearer from [span_tooltip("While not as dangerous as the lack of pressure, the extremely low temperature of space is also a hazard.", "the cold of space")]."
+ var/heat_prot
+ switch (max_heat_protection_temperature)
+ if (400 to 1000)
+ heat_prot = "minor"
+ if (1001 to 1600)
+ heat_prot = "some"
+ if (1601 to 35000)
+ heat_prot = "extreme"
+ if (heat_prot)
+ . += "[src] offers the wearer [heat_protection] protection from heat, up to [max_heat_protection_temperature] kelvin."
+
+ if(min_cold_protection_temperature)
+ readout += "It will insulate the wearer from [min_cold_protection_temperature <= SPACE_SUIT_MIN_TEMP_PROTECT ? span_tooltip("While not as dangerous as the lack of pressure, the extremely low temperature of space is also a hazard.", "the cold of space, down to [min_cold_protection_temperature] kelvin") : "cold, down to [min_cold_protection_temperature] kelvin"]."
if(!length(readout))
readout += "No armor or durability information available."
diff --git a/code/modules/clothing/ears/_ears.dm b/code/modules/clothing/ears/_ears.dm
index 5ae5b628808e1..bdc294f5366ae 100644
--- a/code/modules/clothing/ears/_ears.dm
+++ b/code/modules/clothing/ears/_ears.dm
@@ -26,3 +26,4 @@
. = ..()
AddElement(/datum/element/earhealing)
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
+ AddComponent(/datum/component/adjust_fishing_difficulty, -1)
diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm
index 4ed35997680c0..0178ae09c5322 100644
--- a/code/modules/clothing/glasses/_glasses.dm
+++ b/code/modules/clothing/glasses/_glasses.dm
@@ -112,7 +112,7 @@
throw_speed = 4
attack_verb_continuous = list("slices")
attack_verb_simple = list("slice")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
sharpness = SHARP_EDGED
/obj/item/clothing/glasses/science
@@ -251,7 +251,7 @@
throw_speed = 4
attack_verb_continuous = list("slices")
attack_verb_simple = list("slice")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
sharpness = SHARP_EDGED
glass_colour_type = /datum/client_colour/glass_colour/lightgreen
@@ -344,6 +344,7 @@
/obj/item/clothing/glasses/sunglasses/Initialize(mapload)
. = ..()
add_glasses_slapcraft_component()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -1)
/obj/item/clothing/glasses/sunglasses/proc/add_glasses_slapcraft_component()
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/hudsunsec, /datum/crafting_recipe/hudsunmed, /datum/crafting_recipe/hudsundiag, /datum/crafting_recipe/scienceglasses)
@@ -384,7 +385,7 @@
throw_speed = 4
attack_verb_continuous = list("slices")
attack_verb_simple = list("slice")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
sharpness = SHARP_EDGED
/obj/item/clothing/glasses/sunglasses/gar/orange
@@ -444,9 +445,21 @@
visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT
glass_colour_type = /datum/client_colour/glass_colour/gray
-/obj/item/clothing/glasses/welding/attack_self(mob/user)
+/obj/item/clothing/glasses/welding/Initialize(mapload)
+ . = ..()
+ if(!up)
+ AddComponent(/datum/component/adjust_fishing_difficulty, 8)
+
+/obj/item/clothing/glasses/welding/attack_self(mob/living/user)
adjust_visor(user)
+/obj/item/clothing/glasses/welding/adjust_visor(mob/user)
+ . = ..()
+ if(up)
+ qdel(GetComponent(/datum/component/adjust_fishing_difficulty))
+ else
+ AddComponent(/datum/component/adjust_fishing_difficulty, 8)
+
/obj/item/clothing/glasses/welding/update_icon_state()
. = ..()
icon_state = "[initial(icon_state)][up ? "up" : ""]"
@@ -465,6 +478,10 @@
tint = INFINITY // You WILL Be blind, no matter what
dog_fashion = /datum/dog_fashion/head
+/obj/item/clothing/glasses/blindfold/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 8)
+
/obj/item/clothing/glasses/trickblindfold
name = "blindfold"
desc = "A see-through blindfold perfect for cheating at games like pin the stun baton on the clown."
@@ -502,6 +519,10 @@
flags_cover = GLASSESCOVERSEYES
glass_colour_type = /datum/client_colour/glass_colour/red
+/obj/item/clothing/glasses/thermal/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -4)
+
/obj/item/clothing/glasses/thermal/emp_act(severity)
. = ..()
if(. & EMP_PROTECT_SELF)
@@ -614,6 +635,10 @@
var/list/hudlist = list(DATA_HUD_MEDICAL_ADVANCED, DATA_HUD_DIAGNOSTIC, DATA_HUD_SECURITY_ADVANCED, DATA_HUD_BOT_PATH)
var/xray = FALSE
+/obj/item/clothing/glasses/debug/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -15)
+
/obj/item/clothing/glasses/debug/equipped(mob/user, slot)
. = ..()
if(!(slot & ITEM_SLOT_EYES))
@@ -700,6 +725,10 @@
/// Hallucination datum currently being used for seeing mares
var/datum/hallucination/stored_hallucination
+/obj/item/clothing/glasses/nightmare_vision/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 13)
+
/obj/item/clothing/glasses/nightmare_vision/Destroy()
QDEL_NULL(stored_hallucination)
return ..()
diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm
index dc35ab1dbf4c0..332aba8a71990 100644
--- a/code/modules/clothing/glasses/hud.dm
+++ b/code/modules/clothing/glasses/hud.dm
@@ -204,7 +204,7 @@
throw_speed = 4
attack_verb_continuous = list("slices")
attack_verb_simple = list("slice")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
sharpness = SHARP_EDGED
/obj/item/clothing/glasses/hud/security/sunglasses/gars/giga
diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm
index 5f63e0c3464bf..418f8358f4d2a 100644
--- a/code/modules/clothing/gloves/_gloves.dm
+++ b/code/modules/clothing/gloves/_gloves.dm
@@ -12,6 +12,9 @@
siemens_coefficient = 0.5
body_parts_covered = HANDS
slot_flags = ITEM_SLOT_GLOVES
+ equip_sound = 'sound/items/equip/glove_equip.ogg'
+ drop_sound = 'sound/items/handling/glove_drop.ogg'
+ pickup_sound = 'sound/items/handling/glove_pick_up.ogg'
attack_verb_continuous = list("challenges")
attack_verb_simple = list("challenge")
strip_delay = 20
diff --git a/code/modules/clothing/gloves/bone.dm b/code/modules/clothing/gloves/bone.dm
index 2c75e642ff617..761057054f901 100644
--- a/code/modules/clothing/gloves/bone.dm
+++ b/code/modules/clothing/gloves/bone.dm
@@ -12,6 +12,10 @@
resistance_flags = NONE
armor_type = /datum/armor/gloves_bracer
+/obj/item/clothing/gloves/bracer/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 2)
+
/datum/armor/gloves_bracer
melee = 15
bullet = 25
diff --git a/code/modules/clothing/gloves/botany.dm b/code/modules/clothing/gloves/botany.dm
index af94a6b7bb13e..144477240b29d 100644
--- a/code/modules/clothing/gloves/botany.dm
+++ b/code/modules/clothing/gloves/botany.dm
@@ -12,6 +12,10 @@
clothing_traits = list(TRAIT_PLANT_SAFE)
armor_type = /datum/armor/gloves_botanic_leather
+/obj/item/clothing/gloves/botanic_leather/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2)
+
/datum/armor/gloves_botanic_leather
bio = 50
fire = 70
diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm
index 021d895f69c36..ab6e03ae493d2 100644
--- a/code/modules/clothing/gloves/boxing.dm
+++ b/code/modules/clothing/gloves/boxing.dm
@@ -19,6 +19,7 @@
)
AddComponent(/datum/component/martial_art_giver, style_to_give)
+ AddComponent(/datum/component/adjust_fishing_difficulty, 19)
/obj/item/clothing/gloves/boxing/evil
name = "evil boxing gloves"
diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm
index 82b1eb8f37954..048d575f5f02e 100644
--- a/code/modules/clothing/gloves/color.dm
+++ b/code/modules/clothing/gloves/color.dm
@@ -44,6 +44,7 @@
/obj/item/clothing/gloves/color/fingerless/Initialize(mapload)
. = ..()
var/static/list/slapcraft_recipe_list = list(/datum/crafting_recipe/gripperoffbrand)
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2)
AddElement(
/datum/element/slapcrafting,\
diff --git a/code/modules/clothing/gloves/combat.dm b/code/modules/clothing/gloves/combat.dm
index efc5bd40b0587..a2574c8d23ec7 100644
--- a/code/modules/clothing/gloves/combat.dm
+++ b/code/modules/clothing/gloves/combat.dm
@@ -25,8 +25,16 @@
greyscale_colors = null
inhand_icon_state = null
+/obj/item/clothing/gloves/combat/wizard/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3) //something something wizard casting
+
/obj/item/clothing/gloves/combat/floortile
name = "floortile camouflage gloves"
desc = "Is it just me or is there a pair of gloves on the floor?"
icon_state = "ftc_gloves"
inhand_icon_state = "greyscale_gloves"
+
+/obj/item/clothing/gloves/combat/floortiletile/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3) //tacticool
diff --git a/code/modules/clothing/gloves/insulated.dm b/code/modules/clothing/gloves/insulated.dm
index 19109d68b9c93..d20ae78c6c4cc 100644
--- a/code/modules/clothing/gloves/insulated.dm
+++ b/code/modules/clothing/gloves/insulated.dm
@@ -13,15 +13,18 @@
custom_price = PAYCHECK_CREW * 10
custom_premium_price = PAYCHECK_COMMAND * 6
cut_type = /obj/item/clothing/gloves/cut
- clothing_traits = list(TRAIT_CHUNKYFINGERS)
+
+/obj/item/clothing/gloves/color/yellow/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 10)
/obj/item/clothing/gloves/color/yellow/apply_fantasy_bonuses(bonus)
. = ..()
if(bonus >= 10)
- detach_clothing_traits(TRAIT_CHUNKYFINGERS)
+ qdel(GetComponent(/datum/component/adjust_fishing_difficulty))
/obj/item/clothing/gloves/color/yellow/remove_fantasy_bonuses(bonus)
- attach_clothing_traits(TRAIT_CHUNKYFINGERS)
+ AddComponent(/datum/component/adjust_fishing_difficulty, 10)
return ..()
/datum/armor/color_yellow
@@ -116,6 +119,10 @@
greyscale_colors = null
clothing_traits = list(TRAIT_FINGERPRINT_PASSTHROUGH)
+/obj/item/clothing/gloves/cut/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3)
+
/obj/item/clothing/gloves/cut/heirloom
desc = "The old gloves your great grandfather stole from Engineering, many moons ago. They've seen some tough times recently."
@@ -131,3 +138,7 @@
heat_protection = HANDS
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
resistance_flags = NONE
+
+/obj/item/clothing/gloves/chief_engineer/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -4)
diff --git a/code/modules/clothing/gloves/punch_mitts.dm b/code/modules/clothing/gloves/punch_mitts.dm
index 07d93d5ab021c..96848731a9cbb 100644
--- a/code/modules/clothing/gloves/punch_mitts.dm
+++ b/code/modules/clothing/gloves/punch_mitts.dm
@@ -11,7 +11,7 @@
/obj/item/clothing/gloves/fingerless/punch_mitts/Initialize(mapload)
. = ..()
-
+ AddComponent(/datum/component/adjust_fishing_difficulty, -4)
AddComponent(/datum/component/martial_art_giver, /datum/martial_art/boxing/hunter)
/datum/armor/gloves_mitts
diff --git a/code/modules/clothing/gloves/special.dm b/code/modules/clothing/gloves/special.dm
index 1366a29ac4a6e..98de3145ddc78 100644
--- a/code/modules/clothing/gloves/special.dm
+++ b/code/modules/clothing/gloves/special.dm
@@ -14,6 +14,7 @@
. = ..()
RegisterSignal(src, COMSIG_ITEM_EQUIPPED, PROC_REF(on_glove_equip))
RegisterSignal(src, COMSIG_ITEM_POST_UNEQUIP, PROC_REF(on_glove_unequip))
+ AddComponent(/datum/component/adjust_fishing_difficulty, 19)
/// Called when the glove is equipped. Adds a component to the equipper and stores a weak reference to it.
/obj/item/clothing/gloves/cargo_gauntlet/proc/on_glove_equip(datum/source, mob/equipper, slot)
@@ -59,6 +60,7 @@
/obj/item/clothing/gloves/rapid/Initialize(mapload)
. = ..()
AddComponent(/datum/component/wearertargeting/punchcooldown)
+ AddComponent(/datum/component/adjust_fishing_difficulty, -7)
/obj/item/clothing/gloves/radio
name = "translation gloves"
@@ -74,6 +76,10 @@
icon_state = "black"
greyscale_colors = "#2f2e31"
+/obj/item/clothing/gloves/race/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -7)
+
/obj/item/clothing/gloves/captain
desc = "Regal blue gloves, with a nice gold trim, a diamond anti-shock coating, and an integrated thermal barrier. Swanky."
name = "captain's gloves"
@@ -90,6 +96,10 @@
resistance_flags = NONE
clothing_traits = list(TRAIT_FAST_CUFFING)
+/obj/item/clothing/gloves/captain/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -4)
+
/datum/armor/captain_gloves
bio = 90
fire = 70
@@ -117,6 +127,10 @@
greyscale_colors = "#99eeff"
clothing_traits = list(TRAIT_QUICKER_CARRY, TRAIT_FASTMED)
+/obj/item/clothing/gloves/latex/nitrile/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -4)
+
/obj/item/clothing/gloves/latex/coroner
name = "coroner's gloves"
desc = "Black gloves made from latex with a superhydrophobic coating. Useful for picking bodies up instead of dragging blood behind."
@@ -156,42 +170,50 @@
clothing_traits = list(TRAIT_QUICKER_CARRY, TRAIT_CHUNKYFINGERS)
clothing_flags = THICKMATERIAL
+/obj/item/clothing/gloves/atmos/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 6)
+
///A pair of gloves that both allow the user to fish without the need of a held fishing rod and provides athletics experience.
/obj/item/clothing/gloves/fishing
name = "athletic fishing gloves"
desc = "A pair of gloves to fish without a fishing rod but your raw athletics strength. It doubles as a good workout device. WARNING: May cause injuries when catching bigger fish."
icon_state = "fishing_gloves"
+ ///The current fishing minigame datum the wearer is engaged in.
+ var/datum/fishing_challenge/challenge
/obj/item/clothing/gloves/fishing/Initialize(mapload)
. = ..()
AddComponent(/datum/component/profound_fisher, new /obj/item/fishing_rod/mob_fisher/athletic(src))
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3) //on top of the extra that you get from the athletics skill.
/obj/item/clothing/gloves/fishing/equipped(mob/user, slot)
. = ..()
if(slot == ITEM_SLOT_GLOVES)
- RegisterSignal(user, SIGNAL_ADDTRAIT(TRAIT_ACTIVELY_FISHING), PROC_REF(begin_workout))
+ RegisterSignal(user, COMSIG_MOB_BEGIN_FISHING_MINIGAME, PROC_REF(begin_workout))
/obj/item/clothing/gloves/fishing/dropped(mob/user)
- UnregisterSignal(user, list(SIGNAL_ADDTRAIT(TRAIT_ACTIVELY_FISHING), SIGNAL_REMOVETRAIT(TRAIT_ACTIVELY_FISHING)))
- STOP_PROCESSING(SSprocessing, src)
+ UnregisterSignal(user, COMSIG_MOB_BEGIN_FISHING_MINIGAME)
+ if(challenge)
+ stop_workout(user)
return ..()
-/obj/item/clothing/gloves/fishing/proc/begin_workout(datum/source)
+/obj/item/clothing/gloves/fishing/proc/begin_workout(datum/source, datum/fishing_challenge/challenge)
SIGNAL_HANDLER
- RegisterSignal(source, SIGNAL_REMOVETRAIT(TRAIT_ACTIVELY_FISHING), PROC_REF(stop_workout))
+ RegisterSignal(source, COMSIG_MOB_COMPLETE_FISHING, PROC_REF(stop_workout))
if(HAS_TRAIT(source, TRAIT_PROFOUND_FISHER)) //Only begin working out if we're fishing with these gloves and not some other fishing rod..
START_PROCESSING(SSprocessing, src)
+ src.challenge = challenge
/obj/item/clothing/gloves/fishing/proc/stop_workout(datum/source)
SIGNAL_HANDLER
- UnregisterSignal(source, SIGNAL_REMOVETRAIT(TRAIT_ACTIVELY_FISHING))
+ UnregisterSignal(source, COMSIG_MOB_COMPLETE_FISHING)
+ challenge = null
STOP_PROCESSING(SSprocessing, src)
/obj/item/clothing/gloves/fishing/process(seconds_per_tick)
var/mob/living/wearer = loc
- var/list/trait_source = GET_TRAIT_SOURCES(wearer, TRAIT_ACTIVELY_FISHING)
- var/datum/fishing_challenge/challenge = trait_source[1]
- var/stamina_exhaustion = 2.5 + challenge.difficulty * 0.025
+ var/stamina_exhaustion = 2 + challenge.difficulty * 0.02
var/is_heavy_gravity = wearer.has_gravity() > STANDARD_GRAVITY
var/obj/item/organ/internal/cyberimp/chest/spine/potential_spine = wearer.get_organ_slot(ORGAN_SLOT_SPINE)
if(istype(potential_spine))
@@ -210,11 +232,14 @@
///The internal fishing rod of the athletic fishing gloves. The more athletic you're, the easier the minigame will be.
/obj/item/fishing_rod/mob_fisher/athletic
+ name = "athletics fishing gloves"
icon = /obj/item/clothing/gloves/fishing::icon
icon_state = /obj/item/clothing/gloves/fishing::icon_state
line = null
bait = null
- ui_description = "The integrated fishing rod of a pair of athletic fishing gloves"
+ ui_description = "A pair of gloves to fish without a fishing rod while training your athletics."
+ wiki_description = "It requires the Advanced Fishing Technology Node to be researched to be printed. It may hurt the user when catching larger fish."
+ show_in_wiki = TRUE //Show this cool pair of gloves in the wiki.
/obj/item/fishing_rod/mob_fisher/athletic/Initialize(mapload)
. = ..()
@@ -224,7 +249,7 @@
return list()
/obj/item/fishing_rod/mob_fisher/athletic/hook_hit(atom/atom_hit_by_hook_projectile, mob/user)
- difficulty_modifier = -3 * user.mind?.get_skill_level(/datum/skill/athletics)
+ difficulty_modifier = -3 * (user.mind?.get_skill_level(/datum/skill/athletics) - 1)
return ..()
/obj/item/fishing_rod/mob_fisher/athletic/proc/noodling_is_dangerous(datum/source, atom/movable/reward, mob/living/user)
@@ -246,4 +271,4 @@
if(damage)
var/body_zone = pick(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM)
user.apply_damage(damage, BRUTE, body_zone, user.run_armor_check(body_zone, MELEE))
- playsound(src,'sound/weapons/bite.ogg', damage * 2, TRUE)
+ playsound(src,'sound/items/weapons/bite.ogg', damage * 2, TRUE)
diff --git a/code/modules/clothing/gloves/tacklers.dm b/code/modules/clothing/gloves/tacklers.dm
index bbe7f5dba18b4..d45fa8d0a90b5 100644
--- a/code/modules/clothing/gloves/tacklers.dm
+++ b/code/modules/clothing/gloves/tacklers.dm
@@ -22,6 +22,12 @@
var/tackle_speed = 1
/// See: [/datum/component/tackler/var/skill_mod]
var/skill_mod = 1
+ ///How much these gloves affect fishing difficulty
+ var/fishing_modifier = -5
+
+/obj/item/clothing/gloves/tackler/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, fishing_modifier) //fishing tackle equipment (ba dum tsh)
/obj/item/clothing/gloves/tackler/Destroy()
tackler = null
@@ -55,6 +61,7 @@
tackle_speed = 2
min_distance = 2
skill_mod = -2
+ fishing_modifier = -8
/obj/item/clothing/gloves/tackler/combat
name = "gorilla gloves"
@@ -106,9 +113,11 @@
base_knockdown = 1.75 SECONDS
min_distance = 2
skill_mod = -1
+ fishing_modifier = -3
/obj/item/clothing/gloves/tackler/football
name = "football gloves"
desc = "Gloves for football players! Teaches them how to tackle like a pro."
icon_state = "tackle_gloves"
inhand_icon_state = null
+ fishing_modifier = -3
diff --git a/code/modules/clothing/head/cakehat.dm b/code/modules/clothing/head/cakehat.dm
index 1fc0fa0b05b50..8a389cbf812ff 100644
--- a/code/modules/clothing/head/cakehat.dm
+++ b/code/modules/clothing/head/cakehat.dm
@@ -18,9 +18,9 @@
wound_bonus = 10
bare_wound_bonus = 5
dog_fashion = /datum/dog_fashion/head
- hitsound = 'sound/weapons/tap.ogg'
- var/hitsound_on = 'sound/weapons/sear.ogg' //so we can differentiate between cakehat and energyhat
- var/hitsound_off = 'sound/weapons/tap.ogg'
+ hitsound = 'sound/items/weapons/tap.ogg'
+ var/hitsound_on = 'sound/items/weapons/sear.ogg' //so we can differentiate between cakehat and energyhat
+ var/hitsound_off = 'sound/items/weapons/tap.ogg'
var/force_on = 15
var/throwforce_on = 15
var/damtype_on = BURN
@@ -57,23 +57,30 @@
/obj/item/clothing/head/utility/hardhat/cakehat/energycake
name = "energy cake"
desc = "You put the energy sword on your cake. Brilliant."
- icon_state = "hardhat0_energycake"
+ icon_state = "hardhat1_energycake"
inhand_icon_state = "hardhat0_energycake"
hat_type = "energycake"
- hitsound = 'sound/weapons/tap.ogg'
- hitsound_on = 'sound/weapons/blade1.ogg'
- hitsound_off = 'sound/weapons/tap.ogg'
+ hitsound = 'sound/items/weapons/tap.ogg'
+ hitsound_on = 'sound/items/weapons/blade1.ogg'
+ hitsound_off = 'sound/items/weapons/tap.ogg'
damtype_on = BRUTE
force_on = 18 //same as epen (but much more obvious)
light_range = 3 //ditto
heat = 0
+/obj/item/clothing/head/utility/hardhat/cakehat/energycake/Initialize(mapload)
+ . = ..()
+ //the compiled icon state is how it appears when it's on.
+ //That's how we want it to show on orbies (little virtual PDA pets).
+ //However we should reset their appearance on runtime.
+ update_appearance(UPDATE_ICON_STATE)
+
/obj/item/clothing/head/utility/hardhat/cakehat/energycake/turn_on(mob/living/user)
- playsound(src, 'sound/weapons/saberon.ogg', 5, TRUE)
+ playsound(src, 'sound/items/weapons/saberon.ogg', 5, TRUE)
to_chat(user, span_warning("You turn on \the [src]."))
return ..()
/obj/item/clothing/head/utility/hardhat/cakehat/energycake/turn_off(mob/living/user)
- playsound(src, 'sound/weapons/saberoff.ogg', 5, TRUE)
+ playsound(src, 'sound/items/weapons/saberoff.ogg', 5, TRUE)
to_chat(user, span_warning("You turn off \the [src]."))
return ..()
diff --git a/code/modules/clothing/head/collectable.dm b/code/modules/clothing/head/collectable.dm
index 3cd88ffc8eda0..9f7d01506f1e5 100644
--- a/code/modules/clothing/head/collectable.dm
+++ b/code/modules/clothing/head/collectable.dm
@@ -107,6 +107,10 @@
inhand_icon_state = null
dog_fashion = /datum/dog_fashion/head/pirate
+/obj/item/clothing/head/collectable/pirate/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3)
+
/obj/item/clothing/head/collectable/kitty
name = "collectable kitty ears"
desc = "The fur feels... a bit too realistic."
@@ -129,6 +133,10 @@
icon_state = "wizard"
dog_fashion = /datum/dog_fashion/head/blue_wizard
+/obj/item/clothing/head/collectable/wizard/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -1)
+
/obj/item/clothing/head/collectable/hardhat
name = "collectable hard hat"
desc = "WARNING! Offers no real protection, or luminosity, but damn, is it fancy!"
@@ -173,3 +181,7 @@
inhand_icon_state = "swatsyndie_helmet"
clothing_flags = SNUG_FIT
flags_inv = HIDEHAIR
+
+/obj/item/clothing/head/collectable/swat/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 2)
diff --git a/code/modules/clothing/head/fedora.dm b/code/modules/clothing/head/fedora.dm
index cf0d23ac37f58..7bf295f74553a 100644
--- a/code/modules/clothing/head/fedora.dm
+++ b/code/modules/clothing/head/fedora.dm
@@ -36,6 +36,10 @@
icon_state = "fedora_carpskin"
inhand_icon_state = null
+/obj/item/clothing/head/fedora/carpskin/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -4)
+
/obj/item/clothing/head/fedora/beige/press
name = "press fedora"
desc = "An beige fedora with a piece of paper saying \"PRESS\" stuck in its rim."
diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm
index fc56f83342346..6cd88c1746c7f 100644
--- a/code/modules/clothing/head/hardhat.dm
+++ b/code/modules/clothing/head/hardhat.dm
@@ -40,7 +40,6 @@
/obj/item/clothing/head/utility/hardhat/Initialize(mapload)
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
- RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
/obj/item/clothing/head/utility/hardhat/proc/toggle_helmet_light(mob/living/user)
on = !on
@@ -60,11 +59,11 @@
/obj/item/clothing/head/utility/hardhat/proc/turn_off(mob/user)
set_light_on(FALSE)
-/obj/item/clothing/head/utility/hardhat/proc/on_saboteur(datum/source, disrupt_duration)
- SIGNAL_HANDLER
+/obj/item/clothing/head/utility/hardhat/on_saboteur(datum/source, disrupt_duration)
+ . = ..()
if(on)
toggle_helmet_light()
- return COMSIG_SABOTEUR_SUCCESS
+ return TRUE
/obj/item/clothing/head/utility/hardhat/attack_self(mob/living/user)
toggle_helmet_light(user)
@@ -143,7 +142,7 @@
/obj/item/clothing/head/utility/hardhat/welding/adjust_visor(mob/living/user)
. = ..()
if(.)
- playsound(src, 'sound/mecha/mechmove03.ogg', 50, TRUE)
+ playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 50, TRUE)
/obj/item/clothing/head/utility/hardhat/welding/worn_overlays(mutable_appearance/standing, isinhands)
. = ..()
@@ -239,6 +238,10 @@
dog_fashion = /datum/dog_fashion/head/pumpkin/unlit
clothing_traits = list()
+/obj/item/clothing/head/utility/hardhat/pumpkinhead/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 3)
+
/obj/item/clothing/head/utility/hardhat/pumpkinhead/set_light_on(new_value)
. = ..()
if(isnull(.))
diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm
index 2874a27b72b98..b9b371d498c10 100644
--- a/code/modules/clothing/head/helmet.dm
+++ b/code/modules/clothing/head/helmet.dm
@@ -201,6 +201,10 @@
visor_flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH | PEPPERPROOF
clothing_traits = list(TRAIT_HEAD_INJURY_BLOCKED)
+/obj/item/clothing/head/helmet/toggleable/riot/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 2)
+
/datum/armor/toggleable_riot
melee = 50
bullet = 10
@@ -280,6 +284,10 @@
dog_fashion = null
clothing_traits = list(TRAIT_HEAD_INJURY_BLOCKED)
+/obj/item/clothing/head/helmet/swat/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 3)
+
/datum/armor/helmet_swat
melee = 40
bullet = 30
@@ -429,6 +437,10 @@
dog_fashion = null
clothing_traits = list(TRAIT_HEAD_INJURY_BLOCKED)
+/obj/item/clothing/head/helmet/knight/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 3)
+
/datum/armor/helmet_knight
melee = 50
bullet = 10
diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm
index 5d5fc87d409f4..7b67cda761f2e 100644
--- a/code/modules/clothing/head/jobs.dm
+++ b/code/modules/clothing/head/jobs.dm
@@ -624,6 +624,10 @@
flags_inv = HIDEHAIR //Cover your head doctor!
w_class = WEIGHT_CLASS_SMALL //surgery cap can be easily crumpled
+/obj/item/clothing/head/utility/surgerycap/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2) //FISH DOCTOR?!
+
/obj/item/clothing/head/utility/surgerycap/attack_self(mob/user)
. = ..()
if(.)
@@ -666,6 +670,10 @@
icon_state = "headmirror"
body_parts_covered = NONE
+/obj/item/clothing/head/utility/head_mirror/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2) //FISH DOCTOR?!
+
/obj/item/clothing/head/utility/head_mirror/examine(mob/user)
. = ..()
. += span_notice("In a properly lit room, you can use this to examine people's eyes, ears, and mouth closer.")
diff --git a/code/modules/clothing/head/mind_monkey_helmet.dm b/code/modules/clothing/head/mind_monkey_helmet.dm
index 449df33550560..e9ff99782395f 100644
--- a/code/modules/clothing/head/mind_monkey_helmet.dm
+++ b/code/modules/clothing/head/mind_monkey_helmet.dm
@@ -10,6 +10,10 @@
var/mob/living/carbon/human/magnification = null ///if the helmet is on a valid target (just works like a normal helmet if not (cargo please stop))
var/polling = FALSE///if the helmet is currently polling for targets (special code for removal)
var/light_colors = 1 ///which icon state color this is (red, blue, yellow)
+ /// This chance is increased by 7 every time the helmet fails to get a host, to dissuade spam. starts negative to add 1 safe reuse
+ var/rage_chance = -7
+ /// Holds the steam effect at dangerous rage chance levels.
+ var/obj/effect/abstract/particle_holder/particle_effect
/obj/item/clothing/head/helmet/monkey_sentience/Initialize(mapload)
. = ..()
@@ -18,12 +22,13 @@
/obj/item/clothing/head/helmet/monkey_sentience/examine(mob/user)
. = ..()
- . += span_boldwarning("---WARNING: REMOVAL OF HELMET ON SUBJECT MAY LEAD TO:---")
+ . += span_boldwarning("---WARNING: REMOVAL OF HELMET ON SUBJECT, OR REPEATED SENTIENCE GENERATION FAILURES MAY LEAD TO:---")
. += span_warning("BLOOD RAGE")
. += span_warning("BRAIN DEATH")
. += span_warning("PRIMAL GENE ACTIVATION")
. += span_warning("GENETIC MAKEUP MASS SUSCEPTIBILITY")
- . += span_boldnotice("Ask your CMO if mind magnification is right for you.")
+ . += span_notice("Warranty voided if helmet is placed after more than ") + span_boldnotice("two") + span_notice(" mind magnification failures.")
+ . += span_boldnotice("Ask your CMO if mind magnification is right for you!")
/obj/item/clothing/head/helmet/monkey_sentience/update_icon_state()
. = ..()
@@ -37,7 +42,7 @@
var/mob/living/something = user
to_chat(something, span_boldnotice("You feel a stabbing pain in the back of your head for a moment."))
something.apply_damage(5,BRUTE,BODY_ZONE_HEAD,FALSE,FALSE,FALSE) //notably: no damage resist (it's in your helmet), no damage spread (it's in your helmet)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
return
if(!(GLOB.ghost_role_flags & GHOSTROLE_STATION_SENTIENCE))
say("ERROR: Central Command has temporarily outlawed monkey sentience helmets in this sector. NEAREST LAWFUL SECTOR: 2.537 million light years away.")
@@ -55,9 +60,40 @@
UnregisterSignal(magnification, COMSIG_SPECIES_LOSS)
magnification = null
visible_message(span_notice("[src] falls silent and drops on the floor. Maybe you should try again later?"))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ var/particle_path
+ switch(rage_chance)
+ if(-7 to 0)
+ user.visible_message(span_notice("[src] falls silent and drops on the floor. Try again later?"))
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
+ particle_path = null
+ if(7 to 13)
+ user.visible_message(span_notice("[src] sparkles momentarily, then falls silent and drops on the floor. Maybe you should try again later?"))
+ playsound(src, SFX_SPARKS, 30, TRUE)
+ do_sparks(2, FALSE, src)
+ particle_path = /particles/smoke/steam/mild
+ if(14 to 21)
+ user.visible_message(span_notice("[src] sparkles and shatters ominously, then falls silent and drops on the floor. Maybe you shouldn't try again later."))
+ do_sparks(4, FALSE, src)
+ playsound(src, SFX_SPARKS, 15, TRUE)
+ playsound(src, SFX_SHATTER, 30, TRUE)
+ particle_path = /particles/smoke/steam/bad
+ if(21 to INFINITY)
+ user.visible_message(span_notice("[src] buzzes and smokes heavily, then falls silent and drops on the floor. This is clearly a bad idea."))
+ do_sparks(6, FALSE, src)
+ playsound(src, 'sound/machines/buzz/buzz-two.ogg', 30, TRUE)
+ particle_path = /particles/smoke/steam
+ rage_chance += 7
+
+ QDEL_NULL(particle_effect)
+ if(particle_path)
+ particle_effect = new(src, particle_path)
+ QDEL_IN(particle_effect, 2 MINUTES)
+
+ if((rage_chance > 0) && prob(rage_chance)) // too much spam means agnry gorilla running at you
+ malfunction(user)
user.dropItemToGround(src)
return
+
magnification.key = chosen_one.key
playsound(src, 'sound/machines/microwave/microwave-end.ogg', 100, FALSE)
to_chat(magnification, span_notice("You're a mind magnified monkey! Protect your helmet with your life- if you lose it, your sentience goes with it!"))
@@ -78,25 +114,28 @@
to_chat(magnification, span_userdanger("You feel your flicker of sentience ripped away from you, as everything becomes dim..."))
magnification.ghostize(FALSE)
if(prob(10))
- switch(rand(1,4))
- if(1) //blood rage
- var/datum/ai_controller/monkey/monky_controller = magnification.ai_controller
- monky_controller.set_trip_mode(mode = FALSE)
- monky_controller.set_blackboard_key(BB_MONKEY_AGGRESSIVE, TRUE)
- if(2) //brain death
- magnification.apply_damage(500,BRAIN,BODY_ZONE_HEAD,FALSE,FALSE,FALSE)
- if(3) //primal gene (gorilla)
- magnification.gorillize()
- if(4) //genetic mass susceptibility (gib)
- magnification.gib(DROP_ALL_REMAINS)
+ malfunction(magnification)
//either used up correctly or taken off before polling finished (punish this by destroying the helmet)
UnregisterSignal(magnification, COMSIG_SPECIES_LOSS)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
playsound(src, SFX_SPARKS, 100, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
visible_message(span_warning("[src] fizzles and breaks apart!"))
magnification = null
new /obj/effect/decal/cleanable/ash(drop_location()) //just in case they're in a locker or other containers it needs to use crematorium ash, see the path itself for an explanation
+/obj/item/clothing/head/helmet/monkey_sentience/proc/malfunction(mob/living/carbon/target)
+ switch(rand(1,4))
+ if(1) //blood rage
+ var/datum/ai_controller/monkey/monky_controller = target.ai_controller
+ monky_controller.set_trip_mode(mode = FALSE)
+ monky_controller.set_blackboard_key(BB_MONKEY_AGGRESSIVE, TRUE)
+ if(2) //brain death
+ target.apply_damage(500,BRAIN,BODY_ZONE_HEAD,FALSE,FALSE,FALSE)
+ if(3) //primal gene (gorilla)
+ target.gorillize()
+ if(4) //genetic mass susceptibility (gib)
+ target.gib(DROP_ALL_REMAINS)
+
/obj/item/clothing/head/helmet/monkey_sentience/dropped(mob/user)
. = ..()
if(magnification || polling)
diff --git a/code/modules/clothing/head/moth.dm b/code/modules/clothing/head/moth.dm
index 58be10a1b0fc7..e040d834c9d21 100644
--- a/code/modules/clothing/head/moth.dm
+++ b/code/modules/clothing/head/moth.dm
@@ -15,6 +15,7 @@
/obj/item/clothing/head/mothcap/original/Initialize(mapload)
. = ..()
AddComponent(/datum/component/scope, range_modifier = 1.2, zoom_method = ZOOM_METHOD_ITEM_ACTION, item_action_type = /datum/action/item_action/hands_free/moth_googles)
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2)
/obj/item/clothing/head/mothcap/original/item_action_slot_check(slot, mob/user, datum/action/action)
return (slot & ITEM_SLOT_HEAD)
diff --git a/code/modules/clothing/head/pirate.dm b/code/modules/clothing/head/pirate.dm
index 818478ccb7d4a..6d5d0a67f30f9 100644
--- a/code/modules/clothing/head/pirate.dm
+++ b/code/modules/clothing/head/pirate.dm
@@ -5,8 +5,9 @@
inhand_icon_state = null
dog_fashion = /datum/dog_fashion/head/pirate
-/obj/item/clothing/head/costume/pirate
- var/datum/language/piratespeak/L = new
+/obj/item/clothing/head/costume/pirate/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3)
/obj/item/clothing/head/costume/pirate/equipped(mob/user, slot)
. = ..()
diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm
index f8e7e80532f16..629305740318d 100644
--- a/code/modules/clothing/head/soft_caps.dm
+++ b/code/modules/clothing/head/soft_caps.dm
@@ -175,6 +175,7 @@
. = ..()
AddComponent(/datum/component/speechmod, replacements = strings("crustacean_replacement.json", "crustacean")) //you asked for this.
AddElement(/datum/element/skill_reward, /datum/skill/fishing)
+ AddComponent(/datum/component/adjust_fishing_difficulty, -4)
#define PROPHAT_MOOD "prophat"
diff --git a/code/modules/clothing/head/tophat.dm b/code/modules/clothing/head/tophat.dm
index 612f02ce692e9..e204673743e58 100644
--- a/code/modules/clothing/head/tophat.dm
+++ b/code/modules/clothing/head/tophat.dm
@@ -21,7 +21,7 @@
return
COOLDOWN_START(src, rabbit_cooldown, RABBIT_CD_TIME)
- playsound(get_turf(src), 'sound/weapons/emitter.ogg', 70)
+ playsound(get_turf(src), 'sound/items/weapons/emitter.ogg', 70)
do_smoke(amount = DIAMOND_AREA(1), holder = src, location = src, smoke_type=/obj/effect/particle_effect/fluid/smoke/quick)
if(prob(10))
diff --git a/code/modules/clothing/head/welding.dm b/code/modules/clothing/head/welding.dm
index e3f014875dde4..000448fd7258d 100644
--- a/code/modules/clothing/head/welding.dm
+++ b/code/modules/clothing/head/welding.dm
@@ -18,6 +18,11 @@
resistance_flags = FIRE_PROOF
clothing_flags = SNUG_FIT | STACKABLE_HELMET_EXEMPT
+/obj/item/clothing/head/utility/welding/Initialize(mapload)
+ . = ..()
+ if(!up)
+ AddComponent(/datum/component/adjust_fishing_difficulty, 8)
+
/datum/armor/utility_welding
melee = 10
fire = 100
@@ -26,6 +31,13 @@
/obj/item/clothing/head/utility/welding/attack_self(mob/user)
adjust_visor(user)
+/obj/item/clothing/head/utility/welding/adjust_visor(mob/user)
+ . = ..()
+ if(up)
+ qdel(GetComponent(/datum/component/adjust_fishing_difficulty))
+ else
+ AddComponent(/datum/component/adjust_fishing_difficulty, 8)
+
/obj/item/clothing/head/utility/welding/update_icon_state()
. = ..()
icon_state = "[initial(icon_state)][up ? "up" : ""]"
diff --git a/code/modules/clothing/masks/animal_masks.dm b/code/modules/clothing/masks/animal_masks.dm
index 05e5888168e12..5a92c8faf071c 100644
--- a/code/modules/clothing/masks/animal_masks.dm
+++ b/code/modules/clothing/masks/animal_masks.dm
@@ -133,7 +133,7 @@ GLOBAL_LIST_INIT(cursed_animal_masks, list(
icon_state = "pig"
inhand_icon_state = null
animal_sounds = list("Oink!","Squeeeeeeee!","Oink Oink!")
- curse_spawn_sound = 'sound/magic/pighead_curse.ogg'
+ curse_spawn_sound = 'sound/effects/magic/pighead_curse.ogg'
flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
/obj/item/clothing/mask/animal/pig/cursed
@@ -150,6 +150,18 @@ GLOBAL_LIST_INIT(cursed_animal_masks, list(
animal_sounds_alt = list("HUUUUU!!","SMOOOOOKIN'!!","Hello my baby, hello my honey, hello my rag-time gal.", "Feels bad, man.", "GIT DIS GUY OFF ME!!" ,"SOMEBODY STOP ME!!", "NORMIES, GET OUT!!")
flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
+/obj/item/clothing/mask/animal/frog/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, cursed ? 2 : -2)
+
+/obj/item/clothing/mask/animal/frog/make_cursed()
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 2)
+
+/obj/item/clothing/mask/animal/frog/clear_curse()
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2)
+
/obj/item/clothing/mask/animal/frog/cursed
cursed = TRUE
@@ -158,7 +170,7 @@ GLOBAL_LIST_INIT(cursed_animal_masks, list(
icon_state = "cowmask"
inhand_icon_state = null
flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
- curse_spawn_sound = 'sound/magic/cowhead_curse.ogg'
+ curse_spawn_sound = 'sound/effects/magic/cowhead_curse.ogg'
animal_sounds = list("Moooooooo!","Moo!","Moooo!")
/obj/item/clothing/mask/animal/cowmask/cursed
@@ -172,7 +184,7 @@ GLOBAL_LIST_INIT(cursed_animal_masks, list(
inhand_icon_state = null
animal_sounds = list("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!")
flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDEEYES|HIDEEARS|HIDESNOUT
- curse_spawn_sound = 'sound/magic/horsehead_curse.ogg'
+ curse_spawn_sound = 'sound/effects/magic/horsehead_curse.ogg'
/obj/item/clothing/mask/animal/horsehead/cursed
cursed = TRUE
@@ -227,6 +239,18 @@ GLOBAL_LIST_INIT(cursed_animal_masks, list(
inhand_icon_state = null
animal_sounds = list("RAWR!","Rawr!","GRR!","Growl!")
+/obj/item/clothing/mask/animal/small/bear/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, cursed ? 2 : -2)
+
+/obj/item/clothing/mask/animal/small/bear/make_cursed()
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 2)
+
+/obj/item/clothing/mask/animal/small/bear/clear_curse()
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2)
+
/obj/item/clothing/mask/animal/small/bear/cursed
cursed = TRUE
@@ -275,5 +299,17 @@ GLOBAL_LIST_INIT(cursed_animal_masks, list(
animal_sounds_alt = list("Eekum-bokum!", "Oomenacka!", "In mah head..... Zombi.... Zombi!")
animal_sounds_alt_probability = 5
+/obj/item/clothing/mask/animal/small/tribal/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, cursed ? 4 : -4)
+
+/obj/item/clothing/mask/animal/small/tribal/make_cursed()
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 4)
+
+/obj/item/clothing/mask/animal/small/tribal/clear_curse()
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -4)
+
/obj/item/clothing/mask/animal/small/tribal/cursed //adminspawn only.
cursed = TRUE
diff --git a/code/modules/clothing/masks/boxing.dm b/code/modules/clothing/masks/boxing.dm
index 2e75cebf5d98f..46ad60e58deaa 100644
--- a/code/modules/clothing/masks/boxing.dm
+++ b/code/modules/clothing/masks/boxing.dm
@@ -24,6 +24,10 @@
w_class = WEIGHT_CLASS_SMALL
actions_types = list(/datum/action/item_action/adjust)
+/obj/item/clothing/mask/floortilebalaclava/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3) //tacticool
+
/obj/item/clothing/mask/floortilebalaclava/attack_self(mob/user)
adjust_visor(user)
diff --git a/code/modules/clothing/masks/costume.dm b/code/modules/clothing/masks/costume.dm
index 844b823880ac4..ff980442565a4 100644
--- a/code/modules/clothing/masks/costume.dm
+++ b/code/modules/clothing/masks/costume.dm
@@ -38,6 +38,7 @@
icon_state = "kitsune"
inhand_icon_state = null
w_class = WEIGHT_CLASS_SMALL
+ adjusted_flags = ITEM_SLOT_HEAD
flags_inv = HIDEFACE|HIDEFACIALHAIR
custom_price = PAYCHECK_CREW
greyscale_colors = "#EEEEEE#AA0000"
@@ -45,6 +46,18 @@
greyscale_config_worn = /datum/greyscale_config/kitsune/worn
flags_1 = IS_PLAYER_COLORABLE_1
+/obj/item/clothing/mask/kitsune/examine(mob/user)
+ . = ..()
+ if(up)
+ . += "Use in-hand to wear as a mask!"
+ return
+ else
+ . += "Use in-hand to tie it up to wear as a hat!"
+
+/obj/item/clothing/mask/kitsune/attack_self(mob/user)
+ adjust_visor(user)
+ alternate_worn_layer = up ? ABOVE_BODY_FRONT_HEAD_LAYER : null
+
/obj/item/clothing/mask/rebellion
name = "rebellion mask"
desc = "Mask that is usually used during rebellions by insurgents. It covers the entire face and makes you unrecognizable."
diff --git a/code/modules/clothing/masks/gas_filter.dm b/code/modules/clothing/masks/gas_filter.dm
index 08ae650c24726..e29f80a5ea089 100644
--- a/code/modules/clothing/masks/gas_filter.dm
+++ b/code/modules/clothing/masks/gas_filter.dm
@@ -53,7 +53,7 @@
/obj/item/gas_filter/examine(mob/user)
. = ..()
- . += "[src] is at [filter_status]% durability."
+ . += span_notice("[src] is at [filter_status]% durability.")
/**
* called by the gas mask where the filter is installed, lower the filter_status depending on the breath gas composition and by the strength of the filter
diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm
index c5871d23c1859..6d92b80ebfd47 100644
--- a/code/modules/clothing/masks/gasmask.dm
+++ b/code/modules/clothing/masks/gasmask.dm
@@ -18,6 +18,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
armor_type = /datum/armor/mask_gas
flags_cover = MASKCOVERSEYES | MASKCOVERSMOUTH | PEPPERPROOF
resistance_flags = NONE
+ voice_filter = "lowpass=f=750,volume=2"
///Max numbers of installable filters
var/max_filters = 1
///List to keep track of each filter
@@ -28,19 +29,19 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
var/has_fov = TRUE
///Cigarette in the mask
var/obj/item/cigarette/cig
- voice_filter = "lowpass=f=750,volume=2"
+ ///How much does this mask affect fishing difficulty
+ var/fishing_modifier = 2
/datum/armor/mask_gas
bio = 100
-/obj/item/clothing/mask/gas/worn_overlays(mutable_appearance/standing, isinhands)
- . = ..()
- if(!isinhands && cig)
- . += cig.build_worn_icon(default_layer = FACEMASK_LAYER, default_icon_file = 'icons/mob/clothing/mask.dmi')
-
/obj/item/clothing/mask/gas/Initialize(mapload)
. = ..()
init_fov()
+
+ if(fishing_modifier)
+ AddComponent(/datum/component/adjust_fishing_difficulty, fishing_modifier)
+
if(!max_filters || !starting_filter_type)
return
@@ -49,6 +50,11 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
LAZYADD(gas_filters, inserted_filter)
has_filter = TRUE
+/obj/item/clothing/mask/gas/worn_overlays(mutable_appearance/standing, isinhands)
+ . = ..()
+ if(!isinhands && cig)
+ . += cig.build_worn_icon(default_layer = FACEMASK_LAYER, default_icon_file = 'icons/mob/clothing/mask.dmi')
+
/obj/item/clothing/mask/gas/Destroy()
QDEL_LAZYLIST(gas_filters)
return..()
@@ -222,6 +228,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT
resistance_flags = FIRE_PROOF
clothing_flags = parent_type::clothing_flags | INTERNALS_ADJUST_EXEMPT
+ fishing_modifier = 8
/datum/armor/gas_welding
melee = 10
@@ -235,7 +242,13 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
/obj/item/clothing/mask/gas/welding/adjust_visor(mob/living/user)
. = ..()
if(.)
- playsound(src, 'sound/mecha/mechmove03.ogg', 50, TRUE)
+ playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 50, TRUE)
+ if(!fishing_modifier)
+ return
+ if(up)
+ qdel(GetComponent(/datum/component/adjust_fishing_difficulty))
+ else
+ AddComponent(/datum/component/adjust_fishing_difficulty, fishing_modifier)
/obj/item/clothing/mask/gas/welding/update_icon_state()
. = ..()
@@ -266,6 +279,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
strip_delay = 60
w_class = WEIGHT_CLASS_SMALL
has_fov = FALSE
+ fishing_modifier = 0
/obj/item/clothing/mask/gas/clown_hat
name = "clown wig and mask"
@@ -285,6 +299,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
has_fov = FALSE
var/list/clownmask_designs = list()
voice_filter = null // performer masks expect to be talked through
+ fishing_modifier = 0
/obj/item/clothing/mask/gas/clown_hat/plasmaman
starting_filter_type = /obj/item/gas_filter/plasmaman
@@ -327,6 +342,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
flags_cover = MASKCOVERSEYES
resistance_flags = FLAMMABLE
has_fov = FALSE
+ fishing_modifier = 0
/obj/item/clothing/mask/gas/mime
name = "mime mask"
@@ -340,6 +356,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
actions_types = list(/datum/action/item_action/adjust)
species_exception = list(/datum/species/golem)
has_fov = FALSE
+ fishing_modifier = 0
var/list/mimemask_designs = list()
/obj/item/clothing/mask/gas/mime/plasmaman
@@ -384,6 +401,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
flags_cover = MASKCOVERSEYES
resistance_flags = FLAMMABLE
has_fov = FALSE
+ fishing_modifier = 0
/obj/item/clothing/mask/gas/sexymime
name = "sexy mime mask"
@@ -395,6 +413,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
resistance_flags = FLAMMABLE
species_exception = list(/datum/species/golem)
has_fov = FALSE
+ fishing_modifier = 0
/obj/item/clothing/mask/gas/cyborg
name = "cyborg visor"
@@ -403,6 +422,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
resistance_flags = FLAMMABLE
has_fov = FALSE
flags_cover = MASKCOVERSEYES
+ fishing_modifier = 0
/obj/item/clothing/mask/gas/owl_mask
name = "owl mask"
@@ -413,6 +433,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
flags_cover = MASKCOVERSEYES
resistance_flags = FLAMMABLE
has_fov = FALSE
+ fishing_modifier = -1
/obj/item/clothing/mask/gas/carp
name = "carp mask"
@@ -421,6 +442,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
inhand_icon_state = null
has_fov = FALSE
flags_cover = MASKCOVERSEYES
+ fishing_modifier = -3
/obj/item/clothing/mask/gas/tiki_mask
name = "tiki mask"
@@ -434,6 +456,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
max_integrity = 100
actions_types = list(/datum/action/item_action/adjust)
dog_fashion = null
+ fishing_modifier = -2
var/list/tikimask_designs = list()
/obj/item/clothing/mask/gas/tiki_mask/Initialize(mapload)
@@ -476,6 +499,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
resistance_flags = FIRE_PROOF | ACID_PROOF
flags_inv = HIDEFACIALHAIR|HIDEFACE|HIDEEYES|HIDEEARS|HIDEHAIR|HIDESNOUT
has_fov = FALSE
+ fishing_modifier = -2
/obj/item/clothing/mask/gas/prop
name = "prop gas mask"
@@ -486,6 +510,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
flags_cover = MASKCOVERSMOUTH
resistance_flags = FLAMMABLE
has_fov = FALSE
+ fishing_modifier = 0
/obj/item/clothing/mask/gas/atmosprop
name = "prop atmospheric gas mask"
@@ -497,6 +522,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
flags_cover = MASKCOVERSMOUTH
resistance_flags = FLAMMABLE
has_fov = FALSE
+ fishing_modifier = 0
/obj/item/clothing/mask/gas/driscoll
name = "driscoll mask"
@@ -505,3 +531,4 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
flags_inv = HIDEFACIALHAIR
w_class = WEIGHT_CLASS_NORMAL
inhand_icon_state = null
+ fishing_modifier = 0
diff --git a/code/modules/clothing/masks/hailer.dm b/code/modules/clothing/masks/hailer.dm
index cbfbc166cbcab..aee1ac17b1b3e 100644
--- a/code/modules/clothing/masks/hailer.dm
+++ b/code/modules/clothing/masks/hailer.dm
@@ -57,7 +57,8 @@ GLOBAL_LIST_INIT(hailer_phrases, list(
visor_flags_cover = MASKCOVERSMOUTH
tint = 0
has_fov = FALSE
- unique_death = 'sound/voice/sec_death.ogg'
+ fishing_modifier = 0
+ unique_death = 'sound/items/sec_hailer/sec_death.ogg'
COOLDOWN_DECLARE(hailer_cooldown)
///Decides the phrases available for use; defines used are the last index of a category of available phrases
var/aggressiveness = AGGR_BAD_COP
@@ -86,6 +87,7 @@ GLOBAL_LIST_INIT(hailer_phrases, list(
visor_flags_inv = 0
flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES | PEPPERPROOF
visor_flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES | PEPPERPROOF
+ fishing_modifier = 2
/obj/item/clothing/mask/gas/sechailer/swat/spacepol
name = "spacepol mask"
@@ -103,6 +105,7 @@ GLOBAL_LIST_INIT(hailer_phrases, list(
slot_flags = null
aggressiveness = AGGR_GOOD_COP // Borgs are nicecurity!
actions_types = list(/datum/action/item_action/halt)
+ fishing_modifier = 0
/obj/item/clothing/mask/gas/sechailer/screwdriver_act(mob/living/user, obj/item/I)
. = ..()
@@ -207,7 +210,7 @@ GLOBAL_LIST_INIT(hailer_phrases, list(
return
COOLDOWN_START(src, whistle_cooldown, 10 SECONDS)
user.audible_message("HALT!")
- playsound(src, 'sound/misc/whistle.ogg', 50, FALSE, 4)
+ playsound(src, 'sound/items/whistle/whistle.ogg', 50, FALSE, 4)
/datum/action/item_action/halt
name = "HALT!"
diff --git a/code/modules/clothing/masks/muzzle.dm b/code/modules/clothing/masks/muzzle.dm
index 6154e7762cb52..05bdd086efe86 100644
--- a/code/modules/clothing/masks/muzzle.dm
+++ b/code/modules/clothing/masks/muzzle.dm
@@ -62,7 +62,7 @@
. = ..()
if(user.get_item_by_slot(ITEM_SLOT_MASK) != src)
return
- playsound(user, 'sound/items/duct_tape_rip.ogg', 50, TRUE)
+ playsound(user, 'sound/items/duct_tape/duct_tape_rip.ogg', 50, TRUE)
if(harmful_strip)
user.apply_damage(stripping_damage, BRUTE, BODY_ZONE_HEAD)
INVOKE_ASYNC(user, TYPE_PROC_REF(/mob, emote), "scream")
diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm
index 839f3430cd9cc..7e16bd3bdab83 100644
--- a/code/modules/clothing/neck/_neck.dm
+++ b/code/modules/clothing/neck/_neck.dm
@@ -213,6 +213,10 @@
desc = "An outdated medical apparatus for listening to the sounds of the human body. It also makes you look like you know what you're doing."
icon_state = "stethoscope"
+/obj/item/clothing/neck/stethoscope/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2) //FISH DOCTOR?!
+
/obj/item/clothing/neck/stethoscope/suicide_act(mob/living/carbon/user)
user.visible_message(span_suicide("[user] puts \the [src] to [user.p_their()] chest! It looks like [user.p_they()] won't hear much!"))
return OXYLOSS
@@ -452,6 +456,10 @@
/obj/item/clothing/neck/petcollar/attack_self(mob/user)
tagname = sanitize_name(tgui_input_text(user, "Would you like to change the name on the tag?", "Pet Naming", "Spot", MAX_NAME_LEN))
+ if (!tagname || !length(tagname))
+ name = initial(name)
+ tagname = null
+ return
name = "[initial(name)] - [tagname]"
//////////////
diff --git a/code/modules/clothing/outfits/ert.dm b/code/modules/clothing/outfits/ert.dm
index 101cedfef88eb..b14573a087cf1 100644
--- a/code/modules/clothing/outfits/ert.dm
+++ b/code/modules/clothing/outfits/ert.dm
@@ -573,3 +573,72 @@
head = /obj/item/clothing/head/beret/militia
l_hand = /obj/item/megaphone
suit_store = /obj/item/gun/energy/laser/musket/prime
+
+/datum/outfit/centcom/ert/medical_commander
+ name = "Chief EMT"
+ id = /obj/item/card/id/advanced/centcom/ert/medical
+ uniform = /obj/item/clothing/under/rank/medical/chief_medical_officer
+ l_pocket = /obj/item/healthanalyzer/advanced
+ shoes = /obj/item/clothing/shoes/sneakers/white
+ backpack_contents = list(
+ /obj/item/reagent_containers/hypospray/combat = 1,
+ /obj/item/storage/medkit/regular = 1,
+ /obj/item/storage/medkit/advanced = 1,
+ /obj/item/melee/baton/telescopic = 1,
+ /obj/item/gun/energy/pulse/pistol/loyalpin = 1,
+ /obj/item/stack/medical/poultice = 1, //These stacks contain 15 by default. Great for getting corpses to defib range without surgery.
+ )
+ belt = /obj/item/storage/belt/medical/ert
+ glasses = /obj/item/clothing/glasses/hud/health/sunglasses
+ additional_radio = /obj/item/encryptionkey/heads/cmo
+ mask = /obj/item/clothing/mask/surgical
+ back = /obj/item/mod/control/pre_equipped/emergency_medical/corpsman
+ gloves = null
+ suit = null
+ head = null
+ suit_store = /obj/item/tank/internals/oxygen
+
+/datum/outfit/centcom/ert/medical_technician
+ name = "EMT Paramedic"
+ id = /obj/item/card/id/advanced/centcom/ert/medical
+ uniform = /obj/item/clothing/under/rank/medical/scrubs/blue
+ l_pocket = /obj/item/healthanalyzer
+ backpack_contents = list(
+ /obj/item/reagent_containers/hypospray/combat = 1,
+ /obj/item/storage/medkit/regular = 1,
+ /obj/item/reagent_containers/syringe = 1,
+ /obj/item/reagent_containers/cup/bottle/formaldehyde = 1,
+ /obj/item/reagent_containers/medigel/sterilizine = 1,
+ /obj/item/bodybag = 2,
+ )
+ mask = /obj/item/clothing/mask/surgical
+ belt = /obj/item/storage/belt/medical/ert
+ glasses = /obj/item/clothing/glasses/hud/health
+ additional_radio = /obj/item/encryptionkey/heads/cmo
+ shoes = /obj/item/clothing/shoes/sneakers/blue
+ back = /obj/item/mod/control/pre_equipped/emergency_medical
+ gloves = null
+ suit = null
+ head = null
+ suit_store = /obj/item/tank/internals/oxygen
+
+/obj/item/mod/control/pre_equipped/emergency_medical
+ theme = /datum/mod_theme/medical
+ starting_frequency = MODLINK_FREQ_CENTCOM
+ applied_cell = /obj/item/stock_parts/power_store/cell/hyper
+ applied_modules = list(
+ /obj/item/mod/module/organizer,
+ /obj/item/mod/module/defibrillator,
+ /obj/item/mod/module/flashlight,
+ /obj/item/mod/module/health_analyzer,
+ /obj/item/mod/module/injector,
+ /obj/item/mod/module/surgical_processor/emergency,
+ /obj/item/mod/module/storage/large_capacity,
+ )
+
+/obj/item/mod/control/pre_equipped/emergency_medical/corpsman
+ theme = /datum/mod_theme/medical/corpsman
+
+///Identical to medical MODsuit, but uses the alternate skin by default.
+/datum/mod_theme/medical/corpsman
+ default_skin = "corpsman"
diff --git a/code/modules/clothing/outfits/event.dm b/code/modules/clothing/outfits/event.dm
index fa07b3297b285..b99ea6f526202 100644
--- a/code/modules/clothing/outfits/event.dm
+++ b/code/modules/clothing/outfits/event.dm
@@ -18,7 +18,7 @@
if(visualsOnly)
return
user.fully_replace_character_name(user.real_name, "Santa Claus")
- user.mind.set_assigned_role(SSjob.GetJobType(/datum/job/santa))
+ user.mind.set_assigned_role(SSjob.get_job_type(/datum/job/santa))
user.mind.special_role = ROLE_SANTA
user.hairstyle = "Long Hair 3"
diff --git a/code/modules/clothing/outfits/plasmaman.dm b/code/modules/clothing/outfits/plasmaman.dm
index 1b2e0ead14883..a0e927c631938 100644
--- a/code/modules/clothing/outfits/plasmaman.dm
+++ b/code/modules/clothing/outfits/plasmaman.dm
@@ -302,3 +302,15 @@
uniform = /obj/item/clothing/under/plasmaman //same
gloves = /obj/item/clothing/gloves/color/plasmaman/black
head = /obj/item/clothing/head/helmet/space/plasmaman
+
+/datum/outfit/plasmaman/medical_commander
+ name = "Chief EMT Plasmaman"
+ uniform = /obj/item/clothing/under/plasmaman/chief_medical_officer
+ gloves = /obj/item/clothing/gloves/color/plasmaman/white
+ head = /obj/item/clothing/head/helmet/space/plasmaman/chief_medical_officer
+
+/datum/outfit/plasmaman/medical_technician
+ name = "EMT Paramedic Plasmaman"
+ uniform = /obj/item/clothing/under/plasmaman/medical
+ gloves = /obj/item/clothing/gloves/color/plasmaman/white
+ head = /obj/item/clothing/head/helmet/space/plasmaman/medical
diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm
index 61298e970cbf9..53ab86b07718e 100644
--- a/code/modules/clothing/shoes/_shoes.dm
+++ b/code/modules/clothing/shoes/_shoes.dm
@@ -44,7 +44,7 @@
user.visible_message(span_suicide("[user] is bashing [user.p_their()] own head in with [src]! Ain't that a kick in the head?"))
for(var/i in 1 to 3)
sleep(0.3 SECONDS)
- playsound(user, 'sound/weapons/genhit2.ogg', 50, TRUE)
+ playsound(user, 'sound/items/weapons/genhit2.ogg', 50, TRUE)
return BRUTELOSS
/obj/item/clothing/shoes/worn_overlays(mutable_appearance/standing, isinhands = FALSE)
@@ -88,7 +88,7 @@
/obj/item/clothing/shoes/proc/restore_offsets(mob/user)
equipped_before_drop = FALSE
user.pixel_y -= offset
- worn_y_dimension = world.icon_size
+ worn_y_dimension = ICON_SIZE_Y
/obj/item/clothing/shoes/dropped(mob/user)
var/atom/movable/screen/alert/our_alert = our_alert_ref?.resolve()
diff --git a/code/modules/clothing/shoes/boots.dm b/code/modules/clothing/shoes/boots.dm
index 03f174aa43c31..68a7b1bb0aefd 100644
--- a/code/modules/clothing/shoes/boots.dm
+++ b/code/modules/clothing/shoes/boots.dm
@@ -72,6 +72,10 @@
icon_state = "ftc_boots"
inhand_icon_state = null
+/obj/item/clothing/shoes/jackboots/floortile/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3) //tacticool
+
/obj/item/clothing/shoes/winterboots
name = "winter boots"
desc = "Boots lined with 'synthetic' animal fur."
@@ -175,6 +179,10 @@
icon_state = "pirateboots"
inhand_icon_state = null
+/obj/item/clothing/shoes/pirate/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2)
+
/obj/item/clothing/shoes/pirate/armored
armor_type = /datum/armor/shoes_pirate
strip_delay = 40
diff --git a/code/modules/clothing/shoes/clown.dm b/code/modules/clothing/shoes/clown.dm
index 699d6fa627bf5..c999a242a190f 100644
--- a/code/modules/clothing/shoes/clown.dm
+++ b/code/modules/clothing/shoes/clown.dm
@@ -15,6 +15,7 @@
create_storage(storage_type = /datum/storage/pockets/shoes/clown)
LoadComponent(/datum/component/squeak, squeak_sound, 50, falloff_exponent = 20) //die off quick please
AddElement(/datum/element/swabable, CELL_LINE_TABLE_CLOWN, CELL_VIRUS_TABLE_GENERIC, rand(2,3), 0)
+ AddComponent(/datum/component/adjust_fishing_difficulty, 3) //Goofy
/obj/item/clothing/shoes/clown_shoes/equipped(mob/living/user, slot)
. = ..()
diff --git a/code/modules/clothing/shoes/costume.dm b/code/modules/clothing/shoes/costume.dm
index bf8000d9a0800..3c66c0ac0c6b5 100644
--- a/code/modules/clothing/shoes/costume.dm
+++ b/code/modules/clothing/shoes/costume.dm
@@ -44,7 +44,8 @@
/obj/item/clothing/shoes/bronze/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/squeak, list('sound/machines/clockcult/integration_cog_install.ogg' = 1, 'sound/magic/clockwork/fellowship_armory.ogg' = 1), 50, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
+ AddComponent(/datum/component/squeak, list('sound/machines/clockcult/integration_cog_install.ogg' = 1, 'sound/effects/magic/clockwork/fellowship_armory.ogg' = 1), 50, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
+ AddComponent(/datum/component/adjust_fishing_difficulty, 4)
/obj/item/clothing/shoes/cookflops
desc = "All this talk of antags, greytiding, and griefing... I just wanna grill for god's sake!"
@@ -128,6 +129,7 @@
create_storage(storage_type = /datum/storage/pockets/shoes)
LoadComponent(/datum/component/squeak, list('sound/effects/quack.ogg' = 1), 50, falloff_exponent = 20)
+ AddComponent(/datum/component/adjust_fishing_difficulty, -6) //deploy tactical duckling lure
/obj/item/clothing/shoes/ducky_shoes/equipped(mob/living/user, slot)
. = ..()
diff --git a/code/modules/clothing/shoes/galoshes.dm b/code/modules/clothing/shoes/galoshes.dm
index e743db6b7424c..d42b8ffddb388 100644
--- a/code/modules/clothing/shoes/galoshes.dm
+++ b/code/modules/clothing/shoes/galoshes.dm
@@ -12,11 +12,18 @@
can_be_bloody = FALSE
custom_price = PAYCHECK_CREW * 3
can_be_tied = FALSE
+ ///How much these boots affect fishing difficulty
+ var/fishing_modifier = -3
+
+/obj/item/clothing/shoes/galoshes/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, fishing_modifier)
/obj/item/clothing/shoes/galoshes/dry
name = "absorbent galoshes"
desc = "A pair of purple rubber boots, designed to prevent slipping on wet surfaces while also drying them."
icon_state = "galoshes_dry"
+ fishing_modifier = -6
/datum/armor/shoes_galoshes
bio = 100
diff --git a/code/modules/clothing/shoes/laceup.dm b/code/modules/clothing/shoes/laceup.dm
index 7ee348ea6c434..808bf22f5080d 100644
--- a/code/modules/clothing/shoes/laceup.dm
+++ b/code/modules/clothing/shoes/laceup.dm
@@ -3,3 +3,7 @@
desc = "The height of fashion, and they're pre-polished!"
icon_state = "laceups"
equip_delay_other = 50
+
+/obj/item/clothing/shoes/laceup/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 3) //You aren't going to fish with these are you?
diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm
index 2ae13924dedc9..38d4770244abd 100644
--- a/code/modules/clothing/shoes/magboots.dm
+++ b/code/modules/clothing/shoes/magboots.dm
@@ -20,18 +20,30 @@
var/slowdown_active = 2
/// A list of traits we apply when we get activated
var/list/active_traits = list(TRAIT_NO_SLIP_WATER, TRAIT_NO_SLIP_ICE, TRAIT_NO_SLIP_SLIDE, TRAIT_NEGATES_GRAVITY)
+ /// How much do these boots affect fishing when active
+ var/magpulse_fishing_modifier = 8
+ /// How much do these boots affect fishing when not active
+ var/fishing_modifier = 4
/obj/item/clothing/shoes/magboots/Initialize(mapload)
. = ..()
AddElement(/datum/element/update_icon_updates_onmob)
RegisterSignal(src, COMSIG_SPEED_POTION_APPLIED, PROC_REF(on_speed_potioned))
+ if(fishing_modifier)
+ AddComponent(/datum/component/adjust_fishing_difficulty, fishing_modifier)
/// Signal handler for [COMSIG_SPEED_POTION_APPLIED]. Speed potion removes the active slowdown
/obj/item/clothing/shoes/magboots/proc/on_speed_potioned(datum/source)
SIGNAL_HANDLER
- slowdown_active = 0
// Don't need to touch the actual slowdown here, since the speed potion does it for us
+ slowdown_active = 0
+
+ if(magpulse && magpulse_fishing_modifier)
+ qdel(GetComponent(/datum/component/adjust_fishing_difficulty))
+ if(fishing_modifier)
+ AddComponent(/datum/component/adjust_fishing_difficulty, fishing_modifier)
+ magpulse_fishing_modifier = fishing_modifier
/obj/item/clothing/shoes/magboots/verb/toggle()
set name = "Toggle Magboots"
@@ -47,7 +59,15 @@
if(magpulse)
attach_clothing_traits(active_traits)
slowdown += slowdown_active
+ if(magpulse_fishing_modifier)
+ AddComponent(/datum/component/adjust_fishing_difficulty, magpulse_fishing_modifier)
+ else if(magpulse_fishing_modifier != fishing_modifier)
+ qdel(GetComponent(/datum/component/adjust_fishing_difficulty))
else
+ if(fishing_modifier)
+ AddComponent(/datum/component/adjust_fishing_difficulty, fishing_modifier)
+ else if(magpulse_fishing_modifier != fishing_modifier)
+ qdel(GetComponent(/datum/component/adjust_fishing_difficulty))
detach_clothing_traits(active_traits)
slowdown = max(initial(slowdown), slowdown - slowdown_active) // Just in case, for speed pot shenanigans
@@ -71,9 +91,13 @@
base_icon_state = "advmag"
slowdown_active = SHOES_SLOWDOWN // ZERO active slowdown
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
+ magpulse_fishing_modifier = 3
+ fishing_modifier = 0
/obj/item/clothing/shoes/magboots/syndie
name = "blood-red magboots"
desc = "Reverse-engineered magnetic boots that have a heavy magnetic pull. Property of Gorlex Marauders."
icon_state = "syndiemag0"
base_icon_state = "syndiemag"
+ magpulse_fishing_modifier = 6
+ fishing_modifier = 3
diff --git a/code/modules/clothing/shoes/wheelys.dm b/code/modules/clothing/shoes/wheelys.dm
index 9b67f14d14415..9f5bd1631f9c1 100644
--- a/code/modules/clothing/shoes/wheelys.dm
+++ b/code/modules/clothing/shoes/wheelys.dm
@@ -51,7 +51,7 @@
worn_icon_state = "[initial(worn_icon_state)]-on"
else
worn_icon_state = "[initial(worn_icon_state)]"
- playsound(src, 'sound/weapons/tap.ogg', 10, TRUE)
+ playsound(src, 'sound/items/weapons/tap.ogg', 10, TRUE)
update_appearance()
/obj/item/clothing/shoes/wheelys/Destroy()
diff --git a/code/modules/clothing/spacesuits/_spacesuits.dm b/code/modules/clothing/spacesuits/_spacesuits.dm
index caf84d9562dcb..124dfcee41c0b 100644
--- a/code/modules/clothing/spacesuits/_spacesuits.dm
+++ b/code/modules/clothing/spacesuits/_spacesuits.dm
@@ -25,6 +25,13 @@
resistance_flags = NONE
dog_fashion = null
slowdown = 0.5
+ ///How much this helmet affects fishing difficulty
+ var/fishing_modifier = 3
+
+/obj/item/clothing/head/helmet/space/Initialize(mapload)
+ . = ..()
+ if(fishing_modifier)
+ AddComponent(/datum/component/adjust_fishing_difficulty, fishing_modifier)
/datum/armor/helmet_space
bio = 100
@@ -70,6 +77,8 @@
var/thermal_on = FALSE
/// If this is FALSE the batery status UI will be disabled. This is used for suits that don't use bateries like the changeling's flesh suit mutation.
var/show_hud = TRUE
+ ///How much this suit affects fishing difficulty
+ var/fishing_modifier = 5
/datum/armor/suit_space
bio = 100
@@ -81,6 +90,9 @@
if(ispath(cell))
cell = new cell(src)
+ if(fishing_modifier)
+ AddComponent(/datum/component/adjust_fishing_difficulty, fishing_modifier)
+
/// Start Processing on the space suit when it is worn to heat the wearer
/obj/item/clothing/suit/space/equipped(mob/living/user, slot)
. = ..()
diff --git a/code/modules/clothing/spacesuits/freedom.dm b/code/modules/clothing/spacesuits/freedom.dm
index b0a08f4cc7367..085b9c8deb7c2 100644
--- a/code/modules/clothing/spacesuits/freedom.dm
+++ b/code/modules/clothing/spacesuits/freedom.dm
@@ -9,6 +9,7 @@
strip_delay = 130
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = ACID_PROOF | FIRE_PROOF
+ fishing_modifier = 0
/datum/armor/space_freedom
melee = 20
@@ -31,3 +32,4 @@
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = ACID_PROOF | FIRE_PROOF
slowdown = 0
+ fishing_modifier = 0
diff --git a/code/modules/clothing/spacesuits/pirate.dm b/code/modules/clothing/spacesuits/pirate.dm
index 73feec525c548..8ead0aeaa6619 100644
--- a/code/modules/clothing/spacesuits/pirate.dm
+++ b/code/modules/clothing/spacesuits/pirate.dm
@@ -7,6 +7,7 @@
armor_type = /datum/armor/space_pirate
strip_delay = 40
equip_delay_other = 20
+ fishing_modifier = -2
/datum/armor/space_pirate
melee = 30
@@ -31,6 +32,7 @@
armor_type = /datum/armor/space_pirate
strip_delay = 40
equip_delay_other = 20
+ fishing_modifier = -3
/obj/item/clothing/head/helmet/space/pirate/tophat
name = "designer pirate helmet"
@@ -39,7 +41,7 @@
/obj/item/clothing/suit/space/pirate/silverscale
name = "designer pirate suit"
- desc = "A specially-made Cybersun branded space suit; the fine plastisilk exterior is woven from the coccons of black-market Lümlan mothroaches \
- and the trim is lined with the ivory of the critically endagered Zanzibarian dwarf elephant. Baby seal leather boots sold seperately."
+ desc = "A specially-made Cybersun branded space suit; the fine plastisilk exterior is woven from the cocoons of black-market Lümlan mothroaches \
+ and the trim is lined with the ivory of the critically endangered Zanzibarian dwarf elephant. Baby seal leather boots sold separately."
inhand_icon_state = "syndicate-black"
icon_state = "syndicate-black-white"
diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm
index 380cd0cf3fb53..88767b84b66d5 100644
--- a/code/modules/clothing/spacesuits/plasmamen.dm
+++ b/code/modules/clothing/spacesuits/plasmamen.dm
@@ -8,6 +8,7 @@
resistance_flags = FIRE_PROOF
icon_state = "plasmaman_suit"
inhand_icon_state = "plasmaman_suit"
+ fishing_modifier = 0
var/next_extinguish = 0
var/extinguish_cooldown = 100
var/extinguishes_left = 10
@@ -57,6 +58,7 @@
light_power = 0.8
light_color = "#ffcc99"
light_on = FALSE
+ fishing_modifier = 0
var/helmet_on = FALSE
var/smile = FALSE
var/smile_color = COLOR_RED
@@ -68,6 +70,7 @@
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
flags_cover = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF
visor_flags_inv = HIDEEYES|HIDEFACE
+ slowdown = 0
/datum/armor/space_plasmaman
bio = 100
@@ -78,7 +81,6 @@
. = ..()
visor_toggling()
update_appearance()
- RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
/obj/item/clothing/head/helmet/space/plasmaman/examine()
. = ..()
@@ -106,7 +108,7 @@
to_chat(user, span_notice("Your helmet's torch can't pass through your welding visor!"))
set_light_on(FALSE)
helmet_on = FALSE
- playsound(src, 'sound/mecha/mechmove03.ogg', 50, TRUE) //Visors don't just come from nothing
+ playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 50, TRUE) //Visors don't just come from nothing
update_appearance()
/obj/item/clothing/head/helmet/space/plasmaman/update_icon_state()
@@ -194,13 +196,13 @@
update_item_action_buttons()
-/obj/item/clothing/head/helmet/space/plasmaman/proc/on_saboteur(datum/source, disrupt_duration)
- SIGNAL_HANDLER
+/obj/item/clothing/head/helmet/space/plasmaman/on_saboteur(datum/source, disrupt_duration)
+ . = ..()
if(!helmet_on)
- return
+ return FALSE
helmet_on = FALSE
update_appearance()
- return COMSIG_SABOTEUR_SUCCESS
+ return TRUE
/obj/item/clothing/head/helmet/space/plasmaman/attack_hand_secondary(mob/user)
..()
diff --git a/code/modules/clothing/spacesuits/santa.dm b/code/modules/clothing/spacesuits/santa.dm
index f6bd1657606c5..08f01cc1869bb 100644
--- a/code/modules/clothing/spacesuits/santa.dm
+++ b/code/modules/clothing/spacesuits/santa.dm
@@ -8,6 +8,7 @@
flags_cover = HEADCOVERSEYES
dog_fashion = /datum/dog_fashion/head/santa
slowdown = 0
+ fishing_modifier = 0
/obj/item/clothing/head/helmet/space/santahat/beardless
icon = 'icons/obj/clothing/head/costume.dmi'
@@ -26,3 +27,4 @@
inhand_icon_state = "santa"
slowdown = 0
allowed = list(/obj/item) //for stuffing exta special presents
+ fishing_modifier = 0
diff --git a/code/modules/clothing/spacesuits/softsuit.dm b/code/modules/clothing/spacesuits/softsuit.dm
index 0b644286063ec..3bbb6d0bd0f90 100644
--- a/code/modules/clothing/spacesuits/softsuit.dm
+++ b/code/modules/clothing/spacesuits/softsuit.dm
@@ -95,5 +95,5 @@
name = "torn [src]."
desc = "A bulky suit meant to protect the user during emergency situations, at least until someone tore a hole in the suit."
torn = TRUE
- playsound(loc, 'sound/weapons/slashmiss.ogg', 50, TRUE)
+ playsound(loc, 'sound/items/weapons/slashmiss.ogg', 50, TRUE)
playsound(loc, 'sound/effects/refill.ogg', 50, TRUE)
diff --git a/code/modules/clothing/spacesuits/specialops.dm b/code/modules/clothing/spacesuits/specialops.dm
index caaa32cc24be2..dbe02400aa664 100644
--- a/code/modules/clothing/spacesuits/specialops.dm
+++ b/code/modules/clothing/spacesuits/specialops.dm
@@ -13,6 +13,7 @@
strip_delay = 130
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF | ACID_PROOF
+ fishing_modifier = 0
/datum/armor/space_beret
melee = 80
@@ -41,6 +42,7 @@
strip_delay = 130
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
resistance_flags = FIRE_PROOF | ACID_PROOF
+ fishing_modifier = 0
/datum/armor/space_officer
melee = 80
diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm
index 6e56107173c46..23e4d89ff2dbb 100644
--- a/code/modules/clothing/suits/armor.dm
+++ b/code/modules/clothing/suits/armor.dm
@@ -294,6 +294,10 @@
equip_delay_other = 60
clothing_traits = list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED)
+/obj/item/clothing/suit/armor/riot/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 5)
+
/datum/armor/armor_riot
melee = 50
bullet = 10
@@ -333,7 +337,7 @@
return ..()
/obj/item/clothing/suit/armor/balloon_vest/proc/pop()
- playsound(src, 'sound/effects/cartoon_pop.ogg', 50, vary = TRUE)
+ playsound(src, 'sound/effects/cartoon_sfx/cartoon_pop.ogg', 50, vary = TRUE)
qdel(src)
@@ -413,6 +417,10 @@
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
clothing_traits = list(TRAIT_BRAWLING_KNOCKDOWN_BLOCKED)
+/obj/item/clothing/suit/armor/swat/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 5)
+
//All of the armor below is mostly unused
@@ -554,6 +562,10 @@
armor_type = /datum/armor/vest_durathread
dog_fashion = null
+/obj/item/clothing/suit/armor/vest/durathread/Initialize(mapload)
+ . = ..()
+ allowed |= /obj/item/clothing/suit/apron::allowed
+
/datum/armor/vest_durathread
melee = 20
bullet = 10
@@ -695,6 +707,10 @@
/obj/item/gun/ballistic/bow
)
+/obj/item/clothing/suit/armor/vest/military/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 5)
+
/datum/armor/military
melee = 45
bullet = 25
diff --git a/code/modules/clothing/suits/bio.dm b/code/modules/clothing/suits/bio.dm
index 4ddfd31631d5c..25b28c74d1a7a 100644
--- a/code/modules/clothing/suits/bio.dm
+++ b/code/modules/clothing/suits/bio.dm
@@ -16,6 +16,7 @@
. = ..()
if(flags_inv & HIDEFACE)
AddComponent(/datum/component/clothing_fov_visor, FOV_90_DEGREES)
+ AddComponent(/datum/component/adjust_fishing_difficulty, 6)
/datum/armor/head_bio_hood
bio = 100
@@ -35,11 +36,15 @@
slowdown = 0.5
allowed = list(/obj/item/tank/internals, /obj/item/reagent_containers/dropper, /obj/item/flashlight/pen, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/cup/beaker, /obj/item/gun/syringe)
armor_type = /datum/armor/suit_bio_suit
- flags_inv = HIDEGLOVES|HIDEJUMPSUIT
+ flags_inv = HIDEGLOVES|HIDEJUMPSUIT|HIDEBELT
strip_delay = 70
equip_delay_other = 70
resistance_flags = ACID_PROOF
+/obj/item/clothing/suit/bio_suit/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 6)
+
//Standard biosuit, orange stripe
/datum/armor/suit_bio_suit
bio = 100
diff --git a/code/modules/clothing/suits/costume.dm b/code/modules/clothing/suits/costume.dm
index 566ca5557f728..d3918752056d1 100644
--- a/code/modules/clothing/suits/costume.dm
+++ b/code/modules/clothing/suits/costume.dm
@@ -121,7 +121,7 @@
icon_state = "imperium_monk"
inhand_icon_state = "imperium_monk"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
- flags_inv = HIDESHOES|HIDEJUMPSUIT
+ flags_inv = HIDESHOES|HIDEJUMPSUIT|HIDEBELT
allowed = list(/obj/item/book/bible, /obj/item/nullrod, /obj/item/reagent_containers/cup/glass/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/flashlight/flare/candle, /obj/item/tank/internals/emergency_oxygen)
/obj/item/clothing/suit/costume/chickensuit
@@ -219,6 +219,7 @@
icon_state = "classicponcho"
inhand_icon_state = null
species_exception = list(/datum/species/golem)
+ flags_inv = HIDEBELT
/obj/item/clothing/suit/costume/poncho/green
name = "green poncho"
@@ -248,7 +249,7 @@
icon_state = "white_dress"
inhand_icon_state = "w_suit"
body_parts_covered = CHEST|GROIN|LEGS|FEET
- flags_inv = HIDEJUMPSUIT|HIDESHOES
+ flags_inv = HIDEJUMPSUIT|HIDESHOES|HIDEBELT
/obj/item/clothing/suit/hooded/carp_costume
name = "carp costume"
@@ -263,6 +264,10 @@
allowed = list(/obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman, /obj/item/gun/ballistic/rifle/boltaction/harpoon)
hoodtype = /obj/item/clothing/head/hooded/carp_hood
+/obj/item/clothing/suit/hooded/carp_costume/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2)
+
/obj/item/clothing/head/hooded/carp_hood
name = "carp hood"
desc = "A hood attached to a carp costume."
@@ -274,6 +279,10 @@
min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT
flags_inv = HIDEHAIR|HIDEEARS
+/obj/item/clothing/head/hooded/carp_hood/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3)
+
/obj/item/clothing/head/hooded/carp_hood/equipped(mob/living/carbon/human/user, slot)
..()
if (slot & ITEM_SLOT_HEAD)
@@ -394,6 +403,10 @@
clothing_flags = THICKMATERIAL
hoodtype = /obj/item/clothing/head/hooded/shark_hood
+/obj/item/clothing/suit/hooded/shark_costume/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2)
+
/obj/item/clothing/head/hooded/shark_hood
name = "shark hood"
desc = "A hood attached to a shark costume."
@@ -404,6 +417,10 @@
clothing_flags = THICKMATERIAL
flags_inv = HIDEHAIR|HIDEEARS
+/obj/item/clothing/head/hooded/shark_hood/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3)
+
/obj/item/clothing/suit/hooded/shork_costume // Oh God Why
name = "shork costume"
desc = "Why would you ever do this?"
@@ -415,6 +432,10 @@
clothing_flags = THICKMATERIAL
hoodtype = /obj/item/clothing/head/hooded/shork_hood
+/obj/item/clothing/suit/hooded/shork_costume/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 2)
+
/obj/item/clothing/head/hooded/shork_hood
name = "shork hood"
desc = "A hood attached to a shork costume."
@@ -425,6 +446,10 @@
clothing_flags = THICKMATERIAL
flags_inv = HIDEHAIR|HIDEEARS
+/obj/item/clothing/head/hooded/shork_hood/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 3)
+
/obj/item/clothing/suit/hooded/bloated_human //OH MY GOD WHAT HAVE YOU DONE!?!?!?
name = "bloated human suit"
desc = "A horribly bloated suit made from human skins."
@@ -479,6 +504,7 @@
desc = "Perfect for those who want to stalk around a corner of a bar."
icon_state = "gothcoat"
inhand_icon_state = null
+ flags_inv = HIDEBELT
/obj/item/clothing/suit/costume/xenos
name = "xenos suit"
@@ -486,7 +512,7 @@
icon_state = "xenos"
inhand_icon_state = "xenos_suit"
body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS
- flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
+ flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDEBELT
allowed = list(/obj/item/clothing/mask/facehugger/toy)
/obj/item/clothing/suit/costume/nemes
@@ -589,6 +615,10 @@
flags_1 = IS_PLAYER_COLORABLE_1
species_exception = list(/datum/species/golem)
+/obj/item/clothing/suit/costume/hawaiian/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3)
+
/obj/item/clothing/suit/costume/football_armor
name = "football protective gear"
desc = "Given to members of the football team!"
diff --git a/code/modules/clothing/suits/ethereal.dm b/code/modules/clothing/suits/ethereal.dm
index 6c53329a13e7d..1c86ca34094f3 100644
--- a/code/modules/clothing/suits/ethereal.dm
+++ b/code/modules/clothing/suits/ethereal.dm
@@ -14,6 +14,7 @@
/obj/item/clothing/suit/hooded/ethereal_raincoat/Initialize(mapload)
. = ..()
update_icon(UPDATE_OVERLAYS)
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3)
/obj/item/clothing/suit/hooded/ethereal_raincoat/worn_overlays(mutable_appearance/standing, isinhands, icon_file)
. = ..()
@@ -28,6 +29,11 @@
name = "trailwarden oilcoat"
desc = "A masterfully handcrafted oilslick coat, supposedly makes for excellent camouflage among Sprout's vegetation. You can hear a faint electrical buzz emanating from the luminescent pattern."
greyscale_colors = "#32a87d"
+ hoodtype = /obj/item/clothing/head/hooded/ethereal_rainhood/trailwarden
+
+/obj/item/clothing/suit/hooded/ethereal_raincoat/trailwarden/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -5)
/obj/item/clothing/suit/hooded/ethereal_raincoat/trailwarden/equipped(mob/living/user, slot)
. = ..()
@@ -45,3 +51,9 @@
worn_icon = 'icons/mob/clothing/head/ethereal.dmi'
body_parts_covered = HEAD
flags_inv = HIDEHAIR|HIDEEARS|HIDEFACIALHAIR
+
+/obj/item/clothing/head/hooded/ethereal_rainhood/trailwarden
+
+/obj/item/clothing/head/hooded/ethereal_rainhood/trailwarden/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -4)
diff --git a/code/modules/clothing/suits/ghostsheet.dm b/code/modules/clothing/suits/ghostsheet.dm
index 65213fd176dba..52c19be3bd160 100644
--- a/code/modules/clothing/suits/ghostsheet.dm
+++ b/code/modules/clothing/suits/ghostsheet.dm
@@ -7,7 +7,7 @@
throw_speed = 1
throw_range = 2
w_class = WEIGHT_CLASS_TINY
- flags_inv = HIDEGLOVES|HIDEEARS|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
+ flags_inv = HIDEGLOVES|HIDEEARS|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT|HIDEBELT|HIDEJUMPSUIT
alternate_worn_layer = UNDER_HEAD_LAYER
species_exception = list(/datum/species/golem)
supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
@@ -16,6 +16,7 @@
. = ..()
if(check_holidays(HALLOWEEN))
update_icon(UPDATE_OVERLAYS)
+ AddComponent(/datum/component/adjust_fishing_difficulty, 8)
/obj/item/clothing/suit/costume/ghost_sheet/worn_overlays(mutable_appearance/standing, isinhands, icon_file)
. = ..()
@@ -33,7 +34,7 @@
throw_speed = 1
throw_range = 2
w_class = WEIGHT_CLASS_TINY
- flags_inv = HIDEGLOVES|HIDEEARS|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
+ flags_inv = HIDEGLOVES|HIDEEARS|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT|HIDEBELT|HIDEJUMPSUIT
species_exception = list(/datum/species/golem)
supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION_NO_NEW_ICON
alternate_worn_layer = ABOVE_BODY_FRONT_LAYER //so the bedsheet goes over everything but fire
diff --git a/code/modules/clothing/suits/jacket.dm b/code/modules/clothing/suits/jacket.dm
index 9004f773e35ba..176661dbb57e1 100644
--- a/code/modules/clothing/suits/jacket.dm
+++ b/code/modules/clothing/suits/jacket.dm
@@ -62,6 +62,7 @@
/obj/item/storage/fancy/cigarettes,
/obj/item/lighter,
/obj/item/gun/ballistic/rifle/boltaction/pipegun,
+ /obj/item/gun/energy/laser/musket,
/obj/item/radio,
)
@@ -83,6 +84,7 @@
/obj/item/gun/ballistic/revolver,
/obj/item/gun/ballistic/revolver/c38/detective,
/obj/item/gun/ballistic/rifle/boltaction/pipegun,
+ /obj/item/gun/energy/laser/musket,
/obj/item/radio,
)
@@ -129,6 +131,7 @@
/obj/item/gun/ballistic/revolver,
/obj/item/gun/ballistic/revolver/c38/detective,
/obj/item/gun/ballistic/rifle/boltaction/pipegun,
+ /obj/item/gun/energy/laser/musket,
/obj/item/radio,
)
@@ -163,6 +166,7 @@
/obj/item/gun/ballistic/revolver,
/obj/item/gun/ballistic/revolver/c38/detective,
/obj/item/gun/ballistic/rifle/boltaction/pipegun,
+ /obj/item/gun/energy/laser/musket,
/obj/item/radio,
)
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index 7e6bdde01cdfd..504558229639c 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -52,6 +52,10 @@
greyscale_colors = "#313c6e"
flags_1 = IS_PLAYER_COLORABLE_1
+/obj/item/clothing/suit/apron/overalls/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2)
+
//Captain
/obj/item/clothing/suit/jacket/capjacket
name = "captain's parade jacket"
@@ -120,6 +124,7 @@
armor_type = /datum/armor/jacket_det_suit
cold_protection = CHEST|GROIN|ARMS
heat_protection = CHEST|GROIN|ARMS
+ flags_inv = HIDEBELT
/datum/armor/jacket_det_suit
melee = 25
@@ -349,6 +354,10 @@
/obj/item/tank/internals/emergency_oxygen,
)
+/obj/item/clothing/suit/apron/surgical/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2) // FISH DOCTOR?!
+
//Curator
/obj/item/clothing/suit/jacket/curator
name = "treasure hunter's coat"
diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm
index e5cda21a78a77..954fb9342e3ca 100644
--- a/code/modules/clothing/suits/labcoat.dm
+++ b/code/modules/clothing/suits/labcoat.dm
@@ -38,6 +38,10 @@
icon_state = "labcoat_cmo"
inhand_icon_state = null
+/obj/item/clothing/suit/toggle/labcoat/cmo/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2) //FISH DOCTOR?!
+
/datum/armor/toggle_labcoat
bio = 50
fire = 50
@@ -55,6 +59,10 @@
icon_state = "labcoat_paramedic"
inhand_icon_state = null
+/obj/item/clothing/suit/toggle/labcoat/paramedic/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2) //FISH DOCTOR?!
+
/obj/item/clothing/suit/toggle/labcoat/mad
name = "\proper The Mad's labcoat"
desc = "It makes you look capable of konking someone on the noggin and shooting them into space."
diff --git a/code/modules/clothing/suits/moth.dm b/code/modules/clothing/suits/moth.dm
index dd0a7f016ac17..076a0dd0b3c9a 100644
--- a/code/modules/clothing/suits/moth.dm
+++ b/code/modules/clothing/suits/moth.dm
@@ -16,7 +16,7 @@
/obj/item/clothing/suit/mothcoat/original/Initialize(mapload)
. = ..()
-
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3)
create_storage(storage_type = /datum/storage/pockets)
/obj/item/clothing/suit/mothcoat/winter
diff --git a/code/modules/clothing/suits/reactive_armour.dm b/code/modules/clothing/suits/reactive_armour.dm
index c1889cc77383d..eaa997607f09e 100644
--- a/code/modules/clothing/suits/reactive_armour.dm
+++ b/code/modules/clothing/suits/reactive_armour.dm
@@ -126,7 +126,7 @@
/obj/item/clothing/suit/armor/reactive/teleport/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
owner.visible_message(span_danger("The reactive teleport system flings [owner] clear of [attack_text]!"))
- playsound(get_turf(owner),'sound/magic/blink.ogg', 100, TRUE)
+ playsound(get_turf(owner),'sound/effects/magic/blink.ogg', 100, TRUE)
do_teleport(owner, get_turf(owner), tele_range, no_effects = TRUE, channel = TELEPORT_CHANNEL_BLUESPACE)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return TRUE
@@ -134,8 +134,8 @@
/obj/item/clothing/suit/armor/reactive/teleport/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
owner.visible_message(span_danger("The reactive teleport system flings itself clear of [attack_text], leaving someone behind in the process!"))
owner.dropItemToGround(src, TRUE, TRUE)
- playsound(get_turf(owner),'sound/machines/buzz-sigh.ogg', 50, TRUE)
- playsound(get_turf(owner),'sound/magic/blink.ogg', 100, TRUE)
+ playsound(get_turf(owner),'sound/machines/buzz/buzz-sigh.ogg', 50, TRUE)
+ playsound(get_turf(owner),'sound/effects/magic/blink.ogg', 100, TRUE)
do_teleport(src, get_turf(owner), tele_range, no_effects = TRUE, channel = TELEPORT_CHANNEL_BLUESPACE)
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
return FALSE //you didn't actually evade the attack now did you
@@ -150,7 +150,7 @@
/obj/item/clothing/suit/armor/reactive/fire/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
owner.visible_message(span_danger("[src] blocks [attack_text], sending out jets of flame!"))
- playsound(get_turf(owner),'sound/magic/fireball.ogg', 100, TRUE)
+ playsound(get_turf(owner),'sound/effects/magic/fireball.ogg', 100, TRUE)
for(var/mob/living/carbon/carbon_victim in range(6, get_turf(src)))
if(carbon_victim != owner)
carbon_victim.adjust_fire_stacks(8)
@@ -161,7 +161,7 @@
/obj/item/clothing/suit/armor/reactive/fire/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
owner.visible_message(span_danger("[src] just makes [attack_text] worse by spewing molten death on [owner]!"))
- playsound(get_turf(owner),'sound/magic/fireball.ogg', 100, TRUE)
+ playsound(get_turf(owner),'sound/effects/magic/fireball.ogg', 100, TRUE)
owner.adjust_fire_stacks(12)
owner.ignite_mob()
reactivearmor_cooldown = world.time + reactivearmor_cooldown_duration
@@ -263,7 +263,7 @@
var/repulse_force = MOVE_FORCE_EXTREMELY_STRONG
/obj/item/clothing/suit/armor/reactive/repulse/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- playsound(get_turf(owner),'sound/magic/repulse.ogg', 100, TRUE)
+ playsound(get_turf(owner),'sound/effects/magic/repulse.ogg', 100, TRUE)
owner.visible_message(span_danger("[src] blocks [attack_text], converting the attack into a wave of force!"))
var/turf/owner_turf = get_turf(owner)
var/list/thrown_items = list()
@@ -278,7 +278,7 @@
return TRUE
/obj/item/clothing/suit/armor/reactive/repulse/emp_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- playsound(get_turf(owner),'sound/magic/repulse.ogg', 100, TRUE)
+ playsound(get_turf(owner),'sound/effects/magic/repulse.ogg', 100, TRUE)
owner.visible_message(span_danger("[src] does not block [attack_text], and instead generates an attracting force!"))
var/turf/owner_turf = get_turf(owner)
var/list/thrown_items = list()
@@ -418,7 +418,7 @@
reactivearmor_cooldown_duration = 10 SECONDS
/obj/item/clothing/suit/armor/reactive/barricade/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- playsound(get_turf(owner),'sound/magic/repulse.ogg', 100, TRUE)
+ playsound(get_turf(owner),'sound/effects/magic/repulse.ogg', 100, TRUE)
owner.visible_message(span_danger("The reactive armor interposes matter from another world between [src] and [attack_text]!"))
for (var/atom/movable/target in repulse_targets(owner))
repulse(target, owner)
@@ -480,7 +480,7 @@
reactivearmor_cooldown_duration = 40 SECONDS
/obj/item/clothing/suit/armor/reactive/ectoplasm/reactive_activation(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- playsound(get_turf(owner),'sound/hallucinations/veryfar_noise.ogg', 100, TRUE)
+ playsound(get_turf(owner),'sound/effects/hallucinations/veryfar_noise.ogg', 100, TRUE)
owner.visible_message(span_danger("The [src] lets loose a burst of otherworldly energy!"))
haunt_outburst(epicenter = get_turf(owner), range = 5, haunt_chance = 85, duration = 30 SECONDS)
diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm
index 1541c66a333b6..bb42fb1ed3975 100644
--- a/code/modules/clothing/suits/utility.dm
+++ b/code/modules/clothing/suits/utility.dm
@@ -39,6 +39,10 @@
equip_delay_other = 60
resistance_flags = FIRE_PROOF
+/obj/item/clothing/suit/utility/fire/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 7)
+
/datum/armor/utility_fire
melee = 15
bullet = 5
@@ -104,6 +108,7 @@
. = ..()
if(flags_inv & HIDEFACE)
AddComponent(/datum/component/clothing_fov_visor, FOV_90_DEGREES)
+ AddComponent(/datum/component/adjust_fishing_difficulty, 8)
/datum/armor/utility_bomb_hood
melee = 20
@@ -133,6 +138,10 @@
equip_delay_other = 70
resistance_flags = NONE
+/obj/item/clothing/suit/utility/bomb_suit/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 8)
+
/datum/armor/utility_bomb_suit
melee = 20
laser = 20
@@ -179,6 +188,7 @@
. = ..()
if(flags_inv & HIDEFACE)
AddComponent(/datum/component/clothing_fov_visor, FOV_90_DEGREES)
+ AddComponent(/datum/component/adjust_fishing_difficulty, 7)
/datum/armor/utility_radiation
bio = 60
@@ -212,3 +222,4 @@
/obj/item/clothing/suit/utility/radiation/Initialize(mapload)
. = ..()
AddElement(/datum/element/radiation_protected_clothing)
+ AddComponent(/datum/component/adjust_fishing_difficulty, 7)
diff --git a/code/modules/clothing/suits/wintercoats.dm b/code/modules/clothing/suits/wintercoats.dm
index dcc33fb65376b..f09b6ac2f8585 100644
--- a/code/modules/clothing/suits/wintercoats.dm
+++ b/code/modules/clothing/suits/wintercoats.dm
@@ -49,7 +49,7 @@
/obj/item/clothing/suit/hooded/wintercoat/click_alt(mob/user)
zipped = !zipped
- playsound(src, 'sound/items/zip_up.ogg', 30, TRUE, -3)
+ playsound(src, 'sound/items/zip/zip_up.ogg', 30, TRUE, -3)
worn_icon_state = "[initial(icon_state)][zipped ? "_t" : ""]"
balloon_alert(user, "[zipped ? "" : "un"]zipped")
@@ -221,16 +221,7 @@
desc = "A green and blue winter coat. The zipper tab looks like the flower from a member of Rosa Hesperrhodos, a pretty pink-and-white rose. The colours absolutely clash."
icon_state = "coathydro"
inhand_icon_state = "coathydro"
- allowed = list(
- /obj/item/cultivator,
- /obj/item/hatchet,
- /obj/item/plant_analyzer,
- /obj/item/reagent_containers/spray/plantbgone,
- /obj/item/reagent_containers/cup/bottle,
- /obj/item/reagent_containers/spray/pestspray,
- /obj/item/seeds,
- /obj/item/storage/bag/plants,
- )
+ allowed = /obj/item/clothing/suit/apron::allowed
hoodtype = /obj/item/clothing/head/hooded/winterhood/hydro
/obj/item/clothing/head/hooded/winterhood/hydro
diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm
index 53026e974bf63..704182f3642d2 100644
--- a/code/modules/clothing/suits/wiz_robe.dm
+++ b/code/modules/clothing/suits/wiz_robe.dm
@@ -11,6 +11,12 @@
clothing_flags = SNUG_FIT | CASTING_CLOTHES
resistance_flags = FIRE_PROOF | ACID_PROOF
dog_fashion = /datum/dog_fashion/head/blue_wizard
+ ///How much this hat affects fishing difficulty
+ var/fishing_modifier = -4
+
+/obj/item/clothing/head/wizard/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, fishing_modifier) //A wizard always practices his casting (ba dum tsh)
/datum/armor/head_wizard
melee = 30
@@ -48,6 +54,7 @@
armor_type = /datum/armor/none
resistance_flags = FLAMMABLE
dog_fashion = /datum/dog_fashion/head/blue_wizard
+ fishing_modifier = -1
/obj/item/clothing/head/wizard/chanterelle
name = "chanterelle hat"
@@ -114,6 +121,12 @@
equip_delay_other = 50
clothing_flags = CASTING_CLOTHES
resistance_flags = FIRE_PROOF | ACID_PROOF
+ ///How much this robe affects fishing difficulty
+ var/fishing_modifier = -6
+
+/obj/item/clothing/suit/wizrobe/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, fishing_modifier) //A wizard always practices his casting (ba dum tsh)
/datum/armor/suit_wizrobe
melee = 30
@@ -181,17 +194,20 @@
inhand_icon_state = "wizrobe"
armor_type = /datum/armor/none
resistance_flags = FLAMMABLE
+ fishing_modifier = -2
/obj/item/clothing/head/wizard/marisa/fake
name = "witch hat"
armor_type = /datum/armor/none
resistance_flags = FLAMMABLE
+ fishing_modifier = -1
/obj/item/clothing/head/wizard/tape/fake
name = "tape hat"
desc = "A hat designed exclusively from duct tape. You can barely see."
armor_type = /datum/armor/none
resistance_flags = FLAMMABLE
+ fishing_modifier = -1
/obj/item/clothing/suit/wizrobe/marisa/fake
name = "witch robe"
@@ -200,12 +216,14 @@
inhand_icon_state = null
armor_type = /datum/armor/none
resistance_flags = FLAMMABLE
+ fishing_modifier = -2
/obj/item/clothing/suit/wizrobe/tape/fake
name = "tape robe"
desc = "An outfit designed exclusively from duct tape. It was hard to put on."
armor_type = /datum/armor/none
resistance_flags = FLAMMABLE
+ fishing_modifier = -2
/obj/item/clothing/suit/wizrobe/paper
name = "papier-mache robe" // no non-latin characters!
@@ -222,21 +240,8 @@
icon_state = "durathread-fake"
inhand_icon_state = null
armor_type = /datum/armor/robe_durathread
- allowed = list(
- /obj/item/cultivator,
- /obj/item/geneshears,
- /obj/item/graft,
- /obj/item/hatchet,
- /obj/item/plant_analyzer,
- /obj/item/reagent_containers/cup/beaker,
- /obj/item/reagent_containers/cup/bottle,
- /obj/item/reagent_containers/cup/tube,
- /obj/item/reagent_containers/spray/pestspray,
- /obj/item/reagent_containers/spray/plantbgone,
- /obj/item/secateurs,
- /obj/item/seeds,
- /obj/item/storage/bag/plants,
- )
+ allowed = /obj/item/clothing/suit/apron::allowed
+ fishing_modifier = -4
/datum/armor/robe_durathread
melee = 15
@@ -287,7 +292,7 @@
return
usr.say("Rise, my creation! Off your page into this realm!", forced = "stickman summoning")
- playsound(loc, 'sound/magic/summon_magic.ogg', 50, TRUE, TRUE)
+ playsound(loc, 'sound/effects/magic/summon_magic.ogg', 50, TRUE, TRUE)
var/mob/living/M = new /mob/living/basic/stickman/lesser(get_turf(usr))
M.faction += list("[REF(usr)]")
robe_charge = FALSE
diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm
index 6c6f9608e4b76..85fff72052b23 100644
--- a/code/modules/clothing/under/_under.dm
+++ b/code/modules/clothing/under/_under.dm
@@ -107,11 +107,7 @@
. += accessory_overlay
/obj/item/clothing/under/attackby(obj/item/attacking_item, mob/user, params)
- if(has_sensor == BROKEN_SENSORS && istype(attacking_item, /obj/item/stack/cable_coil))
- var/obj/item/stack/cable_coil/cabling = attacking_item
- to_chat(user, span_notice("You repair the suit sensors on [src] with [cabling]."))
- cabling.use(1)
- has_sensor = HAS_SENSORS
+ if(repair_sensors(attacking_item, user))
return TRUE
if(istype(attacking_item, /obj/item/clothing/accessory))
@@ -130,35 +126,11 @@
/obj/item/clothing/under/update_clothes_damaged_state(damaged_state = CLOTHING_DAMAGED)
. = ..()
if(damaged_state == CLOTHING_SHREDDED && has_sensor > NO_SENSORS)
- has_sensor = BROKEN_SENSORS
+ break_sensors()
else if(damaged_state == CLOTHING_PRISTINE && has_sensor == BROKEN_SENSORS)
- has_sensor = HAS_SENSORS
+ repair_sensors(cable_required = FALSE)
update_appearance()
-/obj/item/clothing/under/emp_act(severity)
- . = ..()
- if(. & EMP_PROTECT_SELF)
- return
- if(has_sensor == NO_SENSORS || has_sensor == BROKEN_SENSORS)
- return
-
- if(severity <= EMP_HEAVY)
- has_sensor = BROKEN_SENSORS
- if(ismob(loc))
- var/mob/M = loc
- to_chat(M,span_warning("[src]'s sensors short out!"))
-
- else
- sensor_mode = pick(SENSOR_OFF, SENSOR_OFF, SENSOR_OFF, SENSOR_LIVING, SENSOR_LIVING, SENSOR_VITALS, SENSOR_VITALS, SENSOR_COORDS)
- if(ismob(loc))
- var/mob/M = loc
- to_chat(M,span_warning("The sensors on the [src] change rapidly!"))
-
- if(ishuman(loc))
- var/mob/living/carbon/human/ooman = loc
- if(ooman.w_uniform == src)
- ooman.update_suit_sensors()
-
/obj/item/clothing/under/visual_equipped(mob/user, slot)
. = ..()
if(adjusted == ALT_STYLE)
@@ -176,13 +148,68 @@
freshly_laundered = FALSE
user.add_mood_event("fresh_laundry", /datum/mood_event/fresh_laundry)
+// Start suit sensor handling
+
+/// Change the suit sensor state to broken and update the mob's status on the global sensor list
+/obj/item/clothing/under/proc/break_sensors()
+ if(has_sensor == BROKEN_SENSORS || has_sensor == NO_SENSORS)
+ return
+
+ visible_message(span_warning("[src]'s medical sensors short out!"), blind_message = span_warning("The [src] makes an electronic sizzling sound!"), vision_distance = COMBAT_MESSAGE_RANGE)
+ has_sensor = BROKEN_SENSORS
+ sensor_malfunction()
+ update_wearer_status()
+
+/**
+ * Repair the suit sensors and update the mob's status on the global sensor list.
+ * Can be called either through player action such as repairing with coil, or as part of a general fixing proc
+ *
+ * Arguments:
+ * * attacking_item - the item being used for the repair, if any
+ * * user - mob that's doing the repair
+ * * cable_required - set to FALSE to bypass consuming cable coil
+ */
+/obj/item/clothing/under/proc/repair_sensors(obj/item/attacking_item, mob/user, cable_required = TRUE)
+ if(has_sensor != BROKEN_SENSORS)
+ return
+
+ if(cable_required)
+ if(!istype(attacking_item, /obj/item/stack/cable_coil))
+ return
+ var/obj/item/stack/cable_coil/cabling = attacking_item
+ if(!cabling.use(1))
+ return
+ cabling.visible_message(span_notice("[user] repairs the suit sensors on [src] with [cabling]."))
+
+ playsound(source = src, soundin = 'sound/effects/sparks/sparks4.ogg', vol = 100, vary = TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE, ignore_walls = FALSE)
+ has_sensor = HAS_SENSORS
+ update_wearer_status()
+
+ return TRUE
+
+/// If the item is being worn, a gentle reminder every 3-5 minutes that the sensors are broken
+/obj/item/clothing/under/proc/sensor_malfunction()
+ if(!QDELETED(src) && has_sensor == BROKEN_SENSORS && ishuman(loc))
+ do_sparks(number = 2, cardinal_only = FALSE, source = src)
+ addtimer(CALLBACK(src, PROC_REF(sensor_malfunction)), rand(BROKEN_SPARKS_MIN, BROKEN_SPARKS_MAX * 0.5), TIMER_UNIQUE | TIMER_NO_HASH_WAIT)
+
+/// If the item is being worn, update the mob's status on the global sensor list
+/obj/item/clothing/under/proc/update_wearer_status()
+ if(!ishuman(loc))
+ return
+
+ var/mob/living/carbon/human/ooman = loc
+ ooman.update_suit_sensors()
+ ooman.med_hud_set_status()
+
/mob/living/carbon/human/update_suit_sensors()
. = ..()
update_sensor_list()
+/// Adds or removes a mob from the global suit sensors list based on sensor status and mode
/mob/living/carbon/human/proc/update_sensor_list()
- var/obj/item/clothing/under/U = w_uniform
- if(istype(U) && U.has_sensor > NO_SENSORS && U.sensor_mode)
+ var/obj/item/clothing/under/uniform = w_uniform
+ if(istype(uniform) && uniform.has_sensor > NO_SENSORS && uniform.sensor_mode)
GLOB.suit_sensors_list |= src
else
GLOB.suit_sensors_list -= src
@@ -190,6 +217,46 @@
/mob/living/carbon/human/dummy/update_sensor_list()
return
+/obj/item/clothing/under/emp_act(severity)
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ if(has_sensor == NO_SENSORS || has_sensor == BROKEN_SENSORS)
+ return
+
+ if(severity <= EMP_HEAVY)
+ break_sensors()
+
+ else
+ sensor_mode = pick(SENSOR_OFF, SENSOR_OFF, SENSOR_OFF, SENSOR_LIVING, SENSOR_LIVING, SENSOR_VITALS, SENSOR_VITALS, SENSOR_COORDS)
+ playsound(source = src, soundin = 'sound/effects/sparks/sparks3.ogg', vol = 75, vary = TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE, ignore_walls = FALSE)
+ visible_message(span_warning("The [src]'s medical sensors flash and change rapidly!"), blind_message = span_warning("The [src] makes an electronic sizzling sound!"), vision_distance = COMBAT_MESSAGE_RANGE)
+
+ update_wearer_status()
+
+/**
+ * Called by medical scanners a simple summary of the status
+ *
+ * Arguments:
+ * * silent: If TRUE, will return blank if everything is fine
+ */
+/obj/item/clothing/under/proc/get_sensor_text(silent = TRUE)
+ if(has_sensor == BROKEN_SENSORS)
+ return "Non-Functional: Repair with cable coil"
+
+ if(silent)
+ return ""
+
+ switch(has_sensor)
+ if(NO_SENSORS)
+ return "Not Present"
+
+ if(LOCKED_SENSORS)
+ return "Functional, Locked"
+
+ if(HAS_SENSORS)
+ return "Functional"
+
// End suit sensor handling
/// Attach the passed accessory to the clothing item
@@ -285,7 +352,7 @@
if(can_adjust)
. += "Alt-click on [src] to wear it [adjusted == ALT_STYLE ? "normally" : "casually"]."
if(has_sensor == BROKEN_SENSORS)
- . += "Its sensors appear to be shorted out. You could repair it with some cabling."
+ . += span_warning("The medical sensors appear to be shorted out. You could repair it with some cabling.")
else if(has_sensor > NO_SENSORS)
switch(sensor_mode)
if(SENSOR_OFF)
@@ -305,7 +372,7 @@
/obj/item/clothing/under/proc/list_accessories_with_icon(mob/user)
var/list/all_accessories = list()
for(var/obj/item/clothing/accessory/attached as anything in attached_accessories)
- all_accessories += attached.get_examine_string(user)
+ all_accessories += attached.examine_title(user)
return all_accessories
@@ -336,10 +403,7 @@
if(SENSOR_COORDS)
to_chat(user_mob, span_notice("Your suit will now report your exact vital lifesigns as well as your coordinate position."))
- if(ishuman(loc))
- var/mob/living/carbon/human/H = loc
- if(H.w_uniform == src)
- H.update_suit_sensors()
+ update_wearer_status()
/obj/item/clothing/under/item_ctrl_click(mob/user)
if(!can_toggle_sensors(user))
@@ -347,6 +411,7 @@
sensor_mode = SENSOR_COORDS
balloon_alert(user, "set to tracking")
+ update_wearer_status()
return CLICK_ACTION_SUCCESS
/// Checks if the toggler is allowed to toggle suit sensors currently
diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm
index cc66e9d406a57..335eded4d4c82 100644
--- a/code/modules/clothing/under/accessories/badges.dm
+++ b/code/modules/clothing/under/accessories/badges.dm
@@ -264,9 +264,9 @@
/obj/item/clothing/accessory/press_badge/attack_self(mob/user, modifiers)
. = ..()
if(!journalist_name)
- journalist_name = tgui_input_text(user, "What is your name?", "Journalist Name", "[user.name]", MAX_NAME_LEN)
+ journalist_name = tgui_input_text(user, "What is your name?", "Journalist Name", "[user.name]", max_length = MAX_NAME_LEN)
if(!press_name)
- press_name = tgui_input_text(user, "For what organization you work?", "Press Name", "Nanotrasen", MAX_CHARTER_LEN)
+ press_name = tgui_input_text(user, "For what organization you work?", "Press Name", "Nanotrasen", max_length = MAX_CHARTER_LEN)
/obj/item/clothing/accessory/press_badge/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
. = ..()
@@ -275,12 +275,12 @@
var/mob/living/interacting_living = interacting_with
if(user.combat_mode)
- playsound(interacting_living, 'sound/weapons/throw.ogg', 30)
+ playsound(interacting_living, 'sound/items/weapons/throw.ogg', 30)
examine(interacting_living)
to_chat(interacting_living, span_userdanger("[user] shoves the [src] up your face!"))
user.visible_message(span_warning("[user] have shoved a [src] into [interacting_living] face."))
else
- playsound(interacting_living, 'sound/weapons/throwsoft.ogg', 20)
+ playsound(interacting_living, 'sound/items/weapons/throwsoft.ogg', 20)
examine(interacting_living)
to_chat(interacting_living, span_boldwarning("[user] shows the [src] to you."))
user.visible_message(span_notice("[user] shows a [src] to [interacting_living]."))
diff --git a/code/modules/clothing/under/accessories/tribal.dm b/code/modules/clothing/under/accessories/tribal.dm
index ad55b26fa89fd..0d5786a20e94b 100644
--- a/code/modules/clothing/under/accessories/tribal.dm
+++ b/code/modules/clothing/under/accessories/tribal.dm
@@ -12,7 +12,7 @@
attachment_slot = GROIN
/obj/item/clothing/accessory/skilt
- name = "Sinew Skirt"
+ name = "sinew skirt"
desc = "For the last time. IT'S A KILT not a skirt."
icon_state = "skilt"
minimize_when_attached = FALSE
diff --git a/code/modules/clothing/under/costume.dm b/code/modules/clothing/under/costume.dm
index ddf325a2201ab..c8550dd0ff6ba 100644
--- a/code/modules/clothing/under/costume.dm
+++ b/code/modules/clothing/under/costume.dm
@@ -417,7 +417,7 @@
can_adjust = FALSE
/obj/item/clothing/under/costume/gi
- name = "Martial Artist Gi"
+ name = "martial gi"
desc = "Assistant, nukie, whatever. You can beat anyone; it's called hard work!"
icon_state = "martial_arts_gi"
greyscale_config = /datum/greyscale_config/gi
@@ -434,13 +434,13 @@
update_icon(UPDATE_OVERLAYS)
/obj/item/clothing/under/costume/gi/goku
- name = "Sacred Gi"
+ name = "sacred gi"
desc = "Created by a man who touched the hearts and lives of many."
icon_state = "martial_arts_gi_goku"
greyscale_colors = "#f89925#3e6dd7"
/obj/item/clothing/under/costume/traditional
- name = "Traditional Suit"
+ name = "traditional suit"
desc = "A full, vibrantly coloured suit. Likely with traditional purposes. Maybe the colours represent a familly, clan, or rank, who knows."
icon_state = "tradition"
inhand_icon_state = null
@@ -448,7 +448,7 @@
can_adjust = FALSE
/obj/item/clothing/under/costume/loincloth
- name = "Leather Loincloth"
+ name = "leather loincloth"
desc = "Just a piece of leather to cover private areas. Itchy to the touch. Whoever made this must have been desperate, or savage."
icon_state = "loincloth"
inhand_icon_state = null
diff --git a/code/modules/clothing/under/jobs/civilian/curator.dm b/code/modules/clothing/under/jobs/civilian/curator.dm
index 8f40e623d8adf..f08657cee0754 100644
--- a/code/modules/clothing/under/jobs/civilian/curator.dm
+++ b/code/modules/clothing/under/jobs/civilian/curator.dm
@@ -28,6 +28,10 @@
inhand_icon_state = null
worn_icon = 'icons/mob/clothing/under/civilian.dmi'
+/obj/item/clothing/under/rank/civilian/curator/treasure_hunter/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3)
+
/obj/item/clothing/under/rank/civilian/curator/nasa
name = "\improper NASA jumpsuit"
desc = "It has a NASA logo on it and is made of space-proofed materials."
diff --git a/code/modules/clothing/under/jobs/medical.dm b/code/modules/clothing/under/jobs/medical.dm
index 1574b64bbf066..2dea332408231 100644
--- a/code/modules/clothing/under/jobs/medical.dm
+++ b/code/modules/clothing/under/jobs/medical.dm
@@ -44,6 +44,10 @@
icon_state = "scrubscmo"
inhand_icon_state = "w_suit"
+/obj/item/clothing/under/rank/medical/chief_medical_officer/scrubs/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2) //FISH DOCTOR?!
+
/obj/item/clothing/under/rank/medical/chief_medical_officer/turtleneck
name = "chief medical officer's turtleneck"
desc = "A light blue turtleneck and tan khakis, for a chief medical officer with a superior sense of style."
@@ -82,6 +86,10 @@
/obj/item/clothing/under/rank/medical/scrubs
name = "medical scrubs"
+/obj/item/clothing/under/rank/medical/scrubs/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2) //FISH DOCTOR?!
+
/obj/item/clothing/under/rank/medical/scrubs/blue
desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue."
icon_state = "scrubsblue"
diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm
index 81002bd8a9e2d..588dd8efc2a68 100644
--- a/code/modules/clothing/under/miscellaneous.dm
+++ b/code/modules/clothing/under/miscellaneous.dm
@@ -15,7 +15,7 @@
icon_state = "blue_pyjamas"
/obj/item/clothing/under/misc/patriotsuit
- name = "Patriotic Suit"
+ name = "patriotic suit"
desc = "Motorcycle not included."
icon_state = "ek"
inhand_icon_state = null
@@ -58,6 +58,10 @@
can_adjust = FALSE
resistance_flags = FIRE_PROOF | ACID_PROOF
+/obj/item/clothing/under/misc/adminsuit/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -15)
+
/datum/armor/clothing_under/adminsuit
melee = 100
bullet = 100
diff --git a/code/modules/clothing/under/skirt_dress.dm b/code/modules/clothing/under/skirt_dress.dm
index 638b754c2b83d..30f74920ef9d6 100644
--- a/code/modules/clothing/under/skirt_dress.dm
+++ b/code/modules/clothing/under/skirt_dress.dm
@@ -40,6 +40,10 @@
body_parts_covered = CHEST|GROIN|LEGS
flags_inv = HIDESHOES
+/obj/item/clothing/under/dress/wedding_dress/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 4) //You aren't going to fish with this are you?
+
/obj/item/clothing/under/dress/eveninggown
name = "evening gown"
desc = "Fancy dress for space bar singers."
@@ -50,6 +54,10 @@
flags_1 = IS_PLAYER_COLORABLE_1
greyscale_colors = "#e11f1f"
+/obj/item/clothing/under/dress/eveninggown/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 4) //You aren't going to fish with this are you?
+
/obj/item/clothing/under/dress/skirt
name = "cardigan skirt"
desc = "A nice skirt with a cute cardigan, very fancy!"
diff --git a/code/modules/clothing/under/suits.dm b/code/modules/clothing/under/suits.dm
index 0dbf1880d7d2f..98f41f407cab9 100644
--- a/code/modules/clothing/under/suits.dm
+++ b/code/modules/clothing/under/suits.dm
@@ -107,8 +107,16 @@
icon_state = "tuxedo"
inhand_icon_state = null
+/obj/item/clothing/under/suit/tuxedo/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, 4) //You aren't going to fish with this are you?
+
/obj/item/clothing/under/suit/carpskin
name = "carpskin suit"
desc = "An luxurious suit made with only the finest scales, perfect for conducting dodgy business deals."
icon_state = "carpskin_suit"
inhand_icon_state = null
+
+/obj/item/clothing/under/suit/carpskin/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2)
diff --git a/code/modules/clothing/under/syndicate.dm b/code/modules/clothing/under/syndicate.dm
index ff3061d3e5992..e4653b1c9bd47 100644
--- a/code/modules/clothing/under/syndicate.dm
+++ b/code/modules/clothing/under/syndicate.dm
@@ -34,6 +34,10 @@
can_adjust = FALSE
supports_variations_flags = NONE
+/obj/item/clothing/under/syndicate/bloodred/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2) //extra-tactical
+
/datum/armor/clothing_under/syndicate_bloodred
melee = 10
bullet = 10
@@ -119,6 +123,10 @@
can_adjust = FALSE
supports_variations_flags = NONE
+/obj/item/clothing/under/syndicate/floortilecamo/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -4) //tacticool
+
/obj/item/clothing/under/syndicate/soviet
name = "Ratnik 5 tracksuit"
desc = "Badly translated labels tell you to clean this in Vodka. Great for squatting in."
@@ -160,6 +168,10 @@
supports_variations_flags = NONE
armor_type = /datum/armor/clothing_under/syndicate_scrubs
+/obj/item/clothing/under/syndicate/scrubs/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2) //FISH DOCTOR?!
+
/datum/armor/clothing_under/syndicate_scrubs
melee = 10
bio = 50
diff --git a/code/modules/deathmatch/deathmatch_controller.dm b/code/modules/deathmatch/deathmatch_controller.dm
index 0b098871624dc..de5132198881a 100644
--- a/code/modules/deathmatch/deathmatch_controller.dm
+++ b/code/modules/deathmatch/deathmatch_controller.dm
@@ -54,7 +54,7 @@
var/datum/deathmatch_lobby/lobby = lobbies[ckey]
if (user.ckey == ckey)
.["hosting"] = TRUE
- if (user.ckey in lobby.observers+lobby.players)
+ if (user.ckey in (lobby.observers+lobby.players))
.["playing"] = ckey
.["lobbies"] += list(list(
name = ckey,
@@ -67,7 +67,7 @@
/datum/deathmatch_controller/proc/find_lobby_by_user(ckey)
for(var/lobbykey in lobbies)
var/datum/deathmatch_lobby/lobby = lobbies[lobbykey]
- if(ckey in lobby.players+lobby.observers)
+ if(ckey in (lobby.players+lobby.observers))
return lobby
/datum/deathmatch_controller/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
diff --git a/code/modules/deathmatch/deathmatch_loadouts.dm b/code/modules/deathmatch/deathmatch_loadouts.dm
index d89fdd383c80f..68d817bf70b21 100644
--- a/code/modules/deathmatch/deathmatch_loadouts.dm
+++ b/code/modules/deathmatch/deathmatch_loadouts.dm
@@ -440,7 +440,7 @@
head = /obj/item/clothing/head/wizard/black
spells_to_add = list(
/datum/action/cooldown/spell/touch/scream_for_me,
- /datum/action/cooldown/spell/teleport/radius_turf/blink,
+ /datum/action/cooldown/spell/conjure/link_worlds,
)
/datum/outfit/deathmatch_loadout/wizard/larp
@@ -768,7 +768,7 @@
id_trim = /datum/id_trim/job/bridge_assistant // half tider half command
id = /obj/item/card/id/advanced/chameleon
uniform = /obj/item/clothing/under/trek/command/next
- l_pocket = /obj/item/gun/energy/e_gun/mini
+ l_pocket = /obj/item/gun/energy/e_gun/mini // they are thej best race in the end. not as impactful as you may think
r_pocket = /obj/item/extinguisher/mini
gloves = /obj/item/clothing/gloves/fingerless
belt = /obj/item/storage/belt/utility/full/inducer
@@ -846,10 +846,11 @@
back = /obj/item/storage/backpack/science
backpack_contents = list(
- /obj/item/dnainjector/shock,
/obj/item/etherealballdeployer,
)
+ mutations_to_add = list(/obj/item/dnainjector/shock) // pretend ethereals are interesting
+
/datum/outfit/deathmatch_loadout/plasmamen
name = "Deathmatch: Plasmaman Species"
display_name = "Plasmamen"
@@ -907,16 +908,10 @@
uniform = /obj/item/clothing/under/pants/jeans
suit = /obj/item/clothing/suit/costume/wellworn_shirt/graphic
- back = /obj/item/storage/backpack
+ r_pocket = /obj/item/stack/rods/twentyfive
+ l_pocket = /obj/item/stack/rods/twentyfive
r_hand = /obj/item/wirecutters
- backpack_contents = list(
- /obj/item/stack/rods/fifty,
- /obj/item/stack/rods/fifty,
- /obj/item/stack/rods/fifty,
- /obj/item/stack/rods/fifty,
- )
-
// We don't want them to just punch each other to death
/datum/outfit/deathmatch_loadout/lattice_battles/pre_equip(mob/living/carbon/human/user, visualsOnly)
@@ -983,14 +978,12 @@
back = /obj/item/storage/backpack/cultpack
backpack_contents = list(
- /obj/item/cult_shift,
/obj/item/reagent_containers/cup/beaker/unholywater,
/obj/item/reagent_containers/cup/beaker/unholywater,
/obj/item/reagent_containers/cup/beaker/unholywater,
)
spells_to_add = list(
- /datum/action/innate/cult/blood_spell/horror,
/datum/action/innate/cult/blood_spell/horror,
/datum/action/innate/cult/blood_spell/stun,
/datum/action/innate/cult/blood_spell/stun,
@@ -999,7 +992,7 @@
/datum/outfit/deathmatch_loadout/cultish/artificer/post_equip(mob/living/carbon/human/user, visualsOnly)
. = ..()
- var/datum/action/innate/cult/blood_spell/manipulation/magick = locate() in user
+ var/datum/action/innate/cult/blood_spell/manipulation/magick = locate() in user.get_all_contents()
magick.charges = 300
/datum/outfit/deathmatch_loadout/heresy
@@ -1018,13 +1011,10 @@
// Heretic Warrior
-// Has spells of Ash, Blade, and Rust. Overall aggressive
-
/datum/outfit/deathmatch_loadout/heresy/warrior
name = "Deathmatch: Heretic Warrior"
display_name = "Heretic Warrior"
- desc = "Prove the furious strength of the Mansus."
- //species_override = /datum/species/plasmaman
+ desc = "Prove the furious strength of the Mansus!"
head = /obj/item/clothing/head/hooded/cult_hoodie/eldritch
neck = /obj/item/clothing/neck/heretic_focus
@@ -1032,7 +1022,7 @@
suit_store = /obj/item/melee/sickly_blade/dark
uniform = /obj/item/clothing/under/color/darkgreen
id_trim = null
- belt = /obj/item/melee/sickly_blade/ash
+ belt = /obj/item/melee/sickly_blade/rust
gloves = null
shoes = /obj/item/clothing/shoes/sandal
l_pocket = /obj/item/flashlight/lantern/jade/on
@@ -1061,16 +1051,12 @@
/datum/action/cooldown/spell/touch/mansus_grasp,
/datum/action/cooldown/spell/realignment,
/datum/action/cooldown/spell/pointed/projectile/furious_steel,
- /datum/action/cooldown/spell/charged/beam/fire_blast,
- /datum/action/cooldown/spell/aoe/fiery_rebirth,
/datum/action/cooldown/spell/cone/staggered/entropic_plume,
/datum/action/cooldown/spell/pointed/rust_construction,
)
// Heretic Scribe
-// Has spells of Void, Moon, and Cosmos. Overall defensive/mobile
-
/datum/outfit/deathmatch_loadout/heresy/scribe
name = "Deathmatch: Heretic Scribe"
display_name = "Heretic Scribe"
@@ -1087,29 +1073,27 @@
gloves = null
shoes = /obj/item/clothing/shoes/winterboots/ice_boots
l_pocket = /obj/item/ammo_box/strilka310/lionhunter
- r_pocket = /obj/item/codex_cicatrix
+ r_pocket = /obj/item/ammo_box/strilka310/lionhunter
back = /obj/item/gun/ballistic/rifle/lionhunter // for his neutral b, he wields a gun
belt_contents = list(
/obj/item/heretic_labyrinth_handbook,
/obj/item/heretic_labyrinth_handbook,
- /obj/item/eldritch_potion/crucible_soul,
- /obj/item/clothing/neck/heretic_focus/moon_amulet = 3,
+ /obj/item/eldritch_potion/duskndawn,
+ /obj/item/eldritch_potion/duskndawn,
)
knowledge_to_grant = list(
/datum/heretic_knowledge/cosmic_grasp,
/datum/heretic_knowledge/moon_grasp,
- /datum/heretic_knowledge/mark/moon_mark,
)
spells_to_add = list(
/datum/action/cooldown/spell/touch/mansus_grasp,
- /datum/action/cooldown/spell/conjure/cosmic_expansion,
/datum/action/cooldown/spell/pointed/projectile/star_blast,
/datum/action/cooldown/spell/touch/star_touch,
- /datum/action/cooldown/spell/cone/staggered/cone_of_cold/void,
+ /datum/action/cooldown/spell/pointed/mind_gate,
/datum/action/cooldown/spell/aoe/void_pull,
)
@@ -1127,7 +1111,7 @@
suit_store = /obj/item/book/bible/booze
uniform = /obj/item/clothing/under/rank/civilian/chaplain
id_trim = null
- belt = /obj/item/nullrod // choose any!
+ belt = /obj/item/nullrod/non_station // choose any!
gloves = /obj/item/clothing/gloves/plate
shoes = /obj/item/clothing/shoes/plate
l_pocket = /obj/item/flashlight/lantern/on
@@ -1150,7 +1134,6 @@
name = "Deathmatch: Clock Cultist"
display_name = "Rat'var Apostate"
desc = "You're in a fight between the servants of gods, and yours is dead. Good luck?"
- //species_override = /datum/species/plasmaman
head = /obj/item/clothing/head/costume/bronze
suit = /obj/item/clothing/suit/costume/bronze
@@ -1160,5 +1143,5 @@
belt = /obj/item/brass_spear
gloves = /obj/item/clothing/gloves/tinkerer
shoes = /obj/item/clothing/shoes/bronze
- l_pocket = /obj/item/reagent_containers/cup/beaker/synthflesh // they used to turn their dmg into tox with a spell. close enough
- r_pocket = /obj/item/reagent_containers/cup/beaker/synthflesh
+ l_pocket = /obj/item/reagent_containers/cup/beaker/synthflesh/named // they used to turn their dmg into tox with a spell. close enough
+ r_pocket = /obj/item/reagent_containers/cup/beaker/synthflesh/named
diff --git a/code/modules/deathmatch/deathmatch_lobby.dm b/code/modules/deathmatch/deathmatch_lobby.dm
index ffc41c887162d..76c8eb5561c08 100644
--- a/code/modules/deathmatch/deathmatch_lobby.dm
+++ b/code/modules/deathmatch/deathmatch_lobby.dm
@@ -285,7 +285,7 @@
/datum/deathmatch_lobby/proc/join(mob/player)
if (playing || !player)
return
- if(!(player.ckey in players+observers))
+ if(!(player.ckey in (players+observers)))
if (players.len >= map.max_players)
add_observer(player)
else
diff --git a/code/modules/deathmatch/deathmatch_mapping.dm b/code/modules/deathmatch/deathmatch_mapping.dm
index b83419490be76..a0651f7da121b 100644
--- a/code/modules/deathmatch/deathmatch_mapping.dm
+++ b/code/modules/deathmatch/deathmatch_mapping.dm
@@ -31,3 +31,4 @@
icon_state = /turf/open/floor/wood::icon_state
base_icon_state = /turf/open/floor/wood::base_icon_state
icon = /turf/open/floor/wood::icon
+ smoothing_flags = NONE
diff --git a/code/modules/detectivework/evidence.dm b/code/modules/detectivework/evidence.dm
index 4f8d8c74cb123..7110e368dce68 100644
--- a/code/modules/detectivework/evidence.dm
+++ b/code/modules/detectivework/evidence.dm
@@ -7,16 +7,19 @@
icon_state = "evidenceobj"
inhand_icon_state = ""
w_class = WEIGHT_CLASS_TINY
+ item_flags = NOBLUDGEON
/obj/item/evidencebag/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- if(interacting_with == loc)
+ if(interacting_with == loc || !isitem(interacting_with) || HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION))
return NONE
- evidencebagEquip(interacting_with, user)
- return ITEM_INTERACT_SUCCESS
+ if(evidencebagEquip(interacting_with, user))
+ return ITEM_INTERACT_SUCCESS
+ return NONE
-/obj/item/evidencebag/attackby(obj/item/I, mob/user, params)
- if(evidencebagEquip(I, user))
- return 1
+/obj/item/evidencebag/item_interaction(mob/living/user, obj/item/tool, list/modifiers)
+ if(evidencebagEquip(tool, user))
+ return ITEM_INTERACT_SUCCESS
+ return NONE
/obj/item/evidencebag/Exited(atom/movable/gone, direction)
. = ..()
@@ -27,7 +30,7 @@
/obj/item/evidencebag/proc/evidencebagEquip(obj/item/I, mob/user)
if(!istype(I) || I.anchored)
- return
+ return FALSE
if(loc.atom_storage && I.atom_storage)
to_chat(user, span_warning("No matter what way you try, you can't get [I] to fit inside [src]."))
@@ -43,24 +46,24 @@
if(loc in I.get_all_contents()) // fixes tg #39452, evidence bags could store their own location, causing I to be stored in the bag while being present inworld still, and able to be teleported when removed.
to_chat(user, span_warning("You find putting [I] in [src] while it's still inside it quite difficult!"))
- return
+ return TRUE
if(I.w_class > WEIGHT_CLASS_NORMAL)
to_chat(user, span_warning("[I] won't fit in [src]!"))
- return
+ return TRUE
if(contents.len)
to_chat(user, span_warning("[src] already has something inside it!"))
- return
+ return TRUE
if(!isturf(I.loc)) //If it isn't on the floor. Do some checks to see if it's in our hands or a box. Otherwise give up.
if(I.loc.atom_storage) //in a container.
I.loc.atom_storage.remove_single(user, I, src)
if(!user.is_holding(I) || HAS_TRAIT(I, TRAIT_NODROP))
- return
+ return TRUE
if(QDELETED(I))
- return
+ return TRUE
user.visible_message(span_notice("[user] puts [I] into [src]."), span_notice("You put [I] inside [src]."),\
span_hear("You hear a rustle as someone puts something into a plastic bag."))
@@ -78,7 +81,7 @@
desc = "An evidence bag containing [I]. [I.desc]"
I.forceMove(src)
update_weight_class(I.w_class)
- return 1
+ return TRUE
/obj/item/evidencebag/attack_self(mob/user)
if(contents.len)
diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm
index 57987eda621d9..002647f7a0816 100644
--- a/code/modules/detectivework/scanner.dm
+++ b/code/modules/detectivework/scanner.dm
@@ -74,10 +74,9 @@
// Clear the logs
log = list()
-/obj/item/detective_scanner/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/living/user)
- return !user.combat_mode
-
/obj/item/detective_scanner/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(SHOULD_SKIP_INTERACTION(interacting_with, src, user))
+ return NONE // lets us put our scanner away without trying to scan the bag
safe_scan(user, interacting_with)
return ITEM_INTERACT_SUCCESS
diff --git a/code/modules/discord/tgs_commands.dm b/code/modules/discord/tgs_commands.dm
index 42d6d19ba99a7..8c6796bc1ea16 100644
--- a/code/modules/discord/tgs_commands.dm
+++ b/code/modules/discord/tgs_commands.dm
@@ -4,7 +4,7 @@
/datum/tgs_chat_command/tgscheck/Run(datum/tgs_chat_user/sender, params)
var/server = CONFIG_GET(string/server)
- return new /datum/tgs_message_content("[GLOB.round_id ? "Round #[GLOB.round_id]: " : ""][GLOB.clients.len] players on [SSmapping.config.map_name]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]")
+ return new /datum/tgs_message_content("[GLOB.round_id ? "Round #[GLOB.round_id]: " : ""][GLOB.clients.len] players on [SSmapping.current_map.map_name]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]")
/datum/tgs_chat_command/gameversion
name = "gameversion"
diff --git a/code/modules/economy/account.dm b/code/modules/economy/account.dm
index d31e204333b31..8ec8e5b2d8a56 100644
--- a/code/modules/economy/account.dm
+++ b/code/modules/economy/account.dm
@@ -44,6 +44,7 @@
payday_modifier = modifier
add_to_accounts = player_account
setup_unique_account_id()
+ update_account_job_lists(job)
pay_token = uppertext("[copytext(newname, 1, 2)][copytext(newname, -1)]-[random_capital_letter()]-[rand(1111,9999)]")
/datum/bank_account/Destroy()
@@ -71,11 +72,23 @@
if(SSeconomy.bank_accounts_by_id["[account_id]"])
stack_trace("Unable to find a unique account ID, substituting currently existing account of id [account_id].")
SSeconomy.bank_accounts_by_id["[account_id]"] = src
- if(account_job)
- LAZYADD(SSeconomy.bank_accounts_by_job[account_job.type], src)
+
+/**
+ * Proc places this account into the right place in the `SSeconomy.bank_accounts_by_job` list, if needed.
+ * If an old job is given, it removes it from its previous place first.
+ */
+/datum/bank_account/proc/update_account_job_lists(datum/job/new_job, datum/job/old_job)
+ if(!add_to_accounts)
+ return
+
+ if(old_job)
+ SSeconomy.bank_accounts_by_job[old_job.type] -= src
+ if(new_job)
+ LAZYADD(SSeconomy.bank_accounts_by_job[new_job.type], src)
/datum/bank_account/vv_edit_var(var_name, var_value) // just so you don't have to do it manually
var/old_id = account_id
+ var/datum/job/old_job = account_job
var/old_balance = account_balance
. = ..()
switch(var_name)
@@ -83,11 +96,15 @@
if(add_to_accounts)
SSeconomy.bank_accounts_by_id -= "[old_id]"
setup_unique_account_id()
+ if(NAMEOF(src, account_job))
+ update_account_job_lists(account_job, old_job)
if(NAMEOF(src, add_to_accounts))
if(add_to_accounts)
setup_unique_account_id()
+ update_account_job_lists(account_job)
else
SSeconomy.bank_accounts_by_id -= "[account_id]"
+ SSeconomy.bank_accounts_by_job[account_job.type] -= src
if(NAMEOF(src, account_balance))
add_log_to_history(var_value - old_balance, "Nanotrasen: Moderator Action")
@@ -217,7 +234,7 @@
return
if(card_holder.can_hear())
- card_holder.playsound_local(get_turf(card_holder), 'sound/machines/twobeep_high.ogg', 50, TRUE)
+ card_holder.playsound_local(get_turf(card_holder), 'sound/machines/beep/twobeep_high.ogg', 50, TRUE)
to_chat(card_holder, "[icon2html(icon_source, card_holder)] [span_notice("[message]")]")
else if(isturf(card.loc)) //If on the ground
var/turf/card_location = card.loc
@@ -225,7 +242,7 @@
if(!potential_hearer.client || (!(get_chat_toggles(potential_hearer.client) & CHAT_BANKCARD) && !force))
continue
if(potential_hearer.can_hear())
- potential_hearer.playsound_local(card_location, 'sound/machines/twobeep_high.ogg', 50, TRUE)
+ potential_hearer.playsound_local(card_location, 'sound/machines/beep/twobeep_high.ogg', 50, TRUE)
to_chat(potential_hearer, "[icon2html(icon_source, potential_hearer)] [span_notice("[message]")]")
else
var/atom/sound_atom
@@ -235,7 +252,7 @@
if(!sound_atom)
sound_atom = card.drop_location() //in case we're inside a bodybag in a crate or something. doing this here to only process it if there's a valid mob who can hear the sound.
if(potential_hearer.can_hear())
- potential_hearer.playsound_local(get_turf(sound_atom), 'sound/machines/twobeep_high.ogg', 50, TRUE)
+ potential_hearer.playsound_local(get_turf(sound_atom), 'sound/machines/beep/twobeep_high.ogg', 50, TRUE)
to_chat(potential_hearer, "[icon2html(icon_source, potential_hearer)] [span_notice("[message]")]")
/**
diff --git a/code/modules/economy/holopay.dm b/code/modules/economy/holopay.dm
index 54f6be3666a22..301a4a5d6f8cd 100644
--- a/code/modules/economy/holopay.dm
+++ b/code/modules/economy/holopay.dm
@@ -61,9 +61,9 @@
/obj/structure/holopay/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
if(BRUTE)
- playsound(loc, 'sound/weapons/egloves.ogg', 80, TRUE)
+ playsound(loc, 'sound/items/weapons/egloves.ogg', 80, TRUE)
if(BURN)
- playsound(loc, 'sound/weapons/egloves.ogg', 80, TRUE)
+ playsound(loc, 'sound/items/weapons/egloves.ogg', 80, TRUE)
/obj/structure/holopay/atom_deconstruct(dissambled = TRUE)
dissipate()
diff --git a/code/modules/emote_panel/emote_panel.dm b/code/modules/emote_panel/emote_panel.dm
index 72caf05e92b27..d064161c23249 100644
--- a/code/modules/emote_panel/emote_panel.dm
+++ b/code/modules/emote_panel/emote_panel.dm
@@ -42,7 +42,7 @@
var/datum/emote/emote = GLOB.emote_list[emote_key][1]
var/emote_param
if(emote.message_param && use_params)
- emote_param = tgui_input_text(ui.user, "Add params to the emote...", emote.message_param)
+ emote_param = tgui_input_text(ui.user, "Add params to the emote...", emote.message_param, max_length = MAX_MESSAGE_LEN)
ui.user.emote(emote_key, message = emote_param, intentional = TRUE)
/datum/emote_panel/ui_interact(mob/user, datum/tgui/ui)
diff --git a/code/modules/escape_menu/details.dm b/code/modules/escape_menu/details.dm
index 49bd19ce97fd7..ab6ff05d3ea46 100644
--- a/code/modules/escape_menu/details.dm
+++ b/code/modules/escape_menu/details.dm
@@ -35,7 +35,7 @@ GLOBAL_DATUM(escape_menu_details, /atom/movable/screen/escape_menu/details)
Round ID: [GLOB.round_id || "Unset"]
Round Time: [ROUND_TIME()]
- Map: [SSmapping.config?.map_name || "Loading..."]
+ Map: [SSmapping.current_map.map_name || "Loading..."]
Time Dilation: [round(SStime_track.time_dilation_current,1)]%
"}
diff --git a/code/modules/events/_event.dm b/code/modules/events/_event.dm
index 0a41f5ffb6c9a..a1e8aef5b6c91 100644
--- a/code/modules/events/_event.dm
+++ b/code/modules/events/_event.dm
@@ -15,7 +15,7 @@
var/earliest_start = 20 MINUTES //The earliest world.time that an event can start (round-duration in deciseconds) default: 20 mins
var/min_players = 0 //The minimum amount of alive, non-AFK human players on server required to start the event.
- var/occurrences = 0 //How many times this event has occured
+ var/occurrences = 0 //How many times this event has occurred
var/max_occurrences = 20 //The maximum number of times this event can occur (naturally), it can still be forced.
//By setting this to 0 you can effectively disable an event.
@@ -143,7 +143,7 @@ Runs the event
*/
/datum/round_event_control/proc/run_event(random = FALSE, announce_chance_override = null, admin_forced = FALSE, event_cause)
/*
- * We clear our signals first so we dont cancel a wanted event by accident,
+ * We clear our signals first so we don't cancel a wanted event by accident,
* the majority of time the admin will probably want to cancel a single midround spawned random events
* and not multiple events called by others admins
* * In the worst case scenario we can still recall a event which we cancelled by accident, which is much better then to have a unwanted event
@@ -220,7 +220,7 @@ Runs the event
SHOULD_CALL_PARENT(FALSE)
return
-///Annouces the event name to deadchat, override this if what an event should show to deadchat is different to its event name.
+///Announces the event name to deadchat, override this if what an event should show to deadchat is different to its event name.
/datum/round_event/proc/announce_deadchat(random, cause)
deadchat_broadcast(" has just been[random ? " randomly" : ""] triggered[cause ? " by [cause]" : ""]!", "[control.name]", message_type=DEADCHAT_ANNOUNCEMENT) //STOP ASSUMING IT'S BADMINS!
@@ -267,8 +267,8 @@ Runs the event
-//Do not override this proc, instead use the appropiate procs.
-//This proc will handle the calls to the appropiate procs.
+//Do not override this proc, instead use the appropriate procs.
+//This proc will handle the calls to the appropriate procs.
/datum/round_event/process()
SHOULD_NOT_OVERRIDE(TRUE)
if(!processing)
diff --git a/code/modules/events/aurora_caelus.dm b/code/modules/events/aurora_caelus.dm
index 875b8c0dcf23a..2fdd161514903 100644
--- a/code/modules/events/aurora_caelus.dm
+++ b/code/modules/events/aurora_caelus.dm
@@ -19,14 +19,14 @@
/datum/round_event/aurora_caelus/announce(fake)
priority_announce("[station_name()]: A harmless cloud of ions is approaching your station, and will exhaust their energy battering the hull. Nanotrasen has approved a short break for all employees to relax and observe this very rare event. During this time, starlight will be bright but gentle, shifting between quiet green and blue colors. Any staff who would like to view these lights for themselves may proceed to the area nearest to them with viewing ports to open space. We hope you enjoy the lights.",
- sound = 'sound/misc/notice2.ogg',
+ sound = 'sound/announcer/notice/notice2.ogg',
sender_override = "Nanotrasen Meteorology Division")
if (fake)
return
for(var/V in GLOB.player_list)
var/mob/M = V
if((M.client.prefs.read_preference(/datum/preference/toggle/sound_midi)) && is_station_level(M.z))
- M.playsound_local(M, 'sound/ambience/aurora_caelus.ogg', 20, FALSE, pressure_affected = FALSE)
+ M.playsound_local(M, 'sound/ambience/aurora_caelus/aurora_caelus.ogg', 20, FALSE, pressure_affected = FALSE)
fade_space(fade_in = TRUE)
fade_kitchen(fade_in = TRUE)
@@ -66,7 +66,7 @@
fade_space()
fade_kitchen()
priority_announce("The aurora caelus event is now ending. Starlight conditions will slowly return to normal. When this has concluded, please return to your workplace and continue work as normal. Have a pleasant shift, [station_name()], and thank you for watching with us.",
- sound = 'sound/misc/notice2.ogg',
+ sound = 'sound/announcer/notice/notice2.ogg',
sender_override = "Nanotrasen Meteorology Division")
/datum/round_event/aurora_caelus/proc/fade_space(fade_in = FALSE)
diff --git a/code/modules/events/earthquake.dm b/code/modules/events/earthquake.dm
index 84945dc99f09d..58b0a7e40821a 100644
--- a/code/modules/events/earthquake.dm
+++ b/code/modules/events/earthquake.dm
@@ -112,10 +112,10 @@
earthquake_witness.playsound_local(
earthquake_witness,
pick(
- 'sound/misc/earth_rumble_distant1.ogg',
- 'sound/misc/earth_rumble_distant2.ogg',
- 'sound/misc/earth_rumble_distant3.ogg',
- 'sound/misc/earth_rumble_distant4.ogg',
+ 'sound/ambience/earth_rumble/earth_rumble_distant1.ogg',
+ 'sound/ambience/earth_rumble/earth_rumble_distant2.ogg',
+ 'sound/ambience/earth_rumble/earth_rumble_distant3.ogg',
+ 'sound/ambience/earth_rumble/earth_rumble_distant4.ogg',
),
75,
)
@@ -150,12 +150,12 @@
playsound(epicenter, 'sound/misc/metal_creak.ogg', 125, TRUE)
/datum/round_event/earthquake/end()
- playsound(epicenter, 'sound/misc/earth_rumble.ogg', 125)
+ playsound(epicenter, 'sound/ambience/earth_rumble/earth_rumble.ogg', 125)
for(var/mob/earthquake_witness as anything in GLOB.player_list)
if(!is_station_level(earthquake_witness.z) || !is_mining_level(earthquake_witness.z))
continue
shake_camera(earthquake_witness, 2 SECONDS, 4)
- earthquake_witness.playsound_local(earthquake_witness, 'sound/effects/explosionfar.ogg', 75)
+ earthquake_witness.playsound_local(earthquake_witness, 'sound/effects/explosion/explosionfar.ogg', 75)
// Step two of the destruction, which detonates the turfs in the earthquake zone. There is no actual explosion, meaning stuff around the earthquake zone is perfectly safe.
// All turfs, and everything else that IS in the earthquake zone, however, will behave as if it were bombed.
diff --git a/code/modules/events/ghost_role/fugitive_event.dm b/code/modules/events/ghost_role/fugitive_event.dm
index 9eb792a6f6ab3..e08304b9925e3 100644
--- a/code/modules/events/ghost_role/fugitive_event.dm
+++ b/code/modules/events/ghost_role/fugitive_event.dm
@@ -54,7 +54,7 @@
gear_fugitive_leader(leader, landing_turf, backstory)
//after spawning
- playsound(src, 'sound/weapons/emitter.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/emitter.ogg', 50, TRUE)
new /obj/item/storage/toolbox/mechanical(landing_turf) //so they can actually escape maint
var/hunter_backstory = pick(
HUNTER_PACK_COPS,
@@ -72,7 +72,7 @@
player_mind.active = TRUE
var/mob/living/carbon/human/S = new(landing_turf)
player_mind.transfer_to(S)
- player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/fugitive))
+ player_mind.set_assigned_role(SSjob.get_job_type(/datum/job/fugitive))
player_mind.special_role = ROLE_FUGITIVE
player_mind.add_antag_datum(/datum/antagonist/fugitive)
var/datum/antagonist/fugitive/fugitiveantag = player_mind.has_antag_datum(/datum/antagonist/fugitive)
diff --git a/code/modules/events/ghost_role/morph_event.dm b/code/modules/events/ghost_role/morph_event.dm
index 21d4b07873d86..b9841283ceed7 100644
--- a/code/modules/events/ghost_role/morph_event.dm
+++ b/code/modules/events/ghost_role/morph_event.dm
@@ -25,10 +25,10 @@
var/mob/living/basic/morph/corpus_accipientis = new(spawn_loc)
player_mind.transfer_to(corpus_accipientis)
- player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/morph))
+ player_mind.set_assigned_role(SSjob.get_job_type(/datum/job/morph))
player_mind.special_role = ROLE_MORPH
player_mind.add_antag_datum(/datum/antagonist/morph)
- SEND_SOUND(corpus_accipientis, sound('sound/magic/mutate.ogg'))
+ SEND_SOUND(corpus_accipientis, sound('sound/effects/magic/mutate.ogg'))
message_admins("[ADMIN_LOOKUPFLW(corpus_accipientis)] has been made into a morph by an event.")
corpus_accipientis.log_message("was spawned as a morph by an event.", LOG_GAME)
spawned_mobs += corpus_accipientis
diff --git a/code/modules/events/ghost_role/nightmare.dm b/code/modules/events/ghost_role/nightmare.dm
index d30108d94b984..9f894c237d41c 100644
--- a/code/modules/events/ghost_role/nightmare.dm
+++ b/code/modules/events/ghost_role/nightmare.dm
@@ -27,11 +27,11 @@
var/mob/living/carbon/human/S = new (spawn_loc)
player_mind.transfer_to(S)
- player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/nightmare))
+ player_mind.set_assigned_role(SSjob.get_job_type(/datum/job/nightmare))
player_mind.special_role = ROLE_NIGHTMARE
player_mind.add_antag_datum(/datum/antagonist/nightmare)
S.set_species(/datum/species/shadow/nightmare)
- playsound(S, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
+ playsound(S, 'sound/effects/magic/ethereal_exit.ogg', 50, TRUE, -1)
message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a Nightmare by an event.")
S.log_message("was spawned as a Nightmare by an event.", LOG_GAME)
spawned_mobs += S
diff --git a/code/modules/events/ghost_role/operative.dm b/code/modules/events/ghost_role/operative.dm
index 98cfe5ecad41e..c7ad41d001d16 100644
--- a/code/modules/events/ghost_role/operative.dm
+++ b/code/modules/events/ghost_role/operative.dm
@@ -22,7 +22,7 @@
operative.randomize_human_appearance(~RANDOMIZE_SPECIES)
operative.dna.update_dna_identity()
var/datum/mind/Mind = new /datum/mind(chosen_one.key)
- Mind.set_assigned_role(SSjob.GetJobType(/datum/job/lone_operative))
+ Mind.set_assigned_role(SSjob.get_job_type(/datum/job/lone_operative))
Mind.special_role = ROLE_LONE_OPERATIVE
Mind.active = TRUE
Mind.transfer_to(operative)
diff --git a/code/modules/events/ghost_role/sentience.dm b/code/modules/events/ghost_role/sentience.dm
index 092813008458a..b12dd5c517423 100644
--- a/code/modules/events/ghost_role/sentience.dm
+++ b/code/modules/events/ghost_role/sentience.dm
@@ -104,9 +104,9 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list(
spawned_mobs += selected
to_chat(selected, span_userdanger("Hello world!"))
- to_chat(selected, "Due to freak radiation and/or chemicals \
+ to_chat(selected, span_warning("Due to freak radiation and/or chemicals \
and/or lucky chance, you have gained human level intelligence \
- and the ability to speak and understand human language!")
+ and the ability to speak and understand human language!"))
return SUCCESSFUL_SPAWN
diff --git a/code/modules/events/ghost_role/space_dragon.dm b/code/modules/events/ghost_role/space_dragon.dm
index 8a39d4a5daea5..56d82ff33c7cf 100644
--- a/code/modules/events/ghost_role/space_dragon.dm
+++ b/code/modules/events/ghost_role/space_dragon.dm
@@ -28,7 +28,7 @@
var/mob/living/basic/space_dragon/dragon = new(spawn_location)
dragon.key = chosen_one.key
dragon.mind.add_antag_datum(/datum/antagonist/space_dragon)
- playsound(dragon, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1)
+ playsound(dragon, 'sound/effects/magic/ethereal_exit.ogg', 50, TRUE, -1)
message_admins("[ADMIN_LOOKUPFLW(dragon)] has been made into a Space Dragon by an event.")
dragon.log_message("was spawned as a Space Dragon by an event.", LOG_GAME)
spawned_mobs += dragon
diff --git a/code/modules/events/heart_attack.dm b/code/modules/events/heart_attack.dm
index 1a1fa3ac67c0c..a0bc06718c08d 100644
--- a/code/modules/events/heart_attack.dm
+++ b/code/modules/events/heart_attack.dm
@@ -67,7 +67,7 @@
if(winner.has_status_effect(/datum/status_effect/exercised)) //Stuff that should "block" a heart attack rather than just deny eligibility for one goes here.
winner.visible_message(span_warning("[winner] grunts and clutches their chest for a moment, catching [winner.p_their()] breath."), span_medal("Your chest lurches in pain for a brief moment, which quickly fades. \
You feel like you've just avoided a serious health disaster."), span_hear("You hear someone's breathing sharpen for a moment, followed by a sigh of relief."), 4)
- winner.playsound_local(get_turf(winner), 'sound/health/slowbeat.ogg', 40, 0, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE)
+ winner.playsound_local(get_turf(winner), 'sound/effects/health/slowbeat.ogg', 40, 0, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE)
winner.Stun(3 SECONDS)
if(winner.client)
winner.client.give_award(/datum/award/achievement/misc/healthy, winner)
diff --git a/code/modules/events/holiday/easter.dm b/code/modules/events/holiday/easter.dm
index 40c7fda57c3c2..d10fb681cc5bd 100644
--- a/code/modules/events/holiday/easter.dm
+++ b/code/modules/events/holiday/easter.dm
@@ -66,7 +66,7 @@
//Bunny Suit
/obj/item/clothing/head/costume/bunnyhead
- name = "Easter Bunny Head"
+ name = "Easter Bunny head"
icon_state = "bunnyhead"
inhand_icon_state = null
desc = "Considerably more cute than 'Frank'."
@@ -75,7 +75,7 @@
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR|HIDESNOUT
/obj/item/clothing/suit/costume/bunnysuit
- name = "Easter Bunny Suit"
+ name = "easter bunny suit"
desc = "Hop Hop Hop!"
icon_state = "bunnysuit"
icon = 'icons/obj/clothing/suits/costume.dmi'
@@ -88,7 +88,7 @@
//Bunny bag!
/obj/item/storage/backpack/satchel/bunnysatchel
- name = "Easter Bunny Satchel"
+ name = "easter bunny satchel"
desc = "Good for your eyes."
icon_state = "satchel_carrot"
inhand_icon_state = null
diff --git a/code/modules/events/meteors/dark_matteor_event.dm b/code/modules/events/meteors/dark_matteor_event.dm
index 86d38f93e6cca..412354b16f13d 100644
--- a/code/modules/events/meteors/dark_matteor_event.dm
+++ b/code/modules/events/meteors/dark_matteor_event.dm
@@ -25,7 +25,7 @@
spawn_meteor(list(/obj/effect/meteor/dark_matteor = 1), null, target)
/datum/round_event/dark_matteor/announce(fake)
- priority_announce("Warning. Excessive tampering of meteor satellites has attracted a dark matt-eor. Signature approaching [GLOB.station_name]. Please brace for impact.", "Meteor Alert", 'sound/misc/airraid.ogg')
+ priority_announce("Warning. Excessive tampering of meteor satellites has attracted a dark matt-eor. Signature approaching [GLOB.station_name]. Please brace for impact.", "Meteor Alert", 'sound/announcer/alarm/airraid.ogg')
/datum/event_admin_setup/warn_admin/dark_matteor
warning_text = "Dark Matt-eors spawn singularities. The round is ending once a dark matt-eor hits the station. Proceed anyways?"
diff --git a/code/modules/events/mice_migration.dm b/code/modules/events/mice_migration.dm
index 450f910080018..cf0071c2e5cd4 100644
--- a/code/modules/events/mice_migration.dm
+++ b/code/modules/events/mice_migration.dm
@@ -23,7 +23,7 @@
priority_announce("Due to [cause], [plural] [name] have [movement] \
into the [location].", "Migration Alert",
- 'sound/creatures/mousesqueek.ogg')
+ 'sound/mobs/non-humanoids/mouse/mousesqueek.ogg')
/datum/round_event/mice_migration/start()
SSminor_mapping.trigger_migration(rand(minimum_mice, maximum_mice))
diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm
index 0ca7800ee22d1..f96b73e66a295 100644
--- a/code/modules/events/portal_storm.dm
+++ b/code/modules/events/portal_storm.dm
@@ -72,11 +72,11 @@
/datum/round_event/portal_storm/announce(fake)
set waitfor = 0
- sound_to_playing_players('sound/magic/lightning_chargeup.ogg')
+ sound_to_playing_players('sound/effects/magic/lightning_chargeup.ogg')
sleep(8 SECONDS)
priority_announce("Massive bluespace anomaly detected en route to [station_name()]. Brace for impact.")
sleep(2 SECONDS)
- sound_to_playing_players('sound/magic/lightningbolt.ogg')
+ sound_to_playing_players('sound/effects/magic/lightningbolt.ogg')
/datum/round_event/portal_storm/tick()
spawn_effects(get_random_station_turf())
@@ -112,7 +112,7 @@
return
T = get_step(T, SOUTHWEST) //align center of image with turf
T.flick_overlay_static(storm_appearances[GET_TURF_PLANE_OFFSET(T) + 1], 15)
- playsound(T, 'sound/magic/lightningbolt.ogg', rand(80, 100), TRUE)
+ playsound(T, 'sound/effects/magic/lightningbolt.ogg', rand(80, 100), TRUE)
/datum/round_event/portal_storm/proc/spawn_hostile()
if(!hostile_types || !hostile_types.len)
diff --git a/code/modules/events/space_vines/vine_mutations.dm b/code/modules/events/space_vines/vine_mutations.dm
index c2f8e2d41393c..57c5c003fd749 100644
--- a/code/modules/events/space_vines/vine_mutations.dm
+++ b/code/modules/events/space_vines/vine_mutations.dm
@@ -180,7 +180,7 @@
if(thorn && prob(40) && !HAS_TRAIT(victim, TRAIT_PIERCEIMMUNE)) //If we found the thorns mutation there is now a chance to get stung instead of lashed or smashed.
victim.apply_damage(50, BRUTE, def_zone = limb, wound_bonus = rand(-20,10), sharpness = SHARP_POINTY) //This one gets a bit lower damage because it ignores armor.
victim.Stun(1 SECONDS) //Stopped in place for a moment.
- playsound(living_mob, 'sound/weapons/pierce.ogg', 50, TRUE, -1)
+ playsound(living_mob, 'sound/items/weapons/pierce.ogg', 50, TRUE, -1)
living_mob.visible_message(span_danger("[living_mob] is nailed by a sharp thorn!"), \
span_userdanger("You are nailed by a sharp thorn!"))
log_combat(vine, living_mob, "aggressively pierced") //"Aggressively" for easy ctrl+F'ing in the attack logs.
@@ -188,7 +188,7 @@
if(prob(80) && !HAS_TRAIT(victim, TRAIT_PIERCEIMMUNE))
victim.apply_damage(60, BRUTE, def_zone = limb, blocked = armor, wound_bonus = rand(-20,10), sharpness = SHARP_EDGED)
victim.Knockdown(2 SECONDS)
- playsound(victim, 'sound/weapons/whip.ogg', 50, TRUE, -1)
+ playsound(victim, 'sound/items/weapons/whip.ogg', 50, TRUE, -1)
living_mob.visible_message(span_danger("[living_mob] is lacerated by an outburst of vines!"), \
span_userdanger("You are lacerated by an outburst of vines!"))
log_combat(vine, living_mob, "aggressively lacerated")
@@ -203,7 +203,7 @@
log_combat(vine, living_mob, "aggressively smashed")
else //Living but not a carbon? Maybe a silicon? Can't be wounded so have a big chunk of simple bruteloss with no special effects. They can be entangled.
living_mob.adjustBruteLoss(75)
- playsound(living_mob, 'sound/weapons/whip.ogg', 50, TRUE, -1)
+ playsound(living_mob, 'sound/items/weapons/whip.ogg', 50, TRUE, -1)
living_mob.visible_message(span_danger("[living_mob] is brutally threshed by [vine]!"), \
span_userdanger("You are brutally threshed by [vine]!"))
log_combat(vine, living_mob, "aggressively spread into") //You aren't being attacked by the vines. You just happen to stand in their way.
diff --git a/code/modules/events/space_vines/vine_structure.dm b/code/modules/events/space_vines/vine_structure.dm
index a7b9aa2fce516..ceb5cca415a89 100644
--- a/code/modules/events/space_vines/vine_structure.dm
+++ b/code/modules/events/space_vines/vine_structure.dm
@@ -91,11 +91,11 @@
switch(damage_type)
if(BRUTE)
if(damage_amount)
- playsound(src, 'sound/weapons/slash.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/slash.ogg', 50, TRUE)
else
- playsound(src, 'sound/weapons/tap.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/tap.ogg', 50, TRUE)
if(BURN)
- playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
+ playsound(src.loc, 'sound/items/tools/welder.ogg', 100, TRUE)
/obj/structure/spacevine/proc/on_entered(datum/source, atom/movable/movable)
SIGNAL_HANDLER
diff --git a/code/modules/events/supermatter_surge.dm b/code/modules/events/supermatter_surge.dm
index c7fb6f969d82b..8d8c50a69f92e 100644
--- a/code/modules/events/supermatter_surge.dm
+++ b/code/modules/events/supermatter_surge.dm
@@ -92,7 +92,7 @@
/datum/round_event/supermatter_surge/announce(fake)
var/class_to_announce = fake ? pick(1, 2, 3, 4) : surge_class
- priority_announce("The Crystal Integrity Monitoring System has detected unusual atmospheric properties in the supermatter chamber, energy output from the supermatter crystal has increased significantly. Engineering intervention is required to stabilize the engine.", "Class [class_to_announce] Supermatter Surge Alert", 'sound/machines/engine_alert3.ogg')
+ priority_announce("The Crystal Integrity Monitoring System has detected unusual atmospheric properties in the supermatter chamber, energy output from the supermatter crystal has increased significantly. Engineering intervention is required to stabilize the engine.", "Class [class_to_announce] Supermatter Surge Alert", 'sound/machines/engine_alert/engine_alert3.ogg')
/datum/round_event/supermatter_surge/start()
engine.bullet_energy = surge_class + SURGE_BULLET_ENERGY_ADDITION
@@ -126,7 +126,7 @@
fakeable = FALSE
/datum/round_event/supermatter_surge/poly/announce(fake)
- priority_announce("The Crystal Integrity Monitoring System has detected unusual parrot type resonance in the supermatter chamber, energy output from the supermatter crystal has increased significantly. Engineering intervention is required to stabilize the engine.", "Class P Supermatter Surge Alert", 'sound/machines/engine_alert3.ogg')
+ priority_announce("The Crystal Integrity Monitoring System has detected unusual parrot type resonance in the supermatter chamber, energy output from the supermatter crystal has increased significantly. Engineering intervention is required to stabilize the engine.", "Class P Supermatter Surge Alert", 'sound/machines/engine_alert/engine_alert3.ogg')
#undef SURGE_DURATION_MIN
#undef SURGE_DURATION_MAX
diff --git a/code/modules/events/wizard/fakeexplosion.dm b/code/modules/events/wizard/fakeexplosion.dm
index 78612ecf863b0..cb7c61823a162 100644
--- a/code/modules/events/wizard/fakeexplosion.dm
+++ b/code/modules/events/wizard/fakeexplosion.dm
@@ -7,5 +7,5 @@
description = "The nuclear explosion cutscene begins to play to scare the crew."
/datum/round_event/wizard/fake_explosion/start()
- sound_to_playing_players('sound/machines/alarm.ogg')
+ sound_to_playing_players('sound/announcer/alarm/nuke_alarm.ogg', 70)
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(play_cinematic), /datum/cinematic/nuke/fake, world), 10 SECONDS)
diff --git a/code/modules/events/wizard/rpgtitles.dm b/code/modules/events/wizard/rpgtitles.dm
index 9bb69a90f2cf4..b78ae483ea687 100644
--- a/code/modules/events/wizard/rpgtitles.dm
+++ b/code/modules/events/wizard/rpgtitles.dm
@@ -38,7 +38,7 @@ GLOBAL_DATUM(rpgtitle_controller, /datum/rpgtitle_controller)
/datum/rpgtitle_controller/proc/on_crewmember_join(datum/source, mob/living/new_crewmember, rank)
SIGNAL_HANDLER
- var/datum/job/job = SSjob.GetJob(rank)
+ var/datum/job/job = SSjob.get_job(rank)
//we must prepare for the mother of all strings
new_crewmember.maptext_height = max(new_crewmember.maptext_height, 32)
diff --git a/code/modules/experisci/destructive_scanner.dm b/code/modules/experisci/destructive_scanner.dm
index d5d87eb631163..c742aaa68c028 100644
--- a/code/modules/experisci/destructive_scanner.dm
+++ b/code/modules/experisci/destructive_scanner.dm
@@ -33,7 +33,7 @@
var/aggressive = FALSE
for(var/mob/living/living_mob in pickup_zone)
if(!(obj_flags & EMAGGED) && ishuman(living_mob)) //Can only kill humans when emagged.
- playsound(src, 'sound/machines/buzz-sigh.ogg', 25)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 25)
say("Cannot scan with humans inside.")
return
aggressive = TRUE
diff --git a/code/modules/experisci/experiment/handlers/experiment_handler.dm b/code/modules/experisci/experiment/handlers/experiment_handler.dm
index bb0cc2fb0a5e3..0eaea94ff5807 100644
--- a/code/modules/experisci/experiment/handlers/experiment_handler.dm
+++ b/code/modules/experisci/experiment/handlers/experiment_handler.dm
@@ -93,7 +93,7 @@
SIGNAL_HANDLER
if ((isnull(selected_experiment) && !(config_flags & EXPERIMENT_CONFIG_ALWAYS_ACTIVE)) || (config_flags & EXPERIMENT_CONFIG_SILENT_FAIL))
return
- playsound(user, 'sound/machines/buzz-sigh.ogg', 25)
+ playsound(user, 'sound/machines/buzz/buzz-sigh.ogg', 25)
to_chat(user, span_notice("[target] is not related to your currently selected experiment."))
/**
@@ -130,7 +130,7 @@
playsound(user, 'sound/machines/ping.ogg', 25)
to_chat(user, span_notice("You scan [target]."))
else if(!(config_flags & EXPERIMENT_CONFIG_SILENT_FAIL))
- playsound(user, 'sound/machines/buzz-sigh.ogg', 25)
+ playsound(user, 'sound/machines/buzz/buzz-sigh.ogg', 25)
to_chat(user, span_notice("[target] is not related to your currently selected experiment."))
/**
@@ -141,7 +141,7 @@
var/atom/movable/our_scanner = parent
if (selected_experiment == null)
if(!(config_flags & EXPERIMENT_CONFIG_SILENT_FAIL))
- playsound(our_scanner, 'sound/machines/buzz-sigh.ogg', 25)
+ playsound(our_scanner, 'sound/machines/buzz/buzz-sigh.ogg', 25)
to_chat(our_scanner, span_notice("No experiment selected!"))
return
var/successful_scan
@@ -153,7 +153,7 @@
playsound(our_scanner, 'sound/machines/ping.ogg', 25)
to_chat(our_scanner, span_notice("The scan succeeds."))
else if(!(config_flags & EXPERIMENT_CONFIG_SILENT_FAIL))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 25)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 25)
our_scanner.say("The scan did not result in anything.")
/// Hooks on a successful autopsy experiment
diff --git a/code/modules/experisci/experiment/types/experiment.dm b/code/modules/experisci/experiment/types/experiment.dm
index add015622f621..358d795f68f59 100644
--- a/code/modules/experisci/experiment/types/experiment.dm
+++ b/code/modules/experisci/experiment/types/experiment.dm
@@ -99,3 +99,9 @@
experiment_handler.selected_experiment = null
var/announcetext = experiment_handler.linked_web.complete_experiment(src)
experiment_handler.announce_message_to_all(announcetext)
+
+/datum/experiment/proc/get_points_reward_text()
+ var/list/english_list_keys = list()
+ for(var/points_type in points_reward)
+ english_list_keys += "[points_reward[points_type]] [points_type]"
+ return "[english_list(english_list_keys)] points"
diff --git a/code/modules/experisci/experiment/types/scanning_fish.dm b/code/modules/experisci/experiment/types/scanning_fish.dm
index f1c2263a84c7e..3ea4f543687ea 100644
--- a/code/modules/experisci/experiment/types/scanning_fish.dm
+++ b/code/modules/experisci/experiment/types/scanning_fish.dm
@@ -64,7 +64,7 @@ GLOBAL_LIST_EMPTY(scanned_fish_by_techweb)
///Only scannable fish will contribute towards the experiment.
/datum/experiment/scanning/fish/final_contributing_index_checks(datum/component/experiment_handler/experiment_handler, obj/item/fish/target, typepath)
- return target.experisci_scannable
+ return target.fish_flags & FISH_FLAG_EXPERIMENT_SCANNABLE
/**
* After a fish scanning experiment is done, more may be unlocked. If so, add them to the techweb
diff --git a/code/modules/explorer_drone/exodrone.dm b/code/modules/explorer_drone/exodrone.dm
index 5754ccc4a5e18..8ba0f64b4a6f6 100644
--- a/code/modules/explorer_drone/exodrone.dm
+++ b/code/modules/explorer_drone/exodrone.dm
@@ -63,12 +63,15 @@ GLOBAL_LIST_EMPTY(exodrone_launchers)
/obj/item/exodrone/Initialize(mapload)
. = ..()
- name = pick(strings(EXODRONE_FILE,"probe_names"))
- if(name_counter[name])
- name_counter[name]++
- name = "[name] \Roman[name_counter[name]]"
+ if(name == /obj/item/exodrone::name)
+ name = pick(strings(EXODRONE_FILE,"probe_names"))
+ if(name_counter[name])
+ name_counter[name]++
+ name = "[name] \Roman[name_counter[name]]"
+ else
+ name_counter[name] = 1
else
- name_counter[name] = 1
+ name = name
GLOB.exodrones += src
// Cargo storage
create_storage(max_slots = EXODRONE_CARGO_SLOTS, canthold = GLOB.blacklisted_cargo_types)
diff --git a/code/modules/fishing/admin.dm b/code/modules/fishing/admin.dm
index ba5c29a7fd172..46212f421283b 100644
--- a/code/modules/fishing/admin.dm
+++ b/code/modules/fishing/admin.dm
@@ -51,12 +51,12 @@ ADMIN_VERB(fishing_calculator, R_DEBUG, "Fishing Calculator", "A calculator... f
temporary_rod.set_slot(new line_type(temporary_rod), ROD_SLOT_LINE)
var/result_table = list()
- var/modified_table = spot.get_modified_fish_table(temporary_rod,user)
+ var/modified_table = spot.get_modified_fish_table(temporary_rod, user, null)
for(var/result_type in spot.fish_table) // through this not modified to display 0 chance ones too
var/list/info = list()
info["result"] = result_type
info["weight"] = modified_table[result_type] || 0
- info["difficulty"] = spot.calculate_difficulty(result_type,temporary_rod, user)
+ info["difficulty"] = spot.calculate_difficulty(result_type, temporary_rod, user) + /datum/fishing_challenge::difficulty
info["count"] = spot.fish_counts[result_type] || "Infinite"
result_table += list(info)
current_table = result_table
diff --git a/code/modules/fishing/aquarium/aquarium.dm b/code/modules/fishing/aquarium/aquarium.dm
index 0d2cd462128d4..56de1a9b10f6a 100644
--- a/code/modules/fishing/aquarium/aquarium.dm
+++ b/code/modules/fishing/aquarium/aquarium.dm
@@ -35,7 +35,7 @@
var/last_feeding
/// Can fish reproduce in this quarium.
- var/allow_breeding = TRUE
+ var/reproduction_and_growth = TRUE
//This is the area where fish can swim
var/aquarium_zone_min_px = 2
@@ -295,7 +295,7 @@
else
dead_fish++
- var/morb = HAS_TRAIT(user, TRAIT_MORBID)
+ var/morb = HAS_MIND_TRAIT(user, TRAIT_MORBID)
//Check if there are live fish - good mood
//All fish dead - bad mood.
//No fish - nothing.
@@ -309,7 +309,7 @@
. = ..()
.["fluidType"] = fluid_type
.["temperature"] = fluid_temp
- .["allowBreeding"] = allow_breeding
+ .["allowBreeding"] = reproduction_and_growth
.["fishData"] = list()
.["feedingInterval"] = feeding_interval / (1 MINUTES)
.["propData"] = list()
@@ -356,8 +356,8 @@
fluid_type = params["fluid"]
SEND_SIGNAL(src, COMSIG_AQUARIUM_FLUID_CHANGED, fluid_type)
. = TRUE
- if("allow_breeding")
- allow_breeding = !allow_breeding
+ if("reproduction_and_growth")
+ reproduction_and_growth = !reproduction_and_growth
. = TRUE
if("feeding_interval")
feeding_interval = params["feeding_interval"] MINUTES
@@ -396,7 +396,7 @@
possible_destinations_for_fish = get_adjacent_open_turfs(droploc)
else
possible_destinations_for_fish = list(droploc)
- playsound(src, 'sound/effects/glassbr3.ogg', 100, TRUE)
+ playsound(src, 'sound/effects/glass/glassbr3.ogg', 100, TRUE)
for(var/atom/movable/fish in contents)
fish.forceMove(pick(possible_destinations_for_fish))
if(fluid_type != AQUARIUM_FLUID_AIR)
diff --git a/code/modules/fishing/aquarium/aquarium_kit.dm b/code/modules/fishing/aquarium/aquarium_kit.dm
index 3257760f6a439..11fd841009d08 100644
--- a/code/modules/fishing/aquarium/aquarium_kit.dm
+++ b/code/modules/fishing/aquarium/aquarium_kit.dm
@@ -122,7 +122,16 @@
/obj/item/aquarium_prop/Initialize(mapload)
. = ..()
- AddComponent(/datum/component/aquarium_content, icon, beauty = beauty)
+ //It's important that we register the signals before the component is attached.
+ RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, PROC_REF(generate_aquarium_appearance))
+ AddComponent(/datum/component/aquarium_content, beauty = beauty)
+ ADD_TRAIT(src, TRAIT_UNIQUE_AQUARIUM_CONTENT, INNATE_TRAIT)
+
+/obj/item/aquarium_prop/proc/generate_aquarium_appearance(datum/source, obj/effect/aquarium/visual)
+ SIGNAL_HANDLER
+ visual.icon = icon
+ visual.icon_state = icon_state
+ visual.layer_mode = layer_mode
/obj/item/aquarium_prop/rocks
name = "decorative rocks"
diff --git a/code/modules/fishing/aquarium/fish_analyzer.dm b/code/modules/fishing/aquarium/fish_analyzer.dm
index 3d01479ef5a2f..905d3549128d5 100644
--- a/code/modules/fishing/aquarium/fish_analyzer.dm
+++ b/code/modules/fishing/aquarium/fish_analyzer.dm
@@ -8,6 +8,7 @@
worn_icon_state = "fish_analyzer"
desc = "A fish-shaped scanner used to monitor fish's status and evolutionary traits."
obj_flags = CONDUCTS_ELECTRICITY
+ custom_price = PAYCHECK_CREW * 3
item_flags = NOBLUDGEON
slot_flags = ITEM_SLOT_BELT
throwforce = 3
@@ -40,7 +41,7 @@
register_item_context()
update_appearance()
-
+ AddComponent(/datum/component/adjust_fishing_difficulty, -3, ITEM_SLOT_HANDS)
/obj/item/fish_analyzer/examine(mob/user)
. = ..()
@@ -127,7 +128,7 @@
"fish_food_color" = fishie.food::color,
"fish_min_temp" = fishie.required_temperature_min,
"fish_max_temp" = fishie.required_temperature_max,
- "fish_hunger" = HAS_TRAIT(fishie, TRAIT_FISH_NO_HUNGER) ? 0 : PERCENT(min((world.time - fishie.last_feeding) / fishie.feeding_frequency, 1)),
+ "fish_hunger" = HAS_TRAIT(fishie, TRAIT_FISH_NO_HUNGER) ? 0 : 1 - fishie.get_hunger(),
"fish_fluid_compatible" = aquarium ? compatible_fluid_type(fishie.required_fluid_type, aquarium.fluid_type) : null,
"fish_fluid_type" = fishie.required_fluid_type,
"fish_breed_timer" = round(max(fishie.breeding_wait - world.time, 0) / 10),
diff --git a/code/modules/fishing/bait.dm b/code/modules/fishing/bait.dm
index 8eb8911a864ed..ec758be704201 100644
--- a/code/modules/fishing/bait.dm
+++ b/code/modules/fishing/bait.dm
@@ -1,6 +1,6 @@
/obj/item/bait_can
name = "can o bait"
- desc = "there's a lot of them in there, getting them out takes a while though"
+ desc = "there's a lot of them in there, getting them out takes a while though."
icon = 'icons/obj/fishing.dmi'
icon_state = "bait_can"
base_icon_state = "bait_can"
@@ -61,21 +61,239 @@
bait_type = /obj/item/food/bait/doughball/synthetic/super
uses_left = 12
+/obj/item/fishing_lure
+ name = "artificial minnow"
+ desc = "A fishing lure meant to attract smaller omnivore fish."
+ icon = 'icons/obj/fishing.dmi'
+ icon_state = "minnow"
+ w_class = WEIGHT_CLASS_SMALL
+ /**
+ * A list with two keys delimiting the spinning interval in which a mouse click has to be pressed while fishing.
+ * This is passed down to the fishing rod, and then to the lure during the minigame.
+ */
+ var/spin_frequency = list(2 SECONDS, 3 SECONDS)
+
+/obj/item/fishing_lure/Initialize(mapload)
+ . = ..()
+ add_traits(list(TRAIT_FISHING_BAIT, TRAIT_BAIT_ALLOW_FISHING_DUD, TRAIT_OMNI_BAIT, TRAIT_BAIT_UNCONSUMABLE), INNATE_TRAIT)
+ RegisterSignal(src, COMSIG_FISHING_EQUIPMENT_SLOTTED, PROC_REF(lure_equipped))
+
+/obj/item/fishing_lure/proc/lure_equipped(datum/source, obj/item/fishing_rod/rod)
+ SIGNAL_HANDLER
+ rod.spin_frequency = spin_frequency
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_removed))
+
+/obj/item/fishing_lure/proc/on_removed(atom/movable/source, obj/item/fishing_rod/rod, dir, forced)
+ SIGNAL_HANDLER
+ UnregisterSignal(src, COMSIG_MOVABLE_MOVED)
+ rod.spin_frequency = null
+
+///Called for every fish subtype by the fishing subsystem when initializing, to populate the list of fish that can be catched with this lure.
+/obj/item/fishing_lure/proc/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
+ var/avg_size = initial(fish_type.average_size)
+ var/intermediate_size = FISH_SIZE_SMALL_MAX + (FISH_SIZE_NORMAL_MAX - FISH_SIZE_SMALL_MAX)
+ if(!ISINRANGE(avg_size, FISH_SIZE_TINY_MAX * 0.5, intermediate_size))
+ return FALSE
+ var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
+ if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater, /datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits))
+ return FALSE
+ return TRUE
+
+/obj/item/fishing_lure/examine(mob/user)
+ . = ..()
+ . += span_info("It has to be spun with a frequency of [spin_frequency[1] * 0.1] to [spin_frequency[2] * 0.1] seconds while fishing.")
+ if(HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT))
+ . += span_tinynotice("Thanks to your experience, you can examine it again to get a list of fish you can catch with it.")
+
+/obj/item/fishing_lure/examine_more(mob/user)
+ . = ..()
+ if(!HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISHING_SPOT))
+ return
+
+ var/list/known_fishes = list()
+ for(var/obj/item/fish/fish_type as anything in SSfishing.lure_catchables[type])
+ if(initial(fish_type.fish_flags) & FISH_FLAG_SHOW_IN_CATALOG)
+ known_fishes += initial(fish_type.name)
+
+ if(!length(known_fishes))
+ return
+
+ . += span_info("You can catch the following fish with this lure: [english_list(known_fishes)].")
+
+///Check if the fish is in the list of catchable fish for this fishing lure. Return value is a multiplier.
+/obj/item/fishing_lure/check_bait(obj/item/fish/fish_type)
+ var/multiplier = 0
+ if(is_type_in_list(/obj/item/fishing_lure, SSfishing.fish_properties[fish_type][FISH_PROPERTIES_FAV_BAIT]))
+ multiplier += 2
+ if(fish_type in SSfishing.lure_catchables[type])
+ multiplier += 10
+ return multiplier
+
+/obj/item/fishing_lure/plug
+ name = "big plug lure"
+ desc = "A fishing lure used to catch larger omnivore fish."
+ icon_state = "plug"
+
+/obj/item/fishing_lure/plug/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
+ var/avg_size = initial(fish_type.average_size)
+ if(avg_size <= FISH_SIZE_SMALL_MAX)
+ return FALSE
+ var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
+ if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater, /datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits))
+ return FALSE
+ return TRUE
+
+/obj/item/fishing_lure/dropping
+ name = "plastic dropping"
+ desc = "A fishing lure to catch all sort of slimy, ratty, disgusting and/or junk-loving fish."
+ icon_state = "dropping"
+ spin_frequency = list(1.5 SECONDS, 2.8 SECONDS)
+
+/obj/item/fishing_lure/dropping/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
+ var/list/sources = list(/datum/fish_source/toilet, /datum/fish_source/moisture_trap)
+ for(var/datum/fish_source/source as anything in sources)
+ var/datum/fish_source/instance = GLOB.preset_fish_sources[/datum/fish_source/toilet]
+ if(fish_type in instance.fish_table)
+ return TRUE
+ var/list/fav_baits = fish_properties[FISH_PROPERTIES_FAV_BAIT]
+ for(var/list/identifier in fav_baits)
+ if(identifier[FISH_BAIT_TYPE] == FISH_BAIT_FOODTYPE && (identifier[FISH_BAIT_VALUE] & (JUNKFOOD|GROSS|TOXIC)))
+ return TRUE
+ if(initial(fish_type.beauty) <= FISH_BEAUTY_DISGUSTING)
+ return TRUE
+ return FALSE
+
+/obj/item/fishing_lure/spoon
+ name = "\improper Indy spoon lure"
+ desc = "A lustrous piece of metal mimicking the scales of a fish. Good for catching small to medium freshwater omnivore fish."
+ icon_state = "spoon"
+ spin_frequency = list(1.25 SECONDS, 2.25 SECONDS)
+
+/obj/item/fishing_lure/spoon/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
+ var/avg_size = initial(fish_type.average_size)
+ if(!ISINRANGE(avg_size, FISH_SIZE_TINY_MAX + 1, FISH_SIZE_NORMAL_MAX))
+ return FALSE
+ var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
+ if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater, /datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits))
+ return FALSE
+ var/fluid_type = initial(fish_type.required_fluid_type)
+ if(fluid_type == AQUARIUM_FLUID_FRESHWATER || fluid_type == AQUARIUM_FLUID_ANADROMOUS || fluid_type == AQUARIUM_FLUID_ANY_WATER)
+ return TRUE
+ if((/datum/fish_trait/amphibious in fish_traits) && fluid_type == AQUARIUM_FLUID_AIR)
+ return TRUE
+ return FALSE
+
+/obj/item/fishing_lure/artificial_fly
+ name = "\improper Silkbuzz artificial fly"
+ desc = "A fishing lure resembling a large wooly fly. Good for catching all sort of picky fish."
+ icon_state = "artificial_fly"
+ spin_frequency = list(1.1 SECONDS, 2 SECONDS)
+
+/obj/item/fishing_lure/artificial_fly/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
+ var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
+ if(/datum/fish_trait/picky_eater in fish_traits)
+ return TRUE
+ return FALSE
+
+/obj/item/fishing_lure/led
+ name = "\improper LED fishing lure"
+ desc = "A heavy, waterproof and fish-looking LED stick, used to catch abyssal and demersal fish alike."
+ icon_state = "led"
+ spin_frequency = list(3 SECONDS, 3.8 SECONDS)
+
+/obj/item/fishing_lure/led/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_BAIT_IGNORE_ENVIRONMENT, INNATE_TRAIT)
+ update_appearance(UPDATE_OVERLAYS)
+
+/obj/item/fishing_lure/led/update_overlays()
+ . = ..()
+ . += emissive_appearance(icon, "led_emissive", src)
+
+/obj/item/fishing_lure/led/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
+ var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
+ if(length(list(/datum/fish_trait/nocturnal, /datum/fish_trait/heavy) & fish_traits))
+ return TRUE
+ return FALSE
+
+/obj/item/fishing_lure/lucky_coin
+ name = "\improper Maneki-Coin lure"
+ desc = "A faux-gold lure used to attract shiny-loving fish."
+ icon_state = "lucky_coin"
+ spin_frequency = list(1.5 SECONDS, 2.7 SECONDS)
+
+/obj/item/fishing_lure/lucky_coin/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
+ var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
+ if(/datum/fish_trait/shiny_lover in fish_traits)
+ return TRUE
+ return FALSE
+
+/obj/item/fishing_lure/algae
+ name = "plastic algae lure"
+ desc = "A soft clump of fake algae used to attract herbivore water critters."
+ icon_state = "algae"
+ spin_frequency = list(3 SECONDS, 5 SECONDS)
+
+/obj/item/fishing_lure/algae/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
+ var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
+ if(/datum/fish_trait/vegan in fish_traits)
+ return TRUE
+ return FALSE
+
+/obj/item/fishing_lure/grub
+ name = "\improper Twister Worm lure"
+ desc = "A soft plastic lure with the body of a grub and a twisting tail. Good for panfish and other small omnivore fish."
+ icon_state = "grub"
+ spin_frequency = list(1 SECONDS, 2.7 SECONDS)
+
+/obj/item/fishing_lure/grub/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
+ if(initial(fish_type.average_size) >= FISH_SIZE_SMALL_MAX)
+ return FALSE
+ var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
+ if(length(list(/datum/fish_trait/vegan, /datum/fish_trait/picky_eater) & fish_traits))
+ return FALSE
+ return TRUE
+
+/obj/item/fishing_lure/buzzbait
+ name = "\improper Electric-Buzz lure"
+ desc = "A metallic, colored clanked attached to a series of cables that somehow attract shock-worthy fish."
+ icon_state = "buzzbait"
+ spin_frequency = list(0.8 SECONDS, 1.7 SECONDS)
+
+/obj/item/fishing_lure/buzzbait/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
+ var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
+ if(/datum/fish_trait/electrogenesis in fish_traits)
+ return TRUE
+ return FALSE
+
+/obj/item/fishing_lure/spinnerbait
+ name = "spinnerbait lure"
+ desc = "A versatile lure, good for catching all sort of predatory freshwater fish."
+ icon_state = "spinnerbait"
+ spin_frequency = list(2 SECONDS, 4 SECONDS)
+
+/obj/item/fishing_lure/spinnerbait/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
+ var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
+ if(!(/datum/fish_trait/predator in fish_traits))
+ return FALSE
+ var/init_fluid_type = initial(fish_type.required_fluid_type)
+ if(init_fluid_type == AQUARIUM_FLUID_FRESHWATER || init_fluid_type == AQUARIUM_FLUID_ANADROMOUS || init_fluid_type == AQUARIUM_FLUID_ANY_WATER)
+ return TRUE
+ if((/datum/fish_trait/amphibious in fish_traits) && init_fluid_type == AQUARIUM_FLUID_AIR) //fluid type is changed to freshwater on init
+ return TRUE
+ return FALSE
-/// Helper proc that checks if a bait matches identifier from fav/disliked bait list
-/proc/is_matching_bait(obj/item/bait, identifier)
- if(ispath(identifier)) //Just a path
- return istype(bait, identifier)
- if(islist(identifier))
- var/list/special_identifier = identifier
- switch(special_identifier["Type"])
- if("Foodtype")
- var/obj/item/food/food_bait = bait
- return istype(food_bait) && food_bait.foodtypes & special_identifier["Value"]
- if("Reagent")
- return bait.reagents?.has_reagent(special_identifier["Value"], special_identifier["Amount"], check_subtypes = TRUE)
- else
- CRASH("Unknown bait identifier in fish favourite/disliked list")
- else
- return HAS_TRAIT(bait, identifier)
+/obj/item/fishing_lure/daisy_chain
+ name = "daisy chain lure"
+ desc = "A lure resembling a small school of fish, good for catching several saltwater predators."
+ icon_state = "daisy_chain"
+ spin_frequency = list(2 SECONDS, 4 SECONDS)
+/obj/item/fishing_lure/daisy_chain/is_catchable_fish(obj/item/fish/fish_type, list/fish_properties)
+ var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
+ if(!(/datum/fish_trait/predator in fish_traits))
+ return FALSE
+ var/init_fluid_type = initial(fish_type.required_fluid_type)
+ if(init_fluid_type == AQUARIUM_FLUID_SALTWATER || init_fluid_type == AQUARIUM_FLUID_ANADROMOUS || init_fluid_type == AQUARIUM_FLUID_ANY_WATER)
+ return TRUE
+ return FALSE
diff --git a/code/modules/fishing/fish/_fish.dm b/code/modules/fishing/fish/_fish.dm
index ef5ab1572d841..5b7cceb72b196 100644
--- a/code/modules/fishing/fish/_fish.dm
+++ b/code/modules/fishing/fish/_fish.dm
@@ -1,12 +1,11 @@
#define FISH_SAD 0
-#define FISH_NEUTRAL 1
-#define FISH_SATISFIED 2
-#define FISH_HAPPY 3
#define FISH_VERY_HAPPY 4
+#define GET_FISH_ELECTROGENESIS(fish) (fish.electrogenesis_power * fish.size * 0.1)
+
// Fish path used for autogenerated fish
/obj/item/fish
- name = "generic looking aquarium fish"
+ name = "fish"
desc = "very bland"
icon = 'icons/obj/aquarium/fish.dmi'
lefthand_file = 'icons/mob/inhands/fish_lefthand.dmi'
@@ -17,14 +16,15 @@
attack_verb_continuous = list("slaps", "whacks")
attack_verb_simple = list("slap", "whack")
hitsound = SFX_DEFAULT_FISH_SLAP
- drop_sound = 'sound/creatures/fish/fish_drop1.ogg'
+ drop_sound = 'sound/mobs/non-humanoids/fish/fish_drop1.ogg'
pickup_sound = SFX_FISH_PICKUP
sound_vary = TRUE
- ///The grind results of the fish. They scale with the weight of the fish.
- grind_results = list(/datum/reagent/blood = 5, /datum/reagent/consumable/liquidgibs = 5)
obj_flags = UNIQUE_RENAME
item_flags = IMMUTABLE_SLOW|SLOWS_WHILE_IN_HAND
+ /// Flags for fish variables that would otherwise be TRUE/FALSE
+ var/fish_flags = FISH_FLAG_SHOW_IN_CATALOG|FISH_DO_FLOP_ANIM|FISH_FLAG_EXPERIMENT_SCANNABLE
+
/// width of aquarium visual icon
var/sprite_width
/// height of aquarium visual icon
@@ -52,23 +52,19 @@
var/datum/reagent/food = /datum/reagent/consumable/nutriment
/// How often the fish needs to be fed
var/feeding_frequency = 5 MINUTES
- /// Time of last feedeing
+ /// Time of last the fish was fed
var/last_feeding
/// Fish status
var/status = FISH_ALIVE
///icon used when the fish is dead, ifset.
var/icon_state_dead
- ///If this fish should do the flopping animation
- var/do_flop_animation = TRUE
/// Current fish health. Dies at 0.
var/health = 100
/// The message shown when the fish dies.
var/death_text = "%SRC dies."
- /// Should this fish type show in fish catalog
- var/show_in_catalog = TRUE
/// How rare this fish is in the random cases
var/random_case_rarity = FISH_RARITY_BASIC
@@ -85,6 +81,8 @@
var/breeding_timeout = 2 MINUTES
/// If set, the fish can also breed with these fishes types
var/list/compatible_types
+ /// If set, when procreating these are the types of fish that will be generate instead of 'type'
+ var/list/spawn_types
/// A list of possible evolutions. If set, offsprings may be of a different, new fish type if conditions are met.
var/list/evolution_types
@@ -118,11 +116,15 @@
var/size
/// Average size for this fish type in centimeters. Will be used as gaussian distribution with 20% deviation for fishing, bought fish are always standard size
var/average_size = 50
+ /// The maximum size this fish can reach, calculated the first time update_size_and_weight() is called.
+ var/maximum_size
/// Weight in grams. Null until update_size_and_weight is called. Grind results scale with it. Don't think too hard how a trout could fit in a blender.
var/weight
/// Average weight for this fish type in grams
var/average_weight = 1000
+ /// The maximum weight this fish can reach, calculated the first time update_size_and_weight() is called.
+ var/maximum_weight
///The general deviation from the average weight and size this fish has in the wild
var/weight_size_deviation = 0.2
@@ -138,27 +140,37 @@
var/min_pressure = WARNING_LOW_PRESSURE
var/max_pressure = HAZARD_HIGH_PRESSURE
- /// If this fish type counts towards the Fish Species Scanning experiments
- var/experisci_scannable = TRUE
/// cooldown on creating tesla zaps
COOLDOWN_DECLARE(electrogenesis_cooldown)
- /// power of the tesla zap created by the fish in a bioelectric generator
- var/electrogenesis_power = 10 MEGA JOULES
+ /// power of the tesla zap created by the fish in a bioelectric generator. Scales with size.
+ var/electrogenesis_power = 2 MEGA JOULES
/// The beauty this fish provides to the aquarium it's inserted in.
var/beauty = FISH_BEAUTY_GENERIC
- ///have we recently pet this fish
- var/recently_petted = FALSE
+
+ /**
+ * If you wonder why this isn't being tracked by the edible component instead:
+ * We reset the this value when revived, and slowly chip it away as we heal.
+ * Of course, it would be daunting to get this to be handled by the edible component
+ * given its complexity.
+ */
+ var/bites_amount = 0
/obj/item/fish/Initialize(mapload, apply_qualities = TRUE)
. = ..()
- AddComponent(/datum/component/aquarium_content, icon, PROC_REF(get_aquarium_animation), list(COMSIG_FISH_STIRRED), beauty)
+ base_icon_state = icon_state
+ //It's important that we register the signals before the component is attached.
+ RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_DO_ANIMATION, PROC_REF(update_aquarium_animation))
+ RegisterSignal(src, AQUARIUM_CONTENT_RANDOMIZE_POSITION, PROC_REF(randomize_aquarium_position))
+ RegisterSignal(src, COMSIG_AQUARIUM_CONTENT_GENERATE_APPEARANCE, PROC_REF(update_aquarium_appearance))
+ AddComponent(/datum/component/aquarium_content, list(COMSIG_FISH_STIRRED), beauty)
RegisterSignal(src, COMSIG_ATOM_ON_LAZARUS_INJECTOR, PROC_REF(use_lazarus))
- if(do_flop_animation)
+ if(fish_flags & FISH_DO_FLOP_ANIM)
RegisterSignal(src, COMSIG_ATOM_TEMPORARY_ANIMATION_START, PROC_REF(on_temp_animation))
- check_environment()
+ check_flopping()
if(status != FISH_DEAD)
+ ADD_TRAIT(src, TRAIT_UNCOMPOSTABLE, REF(src)) //Composting a food that is not real food wouldn't work anyway.
START_PROCESSING(SSobj, src)
//stops new fish from being able to reproduce right away.
@@ -170,44 +182,260 @@
update_size_and_weight()
register_evolutions()
+ register_context()
+ register_item_context()
+
+/obj/item/fish/add_item_context(atom/source, list/context, obj/item/held_item, mob/user)
+ if(HAS_TRAIT(source, TRAIT_CATCH_AND_RELEASE))
+ context[SCREENTIP_CONTEXT_RMB] = "Release"
+ return CONTEXTUAL_SCREENTIP_SET
+ return NONE
+
+/obj/item/fish/add_context(atom/source, list/context, obj/item/held_item, mob/user)
+ if(src == held_item)
+ context[SCREENTIP_CONTEXT_LMB] = "Pet"
+ return CONTEXTUAL_SCREENTIP_SET
+ if(istype(held_item, /obj/item/fish_feed))
+ context[SCREENTIP_CONTEXT_LMB] = "Feed"
+ return CONTEXTUAL_SCREENTIP_SET
+ if(istype(held_item, /obj/item/fish_analyzer))
+ context[SCREENTIP_CONTEXT_LMB] = "Scan"
+ return CONTEXTUAL_SCREENTIP_SET
+ return NONE
+
+/obj/item/fish/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!HAS_TRAIT(interacting_with, TRAIT_CATCH_AND_RELEASE))
+ return NONE
+ if(HAS_TRAIT(src, TRAIT_NODROP))
+ balloon_alert(user, "it's stuck to your hand!")
+ return ITEM_INTERACT_BLOCKING
+ balloon_alert(user, "releasing fish...")
+ if(!do_after(src, 3 SECONDS, interacting_with))
+ return ITEM_INTERACT_BLOCKING
+ balloon_alert(user, "fish released")
+ var/goodbye_text = "Bye bye [name]."
+ if(status == FISH_DEAD && !HAS_MIND_TRAIT(user, TRAIT_NAIVE))
+ goodbye_text = "May it rest in peace..."
+ user.visible_message(span_notice("[user] releases [src] into [interacting_with]"), \
+ span_notice("You release [src] into [interacting_with]. [goodbye_text]"), \
+ span_notice("You hear a splash."))
+ playsound(interacting_with, 'sound/effects/splash.ogg', 50)
+ SEND_SIGNAL(interacting_with, COMSIG_FISH_RELEASED_INTO, src)
+ qdel(src)
+ return ITEM_INTERACT_SUCCESS
+
+///Main proc that makes the fish edible.
+/obj/item/fish/proc/make_edible()
+ var/foodtypes = get_food_types()
+ if(foodtypes & RAW)
+ AddComponent(/datum/component/infective, GLOB.floor_diseases.Copy(), weak = TRUE, weak_infection_chance = PERFORM_ALL_TESTS(edible_fish) ? 100 : 15)
+ else
+ AddComponent(/datum/component/germ_sensitive)
+ var/bites_to_finish = weight / FISH_WEIGHT_BITE_DIVISOR
+ create_reagents(INFINITY) //We'll set this to the total volume of the reagents right after generate_fish_reagents() is over
+ generate_fish_reagents(bites_to_finish)
+ reagents.maximum_volume = round(reagents.total_volume * 1.25) //make some meager space for condiments.
+ AddComponent(/datum/component/edible, \
+ food_flags = FOOD_NO_EXAMINE|FOOD_NO_BITECOUNT, \
+ foodtypes = foodtypes, \
+ volume = reagents.total_volume, \
+ eat_time = 1.5 SECONDS, \
+ bite_consumption = reagents.total_volume / bites_to_finish, \
+ after_eat = CALLBACK(src, PROC_REF(after_eat)), \
+ check_liked = CALLBACK(src, PROC_REF(check_liked)), \
+ reagent_purity = 1, \
+ )
+ RegisterSignals(src, list(COMSIG_ITEM_FRIED, COMSIG_ITEM_BARBEQUE_GRILLED), PROC_REF(on_fish_cooked))
+
+///A proc that returns the food types the edible component has when initialized.
+/obj/item/fish/proc/get_food_types()
+ return SEAFOOD|MEAT|RAW|GORE
+
+///Kill the fish, remove the raw and gore food types, and the infectiveness too if not under-cooked.
+/obj/item/fish/proc/on_fish_cooked(datum/source, cooking_time)
+ SIGNAL_HANDLER
+ SHOULD_NOT_OVERRIDE(TRUE)
+ adjust_health(0)
+
+ //Remove the blood from the reagents holder and reward the player with some extra nutriment added to the fish.
+ var/datum/reagent/consumable/nutriment/protein/protein = reagents.has_reagent(/datum/reagent/consumable/nutriment/protein, check_subtypes = TRUE)
+ var/datum/reagent/blood/blood = reagents.has_reagent(/datum/reagent/blood)
+ var/old_blood_volume = blood ? blood.volume : 0 //we can't use the ?. operator since the above proc doesn't return null but 0
+ reagents.del_reagent(/datum/reagent/blood)
+
+ ///Make space for the additional nutriment
+ if(blood || protein)
+ var/volume_mult = 1
+ var/protein_volume = protein ? protein.volume : 0
+ if(bites_amount)
+ var/initial_bites_left = weight / FISH_WEIGHT_BITE_DIVISOR
+ var/bites_left = initial_bites_left - bites_amount
+ volume_mult = initial_bites_left / bites_left
+ adjust_reagents_capacity((protein_volume - old_blood_volume) * volume_mult)
+ ///Add the extra nutriment
+ if(protein)
+ reagents.multiply_single_reagent(/datum/reagent/consumable/nutriment/protein, 2)
+
+ var/datum/component/edible/edible = GetComponent(/datum/component/edible)
+ edible.foodtypes &= ~(RAW|GORE)
+ if(cooking_time >= FISH_SAFE_COOKING_DURATION)
+ well_cooked()
+
+ ///override the signals so they don't mess with blood and proteins again.
+ RegisterSignals(src, list(COMSIG_ITEM_FRIED, COMSIG_ITEM_BARBEQUE_GRILLED), PROC_REF(on_fish_cooked_again), TRUE)
+
+///Just kill the fish, again, and perhaps remove the infective comp.
+/obj/item/fish/proc/on_fish_cooked_again(datum/source, cooking_time)
+ SIGNAL_HANDLER
+ if(!HAS_TRAIT(src, TRAIT_FISH_SURVIVE_COOKING))
+ adjust_health(0)
+ if(cooking_time >= FISH_SAFE_COOKING_DURATION)
+ well_cooked()
+
+///The fish is well cooked. Change how the fish tastes, remove the infective comp and add the relative trait.
+/obj/item/fish/proc/well_cooked()
+ qdel(GetComponent(/datum/component/infective))
+ AddComponent(/datum/component/germ_sensitive)
+ ADD_TRAIT(src, TRAIT_FISH_WELL_COOKED, INNATE_TRAIT)
+ var/datum/reagent/consumable/nutriment/protein/protein = reagents.has_reagent(/datum/reagent/consumable/nutriment/protein, check_subtypes = TRUE)
+ if(protein)
+ protein.data = get_fish_taste_cooked()
+
+///Checks if the fish is liked or not when eaten by a human.
+/obj/item/fish/proc/check_liked(mob/living/eater)
+ if(HAS_TRAIT(eater, TRAIT_PACIFISM) && (status == FISH_ALIVE ||HAS_MIND_TRAIT(eater, TRAIT_NAIVE)))
+ eater.add_mood_event("eating_fish", /datum/mood_event/pacifist_eating_fish_item)
+ return FOOD_TOXIC
+ if(HAS_TRAIT(eater, TRAIT_AGEUSIA))
+ return
+ if(HAS_TRAIT(eater, TRAIT_FISH_EATER) && !HAS_TRAIT(eater, TRAIT_VEGETARIAN))
+ return FOOD_LIKED
+
+/**
+ * Fish is not a reagent holder yet it's edible, so it doen't behave like most other snacks
+ * which means it has its own way of handling being bitten, which is defined here.
+ */
+/obj/item/fish/proc/after_eat(mob/living/eater, mob/living/feeder)
+ SHOULD_CALL_PARENT(TRUE)
+ if(!reagents.total_volume)
+ return
+ bites_amount++
+ var/bites_to_finish = weight / FISH_WEIGHT_BITE_DIVISOR
+ adjust_health(health - (initial(health) / bites_to_finish) * 3)
+ if(status == FISH_ALIVE && prob(50) && feeder.is_holding(src) && feeder.dropItemToGround(src))
+ to_chat(feeder, span_warning("[src] slips out of your hands in pain!"))
+ var/turf/target_turf = get_ranged_target_turf(get_turf(src), pick(GLOB.alldirs), 2)
+ throw_at(target_turf)
+
+///A proc that returns a static reagent holder with a set reagents that you'd get when eating this fish.
+/obj/item/fish/proc/generate_fish_reagents(multiplier = 1)
+ SHOULD_NOT_OVERRIDE(TRUE)
+ var/list/reagents_to_add = get_base_edible_reagents_to_add()
+ SEND_SIGNAL(src, COMSIG_GENERATE_REAGENTS_TO_ADD, reagents_to_add)
+ if(multiplier != 1)
+ for(var/reagent in reagents_to_add)
+ reagents_to_add[reagent] *= multiplier
+ reagents.add_reagent_list(reagents_to_add, added_purity = 1)
+ var/datum/reagent/consumable/nutriment/protein/protein = reagents.has_reagent(/datum/reagent/consumable/nutriment/protein, check_subtypes = TRUE)
+ if(protein)
+ protein.data = HAS_TRAIT(src, TRAIT_FISH_WELL_COOKED) ? get_fish_taste_cooked() : get_fish_taste()
+
+/obj/item/fish/proc/get_fish_taste()
+ return list("raw fish" = 2.5, "scales" = 1)
+
+/obj/item/fish/proc/get_fish_taste_cooked()
+ return list("cooked fish" = 2)
+
+///The proc that adds in the main reagents this fish has when eaten (without accounting for traits)
+/obj/item/fish/proc/get_base_edible_reagents_to_add()
+ var/return_list = list(
+ /datum/reagent/consumable/nutriment/protein = 2,
+ /datum/reagent/blood = 1,
+ )
+ //It has been at the very least under-cooked.
+ if(HAS_TRAIT(src, TRAIT_FOOD_FRIED) || HAS_TRAIT(src, TRAIT_FOOD_BBQ_GRILLED))
+ return_list[/datum/reagent/consumable/nutriment/protein] *= 2
+ return_list -= /datum/reagent/blood
+ if(required_fluid_type == AQUARIUM_FLUID_SALTWATER)
+ return_list[/datum/reagent/consumable/salt] = 0.4
+ return return_list
+
+///adjusts the maximum volume of the fish reagents holder and update the amount of food to bite
+/obj/item/fish/proc/adjust_reagents_capacity(amount_to_add)
+ if(!reagents)
+ return
+ reagents.maximum_volume += amount_to_add
+ var/bites_to_finish = weight / FISH_WEIGHT_BITE_DIVISOR
+ ///updates how many units of reagent one bite takes if edible.
+ if(IS_EDIBLE(src))
+ AddComponent(/datum/component/edible, bite_consumption = reagents.maximum_volume / bites_to_finish)
+
+///Grinding a fish replaces some the protein it has with blood and gibs. You ain't getting a clean smoothie out of it.
+/obj/item/fish/on_grind()
+ . = ..()
+ if(!reagents)
+ return
+ reagents.convert_reagent(/datum/reagent/consumable/nutriment/protein, /datum/reagent/consumable/liquidgibs, 0.4, include_source_subtypes = TRUE)
+ reagents.convert_reagent(/datum/reagent/consumable/nutriment/protein, /datum/reagent/blood, 0.2, include_source_subtypes = TRUE)
+
+///When processed, the reagents inside this fish will be passed to the created atoms.
+/obj/item/fish/UsedforProcessing(mob/living/user, obj/item/used_item, list/chosen_option, list/created_atoms)
+ var/created_len = length(created_atoms)
+ for(var/atom/movable/created as anything in created_atoms)
+ if(!created.reagents)
+ continue
+ for(var/datum/reagent/reagent as anything in reagents.reagent_list)
+ var/transfer_vol = reagent.volume / created_len
+ var/datum/reagent/result_reagent = created.reagents.has_reagent(reagent.type)
+ if(!result_reagent)
+ created.reagents.add_reagent(reagent.type, transfer_vol, reagents.copy_data(reagent), reagents.chem_temp, reagent.purity, reagent.ph, no_react = TRUE)
+ continue
+ var/multiplier = transfer_vol / result_reagent.volume
+ created.reagents.multiply_single_reagent(reagent.type, multiplier)
+ return ..()
/obj/item/fish/update_icon_state()
if(status == FISH_DEAD && icon_state_dead)
icon_state = icon_state_dead
else
- icon_state = initial(icon_state)
+ icon_state = base_icon_state
return ..()
/obj/item/fish/attackby(obj/item/item, mob/living/user, params)
if(!istype(item, /obj/item/fish_feed))
return ..()
if(!item.reagents.total_volume)
- balloon_alert(user, "[item] is empty!")
+ balloon_alert(user, "[item.name] is empty!")
return TRUE
if(status == FISH_DEAD)
- balloon_alert(user, "[src] is dead!")
+ balloon_alert(user, "[name] [HAS_MIND_TRAIT(user, TRAIT_NAIVE) ? "isn't hungry" : "is dead!"]")
return TRUE
feed(item.reagents)
- balloon_alert(user, "fed [src]")
+ balloon_alert(user, "fed [name]")
return TRUE
/obj/item/fish/examine(mob/user)
. = ..()
if(HAS_MIND_TRAIT(user, TRAIT_EXAMINE_DEEPER_FISH))
if(status == FISH_DEAD)
- . += span_deadsay("it's dead.")
- var/list/warnings = list()
- if(is_hungry())
- warnings += "starving"
- if(!HAS_TRAIT(src, TRAIT_FISH_STASIS) && !proper_environment())
- warnings += "drowning"
- if(health < initial(health) * 0.6)
- warnings += "sick"
- if(length(warnings))
- . += span_warning("it's [english_list(warnings)]")
+ . += span_deadsay("It's [HAS_MIND_TRAIT(user, TRAIT_NAIVE) ? "taking the big snooze" : "dead"].")
+ else
+ var/list/warnings = list()
+ if(is_starving())
+ warnings += "starving"
+ if(!HAS_TRAIT(src, TRAIT_FISH_STASIS) && !proper_environment())
+ warnings += "drowning"
+ if(health < initial(health) * 0.6)
+ warnings += "sick"
+ if(length(warnings))
+ . += span_warning("It's [english_list(warnings)].")
if(HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FISH))
. += span_notice("It's [size] cm long.")
. += span_notice("It weighs [weight] g.")
+ if(HAS_TRAIT(src, TRAIT_FISHING_BAIT))
+ . += span_smallnoticeital("It can be used as a fishing bait.")
+ if(bites_amount)
+ . += span_warning("It's been bitten by someone.")
///Randomizes weight and size.
/obj/item/fish/proc/randomize_size_and_weight(base_size = average_size, base_weight = average_weight, deviation = weight_size_deviation)
@@ -223,11 +451,13 @@
/obj/item/fish/proc/update_size_and_weight(new_size = average_size, new_weight = average_weight)
SEND_SIGNAL(src, COMSIG_FISH_UPDATE_SIZE_AND_WEIGHT, new_size, new_weight)
if(size)
- if(fillet_type)
- RemoveElement(/datum/element/processable, TOOL_KNIFE, fillet_type, num_fillets, 0.5 SECONDS * num_fillets, screentip_verb = "Cut")
+ remove_fillet_type()
if(size > FISH_SIZE_TWO_HANDS_REQUIRED)
qdel(GetComponent(/datum/component/two_handed))
+ else
+ maximum_size = min(new_size * 2, average_size * MAX_FISH_DEVIATION_COEFF)
size = new_size
+
var/init_icon_state = initial(inhand_icon_state)
switch(size)
if(0 to FISH_SIZE_TINY_MAX)
@@ -255,21 +485,39 @@
inhand_icon_state = "fish_huge"
update_weight_class(WEIGHT_CLASS_GIGANTIC)
- if(size > FISH_SIZE_TWO_HANDS_REQUIRED)
+ if(size > FISH_SIZE_TWO_HANDS_REQUIRED || (HAS_TRAIT(src, TRAIT_FISH_SHOULD_TWOHANDED) && w_class >= WEIGHT_CLASS_BULKY))
inhand_icon_state = "[inhand_icon_state]_wielded"
AddComponent(/datum/component/two_handed, require_twohands = TRUE)
- if(fillet_type)
- var/init_fillets = initial(num_fillets)
- var/amount = max(round(init_fillets * size / FISH_FILLET_NUMBER_SIZE_DIVISOR, 1), 1)
- num_fillets = amount
- AddElement(/datum/element/processable, TOOL_KNIFE, fillet_type, num_fillets, 0.5 SECONDS * num_fillets, screentip_verb = "Cut")
+ add_fillet_type()
+ var/make_edible = !weight
if(weight)
for(var/reagent_type in grind_results)
- grind_results[reagent_type] /= FLOOR(weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR, 0.1)
+ grind_results[reagent_type] /= max(FLOOR(weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR, 0.1), 0.1)
+ if(reagents) //This fish has reagents. Adjust the maximum volume of the reagent holder and do some math to adjut the reagents too.
+ var/new_weight_ratio = new_weight / weight
+ var/volume_diff = reagents.maximum_volume * new_weight_ratio - reagents.maximum_volume
+ if(new_weight_ratio > weight)
+ adjust_reagents_capacity(volume_diff)
+ ///As always, we want to maintain proportions here, so we need to get the ratio of bites left and initial bites left.
+ var/weight_diff = new_weight - weight
+ var/multiplier = weight_diff / FISH_WEIGHT_BITE_DIVISOR
+ var/initial_bites_left = weight / FISH_WEIGHT_BITE_DIVISOR
+ var/bites_left = initial_bites_left - bites_amount
+ var/amount_to_gen = bites_left / initial_bites_left * multiplier
+ generate_fish_reagents(amount_to_gen)
+ else
+ reagents.multiply_reagents(new_weight_ratio)
+ adjust_reagents_capacity(volume_diff)
+ else
+ maximum_weight = min(new_weight * 2, new_weight * MAX_FISH_DEVIATION_COEFF)
+
weight = new_weight
+ if(make_edible)
+ make_edible()
+
if(weight >= FISH_WEIGHT_SLOWDOWN)
slowdown = round(((weight/FISH_WEIGHT_SLOWDOWN_DIVISOR)**FISH_WEIGHT_SLOWDOWN_EXPONENT)-1.3, 0.1)
drag_slowdown = round(slowdown * 0.5, 1)
@@ -281,10 +529,33 @@
mob.update_equipment_speed_mods()
for(var/reagent_type in grind_results)
- grind_results[reagent_type] *= FLOOR(weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR, 0.1)
+ grind_results[reagent_type] *= max(FLOOR(weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR, 0.1), 0.1)
update_fish_force()
+/obj/item/fish/proc/remove_fillet_type()
+ if(!fillet_type)
+ return
+ var/amount = max(round(num_fillets * size / FISH_FILLET_NUMBER_SIZE_DIVISOR, 1), 1)
+ var/time = PERFORM_ALL_TESTS(fish_size_weight) ? 0 : 0.5 SECONDS * amount
+ RemoveElement(/datum/element/processable, TOOL_KNIFE, fillet_type, amount, time, screentip_verb = "Cut")
+
+/obj/item/fish/proc/add_fillet_type()
+ if(!fillet_type)
+ return
+ var/amount = max(round(num_fillets * size / FISH_FILLET_NUMBER_SIZE_DIVISOR, 1), 1)
+ var/time = PERFORM_ALL_TESTS(fish_size_weight) ? 0 : 0.5 SECONDS * amount
+ AddElement(/datum/element/processable, TOOL_KNIFE, fillet_type, amount, time, screentip_verb = "Cut")
+ return amount //checked by a unit test
+
+/**
+ * Weight, unlike size, is a bit more exponential, but the world isn't perfect, so isn't my code.
+ * Anyway, this returns a gross estimate of the "rank" of "category" for our fish weight, based on how
+ * weight generaly scales up (250, 500, 1000, 2000, 4000 etc...)
+ */
+/obj/item/fish/proc/get_weight_rank()
+ return max(round(1 + log(2, weight/FISH_WEIGHT_FORCE_DIVISOR), 1), 1)
+
///Reset weapon-related variables of this items and recalculates those values based on the fish weight and size.
/obj/item/fish/proc/update_fish_force()
if(force >= 15 && hitsound == SFX_ALT_FISH_SLAP)
@@ -304,7 +575,7 @@
bare_wound_bonus = initial(bare_wound_bonus)
toolspeed = initial(toolspeed)
- var/weight_rank = max(round(1 + log(2, weight/FISH_WEIGHT_FORCE_DIVISOR), 1), 1)
+ var/weight_rank = get_weight_rank()
throw_range -= weight_rank
get_force_rank()
@@ -317,6 +588,14 @@
SEND_SIGNAL(src, COMSIG_FISH_FORCE_UPDATED, weight_rank, bonus_malus)
+ if(material_flags & MATERIAL_EFFECTS) //struck by metal gen or something.
+ for(var/current_material in custom_materials)
+ var/datum/material/material = GET_MATERIAL_REF(current_material)
+ force *= material.strength_modifier
+ throwforce *= material.strength_modifier
+ if(material.item_sound_override)
+ hitsound = material.item_sound_override
+
if(force >=15 && hitsound == SFX_DEFAULT_FISH_SLAP) // don't override special attack sounds
hitsound = SFX_ALT_FISH_SLAP // do more damage - do heavier slap sound
@@ -359,7 +638,7 @@
fish_traits = fixed_traits?.Copy() || list()
var/list/same_traits = x_traits & y_traits
- var/list/all_traits = (x_traits|y_traits)-removed_traits
+ var/list/all_traits = (y_traits ? (x_traits|y_traits) : x_traits) - removed_traits
/// a list of incompatible traits that'll be filled as it goes on. Don't let any such trait pass onto the fish.
var/list/incompatible_traits = list()
@@ -388,11 +667,16 @@
if(trait_type in incompatible_traits)
continue
var/datum/fish_trait/trait = GLOB.fish_traits[trait_type]
+ if(isnull(trait))
+ stack_trace("Couldn't find trait [trait_type || "null"] in the global fish traits list")
+ continue
if(!isnull(trait.fish_whitelist) && !(type in trait.fish_whitelist))
continue
if(length(fish_traits & trait.incompatible_traits))
continue
- if((trait_type in same_traits) ? prob(trait.inheritability) : prob(trait.diff_traits_inheritability))
+ // If there's no partner, we've been reated through parthenogenesis or growth, therefore, traits are copied
+ // Otherwise, we do some probability checks.
+ if(!y_traits || ((trait_type in same_traits) ? prob(trait.inheritability) : prob(trait.diff_traits_inheritability)))
fish_traits |= trait_type
incompatible_traits |= trait.incompatible_traits
@@ -410,7 +694,7 @@
/obj/item/fish/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
. = ..()
- check_environment()
+ check_flopping()
/obj/item/fish/proc/enter_stasis()
ADD_TRAIT(src, TRAIT_FISH_STASIS, INNATE_TRAIT)
@@ -423,6 +707,15 @@
if(status != FISH_DEAD)
START_PROCESSING(SSobj, src)
+///Returns the 0-1 value for hunger
+/obj/item/fish/proc/get_hunger()
+ . = CLAMP01((world.time - last_feeding) / feeding_frequency)
+ if(HAS_TRAIT(src, TRAIT_FISH_NO_HUNGER))
+ return min(., 0.2)
+
+/obj/item/fish/proc/is_starving()
+ return get_hunger() >= 1
+
///Feed the fishes with the contents of the fish feed
/obj/item/fish/proc/feed(datum/reagents/fed_reagents)
if(status != FISH_ALIVE)
@@ -430,7 +723,7 @@
var/fed_reagent_type
if(fed_reagents.remove_reagent(food, 0.1))
fed_reagent_type = food
- last_feeding = world.time
+ sate_hunger()
else
var/datum/reagent/wrong_reagent = pick(fed_reagents.reagent_list)
if(!wrong_reagent)
@@ -439,11 +732,53 @@
fed_reagents.remove_reagent(fed_reagent_type, 0.1)
SEND_SIGNAL(src, COMSIG_FISH_FED, fed_reagents, fed_reagent_type)
-/obj/item/fish/proc/check_environment()
+/**
+ * Base multiplier of the difference between current size and weight and their maximum value
+ * Used to calculate how much fish grow each time they're fed, alongside with the current hunger,
+ * and the current size and weight, meaning bigger fish naturally tend to grow way more slowly
+ * Growth peaks at 45% hunger but very rapidly wanes past that.
+ */
+#define FISH_GROWTH_MULT 0.38
+#define FISH_GROWTH_PEAK 0.45
+#define FISH_SIZE_WEIGHT_GROWTH_MALUS 0.5
+
+///Proc that should be called when the fish is fed. By default, it grows the fish depending on various variables.
+/obj/item/fish/proc/sate_hunger()
+ if(isaquarium(loc))
+ var/obj/structure/aquarium/aquarium = loc
+ if(!aquarium.reproduction_and_growth)
+ return
+ var/hunger = get_hunger()
+ if(hunger < 0.05) //don't bother growing for very small amounts.
+ return
+ last_feeding = world.time
+ var/new_size = size
+ var/new_weight = weight
+ var/hunger_mult
+ if(hunger < FISH_GROWTH_PEAK)
+ hunger_mult = hunger * (1/FISH_GROWTH_PEAK)
+ else
+ hunger_mult = 1 - (hunger - FISH_GROWTH_PEAK) * 4
+ if(hunger_mult <= 0)
+ return
+ if(size < maximum_size)
+ new_size += CEILING((maximum_size - size) * FISH_GROWTH_MULT / (w_class * FISH_SIZE_WEIGHT_GROWTH_MALUS) * hunger_mult, 1)
+ new_size = min(new_size, maximum_size)
+ if(weight < maximum_weight)
+ new_weight += CEILING((maximum_weight - weight) * FISH_GROWTH_MULT / (get_weight_rank() * FISH_SIZE_WEIGHT_GROWTH_MALUS) * hunger_mult, 1)
+ new_weight = min(new_weight, maximum_weight)
+ if(new_size != size || new_weight != weight)
+ update_size_and_weight(new_size, new_weight)
+
+#undef FISH_SIZE_WEIGHT_GROWTH_MALUS
+#undef FISH_GROWTH_MULT
+#undef FISH_GROWTH_PEAK
+
+/obj/item/fish/proc/check_flopping()
if(QDELETED(src)) //we don't care anymore
return
- if(!do_flop_animation)
+ if(!(fish_flags & FISH_DO_FLOP_ANIM))
return
// Do additional stuff
@@ -476,12 +811,15 @@
if(FISH_ALIVE)
status = FISH_ALIVE
health = initial(health) // since the fishe has been revived
+ regenerate_bites(bites_amount)
last_feeding = world.time //reset hunger
- check_environment()
+ check_flopping()
START_PROCESSING(SSobj, src)
+ ADD_TRAIT(src, TRAIT_UNCOMPOSTABLE, INNATE_TRAIT)
if(FISH_DEAD)
status = FISH_DEAD
STOP_PROCESSING(SSobj, src)
+ REMOVE_TRAIT(src, TRAIT_UNCOMPOSTABLE, INNATE_TRAIT)
stop_flopping()
if(!silent)
var/message = span_notice(replacetext(death_text, "%SRC", "[src]"))
@@ -493,6 +831,51 @@
update_fish_force()
SEND_SIGNAL(src, COMSIG_FISH_STATUS_CHANGED)
+/obj/item/fish/vv_edit_var(var_name, var_value)
+ switch(var_name)
+ if(NAMEOF(src, status))
+ if(var_value != FISH_DEAD && var_value != FISH_ALIVE)
+ var_value = var_value ? FISH_ALIVE : FISH_DEAD
+ set_status(var_value)
+ if(NAMEOF(src, size))
+ if(!isnum(var_value) || var_value == 0)
+ return FALSE
+ update_size_and_weight(var_value, weight)
+ if(NAMEOF(src, weight))
+ if(!isnum(var_value) || var_value == 0)
+ return FALSE
+ update_size_and_weight(size, var_value)
+ if(NAMEOF(src, health))
+ if(!isnum(var_value))
+ return FALSE
+ adjust_health(health)
+ if(NAMEOF(src, fish_flags))
+ var/old_fish_flags = fish_flags
+ fish_flags = var_value
+ if((old_fish_flags ^ fish_flags) & FISH_DO_FLOP_ANIM) //the flopping flag wasn't added nor removed
+ return TRUE
+ if(fish_flags & FISH_DO_FLOP_ANIM)
+ RegisterSignal(src, COMSIG_ATOM_TEMPORARY_ANIMATION_START, PROC_REF(on_temp_animation))
+ else
+ UnregisterSignal(src, COMSIG_ATOM_TEMPORARY_ANIMATION_START)
+ check_flopping()
+ if(NAMEOF(src, fillet_type))
+ if(!ispath(var_value))
+ return FALSE
+ remove_fillet_type()
+ fillet_type = var_value
+ add_fillet_type()
+ if(NAMEOF(src, num_fillets))
+ if(!isnum(var_value))
+ return FALSE
+ remove_fillet_type()
+ num_fillets = var_value
+ add_fillet_type()
+ else
+ return ..()
+
+ return TRUE
+
/obj/item/fish/expose_reagents(list/reagents, datum/reagents/source, methods = TOUCH, volume_modifier = 1, show_message = TRUE)
. = ..()
if(. & COMPONENT_NO_EXPOSE_REAGENTS || status != FISH_DEAD)
@@ -519,6 +902,25 @@
injector.expend(src, user)
return LAZARUS_INJECTOR_USED
+/obj/item/fish/proc/update_aquarium_appearance(datum/source, obj/effect/aquarium/visual)
+ SIGNAL_HANDLER
+ visual.icon = dedicated_in_aquarium_icon || icon
+ visual.icon_state = dedicated_in_aquarium_icon_state || "[initial(icon_state)]_small"
+ visual.color = aquarium_vc_color
+
+/obj/item/fish/proc/randomize_aquarium_position(datum/source, obj/structure/aquarium/current_aquarium, obj/effect/aquarium/visual)
+ SIGNAL_HANDLER
+ var/list/aq_properties = current_aquarium.get_surface_properties()
+ var/avg_width = round(sprite_width * 0.5)
+ var/avg_height = round(sprite_height * 0.5)
+ var/px_min = aq_properties[AQUARIUM_PROPERTIES_PX_MIN] + avg_width - 16
+ var/px_max = aq_properties[AQUARIUM_PROPERTIES_PX_MAX] - avg_width - 16
+ var/py_min = aq_properties[AQUARIUM_PROPERTIES_PY_MIN] + avg_height - 16
+ var/py_max = aq_properties[AQUARIUM_PROPERTIES_PY_MAX] - avg_width - 16
+
+ visual.pixel_x = visual.base_px = rand(px_min,px_max)
+ visual.pixel_y = visual.base_py = rand(py_min,py_max)
+
/obj/item/fish/proc/get_aquarium_animation()
var/obj/structure/aquarium/aquarium = loc
if(!istype(aquarium) || aquarium.fluid_type == AQUARIUM_FLUID_AIR || status == FISH_DEAD)
@@ -526,8 +928,61 @@
else
return AQUARIUM_ANIMATION_FISH_SWIM
+/obj/item/fish/proc/update_aquarium_animation(datum/source, current_animation, obj/structure/current_aquarium, obj/effect/visual)
+ SIGNAL_HANDLER
+ var/animation = get_aquarium_animation()
+ if(animation == current_animation)
+ return
+ switch(animation)
+ if(AQUARIUM_ANIMATION_FISH_SWIM)
+ swim_animation(current_aquarium, visual)
+ if(AQUARIUM_ANIMATION_FISH_DEAD)
+ dead_animation(current_aquarium, visual)
+
+/// Create looping random path animation, pixel offsets parameters include offsets already
+/obj/item/fish/proc/swim_animation(obj/structure/aquarium/current_aquarium, obj/effect/aquarium/visual)
+ var/avg_width = round(sprite_width / 2)
+ var/avg_height = round(sprite_height / 2)
+
+ var/list/aq_properties = current_aquarium.get_surface_properties()
+ var/px_min = aq_properties[AQUARIUM_PROPERTIES_PX_MIN] + avg_width - 16
+ var/px_max = aq_properties[AQUARIUM_PROPERTIES_PX_MAX] - avg_width - 16
+ var/py_min = aq_properties[AQUARIUM_PROPERTIES_PY_MIN] + avg_height - 16
+ var/py_max = aq_properties[AQUARIUM_PROPERTIES_PY_MAX] - avg_width - 16
+
+ var/origin_x = visual.base_px
+ var/origin_y = visual.base_py
+ var/prev_x = origin_x
+ var/prev_y = origin_y
+ animate(visual, pixel_x = origin_x, time = 0, loop = -1) //Just to start the animation
+ var/move_number = rand(3, 5) //maybe unhardcode this
+ for(var/i in 1 to move_number)
+ //If it's last movement, move back to start otherwise move to some random point
+ var/target_x = i == move_number ? origin_x : rand(px_min,px_max) //could do with enforcing minimal delta for prettier zigzags
+ var/target_y = i == move_number ? origin_y : rand(py_min,py_max)
+ var/dx = prev_x - target_x
+ var/dy = prev_y - target_y
+ prev_x = target_x
+ prev_y = target_y
+ var/dist = abs(dx) + abs(dy)
+ var/eyeballed_time = dist * 2 //2ds per px
+ //Face the direction we're going
+ var/matrix/dir_mx = matrix(visual.transform)
+ if(dx <= 0) //assuming default sprite is facing left here
+ dir_mx.Scale(-1, 1)
+ animate(transform = dir_mx, time = 0, loop = -1)
+ animate(pixel_x = target_x, pixel_y = target_y, time = eyeballed_time, loop = -1)
+
+/obj/item/fish/proc/dead_animation(obj/structure/aquarium/current_aquarium, obj/effect/aquarium/visual)
+ //Set base_py to lowest possible value
+ var/avg_height = round(sprite_height / 2)
+ var/list/aq_properties = current_aquarium.get_surface_properties()
+ var/py_min = aq_properties[AQUARIUM_PROPERTIES_PY_MIN] + avg_height - 16
+ visual.base_py = py_min
+ animate(visual, pixel_y = py_min, time = 1) //flop to bottom and end current animation.
+
/// Checks if our current environment lets us live.
-/obj/item/fish/proc/proper_environment()
+/obj/item/fish/proc/proper_environment(temp_range_min = required_temperature_min, temp_range_max = required_temperature_max)
var/obj/structure/aquarium/aquarium = loc
if(istype(aquarium))
if(!compatible_fluid_type(required_fluid_type, aquarium.fluid_type))
@@ -551,23 +1006,38 @@
return FALSE
return TRUE
-/obj/item/fish/proc/is_hungry()
- return !HAS_TRAIT(src, TRAIT_FISH_NO_HUNGER) && world.time - last_feeding >= feeding_frequency
-
/obj/item/fish/proc/process_health(seconds_per_tick)
var/health_change_per_second = 0
if(!proper_environment())
health_change_per_second -= 3 //Dying here
- if(is_hungry())
+ if(is_starving())
health_change_per_second -= 0.5 //Starving
else
health_change_per_second += 0.5 //Slowly healing
adjust_health(health + health_change_per_second * seconds_per_tick)
-/obj/item/fish/proc/adjust_health(amt)
- health = clamp(amt, 0, initial(health))
+/obj/item/fish/proc/adjust_health(amount)
+ if(status == FISH_DEAD || amount == health)
+ return
+ var/pre_health = health
+ var/initial_health = initial(health)
+ health = clamp(amount, 0, initial_health)
if(health <= 0)
set_status(FISH_DEAD)
+ return
+ if(amount < pre_health || !bites_amount)
+ return
+ var/health_to_pre_health_diff = amount - pre_health
+ var/init_health_to_pre_diff = initial_health - pre_health
+ var/bites_to_recover = bites_amount * (health_to_pre_health_diff / init_health_to_pre_diff)
+ regenerate_bites(bites_to_recover)
+
+/obj/item/fish/proc/regenerate_bites(amount)
+ amount = min(amount, bites_amount)
+ if(amount <= 0)
+ return
+ bites_amount -= amount
+ generate_fish_reagents(amount)
/obj/item/fish/proc/ready_to_reproduce(being_targeted = FALSE)
var/obj/structure/aquarium/aquarium = loc
@@ -577,7 +1047,7 @@
return FALSE
if(!being_targeted && length(aquarium.get_fishes()) >= AQUARIUM_MAX_BREEDING_POPULATION)
return FALSE
- return aquarium.allow_breeding && health >= initial(health) * 0.8 && stable_population >= 1 && world.time >= breeding_wait
+ return aquarium.reproduction_and_growth && health >= initial(health) * 0.8 && stable_population >= 1 && world.time >= breeding_wait
/obj/item/fish/proc/try_to_reproduce()
var/obj/structure/aquarium/aquarium = loc
@@ -635,25 +1105,28 @@
if(evolution.check_conditions(second_fish, src, aquarium))
possible_evolutions += evolution
+ var/list/types = spawn_types || list(type)
if(length(possible_evolutions))
chosen_evolution = pick(possible_evolutions)
chosen_type = chosen_evolution.new_fish_type
else if(second_fish)
+ var/list/second_fish_types = second_fish.spawn_types || list(second_fish.type)
var/recessive = HAS_TRAIT(src, TRAIT_FISH_RECESSIVE)
var/recessive_partner = HAS_TRAIT(second_fish, TRAIT_FISH_RECESSIVE)
if(length(aquarium.tracked_fish_by_type[type]) >= stable_population)
if(recessive_partner && !recessive)
return FALSE
- chosen_type = second_fish.type
+ chosen_type = pick(second_fish_types)
else
if(recessive && !recessive_partner)
- chosen_type = second_fish.type
+ chosen_type = pick(second_fish_types)
else if(recessive_partner && !recessive)
- chosen_type = type
+ chosen_type = pick(types)
else
- chosen_type = pick(second_fish.type, type)
+ var/list/picks = second_fish_types + types
+ chosen_type = pick(picks)
else
- chosen_type = type
+ chosen_type = pick(types)
return create_offspring(chosen_type, second_fish, chosen_evolution)
@@ -662,13 +1135,16 @@
//Try to pass down compatible traits based on inheritability
new_fish.inherit_traits(fish_traits, partner?.fish_traits, evolution?.new_traits, evolution?.removed_traits)
+ //We combine two methods for determining the size and weight of the offspring for less extreme results.
if(partner)
+ var/ratio_size = new_fish.average_size * (((size / average_size) + (partner.size / partner.average_size)) / 2)
var/mean_size = (size + partner.size)/2
+ var/ratio_weight = new_fish.average_size * (((weight / average_weight) + (partner.weight / partner.average_weight)) / 2)
var/mean_weight = (weight + partner.weight)/2
- new_fish.randomize_size_and_weight(mean_size, mean_weight, 0.3, TRUE)
+ new_fish.randomize_size_and_weight((mean_size + ratio_size) * 0.5, (mean_weight + ratio_weight) * 0.5, 0.3)
partner.breeding_wait = world.time + breeding_timeout
else //Make a close of this fish.
- new_fish.update_size_and_weight(size, weight, TRUE)
+ new_fish.update_size_and_weight(size, weight)
breeding_wait = world.time + breeding_timeout
@@ -738,7 +1214,7 @@
flop_animation()
/obj/item/fish/proc/try_electrogenesis()
- if(status == FISH_DEAD || is_hungry())
+ if(status == FISH_DEAD || is_starving())
return
COOLDOWN_START(src, electrogenesis_cooldown, ELECTROGENESIS_DURATION + ELECTROGENESIS_VARIANCE)
var/fish_zap_range = 1
@@ -746,7 +1222,7 @@
var/fish_zap_flags = ZAP_MOB_DAMAGE
if(istype(loc, /obj/structure/aquarium/bioelec_gen))
fish_zap_range = 5
- fish_zap_power = electrogenesis_power
+ fish_zap_power = GET_FISH_ELECTROGENESIS(src)
fish_zap_flags |= (ZAP_GENERATES_POWER | ZAP_MOB_STUN)
tesla_zap(source = get_turf(src), zap_range = fish_zap_range, power = fish_zap_power, cutoff = 1 MEGA JOULES, zap_flags = fish_zap_flags)
@@ -757,11 +1233,12 @@
if(HAS_TRAIT(src, TRAIT_FISH_FROM_CASE)) //Avoid printing money by simply ordering fish and sending it back.
calculated_price *= 0.05
return round(calculated_price)
+
/obj/item/fish/proc/get_happiness_value()
var/happiness_value = 0
- if(recently_petted)
+ if(fish_flags & FISH_FLAG_PETTED)
happiness_value++
- if(HAS_TRAIT(src, TRAIT_FISH_NO_HUNGER) || min((world.time - last_feeding) / feeding_frequency, 1) < 0.5)
+ if(get_hunger() < 0.5)
happiness_value++
var/obj/structure/aquarium/aquarium = loc
if(!istype(aquarium))
@@ -770,18 +1247,62 @@
happiness_value++
if(ISINRANGE(aquarium.fluid_temp, required_temperature_min, required_temperature_max))
happiness_value++
- return happiness_value
+ if(bites_amount) // ouch
+ happiness_value -= 2
+ if(health < initial(health) * 0.6)
+ happiness_value -= 1
+ return clamp(happiness_value, FISH_SAD, FISH_VERY_HAPPY)
+
+/obj/item/fish/attack_self(mob/living/user)
+ . = ..()
+ pet_fish(user)
/obj/item/fish/proc/pet_fish(mob/living/user)
- if(recently_petted)
- to_chat(user, span_warning("[src] runs away from your finger as you dip it into the water!"))
- return
- if(electrogenesis_power > 15 MEGA JOULES)
+ var/in_aquarium = isaquarium(loc)
+ if(status == FISH_DEAD)
+ to_chat(user, span_warning("You try to pet [src], but [p_theyre()] motionless!"))
+ return FALSE
+ if(!proper_environment())
+ to_chat(user, span_warning("You try to pet [src], but [p_theyre()] not feeling well!"))
+ return FALSE
+ if(fish_flags & FISH_FLAG_PETTED)
+ if(in_aquarium)
+ to_chat(user, span_warning("[src] runs away from your finger as you dip it into the water!"))
+ else
+ to_chat(user, span_warning("You try to pet [src] but [p_they()] squirms away!"))
+ return FALSE
+ if(HAS_TRAIT(src, TRAIT_FISH_ELECTROGENESIS) && GET_FISH_ELECTROGENESIS(src) > 15 MEGA JOULES)
user.electrocute_act(5, src) //was it all worth it?
- recently_petted = TRUE
- SEND_SIGNAL(src, COMSIG_FISH_PETTED)
- to_chat(user, span_notice("[src] dances around!"))
- addtimer(VARSET_CALLBACK(src, recently_petted, FALSE), 30 SECONDS)
+ fish_flags |= FISH_FLAG_PETTED
+ new /obj/effect/temp_visual/heart(get_turf(src))
+ if((/datum/fish_trait/aggressive in fish_traits) && prob(50))
+ if(!in_aquarium)
+ user.visible_message(
+ span_warning("[src] dances around before biting [user]!"),
+ span_warning("[src] dances around before biting you!"),
+ vision_distance = DEFAULT_MESSAGE_RANGE - 3,
+ )
+ else
+ user.visible_message(
+ span_warning("[src] bites [user]'s hand!"),
+ span_warning("You pet [src] as you hold it, only for [p_them()] to happily bite back!"),
+ vision_distance = DEFAULT_MESSAGE_RANGE - 3,
+ )
+ var/body_zone = pick(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM)
+ user.apply_damage((force * 0.2) + w_class * 2, BRUTE, body_zone, user.run_armor_check(body_zone, MELEE))
+ playsound(src,'sound/items/weapons/bite.ogg', 45, TRUE, -1)
+ else
+ if(in_aquarium)
+ to_chat(user, span_notice("[src] dances around!"))
+ else
+ to_chat(user, span_notice("You pet [src] as you hold it."))
+ user.add_mood_event("petted_fish", /datum/mood_event/fish_petting, src, HAS_MIND_TRAIT(user, TRAIT_MORBID))
+ playsound(src, 'sound/items/weapons/thudswoosh.ogg', 30, TRUE, -1)
+ addtimer(CALLBACK(src, PROC_REF(undo_petted)), 30 SECONDS)
+ return TRUE
+
+/obj/item/fish/proc/undo_petted()
+ fish_flags &= ~FISH_FLAG_PETTED
/// Returns random fish, using random_case_rarity probabilities.
/proc/random_fish_type(required_fluid)
@@ -812,3 +1333,7 @@
return fluid_type == AQUARIUM_FLUID_SALTWATER || fluid_type == AQUARIUM_FLUID_FRESHWATER
else
return fish_fluid_type == fluid_type
+
+#undef GET_FISH_ELECTROGENESIS
+#undef FISH_SAD
+#undef FISH_VERY_HAPPY
diff --git a/code/modules/fishing/fish/fish_evolution.dm b/code/modules/fishing/fish/fish_evolution.dm
index 688b0c201c7b4..25ce133c98d40 100644
--- a/code/modules/fishing/fish/fish_evolution.dm
+++ b/code/modules/fishing/fish/fish_evolution.dm
@@ -1,4 +1,7 @@
+///A global list of fish evolutions, which are singletons.
GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolution, list()))
+///A list of fish evolution types, each having an associated list containing all fish types that have it.
+GLOBAL_LIST_EMPTY(fishes_by_fish_evolution)
/**
* Fish evolution datums
@@ -7,46 +10,74 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio
* then there's a chance the offspring may be of a new type rather than the same as its source or mate (if any).
*/
/datum/fish_evolution
+ ///The name of the evolution. If not set, it'll be generated on runtime from the name of the new fish type.
var/name
+ ///The probability that this evolution can happen.
var/probability = 0
///The obj/item/fish path of the new fish
var/obj/item/fish/new_fish_type = /obj/item/fish
///The minimum required temperature for the evolved fish to spawn
- var/required_temperature_min = MIN_AQUARIUM_TEMP
+ var/required_temperature_min = 0
///The maximum required temperature for the evolved fish to spawn
- var/required_temperature_max = MAX_AQUARIUM_TEMP
+ var/required_temperature_max = INFINITY
///A list of traits added to the new fish. These take priority over the parents' traits.
var/list/new_traits
///If set, these traits will be removed from the new fish.
var/list/removed_traits
///A text string shown in the catalog, containing information on conditions specific to this evolution.
var/conditions_note
+ ///Is this evolution shown on the wiki?
+ var/show_on_wiki = TRUE
+ ///Is the result of this evolution shown on the wiki?
+ var/show_result_on_wiki = TRUE
/datum/fish_evolution/New()
+ ..()
+ SHOULD_CALL_PARENT(TRUE)
if(!ispath(new_fish_type, /obj/item/fish))
stack_trace("[type] instantiated with a new fish type of [new_fish_type]. That's not a fish, hun, things will break.")
if(!name)
name = full_capitalize(initial(new_fish_type.name))
/**
* The main proc that checks whether this can happen or not.
- * Please do keep in mind a mate may not be present for fish with the
- * self-reproductive trait.
+ * Keep in mind the mate and aquarium arguments may be null if
+ * the fish is self-reproducing or this evolution is a result of a fish_growth component
*/
/datum/fish_evolution/proc/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium)
SHOULD_CALL_PARENT(TRUE)
- //chances are halved if only one parent has this evolution.
- var/real_probability = (mate && (type in mate.evolution_types)) ? probability : probability/2
- if(!prob(real_probability))
- return FALSE
- if(!ISINRANGE(aquarium.fluid_temp, required_temperature_min, required_temperature_max))
+ if(aquarium)
+ //chances are halved if only one parent has this evolution.
+ var/real_probability = (mate && (type in mate.evolution_types)) ? probability : probability/2
+ if(!prob(real_probability))
+ return FALSE
+ if(!ISINRANGE(aquarium.fluid_temp, required_temperature_min, required_temperature_max))
+ return FALSE
+ else if(!source.proper_environment(required_temperature_min, required_temperature_max))
return FALSE
return TRUE
+///This is called when the evolution is set as the result type of a fish_growth component
+/datum/fish_evolution/proc/growth_checks(obj/item/fish/source, seconds_per_tick, growth)
+ SIGNAL_HANDLER
+ SHOULD_CALL_PARENT(TRUE)
+ if(source.health < initial(source.health) * 0.5)
+ return COMPONENT_DONT_GROW
+ if(source.get_hunger() >= 0.5) //too hungry to grow
+ return COMPONENT_DONT_GROW
+ var/obj/structure/aquarium/aquarium = source.loc
+ if(istype(aquarium) && !aquarium.reproduction_and_growth) //the aquarium has breeding disabled
+ return COMPONENT_DONT_GROW
+ else
+ aquarium = null
+ if(!check_conditions(source, aquarium = aquarium))
+ return COMPONENT_DONT_GROW
+
///Called by the fish analyzer right click function. Returns a text string used as tooltip.
/datum/fish_evolution/proc/get_evolution_tooltip()
. = ""
- if(required_temperature_min != MIN_AQUARIUM_TEMP || required_temperature_max != MAX_AQUARIUM_TEMP)
- . = "An aquarium temperature between [required_temperature_min] and [required_temperature_max] is required."
+ if(required_temperature_min > 0 || required_temperature_max < INFINITY)
+ var/max_temp = required_temperature_max < INFINITY ? " and [required_temperature_max]" : ""
+ . = "An aquarium temperature between [required_temperature_min][max_temp] is required."
if(conditions_note)
. += " [conditions_note]"
return .
@@ -87,6 +118,7 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio
new_fish_type = /obj/item/fish/mastodon
new_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/amphibious, /datum/fish_trait/predator, /datum/fish_trait/aggressive)
conditions_note = "The fish (and its mate) needs to be unusually big both in size and weight."
+ show_result_on_wiki = FALSE
/datum/fish_evolution/mastodon/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium)
if((source.size < 120 || source.weight < 3000) || (mate && (mate.size < 120 || mate.weight < 3000)))
@@ -106,13 +138,11 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio
required_temperature_max = MIN_AQUARIUM_TEMP+10
/datum/fish_evolution/three_eyes
- name = "Three-eyed Goldfish"
probability = 3
new_fish_type = /obj/item/fish/goldfish/three_eyes
new_traits = list(/datum/fish_trait/recessive)
/datum/fish_evolution/chainsawfish
- name = "Chainsawfish"
probability = 30
new_fish_type = /obj/item/fish/chainsawfish
new_traits = list(/datum/fish_trait/predator, /datum/fish_trait/aggressive)
@@ -124,3 +154,29 @@ GLOBAL_LIST_INIT(fish_evolutions, init_subtypes_w_path_keys(/datum/fish_evolutio
if(source.size >= double_avg_size && source.weight >= double_avg_weight && (/datum/fish_trait/aggressive in source.fish_traits))
return ..()
return FALSE
+
+/datum/fish_evolution/armored_pike
+ probability = 75
+ new_fish_type = /obj/item/fish/pike/armored
+ conditions_note = "The fish needs to have the stinger trait"
+
+/datum/fish_evolution/armored_pike/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium)
+ if(HAS_TRAIT(source, TRAIT_FISH_STINGER))
+ return ..()
+ return FALSE
+
+/datum/fish_evolution/fritterish
+ new_fish_type = /obj/item/fish/fryish/fritterish
+ removed_traits = list(/datum/fish_trait/no_mating)
+ conditions_note = "Fryish will grow into it over time."
+
+/datum/fish_evolution/nessie
+ name = "???"
+ new_fish_type = /obj/item/fish/fryish/nessie
+ conditions_note = "The final stage of fritterfish growth. It gotta be big!"
+ show_result_on_wiki = FALSE
+
+/datum/fish_evolution/nessiefish/check_conditions(obj/item/fish/source, obj/item/fish/mate, obj/structure/aquarium/aquarium)
+ if(source.size >= (/obj/item/fish/fryish/fritterish::average_size * 1.5) && source.size >= (/obj/item/fish/fryish/fritterish::average_weight * 1.5))
+ return ..()
+ return FALSE
diff --git a/code/modules/fishing/fish/fish_traits.dm b/code/modules/fishing/fish/fish_traits.dm
index 3667a038bff49..c9ab3325af367 100644
--- a/code/modules/fishing/fish/fish_traits.dm
+++ b/code/modules/fishing/fish/fish_traits.dm
@@ -37,7 +37,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
var/list/fish_whitelist
/// Depending on the value, fish with trait will be reported as more or less difficult in the catalog.
var/added_difficulty = 0
- /// Reagents added to the fish when gained
+ /// Reagents to add to the fish whenever the COMSIG_GENERATE_REAGENTS_TO_ADD signal is sent. Their values will be multiplied later.
var/list/reagents_to_add
/// Difficulty modifier from this mod, needs to return a list with two values
@@ -46,7 +46,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
return list(ADDITIVE_FISHING_MOD = 0, MULTIPLICATIVE_FISHING_MOD = 1)
/// Catch weight table modifier from this mod, needs to return a list with two values
-/datum/fish_trait/proc/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman)
+/datum/fish_trait/proc/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type)
SHOULD_CALL_PARENT(TRUE)
return list(ADDITIVE_FISHING_MOD = 0, MULTIPLICATIVE_FISHING_MOD = 1)
@@ -57,10 +57,8 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/// Applies some special qualities to the fish that has been spawned
/datum/fish_trait/proc/apply_to_fish(obj/item/fish/fish)
SHOULD_CALL_PARENT(TRUE)
- if(reagents_to_add)
- for(var/reagent in reagents_to_add)
- add_to_reagents(fish, reagent, reagents_to_add[reagent])
- RegisterSignal(fish, COMSIG_ATOM_PROCESSED, PROC_REF(process_reagents))
+ if(length(reagents_to_add))
+ RegisterSignal(fish, COMSIG_GENERATE_REAGENTS_TO_ADD, PROC_REF(add_reagents))
/// Applies some special qualities to basic mobs generated by fish (i.e. chasm chrab --> young lobstrosity --> lobstrosity).
/datum/fish_trait/proc/apply_to_mob(mob/living/basic/mob)
@@ -73,31 +71,21 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/// Proc used by both the predator and necrophage traits.
/datum/fish_trait/proc/eat_fish(obj/item/fish/predator, obj/item/fish/prey)
- predator.last_feeding = world.time
var/message = prey.status == FISH_DEAD ? "[src] eats [prey]'s carcass." : "[src] hunts down and eats [prey]."
predator.loc.visible_message(span_warning(message))
SEND_SIGNAL(prey, COMSIG_FISH_EATEN_BY_OTHER_FISH, predator)
qdel(prey)
+ predator.sate_hunger()
-/// Proc that inserts a reagent to the grind_results list of the fish. You'll still have to set the processed comsig proc yourself.
-/datum/fish_trait/proc/add_to_reagents(obj/item/fish/fish, reagent_type, amount)
- LAZYINITLIST(fish.grind_results)
- fish.grind_results.Insert(1, reagent_type)
- fish.grind_results[reagent_type] = amount
-/// Proc that handles adding reagents from the trait to the fillets from butchered fish.
-/datum/fish_trait/proc/process_reagents(obj/item/fish/source, mob/living/user, obj/item/process_item, list/results)
+/**
+ * Signal sent when we need to generate an abstract holder containing
+ * reagents to be transfered, usually as a result of the fish being eaten by someone
+ */
+/datum/fish_trait/proc/add_reagents(obj/item/fish/fish, list/reagents)
SIGNAL_HANDLER
- var/results_with_reagents = 0
- for(var/atom/result as anything in results)
- if(result.reagents)
- results_with_reagents++
- if(!results_with_reagents)
- return
for(var/reagent in reagents_to_add)
- var/amount = round(source.grind_results[reagent] / results_with_reagents, 0.1)
- for(var/atom/result as anything in results)
- result.reagents?.add_reagent(reagent, amount)
+ reagents[reagent] += reagents_to_add[reagent]
/// Proc that adds or changes the venomous when the fish size and/or weight are updated
/datum/fish_trait/proc/add_venom(obj/item/fish/source, venom_path, new_weight, mult = 0.25)
@@ -113,8 +101,8 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
var/live_amount = max(round((source.weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR) * live_mult, 0.1), live_mult)
var/dead_amount = max(round((source.weight/FISH_GRIND_RESULTS_WEIGHT_DIVISOR) * dead_mult, 0.1), dead_mult)
var/is_dead = source.status == FISH_DEAD
- source.RemoveElement(/datum/element/venomous, venom_path, is_dead ? live_amount : dead_amount)
- source.AddElement(/datum/element/venomous, venom_path, is_dead ? dead_amount : live_amount)
+ source.RemoveElement(/datum/element/venomous, venom_path, is_dead ? live_amount : dead_amount, thrown_effect = TRUE)
+ source.AddElement(/datum/element/venomous, venom_path, is_dead ? dead_amount : live_amount, thrown_effect = TRUE)
/datum/fish_trait/wary
name = "Wary"
@@ -132,33 +120,52 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/datum/fish_trait/shiny_lover/difficulty_mod(obj/item/fishing_rod/rod, mob/fisherman)
. = ..()
- // These fish are easier to catch with shiny lure
+ // These fish are easier to catch with shiny hook
if(rod.hook && rod.hook.fishing_hook_traits & FISHING_HOOK_SHINY)
.[ADDITIVE_FISHING_MOD] -= FISH_TRAIT_MINOR_DIFFICULTY_BOOST
+/datum/fish_trait/shiny_lover/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman)
+ . = ..()
+ // These fish are harder to find without a shiny hook
+ if(rod.hook && rod.hook.fishing_hook_traits & FISHING_HOOK_SHINY)
+ .[MULTIPLICATIVE_FISHING_MOD] = 0.5
+
/datum/fish_trait/picky_eater
name = "Picky Eater"
- catalog_description = "This fish is very picky and will ignore low quality bait."
+ catalog_description = "This fish is very picky and will ignore low quality bait (unless it's amongst its favorites)."
-/datum/fish_trait/picky_eater/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman)
+/datum/fish_trait/picky_eater/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type)
. = ..()
if(!rod.bait)
.[MULTIPLICATIVE_FISHING_MOD] = 0
return
if(HAS_TRAIT(rod.bait, TRAIT_OMNI_BAIT))
return
+
+ var/list/fav_baits = SSfishing.fish_properties[fish_type][FISH_PROPERTIES_FAV_BAIT]
+ for(var/identifier in fav_baits)
+ if(is_matching_bait(rod.bait, identifier)) //we like this bait anyway
+ return
+
+ var/list/bad_baits = SSfishing.fish_properties[fish_type][FISH_PROPERTIES_BAD_BAIT]
+ for(var/identifier in bad_baits)
+ if(is_matching_bait(rod.bait, identifier)) //we hate this bait.
+ .[MULTIPLICATIVE_FISHING_MOD] = 0
+ return
+
if(!HAS_TRAIT(rod.bait, TRAIT_GOOD_QUALITY_BAIT) && !HAS_TRAIT(rod.bait, TRAIT_GREAT_QUALITY_BAIT))
.[MULTIPLICATIVE_FISHING_MOD] = 0
-
/datum/fish_trait/nocturnal
name = "Nocturnal"
catalog_description = "This fish avoids bright lights, fishing and storing in darkness recommended."
-/datum/fish_trait/nocturnal/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman)
+/datum/fish_trait/nocturnal/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type)
. = ..()
- var/turf/turf = get_turf(fisherman)
- var/light_amount = turf.get_lumcount()
+ if(rod.bait && HAS_TRAIT(rod.bait, TRAIT_BAIT_IGNORE_ENVIRONMENT))
+ return
+ var/turf/turf = get_turf(location)
+ var/light_amount = turf?.get_lumcount()
if(light_amount > SHADOW_SPECIES_LIGHT_THRESHOLD)
.[MULTIPLICATIVE_FISHING_MOD] = 0
@@ -195,7 +202,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
mob.apply_status_effect(/datum/status_effect/shadow_regeneration)
/datum/fish_trait/heavy
- name = "Heavy"
+ name = "Demersal"
catalog_description = "This fish tends to stay near the waterbed."
/datum/fish_trait/heavy/apply_to_mob(mob/living/basic/mob)
@@ -215,18 +222,20 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "This fish can only be baited with meat."
incompatible_traits = list(/datum/fish_trait/vegan)
-/datum/fish_trait/carnivore/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman)
+/datum/fish_trait/carnivore/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type)
. = ..()
if(!rod.bait)
.[MULTIPLICATIVE_FISHING_MOD] = 0
return
if(HAS_TRAIT(rod.bait, TRAIT_OMNI_BAIT))
return
+ if(isfish(rod.bait))
+ return
if(!istype(rod.bait, /obj/item/food))
.[MULTIPLICATIVE_FISHING_MOD] = 0
return
var/obj/item/food/food_bait = rod.bait
- if(!(food_bait.foodtypes & MEAT))
+ if(!(food_bait.foodtypes & (MEAT|SEAFOOD|BUGS)))
.[MULTIPLICATIVE_FISHING_MOD] = 0
/datum/fish_trait/vegan
@@ -234,14 +243,20 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "This fish can only be baited with fresh produce."
incompatible_traits = list(/datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/necrophage)
-/datum/fish_trait/vegan/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman)
+/datum/fish_trait/vegan/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type)
. = ..()
if(!rod.bait)
.[MULTIPLICATIVE_FISHING_MOD] = 0
return
if(HAS_TRAIT(rod.bait, TRAIT_OMNI_BAIT))
return
- if(!istype(rod.bait, /obj/item/food/grown))
+ if(!istype(rod.bait, /obj/item/food))
+ .[MULTIPLICATIVE_FISHING_MOD] = 0
+ return
+ if(istype(rod.bait, /obj/item/food/grown))
+ return
+ var/obj/item/food/food_bait = rod.bait
+ if(food_bait.foodtypes & (MEAT|SEAFOOD|GORE|BUGS|DAIRY) || !(food_bait.foodtypes & (VEGETABLES|FRUIT)))
.[MULTIPLICATIVE_FISHING_MOD] = 0
/datum/fish_trait/emulsijack
@@ -265,7 +280,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
emulsified = TRUE
if(emulsified)
source.adjust_health(source.health + 3 * seconds_per_tick)
- source.last_feeding = world.time //it feeds on the emulsion!
+ source.sate_hunger()
/datum/fish_trait/emulsijack/apply_to_mob(mob/living/basic/mob)
. = ..()
@@ -294,7 +309,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/datum/fish_trait/necrophage/proc/eat_dead_fishes(obj/item/fish/source, seconds_per_tick)
SIGNAL_HANDLER
- if(!source.is_hungry() || !isaquarium(source.loc))
+ if(source.get_hunger() > 0.75 || !isaquarium(source.loc))
return
for(var/obj/item/fish/victim in source.loc)
if(victim.status != FISH_DEAD || victim == source || HAS_TRAIT(victim, TRAIT_YUCKY_FISH))
@@ -314,13 +329,18 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/**
* Useful for those species with the parthenogenesis trait if you don't want them to mate with each other,
- * or for similar shenanigeans, I don't know.
+ * or for similar shenanigans, I don't know.
* Otherwise you could just set the stable_population to 1.
*/
/datum/fish_trait/no_mating
name = "Mateless"
catalog_description = "This fish cannot reproduce with other fishes."
incompatible_traits = list(/datum/fish_trait/crossbreeder)
+ spontaneous_manifest_types = list(
+ /obj/item/fish/fryish = 100,
+ /obj/item/fish/fryish/fritterish = 0,
+ /obj/item/fish/fryish/nessie = 0
+ )
/datum/fish_trait/no_mating/apply_to_fish(obj/item/fish/fish)
. = ..()
@@ -371,13 +391,18 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
catalog_description = "It's a predatory fish. It'll hunt down and eat live fishes of smaller size when hungry."
incompatible_traits = list(/datum/fish_trait/vegan)
+/datum/fish_trait/predator/catch_weight_mod(obj/item/fishing_rod/rod, mob/fisherman, atom/location, obj/item/fish/fish_type)
+ . = ..()
+ if(isfish(rod.bait))
+ .[MULTIPLICATIVE_FISHING_MOD] *= 2
+
/datum/fish_trait/predator/apply_to_fish(obj/item/fish/fish)
. = ..()
RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(eat_fishes))
/datum/fish_trait/predator/proc/eat_fishes(obj/item/fish/source, seconds_per_tick)
SIGNAL_HANDLER
- if(!source.is_hungry() || !isaquarium(source.loc))
+ if(source.get_hunger() > 0.75 || !isaquarium(source.loc))
return
var/obj/structure/aquarium/aquarium = source.loc
for(var/obj/item/fish/victim in aquarium.get_fishes(TRUE, source))
@@ -391,7 +416,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/datum/fish_trait/yucky
name = "Yucky"
catalog_description = "This fish tastes so repulsive, other fishes won't try to eat it."
- reagents_to_add = list(/datum/reagent/yuck = 3)
+ reagents_to_add = list(/datum/reagent/yuck = 1.2)
/datum/fish_trait/yucky/apply_to_fish(obj/item/fish/fish)
. = ..()
@@ -399,9 +424,9 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/datum/fish_trait/toxic
name = "Toxic"
- catalog_description = "This fish contains toxins. Feeding it to predatory fishes or people is not reccomended."
+ catalog_description = "This fish contains toxins. Feeding it to predatory fishes or people is not recommended."
diff_traits_inheritability = 25
- reagents_to_add = list(/datum/reagent/toxin/tetrodotoxin = 2.5)
+ reagents_to_add = list(/datum/reagent/toxin/tetrodotoxin = 1)
/datum/fish_trait/toxic/apply_to_fish(obj/item/fish/fish)
. = ..()
@@ -490,6 +515,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
spontaneous_manifest_types = list(/obj/item/fish/clownfish/lube = 100)
catalog_description = "This fish exudes a viscous, slippery lubrificant. It's recommended not to step on it."
added_difficulty = 5
+ reagents_to_add = list(/datum/reagent/lube = 1.2)
/datum/fish_trait/lubed/apply_to_fish(obj/item/fish/fish)
. = ..()
@@ -575,11 +601,30 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
inheritability = 60
diff_traits_inheritability = 30
catalog_description = "This fish is electroreceptive, and will generate electric fields. Can be harnessed inside a bioelectric generator."
+ reagents_to_add = list(/datum/reagent/consumable/liquidelectricity = 1.5)
/datum/fish_trait/electrogenesis/apply_to_fish(obj/item/fish/fish)
. = ..()
ADD_TRAIT(fish, TRAIT_FISH_ELECTROGENESIS, FISH_TRAIT_DATUM)
RegisterSignal(fish, COMSIG_FISH_FORCE_UPDATED, PROC_REF(on_force_updated))
+ RegisterSignals(fish, list(COMSIG_ITEM_FRIED, TRAIT_FOOD_BBQ_GRILLED), PROC_REF(on_fish_cooked))
+
+/datum/fish_trait/electrogenesis/proc/on_fish_cooked(obj/item/fish/fish, cooked_time)
+ SIGNAL_HANDLER
+ if(cooked_time >= FISH_SAFE_COOKING_DURATION)
+ fish.reagents.del_reagent(/datum/reagent/consumable/liquidelectricity)
+ else
+ fish.reagents.multiply_single_reagent(/datum/reagent/consumable/liquidelectricity, 0.66)
+
+/datum/fish_trait/electrogenesis/add_reagents(obj/item/fish/fish, list/reagents)
+ . = ..()
+ if(HAS_TRAIT(fish, TRAIT_FISH_WELL_COOKED)) // Cooking it well removes all liquid electricity
+ reagents -= /datum/reagent/consumable/liquidelectricity
+ else
+ reagents -= /datum/reagent/blood
+ //Otherwise, undercooking it will remove 2/3 of it.
+ if(!HAS_TRAIT(fish, TRAIT_FOOD_FRIED) && !HAS_TRAIT(fish, TRAIT_FOOD_BBQ_GRILLED))
+ reagents[/datum/reagent/consumable/liquidelectricity] -= 1
/datum/fish_trait/electrogenesis/proc/on_force_updated(obj/item/fish/fish, weight_rank, bonus_or_malus)
SIGNAL_HANDLER
@@ -588,7 +633,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
fish.damtype = BURN
fish.attack_verb_continuous = list("shocks", "zaps")
fish.attack_verb_simple = list("shock", "zap")
- fish.hitsound = 'sound/effects/sparks4.ogg'
+ fish.hitsound = 'sound/effects/sparks/sparks4.ogg'
/datum/fish_trait/electrogenesis/apply_to_mob(mob/living/basic/mob)
. = ..()
@@ -599,7 +644,7 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
/datum/fish_trait/stunted
name = "Stunted Growth"
catalog_description = "This chrab's development is stunted, and will not properly reach adulthood."
- spontaneous_manifest_types = list(/obj/item/fish/chasm_crab = 12, /obj/item/fish/chasm_crab/ice = 12)
+ spontaneous_manifest_types = list(/obj/item/fish/chasm_crab = 12)
fish_whitelist = list(/obj/item/fish/chasm_crab, /obj/item/fish/chasm_crab/ice)
diff_traits_inheritability = 40
@@ -612,7 +657,12 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
inheritability = 80
diff_traits_inheritability = 35
catalog_description = "This fish is equipped with a sharp stringer or bill capable of delivering damage and toxins."
- spontaneous_manifest_types = list(/obj/item/fish/stingray = 100, /obj/item/fish/swordfish = 100, /obj/item/fish/chainsawfish = 100)
+ spontaneous_manifest_types = list(
+ /obj/item/fish/stingray = 100,
+ /obj/item/fish/swordfish = 100,
+ /obj/item/fish/chainsawfish = 100,
+ /obj/item/fish/pike/armored = 100,
+ )
/datum/fish_trait/stinger/apply_to_fish(obj/item/fish/fish)
. = ..()
@@ -647,3 +697,70 @@ GLOBAL_LIST_INIT(spontaneous_fish_traits, populate_spontaneous_fish_traits())
if(!HAS_TRAIT(source, TRAIT_FISH_STINGER))
return
change_venom_on_death(source, /datum/reagent/toxin/venom, 0.7, 0.3)
+
+/datum/fish_trait/hallucinogenic
+ name = "Hallucinogenic"
+ catalog_description = "This fish is coated with hallucinogenic neurotoxin. We advise cooking it before consumption."
+ reagents_to_add = list(/datum/reagent/toxin/mindbreaker/fish = 1)
+
+/datum/fish_trait/hallucinogenic/add_reagents(obj/item/fish/fish, list/reagents)
+ if(!HAS_TRAIT(src, TRAIT_FOOD_FRIED) && !HAS_TRAIT(src, TRAIT_FOOD_BBQ_GRILLED))
+ return ..()
+
+/datum/fish_trait/ink
+ name = "Ink Production"
+ catalog_description = "This fish possess a sac that produces ink."
+ diff_traits_inheritability = 70
+ spontaneous_manifest_types = list(/obj/item/fish/squid = 35)
+
+/datum/fish_trait/ink/apply_to_fish(obj/item/fish/fish)
+ . = ..()
+ RegisterSignal(fish, COMSIG_ATOM_PROCESSED, PROC_REF(on_process))
+ RegisterSignal(fish, COMSIG_ITEM_ATTACK_ZONE, PROC_REF(attacked_someone))
+
+/datum/fish_trait/ink/proc/attacked_someone(obj/item/fish/source, mob/living/target, mob/living/user, zone)
+ SIGNAL_HANDLER
+ if(HAS_TRAIT(source, TRAIT_FISH_INK_ON_COOLDOWN) || source.status == FISH_DEAD)
+ return
+ if(!iscarbon(target) || target.get_bodypart(BODY_ZONE_HEAD))
+ target.adjust_temp_blindness_up_to(4 SECONDS, 8 SECONDS)
+ target.adjust_confusion_up_to(1.5 SECONDS, 4 SECONDS)
+ target.AddComponent(/datum/component/face_decal/splat, \
+ color = COLOR_NEARLY_ALL_BLACK, \
+ memory_type = /datum/memory/witnessed_inking, \
+ mood_event_type = /datum/mood_event/inked, \
+ )
+ target.visible_message(span_warning("[target] is inked by [source]!"), span_userdanger("You've been inked by [source]!"))
+ playsound(target, SFX_DESECRATION, 50, TRUE)
+ ADD_TRAIT(source, TRAIT_FISH_INK_ON_COOLDOWN, FISH_TRAIT_DATUM)
+ addtimer(TRAIT_CALLBACK_REMOVE(source, TRAIT_FISH_INK_ON_COOLDOWN, FISH_TRAIT_DATUM), 9 SECONDS)
+
+/datum/fish_trait/ink/proc/on_process(obj/item/fish/source, mob/living/user, obj/item/process_item, list/results)
+ SIGNAL_HANDLER
+ new /obj/item/food/ink_sac(source.drop_location())
+
+/datum/fish_trait/camouflage
+ name = "Camouflage"
+ catalog_description = "This fish possess the ability to blend with its surroundings."
+ spontaneous_manifest_types = list(/obj/item/fish/squid = 35)
+ added_difficulty = 5
+
+/datum/fish_trait/camouflage/minigame_mod(obj/item/fishing_rod/rod, mob/fisherman, datum/fishing_challenge/minigame)
+ minigame.special_effects |= FISHING_MINIGAME_RULE_CAMO
+
+/datum/fish_trait/camouflage/apply_to_fish(obj/item/fish/fish)
+ . = ..()
+ RegisterSignal(fish, COMSIG_FISH_LIFE, PROC_REF(fade_out))
+ RegisterSignals(fish, list(COMSIG_MOVABLE_MOVED, COMSIG_FISH_STATUS_CHANGED), PROC_REF(reset_alpha))
+
+/datum/fish_trait/camouflage/proc/fade_out(obj/item/fish/source, seconds_per_tick)
+ SIGNAL_HANDLER
+ if(source.status == FISH_DEAD || source.last_move + 5 SECONDS >= world.time)
+ return
+ source.alpha = max(source.alpha - 10 * seconds_per_tick, 10)
+
+/datum/fish_trait/camouflage/proc/reset_alpha(obj/item/fish/source)
+ SIGNAL_HANDLER
+ var/init_alpha = initial(source.alpha)
+ if(init_alpha != source.alpha)
+ animate(source.alpha, alpha = init_alpha, time = 1.2 SECONDS, easing = CIRCULAR_EASING|EASE_OUT)
diff --git a/code/modules/fishing/fish/fish_types.dm b/code/modules/fishing/fish/fish_types.dm
deleted file mode 100644
index 81c716cecc2e4..0000000000000
--- a/code/modules/fishing/fish/fish_types.dm
+++ /dev/null
@@ -1,1205 +0,0 @@
-// Freshwater fish
-
-/obj/item/fish/goldfish
- name = "goldfish"
- desc = "Despite common belief, goldfish do not have three-second memories. \
- They can actually remember things that happened up to three months ago."
- icon_state = "goldfish"
- dedicated_in_aquarium_icon_state = "fish_greyscale"
- aquarium_vc_color = "#D8540D"
- sprite_width = 5
- sprite_height = 3
- stable_population = 3
- average_size = 30
- average_weight = 500
- weight_size_deviation = 0.35
- favorite_bait = list(/obj/item/food/bait/worm)
- required_temperature_min = MIN_AQUARIUM_TEMP+18
- required_temperature_max = MIN_AQUARIUM_TEMP+26
- evolution_types = list(/datum/fish_evolution/three_eyes, /datum/fish_evolution/chainsawfish)
- compatible_types = list(/obj/item/fish/goldfish/gill, /obj/item/fish/goldfish/three_eyes, /obj/item/fish/goldfish/three_eyes/gill)
-
-/obj/item/fish/goldfish/gill
- name = "McGill"
- desc = "A great rubber duck tool for Lawyers who can't get a grasp over their case."
- stable_population = 1
- random_case_rarity = FISH_RARITY_NOPE
- show_in_catalog = FALSE
- beauty = FISH_BEAUTY_GOOD
- compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/goldfish/three_eyes)
- fish_traits = list(/datum/fish_trait/recessive)
-
-/obj/item/fish/goldfish/three_eyes
- name = "three-eyed goldfish"
- desc = "A goldfish with an extra half a pair of eyes. You wonder what it's been feeding on lately..."
- icon_state = "three_eyes"
- stable_population = 4
- fish_traits = list(/datum/fish_trait/recessive, /datum/fish_trait/shiny_lover)
- compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/goldfish/gill, /obj/item/fish/goldfish/three_eyes/gill)
- beauty = FISH_BEAUTY_GOOD
- fishing_difficulty_modifier = 10
- random_case_rarity = FISH_RARITY_VERY_RARE
- food = /datum/reagent/toxin/mutagen
- favorite_bait = list(
- list(
- "Type" = "Reagent",
- "Value" = /datum/reagent/toxin/mutagen,
- "Amount" = 3,
- ),
- )
-
-/obj/item/fish/goldfish/three_eyes/gill
- name = "McGill"
- desc = "A great rubber duck tool for Lawyers who can't get a grasp over their case. It looks kinda different today..."
- compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/goldfish/three_eyes)
- beauty = FISH_BEAUTY_GREAT
- show_in_catalog = FALSE
- stable_population = 1
- random_case_rarity = FISH_RARITY_NOPE
-
-/obj/item/fish/angelfish
- name = "angelfish"
- desc = "Young Angelfish often live in groups, while adults prefer solitary life. They become territorial and aggressive toward other fish when they reach adulthood."
- icon_state = "angelfish"
- sprite_width = 4
- sprite_height = 7
- average_size = 30
- average_weight = 500
- stable_population = 3
- fish_traits = list(/datum/fish_trait/aggressive)
- required_temperature_min = MIN_AQUARIUM_TEMP+22
- required_temperature_max = MIN_AQUARIUM_TEMP+30
-
-/obj/item/fish/guppy
- name = "guppy"
- desc = "Guppy is also known as rainbow fish because of the brightly colored body and fins."
- icon_state = "guppy"
- sprite_width = 5
- sprite_height = 2
- average_size = 30
- average_weight = 500
- stable_population = 6
- required_temperature_min = MIN_AQUARIUM_TEMP+20
- required_temperature_max = MIN_AQUARIUM_TEMP+28
-
-/obj/item/fish/plasmatetra
- name = "plasma tetra"
- desc = "Due to their small size, tetras are prey to many predators in their watery world, including eels, crustaceans, and invertebrates."
- icon_state = "plastetra"
- sprite_width = 4
- sprite_height = 2
- average_size = 30
- average_weight = 500
- stable_population = 3
- required_temperature_min = MIN_AQUARIUM_TEMP+20
- required_temperature_max = MIN_AQUARIUM_TEMP+28
-
-/obj/item/fish/catfish
- name = "catfish"
- desc = "A catfish has about 100,000 taste buds, and their bodies are covered with them to help detect chemicals present in the water and also to respond to touch."
- icon_state = "catfish"
- sprite_width = 8
- sprite_height = 4
- average_size = 80
- average_weight = 1600
- weight_size_deviation = 0.35
- stable_population = 3
- favorite_bait = list(
- list(
- "Type" = "Foodtype",
- "Value" = JUNKFOOD
- )
- )
- required_temperature_min = MIN_AQUARIUM_TEMP+12
- required_temperature_max = MIN_AQUARIUM_TEMP+30
- beauty = FISH_BEAUTY_GOOD
-
-/obj/item/fish/tadpole
- name = "tadpole"
- desc = "The larval spawn of an amphibian. A very minuscle, round creature with a long tail it uses to swim around."
- icon_state = "tadpole"
- average_size = 3
- average_weight = 10
- sprite_width = 3
- sprite_height = 1
- health = 50
- feeding_frequency = 1.5 MINUTES
- required_temperature_min = MIN_AQUARIUM_TEMP+15
- required_temperature_max = MIN_AQUARIUM_TEMP+20
- fillet_type = null
- fish_traits = list(/datum/fish_trait/no_mating) //They grow into frogs and that's it.
- beauty = FISH_BEAUTY_NULL
- random_case_rarity = FISH_RARITY_NOPE //Why would you want generic frog tadpoles you get from ponds inside fish cases?
- /// Once dead, tadpoles disappear after a dozen seconds, since you can get infinite tadpoles.
- var/del_timerid
-
-/obj/item/fish/tadpole/Initialize(mapload, apply_qualities = TRUE)
- . = ..()
- AddComponent(/datum/component/fish_growth, /mob/living/basic/frog, 100 / rand(2.5, 3 MINUTES) * 10)
- RegisterSignal(src, COMSIG_FISH_BEFORE_GROWING, PROC_REF(growth_checks))
- RegisterSignal(src, COMSIG_FISH_FINISH_GROWING, PROC_REF(on_growth))
-
-/obj/item/fish/tadpole/set_status(new_status, silent = FALSE)
- . = ..()
- if(status == FISH_DEAD)
- del_timerid = QDEL_IN_STOPPABLE(src, 12 SECONDS)
- else
- deltimer(del_timerid)
-
-/obj/item/fish/tadpole/proc/growth_checks(datum/source, seconds_per_tick)
- SIGNAL_HANDLER
- var/hunger = CLAMP01((world.time - last_feeding) / feeding_frequency)
- if(hunger >= 0.7) //too hungry to grow
- return COMPONENT_DONT_GROW
- var/obj/structure/aquarium/aquarium = loc
- if(!aquarium.allow_breeding) //the aquarium has breeding disabled
- return COMPONENT_DONT_GROW
-
-/obj/item/fish/tadpole/proc/on_growth(datum/source, mob/living/basic/frog/result)
- SIGNAL_HANDLER
- playsound(result, result.attack_sound, 50, TRUE) // reeeeeeeeeeeeeee...
-
-/obj/item/fish/tadpole/get_export_price(price, percent)
- return 2 //two credits. Tadpoles aren't really that valueable.
-
-// Saltwater fish below
-
-/obj/item/fish/clownfish
- name = "clownfish"
- desc = "Clownfish catch prey by swimming onto the reef, attracting larger fish, and luring them back to the anemone. The anemone will sting and eat the larger fish, leaving the remains for the clownfish."
- icon_state = "clownfish"
- required_fluid_type = AQUARIUM_FLUID_SALTWATER
- sprite_width = 7
- sprite_height = 4
- average_size = 30
- average_weight = 500
- stable_population = 4
- fish_traits = list(/datum/fish_trait/picky_eater)
- evolution_types = list(/datum/fish_evolution/lubefish)
- compatible_types = list(/obj/item/fish/clownfish/lube)
- required_temperature_min = MIN_AQUARIUM_TEMP+22
- required_temperature_max = MIN_AQUARIUM_TEMP+30
-
-/obj/item/fish/clownfish/lube
- name = "lubefish"
- desc = "A clownfish exposed to cherry-flavored lube for far too long. First discovered the days following a cargo incident around the seas of Europa, when thousands of thousands of thousands..."
- icon_state = "lubefish"
- random_case_rarity = FISH_RARITY_VERY_RARE
- fish_traits = list(/datum/fish_trait/picky_eater, /datum/fish_trait/lubed)
- evolution_types = null
- compatible_types = list(/obj/item/fish/clownfish)
- food = /datum/reagent/lube
- fishing_difficulty_modifier = 5
- beauty = FISH_BEAUTY_GREAT
-
-/obj/item/fish/cardinal
- name = "cardinalfish"
- desc = "Cardinalfish are often found near sea urchins, where the fish hide when threatened."
- icon_state = "cardinalfish"
- required_fluid_type = AQUARIUM_FLUID_SALTWATER
- sprite_width = 6
- sprite_height = 3
- average_size = 30
- average_weight = 500
- stable_population = 4
- fish_traits = list(/datum/fish_trait/vegan)
- required_temperature_min = MIN_AQUARIUM_TEMP+22
- required_temperature_max = MIN_AQUARIUM_TEMP+30
-
-/obj/item/fish/greenchromis
- name = "green chromis"
- desc = "The Chromis can vary in color from blue to green depending on the lighting and distance from the lights."
- icon_state = "greenchromis"
- required_fluid_type = AQUARIUM_FLUID_SALTWATER
- sprite_width = 5
- sprite_height = 3
- average_size = 30
- average_weight = 500
- stable_population = 5
- required_temperature_min = MIN_AQUARIUM_TEMP+23
- required_temperature_max = MIN_AQUARIUM_TEMP+28
-
- fishing_difficulty_modifier = 5 // Bit harder
-
-/obj/item/fish/firefish
- name = "firefish goby"
- desc = "To communicate in the wild, the firefish uses its dorsal fin to alert others of potential danger."
- icon_state = "firefish"
- sprite_width = 5
- sprite_height = 3
- required_fluid_type = AQUARIUM_FLUID_SALTWATER
- average_size = 30
- average_weight = 500
- stable_population = 3
- disliked_bait = list(/obj/item/food/bait/worm, /obj/item/food/bait/doughball)
- fish_movement_type = /datum/fish_movement/zippy
- required_temperature_min = MIN_AQUARIUM_TEMP+23
- required_temperature_max = MIN_AQUARIUM_TEMP+28
-
-/obj/item/fish/pufferfish
- name = "pufferfish"
- desc = "They say that one pufferfish contains enough toxins to kill 30 people, although in the last few decades they've been genetically engineered en masse to be less poisonous."
- icon_state = "pufferfish"
- required_fluid_type = AQUARIUM_FLUID_SALTWATER
- sprite_width = 8
- sprite_height = 6
- average_size = 60
- average_weight = 1000
- stable_population = 3
- required_temperature_min = MIN_AQUARIUM_TEMP+23
- required_temperature_max = MIN_AQUARIUM_TEMP+28
- fillet_type = /obj/item/food/fishmeat/quality //Too bad they're poisonous
- fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/toxic)
- beauty = FISH_BEAUTY_GOOD
-
-/obj/item/fish/lanternfish
- name = "lanternfish"
- desc = "Typically found in areas below 6600 feet below the surface of the ocean, they live in complete darkness."
- icon_state = "lanternfish"
- required_fluid_type = AQUARIUM_FLUID_SALTWATER
- random_case_rarity = FISH_RARITY_VERY_RARE
- sprite_width = 6
- sprite_height = 5
- average_size = 50
- average_weight = 1000
- stable_population = 3
- fish_traits = list(/datum/fish_trait/nocturnal)
- required_temperature_min = MIN_AQUARIUM_TEMP+2 //My source is that the water at a depth 6600 feet is pretty darn cold.
- required_temperature_max = MIN_AQUARIUM_TEMP+18
- beauty = FISH_BEAUTY_NULL
-
-//Tiziran Fish
-/obj/item/fish/dwarf_moonfish
- name = "dwarf moonfish"
- desc = "Ordinarily in the wild, the Zagoskian moonfish is around the size of a tuna, however through selective breeding a smaller breed suitable for being kept as an aquarium pet has been created."
- icon_state = "dwarf_moonfish"
- sprite_height = 6
- sprite_width = 6
- required_fluid_type = AQUARIUM_FLUID_SALTWATER
- stable_population = 2
- fillet_type = /obj/item/food/fishmeat/moonfish
- average_size = 60
- average_weight = 1000
- required_temperature_min = MIN_AQUARIUM_TEMP+20
- required_temperature_max = MIN_AQUARIUM_TEMP+30
- beauty = FISH_BEAUTY_GOOD
-
-/obj/item/fish/gunner_jellyfish
- name = "gunner jellyfish"
- desc = "So called due to their resemblance to an artillery shell, the gunner jellyfish is native to Tizira, where it is enjoyed as a delicacy. Produces a mild hallucinogen that is destroyed by cooking."
- icon_state = "gunner_jellyfish"
- sprite_height = 4
- sprite_width = 5
- required_fluid_type = AQUARIUM_FLUID_SALTWATER
- stable_population = 4
- fillet_type = /obj/item/food/fishmeat/gunner_jellyfish
- required_temperature_min = MIN_AQUARIUM_TEMP+24
- required_temperature_max = MIN_AQUARIUM_TEMP+32
- beauty = FISH_BEAUTY_GOOD
-
-/obj/item/fish/needlefish
- name = "needlefish"
- desc = "A tiny, transparent fish which resides in large schools in the oceans of Tizira. A common food for other, larger fish."
- icon_state = "needlefish"
- sprite_height = 3
- sprite_width = 7
- required_fluid_type = AQUARIUM_FLUID_SALTWATER
- stable_population = 12
- breeding_timeout = 1 MINUTES
- fillet_type = null
- average_size = 20
- average_weight = 300
- fish_traits = list(/datum/fish_trait/carnivore)
- required_temperature_min = MIN_AQUARIUM_TEMP+10
- required_temperature_max = MIN_AQUARIUM_TEMP+32
-
-/obj/item/fish/armorfish
- name = "armorfish"
- desc = "A small shellfish native to Tizira's oceans, known for its exceptionally hard shell. Consumed similarly to prawns."
- icon_state = "armorfish"
- sprite_height = 5
- sprite_width = 6
- average_size = 25
- average_weight = 350
- required_fluid_type = AQUARIUM_FLUID_SALTWATER
- stable_population = 10
- breeding_timeout = 1.25 MINUTES
- fillet_type = /obj/item/food/fishmeat/armorfish
- fish_movement_type = /datum/fish_movement/slow
- required_temperature_min = MIN_AQUARIUM_TEMP+10
- required_temperature_max = MIN_AQUARIUM_TEMP+32
-
-/// Commonly found on the mining fishing spots. Can be grown into lobstrosities
-/obj/item/fish/chasm_crab
- name = "chasm chrab"
- desc = "The young of the lobstrosity mature in pools below the earth, eating what falls in until large enough to clamber out. Those found near the station are well-fed."
- icon_state = "chrab"
- sprite_height = 9
- sprite_width = 8
- stable_population = 4
- feeding_frequency = 10 MINUTES
- random_case_rarity = FISH_RARITY_RARE
- fillet_type = /obj/item/food/meat/slab/rawcrab
- required_temperature_min = MIN_AQUARIUM_TEMP+9
- required_temperature_max = LAVALAND_MAX_TEMPERATURE+50
- min_pressure = HAZARD_LOW_PRESSURE
- safe_air_limits = list(
- /datum/gas/oxygen = list(2, 100),
- /datum/gas/nitrogen,
- /datum/gas/carbon_dioxide = list(0, 20),
- /datum/gas/water_vapor,
- /datum/gas/plasma = list(0, 5),
- /datum/gas/bz = list(0, 5),
- /datum/gas/miasma = list(0, 5),
- )
- evolution_types = list(/datum/fish_evolution/ice_chrab)
- compatible_types = list(/obj/item/fish/chasm_crab/ice)
- beauty = FISH_BEAUTY_GOOD
- ///This value represents how much the crab needs aren't being met. Higher values translate to a more likely hostile lobstrosity.
- var/anger = 0
- ///The lobstrosity type this matures into
- var/lob_type = /mob/living/basic/mining/lobstrosity/juvenile/lava
- ///at which rate the crab gains maturation
- var/growth_rate = 100 / (10 MINUTES) * 10
-
-/obj/item/fish/chasm_crab/Initialize(mapload, apply_qualities = TRUE)
- . = ..()
- RegisterSignal(src, COMSIG_FISH_BEFORE_GROWING, PROC_REF(growth_checks))
- RegisterSignal(src, COMSIG_FISH_FINISH_GROWING, PROC_REF(on_growth))
-
-///A chasm crab growth speed is determined by its initial weight and size, ergo bigger crabs for faster lobstrosities
-/obj/item/fish/chasm_crab/update_size_and_weight(new_size = average_size, new_weight = average_weight)
- . = ..()
- var/multiplier = 1
- switch(size)
- if(0 to FISH_SIZE_TINY_MAX)
- multiplier -= 0.2
- if(FISH_SIZE_SMALL_MAX to FISH_SIZE_NORMAL_MAX)
- multiplier += 0.2
- if(FISH_SIZE_NORMAL_MAX to FISH_SIZE_BULKY_MAX)
- multiplier += 0.5
- if(FISH_SIZE_BULKY_MAX to INFINITY)
- multiplier += 0.8
-
- if(weight <= 800)
- multiplier -= 0.1 * round((1000 - weight) / 200)
- else if(weight >= 1500)
- multiplier += min(0.1 * round((weight - 1000) / 500), 2)
-
- AddComponent(/datum/component/fish_growth, lob_type, initial(growth_rate) * multiplier)
-
-/obj/item/fish/chasm_crab/proc/growth_checks(datum/source, seconds_per_tick)
- SIGNAL_HANDLER
- var/hunger = CLAMP01((world.time - last_feeding) / feeding_frequency)
- if(health <= initial(health) * 0.6 || hunger >= 0.6) //if too hurt or hungry, don't grow.
- anger += growth_rate * 2 * seconds_per_tick
- return COMPONENT_DONT_GROW
-
- if(hunger >= 0.4) //I'm hungry and angry
- anger += growth_rate * 0.6 * seconds_per_tick
-
- if(!isaquarium(loc))
- return
-
- var/obj/structure/aquarium/aquarium = loc
- if(!aquarium.allow_breeding) //the aquarium has breeding disabled
- return COMPONENT_DONT_GROW
- if(!locate(/obj/item/aquarium_prop) in aquarium) //the aquarium deco is quite barren
- anger += growth_rate * 0.25 * seconds_per_tick
- var/fish_count = length(aquarium.get_fishes())
- if(!ISINRANGE(fish_count, 3, AQUARIUM_MAX_BREEDING_POPULATION * 0.5)) //too lonely or overcrowded
- anger += growth_rate * 0.3 * seconds_per_tick
- if(fish_count > AQUARIUM_MAX_BREEDING_POPULATION * 0.5) //check if there's enough room to maturate.
- return COMPONENT_DONT_GROW
-
-/obj/item/fish/chasm_crab/proc/on_growth(datum/source, mob/living/basic/mining/lobstrosity/juvenile/result)
- SIGNAL_HANDLER
- if(!prob(anger))
- result.AddElement(/datum/element/ai_retaliate)
- qdel(result.ai_controller)
- result.ai_controller = new /datum/ai_controller/basic_controller/lobstrosity/juvenile/calm(result)
- else if(anger < 30) //not really that mad, just a bit unstable.
- qdel(result.ai_controller)
- result.ai_controller = new /datum/ai_controller/basic_controller/lobstrosity/juvenile/capricious(result)
-
-/obj/item/fish/chasm_crab/ice
- name = "arctic chrab"
- desc = "A subspecies of chasm chrabs that has adapted to the cold climate and lack of abysmal holes of the icemoon."
- icon_state = "arctic_chrab"
- required_temperature_min = ICEBOX_MIN_TEMPERATURE-20
- required_temperature_max = MIN_AQUARIUM_TEMP+15
- evolution_types = list(/datum/fish_evolution/chasm_chrab)
- compatible_types = list(/obj/item/fish/chasm_crab)
- beauty = FISH_BEAUTY_GREAT
- lob_type = /mob/living/basic/mining/lobstrosity/juvenile
-
-/obj/item/fish/donkfish
- name = "donk co. company patent donkfish"
- desc = "A lab-grown donkfish. Its invention was an accident for the most part, as it was intended to be consumed in donk pockets. Unfortunately, it tastes horrible, so it has now become a pseudo-mascot."
- icon_state = "donkfish"
- random_case_rarity = FISH_RARITY_VERY_RARE
- required_fluid_type = AQUARIUM_FLUID_FRESHWATER
- stable_population = 4
- sprite_width = 5
- sprite_height = 4
- fillet_type = /obj/item/food/fishmeat/donkfish
- fish_traits = list(/datum/fish_trait/yucky)
- required_temperature_min = MIN_AQUARIUM_TEMP+15
- required_temperature_max = MIN_AQUARIUM_TEMP+28
- beauty = FISH_BEAUTY_EXCELLENT
-
-/obj/item/fish/emulsijack
- name = "toxic emulsijack"
- desc = "Ah, the terrifying emulsijack. Created in a laboratory, the only real use of this slimey, scaleless fish is for completely ruining a tank."
- icon_state = "emulsijack"
- random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
- required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
- stable_population = 3
- sprite_width = 7
- sprite_height = 3
- fish_traits = list(/datum/fish_trait/emulsijack)
- required_temperature_min = MIN_AQUARIUM_TEMP+5
- required_temperature_max = MIN_AQUARIUM_TEMP+40
- beauty = FISH_BEAUTY_BAD
-
-/obj/item/fish/jumpercable
- name = "monocloning jumpercable"
- desc = "A surprisingly useful if nasty looking creation from the syndicate fish labs. Drop one in a tank, and \
- watch it self-feed and multiply. Generates more and more power as a growing swarm!"
- icon_state = "jumpercable"
- sprite_width = 16
- sprite_height = 5
- stable_population = 12
- average_size = 110
- average_weight = 6000
- random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
- required_temperature_min = MIN_AQUARIUM_TEMP+10
- required_temperature_max = MIN_AQUARIUM_TEMP+30
- favorite_bait = list(/obj/item/stock_parts/power_store/cell/lead)
- fish_traits = list(
- /datum/fish_trait/parthenogenesis,
- /datum/fish_trait/mixotroph,
- /datum/fish_trait/electrogenesis,
- )
- beauty = FISH_BEAUTY_UGLY
-
-/obj/item/fish/ratfish
- name = "ratfish"
- desc = "A rat exposed to the murky waters of maintenance too long. Any higher power, if it revealed itself, would state that the ratfish's continued existence is extremely unwelcome."
- icon_state = "ratfish"
- sprite_width = 7
- sprite_height = 5
- random_case_rarity = FISH_RARITY_RARE
- required_fluid_type = AQUARIUM_FLUID_FRESHWATER
- stable_population = 10 //set by New, but this is the default config value
- fillet_type = /obj/item/food/meat/slab/human/mutant/zombie //eww...
- fish_traits = list(/datum/fish_trait/necrophage)
- required_temperature_min = MIN_AQUARIUM_TEMP+15
- required_temperature_max = MIN_AQUARIUM_TEMP+35
- fish_movement_type = /datum/fish_movement/zippy
- favorite_bait = list(
- list(
- "Type" = "Foodtype",
- "Value" = DAIRY
- )
- )
- beauty = FISH_BEAUTY_DISGUSTING
-
-/obj/item/fish/ratfish/Initialize(mapload, apply_qualities = TRUE)
- . = ..()
- //stable pop reflects the config for how many mice migrate. powerful...
- stable_population = CONFIG_GET(number/mice_roundstart)
-
-/obj/item/fish/sludgefish
- name = "sludgefish"
- desc = "A misshapen, fragile, loosely fish-like living goop, the only thing that'd ever thrive in the acidic and claustrophobic cavities of the station's organic waste disposal system."
- icon_state = "sludgefish"
- sprite_width = 7
- sprite_height = 6
- required_fluid_type = AQUARIUM_FLUID_SULPHWATEVER
- stable_population = 8
- average_size = 20
- average_weight = 400
- health = 50
- breeding_timeout = 2.5 MINUTES
- fish_traits = list(/datum/fish_trait/parthenogenesis, /datum/fish_trait/no_mating)
- required_temperature_min = MIN_AQUARIUM_TEMP+10
- required_temperature_max = MIN_AQUARIUM_TEMP+40
- evolution_types = list(/datum/fish_evolution/purple_sludgefish)
- beauty = FISH_BEAUTY_NULL
-
-/obj/item/fish/sludgefish/purple
- name = "purple sludgefish"
- desc = "A misshapen, fragile, loosely fish-like living goop. This one has developed sexual reproduction mechanisms, and a purple tint to boot."
- icon_state = "sludgefish_purple"
- random_case_rarity = FISH_RARITY_NOPE
- fish_traits = list(/datum/fish_trait/parthenogenesis)
-
-/obj/item/fish/slimefish
- name = "acquatic slime"
- desc = "Kids, this is what happens when a slime overcomes its hydrophobic nature. It goes glug glug."
- icon_state = "slimefish"
- icon_state_dead = "slimefish_dead"
- sprite_width = 7
- sprite_height = 7
- do_flop_animation = FALSE //it already has a cute bouncy wiggle. :3
- random_case_rarity = FISH_RARITY_VERY_RARE
- required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
- stable_population = 4
- health = 150
- fillet_type = /obj/item/slime_extract/grey
- grind_results = list(/datum/reagent/toxin/slimejelly = 10)
- fish_traits = list(/datum/fish_trait/toxin_immunity, /datum/fish_trait/crossbreeder)
- favorite_bait = list(
- list(
- "Type" = "Foodtype",
- "Value" = TOXIC,
- ),
- list(
- "Type" = "Reagent",
- "Value" = /datum/reagent/toxin,
- "Amount" = 5,
- ),
- )
- required_temperature_min = MIN_AQUARIUM_TEMP+20
- beauty = FISH_BEAUTY_GREAT
-
-/obj/item/fish/boned
- name = "unmarine bonemass"
- desc = "What one could mistake for fish remains, is in reality a species that chose to discard its weak flesh a long time ago. A living fossil, in its most literal sense."
- icon_state = "bonemass"
- sprite_width = 10
- sprite_height = 7
- fish_movement_type = /datum/fish_movement/zippy
- random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
- required_fluid_type = AQUARIUM_FLUID_ANY_WATER
- min_pressure = HAZARD_LOW_PRESSURE
- health = 150
- stable_population = 3
- grind_results = list(/datum/reagent/bone_dust = 10)
- fillet_type = /obj/item/stack/sheet/bone
- num_fillets = 2
- fish_traits = list(/datum/fish_trait/revival, /datum/fish_trait/carnivore)
- average_size = 70
- average_weight = 2000
- death_text = "%SRC stops moving." //It's dead... or is it?
- evolution_types = list(/datum/fish_evolution/mastodon)
- beauty = FISH_BEAUTY_UGLY
-
-/obj/item/fish/mastodon
- name = "unmarine mastodon"
- desc = "A monster of exposed muscles and innards, wrapped in a fish-like skeleton. You don't remember ever seeing it on the catalog."
- icon = 'icons/obj/aquarium/wide.dmi'
- icon_state = "mastodon"
- base_pixel_x = -16
- pixel_x = -16
- sprite_width = 12
- sprite_height = 7
- show_in_catalog = FALSE
- random_case_rarity = FISH_RARITY_NOPE
- fishing_difficulty_modifier = 30
- required_fluid_type = AQUARIUM_FLUID_ANY_WATER
- min_pressure = HAZARD_LOW_PRESSURE
- health = 300
- stable_population = 1 //This means they can only crossbreed.
- grind_results = list(/datum/reagent/bone_dust = 5, /datum/reagent/consumable/liquidgibs = 5)
- fillet_type = /obj/item/stack/sheet/bone
- num_fillets = 2
- feeding_frequency = 2 MINUTES
- breeding_timeout = 5 MINUTES
- average_size = 180
- average_weight = 5000
- death_text = "%SRC stops moving."
- fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/amphibious, /datum/fish_trait/revival, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/aggressive)
- beauty = FISH_BEAUTY_BAD
-
-/obj/item/fish/holo
- name = "holographic goldfish"
- desc = "A holographic representation of a common goldfish, slowly flickering out, removed from its holo-habitat."
- icon_state = /obj/item/fish/goldfish::icon_state
- show_in_catalog = FALSE
- random_case_rarity = FISH_RARITY_NOPE
- dedicated_in_aquarium_icon_state = /obj/item/fish/goldfish::dedicated_in_aquarium_icon_state
- aquarium_vc_color = /obj/item/fish/goldfish::aquarium_vc_color
- sprite_width = /obj/item/fish/goldfish::sprite_width
- sprite_height = /obj/item/fish/goldfish::sprite_height
- stable_population = 1
- average_size = /obj/item/fish/goldfish::average_size
- average_weight = /obj/item/fish/goldfish::average_weight
- required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
- grind_results = null
- fillet_type = null
- death_text = "%SRC gently disappears."
- fish_traits = list(/datum/fish_trait/no_mating) //just to be sure, these shouldn't reproduce
- experisci_scannable = FALSE
-
-/obj/item/fish/holo/Initialize(mapload, apply_qualities = TRUE)
- . = ..()
- var/area/station/holodeck/holo_area = get_area(src)
- if(!istype(holo_area))
- addtimer(CALLBACK(src, PROC_REF(set_status), FISH_DEAD), 1 MINUTES)
- return
- holo_area.linked.add_to_spawned(src)
-
-/obj/item/fish/holo/set_status(new_status, silent = FALSE)
- . = ..()
- if(status == FISH_DEAD)
- animate(src, alpha = 0, 3 SECONDS, easing = SINE_EASING)
- QDEL_IN(src, 3 SECONDS)
-
-/obj/item/fish/holo/crab
- name = "holographic crab"
- desc = "A holographic represantion of a soul-crushingly soulless crab, unlike the cuter ones occasionally roaming around. It stares at you, with empty, beady eyes."
- icon_state = "crab"
- dedicated_in_aquarium_icon_state = null
- aquarium_vc_color = null
- average_size = 30
- average_weight = 1000
- sprite_height = 6
- sprite_width = 10
-
-/obj/item/fish/holo/puffer
- name = "holographic pufferfish"
- desc ="A holographic representation of 100% safe-to-eat pufferfish... that is, if holographic fishes were even edible."
- icon_state = /obj/item/fish/pufferfish::icon_state
- dedicated_in_aquarium_icon_state = /obj/item/fish/pufferfish::dedicated_in_aquarium_icon_state
- aquarium_vc_color = /obj/item/fish/pufferfish::aquarium_vc_color
- average_size = /obj/item/fish/pufferfish::average_size
- average_weight = /obj/item/fish/pufferfish::average_weight
- sprite_height = /obj/item/fish/pufferfish::sprite_height
- sprite_width = /obj/item/fish/pufferfish::sprite_width
- beauty = FISH_BEAUTY_GOOD
-
-/obj/item/fish/holo/angel
- name = "holographic angelfish"
- desc = "A holographic representation of a angelfish. I got nothing snarky to say about this one."
- icon_state = /obj/item/fish/angelfish::icon_state
- dedicated_in_aquarium_icon_state = /obj/item/fish/angelfish::dedicated_in_aquarium_icon_state
- aquarium_vc_color = /obj/item/fish/angelfish::aquarium_vc_color
- average_size = /obj/item/fish/angelfish::average_size
- average_weight = /obj/item/fish/angelfish::average_weight
- sprite_height = /obj/item/fish/angelfish::sprite_height
- sprite_width = /obj/item/fish/angelfish::sprite_width
-
-/obj/item/fish/holo/clown
- name = "holographic clownfish"
- icon_state = "holo_clownfish"
- desc = "A holographic representation of a clownfish, or at least how they used to look like five centuries ago."
- dedicated_in_aquarium_icon_state = null
- aquarium_vc_color = /obj/item/fish/clownfish::aquarium_vc_color
- average_size = /obj/item/fish/clownfish::average_size
- average_weight = /obj/item/fish/clownfish::average_weight
- sprite_height = /obj/item/fish/clownfish::sprite_height
- sprite_width = /obj/item/fish/clownfish::sprite_width
-
-/obj/item/fish/holo/checkered
- name = "unrendered holographic fish"
- desc = "A checkered silhoutte of searing purple and pitch black presents itself before your eyes, like a tear in fabric of reality. It hurts to watch."
- icon_state = "checkered" //it's a meta joke, buddy.
- dedicated_in_aquarium_icon_state = null
- aquarium_vc_color = null
- average_size = 30
- average_weight = 500
- sprite_width = 4
- sprite_height = 3
- beauty = FISH_BEAUTY_NULL
-
-/obj/item/fish/holo/halffish
- name = "holographic half-fish"
- desc = "A holographic representation of... a fish reduced to all bones, except for its head. Isn't it supposed to be dead? Ehr, holo-dead?"
- icon_state = "half_fish"
- dedicated_in_aquarium_icon_state = null
- aquarium_vc_color = null
- sprite_height = 4
- sprite_width = 10
- average_size = 50
- average_weight = 500
- beauty = FISH_BEAUTY_UGLY
-
-/obj/item/fish/starfish
- name = "cosmostarfish"
- desc = "A peculiar, gravity-defying, echinoderm-looking critter from hyperspace."
- icon_state = "starfish"
- icon_state_dead = "starfish_dead"
- sprite_width = 4
- sprite_height = 3
- average_size = 30
- average_weight = 300
- stable_population = 3
- required_fluid_type = AQUARIUM_FLUID_AIR
- random_case_rarity = FISH_RARITY_NOPE
- required_temperature_min = 0
- required_temperature_max = INFINITY
- safe_air_limits = null
- min_pressure = 0
- max_pressure = INFINITY
- grind_results = list(/datum/reagent/bluespace = 10)
- fillet_type = null
- fish_traits = list(/datum/fish_trait/antigrav, /datum/fish_trait/mixotroph)
- beauty = FISH_BEAUTY_GREAT
-
-/obj/item/fish/starfish/Initialize(mapload, apply_qualities = TRUE)
- . = ..()
- update_appearance(UPDATE_OVERLAYS)
-
-/obj/item/fish/starfish/update_overlays()
- . = ..()
- if(status == FISH_ALIVE)
- . += emissive_appearance(icon, "starfish_emissive", src)
-
-///It spins, and dimly glows in the dark.
-/obj/item/fish/starfish/flop_animation()
- DO_FLOATING_ANIM(src)
-
-/obj/item/fish/lavaloop
- name = "lavaloop fish"
- desc = "Due to its curvature, it can be used as make-shift boomerang."
- icon_state = "lava_loop"
- sprite_width = 3
- sprite_height = 5
- average_size = 30
- average_weight = 500
- resistance_flags = FIRE_PROOF | LAVA_PROOF
- required_fluid_type = AQUARIUM_FLUID_ANY_WATER //if we can survive hot lava and freezing plasrivers, we can survive anything
- fish_movement_type = /datum/fish_movement/zippy
- min_pressure = HAZARD_LOW_PRESSURE
- required_temperature_min = MIN_AQUARIUM_TEMP+30
- required_temperature_max = MIN_AQUARIUM_TEMP+35
- fish_traits = list(
- /datum/fish_trait/carnivore,
- /datum/fish_trait/heavy,
- )
- hitsound = null
- throwforce = 5
- beauty = FISH_BEAUTY_GOOD
- ///maximum bonus damage when winded up
- var/maximum_bonus = 25
-
-/obj/item/fish/lavaloop/Initialize(mapload, apply_qualities = TRUE)
- . = ..()
- ADD_TRAIT(src, TRAIT_BYPASS_RANGED_ARMOR, INNATE_TRAIT)
- AddComponent(/datum/component/boomerang, throw_range, TRUE)
- AddComponent(\
- /datum/component/throwbonus_on_windup,\
- maximum_bonus = maximum_bonus,\
- windup_increment_speed = 2,\
- throw_text = "starts cooking in your hands, it may explode soon!",\
- pass_maximum_callback = CALLBACK(src, PROC_REF(explode_on_user)),\
- apply_bonus_callback = CALLBACK(src, PROC_REF(on_fish_land)),\
- sound_on_success = 'sound/weapons/parry.ogg',\
- effect_on_success = /obj/effect/temp_visual/guardian/phase,\
- )
-
-/obj/item/fish/lavaloop/proc/explode_on_user(mob/living/user)
- var/obj/item/bodypart/arm/active_arm = user.get_active_hand()
- active_arm?.dismember()
- to_chat(user, span_warning("[src] explodes!"))
- playsound(src, 'sound/effects/explosion1.ogg', 40, TRUE)
- user.flash_act(1, 1)
- qdel(src)
-
-/obj/item/fish/lavaloop/proc/on_fish_land(mob/living/target, bonus_value)
- if(!istype(target))
- return FALSE
- return (target.mob_size >= MOB_SIZE_LARGE)
-
-/obj/item/fish/lavaloop/plasma_river
- maximum_bonus = 30
-
-/obj/item/fish/lavaloop/plasma_river/explode_on_user(mob/living/user)
- playsound(src, 'sound/effects/explosion1.ogg', 40, TRUE)
- user.flash_act(1, 1)
- user.apply_status_effect(/datum/status_effect/ice_block_talisman, 5 SECONDS)
- qdel(src)
-
-/obj/item/fish/lavaloop/plasma_river/on_fish_land(mob/living/target, bonus_value)
- if(!istype(target))
- return FALSE
- if(target.mob_size < MOB_SIZE_LARGE)
- return FALSE
- var/freeze_timer = (bonus_value * 0.1)
- if(freeze_timer <= 0)
- return FALSE
- target.apply_status_effect(/datum/status_effect/ice_block_talisman, freeze_timer SECONDS)
- return FALSE
-
-/obj/item/fish/zipzap
- name = "anxious zipzap"
- desc = "A fish overflowing with crippling anxiety and electric potential. Worried about the walls of its tank closing in constantly. Both literally and as a general metaphorical unease about life's direction."
- icon_state = "zipzap"
- icon_state_dead = "zipzap_dead"
- sprite_width = 6
- sprite_height = 3
- stable_population = 3
- average_size = 30
- average_weight = 500
- random_case_rarity = FISH_RARITY_VERY_RARE
- favorite_bait = list(/obj/item/stock_parts/power_store/cell/lead)
- required_temperature_min = MIN_AQUARIUM_TEMP+18
- required_temperature_max = MIN_AQUARIUM_TEMP+26
- fish_traits = list(
- /datum/fish_trait/no_mating,
- /datum/fish_trait/wary,
- /datum/fish_trait/anxiety,
- /datum/fish_trait/electrogenesis,
- )
- //anxiety naturally limits the amount of zipzaps per tank, so they are stronger alone
- electrogenesis_power = 20 MEGA JOULES
- beauty = FISH_BEAUTY_GOOD
-
-/obj/item/fish/sockeye_salmon
- name = "sockeye salmon"
- desc = "A fairly common and iconic salmon endemic of the Pacific Ocean. At some point imported into outer space, where we're now."
- icon_state = "sockeye"
- sprite_width = 6
- sprite_height = 4
- stable_population = 6
- required_temperature_min = MIN_AQUARIUM_TEMP+3
- required_temperature_max = MIN_AQUARIUM_TEMP+19
- required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
- fillet_type = /obj/item/food/fishmeat/salmon
- beauty = FISH_BEAUTY_GOOD
-
-/obj/item/fish/arctic_char
- name = "arctic char"
- desc = "A cold-water anadromous fish widespread around the Northern Hemisphere of Earth, yet it has somehow found a way here."
- icon_state = "arctic_char"
- sprite_width = 7
- sprite_height = 4
- stable_population = 6
- average_size = 60
- average_weight = 1200
- weight_size_deviation = 0.5 // known for their size dismophism
- required_temperature_min = MIN_AQUARIUM_TEMP+3
- required_temperature_max = MIN_AQUARIUM_TEMP+19
- required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
-
-/obj/item/fish/stingray
- name = "stingray"
- desc = "A type of ray, most known for its venomous stinger. Despite that, They're normally docile, if not a bit easily frightened."
- icon_state = "stingray"
- stable_population = 4
- sprite_height = 7
- sprite_width = 8
- average_size = 60
- average_weight = 700
- beauty = FISH_BEAUTY_GREAT
- random_case_rarity = FISH_RARITY_RARE
- required_fluid_type = AQUARIUM_FLUID_SALTWATER //Someone ought to add river rays later I guess.
- fish_traits = list(/datum/fish_trait/stinger, /datum/fish_trait/toxic_barbs, /datum/fish_trait/wary, /datum/fish_trait/carnivore, /datum/fish_trait/predator)
-
-/obj/item/fish/sand_surfer
- name = "sand surfer"
- desc = "A bronze alien \"fish\" living and swimming underneath faraway sandy places."
- icon_state = "sand_surfer"
- sprite_height = 6
- sprite_width = 6
- stable_population = 5
- average_size = 65
- average_weight = 1100
- weight_size_deviation = 0.35
- random_case_rarity = FISH_RARITY_RARE
- required_fluid_type = AQUARIUM_FLUID_AIR
- required_temperature_min = MIN_AQUARIUM_TEMP+25
- required_temperature_max = MIN_AQUARIUM_TEMP+60
- fish_movement_type = /datum/fish_movement/plunger
- fishing_difficulty_modifier = 5
- fish_traits = list(/datum/fish_trait/shiny_lover)
- beauty = FISH_BEAUTY_GOOD
-
-/obj/item/fish/sand_crab
- name = "burrower crab"
- desc = "A sand-dwelling crustacean. It looks like a crab and tastes like a crab, but waddles like a fish."
- icon_state = "crab"
- sprite_height = 6
- sprite_width = 10
- average_size = 60
- average_weight = 1000
- weight_size_deviation = 0.1
- required_fluid_type = AQUARIUM_FLUID_SALTWATER
- required_temperature_min = MIN_AQUARIUM_TEMP+20
- required_temperature_max = MIN_AQUARIUM_TEMP+40
- fillet_type = /obj/item/food/meat/slab/rawcrab
- fish_traits = list(/datum/fish_trait/amphibious, /datum/fish_trait/shiny_lover, /datum/fish_trait/carnivore)
- fish_movement_type = /datum/fish_movement/slow
- favorite_bait = list(
- list(
- "Type" = "Foodtype",
- "Value" = SEAFOOD,
- ),
- )
-
-/obj/item/fish/bumpy
- name = "bump-fish"
- desc = "An misshapen fish-thing all covered in stubby little tendrils"
- icon_state = "bumpy"
- sprite_height = 4
- sprite_width = 5
- stable_population = 4
- required_fluid_type = AQUARIUM_FLUID_ANY_WATER
- required_temperature_min = MIN_AQUARIUM_TEMP+15
- required_temperature_max = MIN_AQUARIUM_TEMP+40
- beauty = FISH_BEAUTY_BAD
- fish_traits = list(/datum/fish_trait/amphibious, /datum/fish_trait/vegan)
- favorite_bait = list(
- list(
- "Type" = "Foodtype",
- "Value" = VEGETABLES,
- ),
- )
-
-/obj/item/fish/swordfish
- name = "swordfish"
- desc = "A large billfish, most famous for its elongated bill, while also fairly popular for cooking, and as a fearsome weapon in the hands of a veteran spess-fisherman."
- icon = 'icons/obj/aquarium/wide.dmi'
- icon_state = "swordfish"
- inhand_icon_state = "swordfish"
- force = 18
- sharpness = SHARP_EDGED
- attack_verb_continuous = list("slashes", "cuts", "pierces")
- attack_verb_simple = list("slash", "cut", "pierce")
- block_sound = 'sound/weapons/parry.ogg'
- hitsound = 'sound/weapons/rapierhit.ogg'
- demolition_mod = 0.75
- attack_speed = 1 SECONDS
- block_chance = 50
- wound_bonus = 10
- bare_wound_bonus = 20
- armour_penetration = 75
- base_pixel_x = -18
- pixel_x = -18
- sprite_width = 13
- sprite_height = 6
- stable_population = 3
- average_size = 140
- average_weight = 4000
- breeding_timeout = 4.5 MINUTES
- feeding_frequency = 4 MINUTES
- health = 180
- beauty = FISH_BEAUTY_EXCELLENT
- random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
- required_fluid_type = AQUARIUM_FLUID_SALTWATER
- fish_movement_type = /datum/fish_movement/plunger
- fishing_difficulty_modifier = 25
- fillet_type = /obj/item/food/fishmeat/quality
- favorite_bait = list(
- list(
- "Type" = "Foodtype",
- "Value" = SEAFOOD,
- ),
- )
- fish_traits = list(/datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/stinger)
-
-/obj/item/fish/swordfish/get_force_rank()
- switch(w_class)
- if(WEIGHT_CLASS_TINY)
- force -= 11
- attack_speed -= 0.4 SECONDS
- block_chance -= 45
- armour_penetration -= 20
- wound_bonus -= 15
- bare_wound_bonus -= 20
- if(WEIGHT_CLASS_SMALL)
- force -= 8
- attack_speed -= 0.3 SECONDS
- block_chance -= 30
- armour_penetration -= 15
- wound_bonus -= 10
- bare_wound_bonus -= 20
- if(WEIGHT_CLASS_NORMAL)
- force -= 5
- attack_speed -= 0.2 SECONDS
- block_chance -= 20
- armour_penetration -= 10
- wound_bonus -= 10
- bare_wound_bonus -= 15
- if(WEIGHT_CLASS_BULKY)
- force -= 3
- attack_speed -= 0.1 SECONDS
- block_chance -= 10
- armour_penetration -= 5
- wound_bonus -= 5
- bare_wound_bonus -= 10
- if(WEIGHT_CLASS_GIGANTIC)
- force += 5
- attack_speed += 0.2 SECONDS
- demolition_mod += 0.15
- block_chance += 10
- armour_penetration += 5
- wound_bonus += 5
- bare_wound_bonus += 10
-
- if(status == FISH_DEAD)
- force -= 4 + w_class
- block_chance -= 25
- armour_penetration -= 30
- wound_bonus -= 10
- bare_wound_bonus -= 10
-
-/obj/item/fish/swordfish/calculate_fish_force_bonus(bonus_malus)
- . = ..()
- armour_penetration += bonus_malus * 5
- wound_bonus += bonus_malus * 3
- bare_wound_bonus += bonus_malus * 5
- block_chance += bonus_malus * 7
-
-/obj/item/fish/chainsawfish
- name = "chainsawfish"
- desc = "A very, very angry bioweapon, whose sole purpose is to rip and tear."
- icon = 'icons/obj/aquarium/wide.dmi'
- icon_state = "chainsawfish"
- inhand_icon_state = "chainsawfish"
- icon_state_dead = "chainsawfish_dead"
- force = 22
- demolition_mod = 1.5
- block_chance = 15
- attack_verb_continuous = list("saws", "tears", "lacerates", "cuts", "chops", "dices")
- attack_verb_simple = list("saw", "tear", "lacerate", "cut", "chop", "dice")
- hitsound = 'sound/weapons/chainsawhit.ogg'
- sharpness = SHARP_EDGED
- tool_behaviour = TOOL_SAW
- toolspeed = 0.5
- base_pixel_x = -16
- pixel_x = -16
- sprite_width = 8
- sprite_height = 5
- stable_population = 3
- average_size = 85
- average_weight = 2500
- breeding_timeout = 4.25 MINUTES
- feeding_frequency = 3 MINUTES
- health = 180
- beauty = FISH_BEAUTY_GREAT
- random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
- required_fluid_type = AQUARIUM_FLUID_FRESHWATER
- fish_movement_type = /datum/fish_movement/accelerando
- fishing_difficulty_modifier = 30
- favorite_bait = list(
- list(
- "Type" = "Foodtype",
- "Value" = GORE
- ),
- )
- fish_traits = list(/datum/fish_trait/aggressive, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/stinger)
- required_temperature_min = MIN_AQUARIUM_TEMP+18
- required_temperature_max = MIN_AQUARIUM_TEMP+26
-
-/obj/item/fish/chainsawfish/Initialize(mapload)
- . = ..()
- AddElement(/datum/element/update_icon_updates_onmob)
-
-/obj/item/fish/chainsawfish/update_icon_state()
- if(status == FISH_DEAD)
- inhand_icon_state = "chainsawfish_dead"
- else
- inhand_icon_state = "chainsawfish"
- if(HAS_TRAIT(src, TRAIT_WIELDED))
- inhand_icon_state = "[inhand_icon_state]_wielded"
- return ..()
-
-/obj/item/fish/chainsawfish/get_force_rank()
- switch(w_class)
- if(WEIGHT_CLASS_TINY)
- force -= 10
- attack_speed -= 0.2 SECONDS
- demolition_mod -= 0.4
- block_chance -= 15
- armour_penetration -= 10
- wound_bonus -= 10
- bare_wound_bonus -= 10
- toolspeed += 0.6
- if(WEIGHT_CLASS_SMALL)
- force -= 8
- attack_speed -= 0.1 SECONDS
- demolition_mod -= 0.3
- block_chance -= 10
- armour_penetration -= 10
- wound_bonus -= 10
- bare_wound_bonus -= 10
- toolspeed += 0.4
- if(WEIGHT_CLASS_NORMAL)
- force -= 5
- demolition_mod -= 0.15
- block_chance -= 5
- armour_penetration -= 5
- wound_bonus -= 5
- bare_wound_bonus -= 5
- toolspeed += 0.2
- if(WEIGHT_CLASS_HUGE)
- force += 2
- attack_speed += 0.2 SECONDS
- demolition_mod += 0.15
- armour_penetration += 10
- block_chance += 10
- wound_bonus += 10
- bare_wound_bonus += 5
- if(WEIGHT_CLASS_GIGANTIC)
- force += 4
- attack_speed += 0.4 SECONDS
- demolition_mod += 0.3
- block_chance += 20
- armour_penetration += 20
- wound_bonus += 15
- bare_wound_bonus += 10
- toolspeed -= 0.1
-
- if(status == FISH_DEAD)
- force -= 8 + w_class
- hitsound = SFX_SWING_HIT
- block_chance -= 25
- demolition_mod -= 0.3
- armour_penetration -= 15
- wound_bonus -= 5
- bare_wound_bonus -= 5
- toolspeed += 1
-
-/obj/item/fish/chainsawfish/calculate_fish_force_bonus(bonus_malus)
- . = ..()
- armour_penetration += bonus_malus * 3
- wound_bonus += bonus_malus * 2
- bare_wound_bonus += bonus_malus * 3
- block_chance += bonus_malus * 2
- toolspeed -= bonus_malus * 0.1
-
-/obj/item/fish/soul
- name = "soulfish"
- desc = "A distant yet vaguely close critter, like a long lost relative. You feel your soul rejuvenated just from looking at it... Also, what the fuck is this shit?!"
- icon_state = "soulfish"
- sprite_width = 7
- sprite_height = 6
- average_size = 60
- average_weight = 1200
- stable_population = 4
- show_in_catalog = FALSE
- beauty = FISH_BEAUTY_EXCELLENT
- fish_movement_type = /datum/fish_movement/choppy //Glideless legacy movement? in my fishing minigame?
- favorite_bait = list(
- list(
- "Type" = "Foodtype",
- "Value" = FRIED
- ),
- )
- fillet_type = /obj/item/food/meat/cutlet/plain/human
- required_temperature_min = MIN_AQUARIUM_TEMP+3
- required_temperature_max = MIN_AQUARIUM_TEMP+38
- random_case_rarity = FISH_RARITY_NOPE
-
-/obj/item/fish/skin_crab
- name = "skin crab"
- desc = "\"And on the eighth day, a demential mockery of both humanity and crabity was made.\" Fascinating."
- icon_state = "skin_crab"
- sprite_width = 7
- sprite_height = 6
- average_size = 40
- average_weight = 750
- stable_population = 5
- show_in_catalog = FALSE
- beauty = FISH_BEAUTY_GREAT
- favorite_bait = list(
- list(
- "Type" = "Foodtype",
- "Value" = FRIED
- ),
- )
- fillet_type = /obj/item/food/meat/slab/rawcrab
- random_case_rarity = FISH_RARITY_NOPE
diff --git a/code/modules/fishing/fish/types/air_space.dm b/code/modules/fishing/fish/types/air_space.dm
new file mode 100644
index 0000000000000..dda3794ff4e1c
--- /dev/null
+++ b/code/modules/fishing/fish/types/air_space.dm
@@ -0,0 +1,101 @@
+/obj/item/fish/sand_surfer
+ name = "sand surfer"
+ desc = "A bronze alien \"fish\" living and swimming underneath faraway sandy places."
+ icon_state = "sand_surfer"
+ sprite_height = 6
+ sprite_width = 6
+ stable_population = 5
+ average_size = 65
+ average_weight = 1100
+ weight_size_deviation = 0.35
+ random_case_rarity = FISH_RARITY_RARE
+ required_fluid_type = AQUARIUM_FLUID_AIR
+ required_temperature_min = MIN_AQUARIUM_TEMP+25
+ required_temperature_max = MIN_AQUARIUM_TEMP+60
+ fish_movement_type = /datum/fish_movement/plunger
+ fishing_difficulty_modifier = 5
+ fish_traits = list(/datum/fish_trait/shiny_lover)
+ beauty = FISH_BEAUTY_GOOD
+
+/obj/item/fish/sand_crab
+ name = "burrower crab"
+ desc = "A sand-dwelling crustacean. It looks like a crab and tastes like a crab, but waddles like a fish."
+ icon_state = "crab"
+ dedicated_in_aquarium_icon_state = "crab_small"
+ sprite_height = 6
+ sprite_width = 10
+ average_size = 60
+ average_weight = 1000
+ weight_size_deviation = 0.1
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ required_temperature_min = MIN_AQUARIUM_TEMP+20
+ required_temperature_max = MIN_AQUARIUM_TEMP+40
+ fillet_type = /obj/item/food/meat/slab/rawcrab
+ fish_traits = list(/datum/fish_trait/amphibious, /datum/fish_trait/shiny_lover, /datum/fish_trait/carnivore)
+ fish_movement_type = /datum/fish_movement/slow
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = SEAFOOD,
+ ),
+ )
+
+/obj/item/fish/sand_crab/get_fish_taste()
+ return list("raw crab" = 2)
+
+/obj/item/fish/sand_crab/get_fish_taste_cooked()
+ return list("cooked crab" = 2)
+
+/obj/item/fish/bumpy
+ name = "bump-fish"
+ desc = "An misshapen fish-thing all covered in stubby little tendrils"
+ icon_state = "bumpy"
+ sprite_height = 4
+ sprite_width = 5
+ stable_population = 4
+ required_fluid_type = AQUARIUM_FLUID_ANY_WATER
+ required_temperature_min = MIN_AQUARIUM_TEMP+15
+ required_temperature_max = MIN_AQUARIUM_TEMP+40
+ beauty = FISH_BEAUTY_BAD
+ fish_traits = list(/datum/fish_trait/amphibious, /datum/fish_trait/vegan)
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = VEGETABLES,
+ ),
+ )
+
+/obj/item/fish/starfish
+ name = "cosmostarfish"
+ desc = "A peculiar, gravity-defying, echinoderm-looking critter from hyperspace."
+ icon_state = "starfish"
+ icon_state_dead = "starfish_dead"
+ sprite_height = 3
+ sprite_width = 4
+ average_size = 30
+ average_weight = 300
+ stable_population = 3
+ required_fluid_type = AQUARIUM_FLUID_AIR
+ random_case_rarity = FISH_RARITY_NOPE
+ required_temperature_min = 0
+ required_temperature_max = INFINITY
+ safe_air_limits = null
+ min_pressure = 0
+ max_pressure = INFINITY
+ grind_results = list(/datum/reagent/bluespace = 10)
+ fillet_type = null
+ fish_traits = list(/datum/fish_trait/antigrav, /datum/fish_trait/mixotroph)
+ beauty = FISH_BEAUTY_GREAT
+
+/obj/item/fish/starfish/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ update_appearance(UPDATE_OVERLAYS)
+
+/obj/item/fish/starfish/update_overlays()
+ . = ..()
+ if(status == FISH_ALIVE)
+ . += emissive_appearance(icon, "starfish_emissive", src)
+
+///It spins, and dimly glows in the dark.
+/obj/item/fish/starfish/flop_animation()
+ DO_FLOATING_ANIM(src)
diff --git a/code/modules/fishing/fish/types/anadromous.dm b/code/modules/fishing/fish/types/anadromous.dm
new file mode 100644
index 0000000000000..5afb2cb48ce5a
--- /dev/null
+++ b/code/modules/fishing/fish/types/anadromous.dm
@@ -0,0 +1,68 @@
+/obj/item/fish/sockeye_salmon
+ name = "sockeye salmon"
+ desc = "A fairly common and iconic salmon endemic of the Pacific Ocean. At some point imported into outer space, where we're now."
+ icon_state = "sockeye"
+ sprite_width = 6
+ sprite_height = 4
+ stable_population = 6
+ required_temperature_min = MIN_AQUARIUM_TEMP+3
+ required_temperature_max = MIN_AQUARIUM_TEMP+19
+ required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
+ fillet_type = /obj/item/food/fishmeat/salmon
+ beauty = FISH_BEAUTY_GOOD
+
+/obj/item/fish/sockeye_salmon/get_base_edible_reagents_to_add()
+ var/return_list = ..()
+ return_list[/datum/reagent/consumable/nutriment/fat] = 1
+ return return_list
+
+/obj/item/fish/arctic_char
+ name = "arctic char"
+ desc = "A cold-water anadromous fish widespread around the Northern Hemisphere of Earth, yet it has somehow found a way here."
+ icon_state = "arctic_char"
+ sprite_width = 7
+ sprite_height = 4
+ stable_population = 6
+ average_size = 60
+ average_weight = 1200
+ weight_size_deviation = 0.5 // known for their size dismophism
+ required_temperature_min = MIN_AQUARIUM_TEMP+3
+ required_temperature_max = MIN_AQUARIUM_TEMP+19
+ required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
+
+/obj/item/fish/pike
+ name = "pike"
+ desc = "A long-bodied predator with a snout that almost looks like a beak. Definitely not a weapon to swing around."
+ icon = 'icons/obj/aquarium/wide.dmi'
+ icon_state = "pike"
+ inhand_icon_state = "pike"
+ base_pixel_x = -16
+ pixel_x = -16
+ stable_population = 4
+ sprite_width = 10
+ sprite_height = 3
+ average_size = 100
+ average_weight = 2000
+ breeding_timeout = 4 MINUTES
+ health = 150
+ beauty = FISH_BEAUTY_GOOD
+ required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
+ random_case_rarity = FISH_RARITY_RARE
+ fish_movement_type = /datum/fish_movement/plunger
+ fishing_difficulty_modifier = 10
+ required_temperature_min = MIN_AQUARIUM_TEMP+12
+ required_temperature_max = MIN_AQUARIUM_TEMP+27
+ fish_traits = list(/datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/aggressive)
+ evolution_types = list(/datum/fish_evolution/armored_pike)
+ compatible_types = list(/obj/item/fish/pike/armored)
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = SEAFOOD|MEAT,
+ ),
+ /obj/item/fish,
+ )
+
+/obj/item/fish/pike/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_FISH_SHOULD_TWOHANDED, INNATE_TRAIT)
diff --git a/code/modules/fishing/fish/types/freshwater.dm b/code/modules/fishing/fish/types/freshwater.dm
new file mode 100644
index 0000000000000..fe0ff437c7d8c
--- /dev/null
+++ b/code/modules/fishing/fish/types/freshwater.dm
@@ -0,0 +1,228 @@
+/obj/item/fish/goldfish
+ name = "goldfish"
+ desc = "Despite common belief, goldfish do not have three-second memories. \
+ They can actually remember things that happened up to three months ago."
+ icon_state = "goldfish"
+ dedicated_in_aquarium_icon_state = "fish_greyscale"
+ aquarium_vc_color = "#D8540D"
+ sprite_width = 5
+ sprite_height = 3
+ stable_population = 9
+ average_size = 20
+ average_weight = 200
+ weight_size_deviation = 0.35
+ favorite_bait = list(/obj/item/food/bait/worm)
+ required_temperature_min = MIN_AQUARIUM_TEMP+18
+ required_temperature_max = MIN_AQUARIUM_TEMP+26
+ evolution_types = list(/datum/fish_evolution/three_eyes, /datum/fish_evolution/chainsawfish)
+ compatible_types = list(/obj/item/fish/goldfish/gill, /obj/item/fish/goldfish/three_eyes, /obj/item/fish/goldfish/three_eyes/gill)
+
+/obj/item/fish/goldfish/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ add_traits(list(TRAIT_FISHING_BAIT, TRAIT_GOOD_QUALITY_BAIT), INNATE_TRAIT)
+
+/obj/item/fish/goldfish/gill
+ name = "McGill"
+ desc = "A great rubber duck tool for Lawyers who can't get a grasp over their case."
+ stable_population = 1
+ random_case_rarity = FISH_RARITY_NOPE
+ fish_flags = parent_type::fish_flags & ~FISH_FLAG_SHOW_IN_CATALOG
+ beauty = FISH_BEAUTY_GOOD
+ compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/goldfish/three_eyes)
+ fish_traits = list(/datum/fish_trait/recessive)
+
+/obj/item/fish/goldfish/gill/get_fish_taste()
+ return list("raw fish" = 2.5, "objection" = 1)
+
+/obj/item/fish/goldfish/three_eyes
+ name = "three-eyed goldfish"
+ desc = "A goldfish with an extra half a pair of eyes. You wonder what it's been feeding on lately..."
+ icon_state = "three_eyes"
+ stable_population = 4
+ fish_traits = list(/datum/fish_trait/recessive, /datum/fish_trait/shiny_lover)
+ compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/goldfish/gill, /obj/item/fish/goldfish/three_eyes/gill)
+ beauty = FISH_BEAUTY_GOOD
+ fishing_difficulty_modifier = 10
+ random_case_rarity = FISH_RARITY_VERY_RARE
+ food = /datum/reagent/toxin/mutagen
+ favorite_bait = list(
+ list(
+ "Type" = "Reagent",
+ "Value" = /datum/reagent/toxin/mutagen,
+ "Amount" = 3,
+ ),
+ )
+
+/obj/item/fish/goldfish/three_eyes/get_fish_taste()
+ return list("raw fish" = 2.5, "chemical waste" = 0.5)
+
+/obj/item/fish/goldfish/three_eyes/gill
+ name = "McGill"
+ desc = "A great rubber duck tool for Lawyers who can't get a grasp over their case. It looks kinda different today..."
+ compatible_types = list(/obj/item/fish/goldfish, /obj/item/fish/goldfish/three_eyes)
+ beauty = FISH_BEAUTY_GREAT
+ fish_flags = parent_type::fish_flags & ~FISH_FLAG_SHOW_IN_CATALOG
+ stable_population = 1
+ random_case_rarity = FISH_RARITY_NOPE
+
+/obj/item/fish/goldfish/three_eyes/gill/get_fish_taste()
+ return list("raw fish" = 2.5, "objection" = 1)
+
+/obj/item/fish/angelfish
+ name = "angelfish"
+ desc = "Young Angelfish often live in groups, while adults prefer solitary life. They become territorial and aggressive toward other fish when they reach adulthood."
+ icon_state = "angelfish"
+ sprite_width = 4
+ sprite_height = 7
+ average_size = 30
+ average_weight = 500
+ stable_population = 3
+ fish_traits = list(/datum/fish_trait/aggressive)
+ required_temperature_min = MIN_AQUARIUM_TEMP+22
+ required_temperature_max = MIN_AQUARIUM_TEMP+30
+
+/obj/item/fish/guppy
+ name = "guppy"
+ desc = "Guppy is also known as rainbow fish because of the brightly colored body and fins."
+ icon_state = "guppy"
+ sprite_width = 5
+ sprite_height = 2
+ sprite_width = 8
+ sprite_height = 5
+ average_size = 30
+ average_weight = 500
+ stable_population = 6
+ required_temperature_min = MIN_AQUARIUM_TEMP+20
+ required_temperature_max = MIN_AQUARIUM_TEMP+28
+
+/obj/item/fish/plasmatetra
+ name = "plasma tetra"
+ desc = "Due to their small size, tetras are prey to many predators in their watery world, including eels, crustaceans, and invertebrates."
+ icon_state = "plastetra"
+ sprite_width = 4
+ sprite_height = 2
+ average_size = 30
+ average_weight = 500
+ stable_population = 3
+ required_temperature_min = MIN_AQUARIUM_TEMP+20
+ required_temperature_max = MIN_AQUARIUM_TEMP+28
+
+/obj/item/fish/catfish
+ name = "catfish"
+ desc = "A catfish has about 100,000 taste buds, and their bodies are covered with them to help detect chemicals present in the water and also to respond to touch."
+ icon_state = "catfish"
+ sprite_width = 8
+ sprite_height = 4
+ average_size = 80
+ average_weight = 1600
+ weight_size_deviation = 0.35
+ stable_population = 3
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = JUNKFOOD
+ )
+ )
+ required_temperature_min = MIN_AQUARIUM_TEMP+12
+ required_temperature_max = MIN_AQUARIUM_TEMP+30
+ beauty = FISH_BEAUTY_GOOD
+
+/obj/item/fish/zipzap
+ name = "anxious zipzap"
+ desc = "A fish overflowing with crippling anxiety and electric potential. Worried about the walls of its tank closing in constantly. Both literally and as a general metaphorical unease about life's direction."
+ icon_state = "zipzap"
+ icon_state_dead = "zipzap_dead"
+ sprite_width = 6
+ sprite_height = 3
+ stable_population = 3
+ average_size = 30
+ average_weight = 500
+ random_case_rarity = FISH_RARITY_VERY_RARE
+ favorite_bait = list(/obj/item/stock_parts/power_store/cell/lead)
+ required_temperature_min = MIN_AQUARIUM_TEMP+18
+ required_temperature_max = MIN_AQUARIUM_TEMP+26
+ fish_traits = list(
+ /datum/fish_trait/no_mating,
+ /datum/fish_trait/wary,
+ /datum/fish_trait/anxiety,
+ /datum/fish_trait/electrogenesis,
+ )
+ //anxiety naturally limits the amount of zipzaps per tank, so they are stronger alone
+ electrogenesis_power = 6.7 MEGA JOULES
+ beauty = FISH_BEAUTY_GOOD
+
+/obj/item/fish/zipzap/get_fish_taste()
+ return list("raw fish" = 2, "anxiety" = 1)
+
+/obj/item/fish/tadpole
+ name = "tadpole"
+ desc = "The larval spawn of an amphibian. A very minuscle, round creature with a long tail it uses to swim around."
+ icon_state = "tadpole"
+ average_size = 3
+ average_weight = 10
+ sprite_width = 3
+ sprite_height = 1
+ health = 50
+ feeding_frequency = 1.5 MINUTES
+ required_temperature_min = MIN_AQUARIUM_TEMP+15
+ required_temperature_max = MIN_AQUARIUM_TEMP+20
+ fillet_type = null
+ fish_traits = list(/datum/fish_trait/no_mating) //They grow into frogs and that's it.
+ beauty = FISH_BEAUTY_NULL
+ random_case_rarity = FISH_RARITY_NOPE //Why would you want generic frog tadpoles you get from ponds inside fish cases?
+ /// Once dead, tadpoles disappear after a dozen seconds, since you can get infinite tadpoles.
+ var/del_timerid
+
+/obj/item/fish/tadpole/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ AddComponent(/datum/component/fish_growth, /mob/living/basic/frog, rand(2.5, 3 MINUTES))
+ RegisterSignal(src, COMSIG_FISH_BEFORE_GROWING, PROC_REF(growth_checks))
+ RegisterSignal(src, COMSIG_FISH_FINISH_GROWING, PROC_REF(on_growth))
+
+/obj/item/fish/tadpole/make_edible()
+ return
+
+/obj/item/fish/tadpole/set_status(new_status, silent = FALSE)
+ . = ..()
+ if(status == FISH_DEAD)
+ del_timerid = QDEL_IN_STOPPABLE(src, 12 SECONDS)
+ else
+ deltimer(del_timerid)
+
+/obj/item/fish/tadpole/proc/growth_checks(datum/source, seconds_per_tick, growth)
+ SIGNAL_HANDLER
+ var/hunger = get_hunger()
+ if(hunger >= 0.7) //too hungry to grow
+ return COMPONENT_DONT_GROW
+ var/obj/structure/aquarium/aquarium = loc
+ if(istype(aquarium) && !aquarium.reproduction_and_growth) //the aquarium has breeding disabled
+ return COMPONENT_DONT_GROW
+
+/obj/item/fish/tadpole/proc/on_growth(datum/source, mob/living/basic/frog/result)
+ SIGNAL_HANDLER
+ playsound(result, result.attack_sound, 50, TRUE) // reeeeeeeeeeeeeee...
+
+/obj/item/fish/tadpole/get_export_price(price, percent)
+ return 2 //two credits. Tadpoles aren't really that valueable.
+
+/obj/item/fish/perch
+ name = "perch"
+ desc = "An all around popular panfish, game fish and unfortunate prey to other, bigger predators."
+ icon_state = "perch"
+ dedicated_in_aquarium_icon_state = "fish_greyscale"
+ aquarium_vc_color = "#9D8C64"
+ sprite_width = 5
+ sprite_height = 3
+ stable_population = 7
+ average_size = 25
+ average_weight = 400
+ required_temperature_min = MIN_AQUARIUM_TEMP+5
+ required_temperature_max = MIN_AQUARIUM_TEMP+26
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = BUGS,
+ ),
+ /obj/item/fish,
+ /obj/item/fishing_lure, //they love lures in general.
+ )
diff --git a/code/modules/fishing/fish/types/holographic.dm b/code/modules/fishing/fish/types/holographic.dm
new file mode 100644
index 0000000000000..64de7d866d695
--- /dev/null
+++ b/code/modules/fishing/fish/types/holographic.dm
@@ -0,0 +1,109 @@
+
+/obj/item/fish/holo
+ name = "holographic goldfish"
+ desc = "A holographic representation of a common goldfish, slowly flickering out, removed from its holo-habitat."
+ icon_state = /obj/item/fish/goldfish::icon_state
+ fish_flags = parent_type::fish_flags & ~(FISH_FLAG_SHOW_IN_CATALOG|FISH_FLAG_EXPERIMENT_SCANNABLE)
+ random_case_rarity = FISH_RARITY_NOPE
+ dedicated_in_aquarium_icon_state = /obj/item/fish/goldfish::dedicated_in_aquarium_icon_state
+ aquarium_vc_color = /obj/item/fish/goldfish::aquarium_vc_color
+ sprite_width = /obj/item/fish/goldfish::sprite_width
+ sprite_height = /obj/item/fish/goldfish::sprite_height
+ stable_population = 1
+ average_size = /obj/item/fish/goldfish::average_size
+ average_weight = /obj/item/fish/goldfish::average_weight
+ required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
+ fillet_type = null
+ death_text = "%SRC gently disappears."
+ fish_traits = list(/datum/fish_trait/no_mating) //just to be sure, these shouldn't reproduce
+ beauty = /obj/item/fish/goldfish::beauty
+
+/obj/item/fish/holo/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ var/area/station/holodeck/holo_area = get_area(src)
+ if(!istype(holo_area))
+ addtimer(CALLBACK(src, PROC_REF(set_status), FISH_DEAD), 1 MINUTES)
+ return
+ holo_area.linked.add_to_spawned(src)
+
+/obj/item/fish/holo/make_edible(weight_val)
+ return
+
+/obj/item/fish/holo/set_status(new_status, silent = FALSE)
+ . = ..()
+ if(status == FISH_DEAD)
+ animate(src, alpha = 0, 3 SECONDS, easing = SINE_EASING)
+ QDEL_IN(src, 3 SECONDS)
+
+/obj/item/fish/holo/crab
+ name = "holographic crab"
+ desc = "A holographic represantion of a soul-crushingly soulless crab, unlike the cuter ones occasionally roaming around. It stares at you, with empty, beady eyes."
+ icon_state = "crab"
+ dedicated_in_aquarium_icon_state = null
+ aquarium_vc_color = null
+ average_size = 30
+ average_weight = 1000
+ sprite_height = 6
+ sprite_width = 10
+ beauty = FISH_BEAUTY_GOOD
+
+/obj/item/fish/holo/puffer
+ name = "holographic pufferfish"
+ desc ="A holographic representation of 100% safe-to-eat pufferfish... that is, if holographic fishes were even edible."
+ icon_state = /obj/item/fish/pufferfish::icon_state
+ dedicated_in_aquarium_icon_state = /obj/item/fish/pufferfish::dedicated_in_aquarium_icon_state
+ aquarium_vc_color = /obj/item/fish/pufferfish::aquarium_vc_color
+ average_size = /obj/item/fish/pufferfish::average_size
+ average_weight = /obj/item/fish/pufferfish::average_weight
+ sprite_height = /obj/item/fish/pufferfish::sprite_height
+ sprite_width = /obj/item/fish/pufferfish::sprite_width
+ beauty = /obj/item/fish/pufferfish::beauty
+
+/obj/item/fish/holo/angel
+ name = "holographic angelfish"
+ desc = "A holographic representation of a angelfish. I got nothing snarky to say about this one."
+ icon_state = /obj/item/fish/angelfish::icon_state
+ dedicated_in_aquarium_icon_state = /obj/item/fish/angelfish::dedicated_in_aquarium_icon_state
+ aquarium_vc_color = /obj/item/fish/angelfish::aquarium_vc_color
+ average_size = /obj/item/fish/angelfish::average_size
+ average_weight = /obj/item/fish/angelfish::average_weight
+ sprite_height = /obj/item/fish/angelfish::sprite_height
+ sprite_width = /obj/item/fish/angelfish::sprite_width
+ beauty = /obj/item/fish/angelfish::beauty
+
+/obj/item/fish/holo/clown
+ name = "holographic clownfish"
+ icon_state = "holo_clownfish"
+ desc = "A holographic representation of a clownfish, or at least how they used to look like five centuries ago."
+ dedicated_in_aquarium_icon_state = null
+ aquarium_vc_color = /obj/item/fish/clownfish::aquarium_vc_color
+ average_size = /obj/item/fish/clownfish::average_size
+ average_weight = /obj/item/fish/clownfish::average_weight
+ sprite_height = /obj/item/fish/clownfish::sprite_height
+ sprite_width = /obj/item/fish/clownfish::sprite_width
+ required_fluid_type = /obj/item/fish/clownfish::required_fluid_type
+ beauty = /obj/item/fish/clownfish::beauty
+
+/obj/item/fish/holo/checkered
+ name = "unrendered holographic fish"
+ desc = "A checkered silhoutte of searing purple and pitch black presents itself before your eyes, like a tear in fabric of reality. It hurts to watch."
+ icon_state = "checkered" //it's a meta joke, buddy.
+ dedicated_in_aquarium_icon_state = null
+ aquarium_vc_color = null
+ average_size = 30
+ average_weight = 500
+ sprite_width = 4
+ sprite_height = 3
+ beauty = FISH_BEAUTY_NULL
+
+/obj/item/fish/holo/halffish
+ name = "holographic half-fish"
+ desc = "A holographic representation of... a fish reduced to all bones, except for its head. Isn't it supposed to be dead? Ehr, holo-dead?"
+ icon_state = "half_fish"
+ dedicated_in_aquarium_icon_state = null
+ aquarium_vc_color = null
+ sprite_height = 4
+ sprite_width = 10
+ average_size = 50
+ average_weight = 500
+ beauty = FISH_BEAUTY_UGLY
diff --git a/code/modules/fishing/fish/types/mining.dm b/code/modules/fishing/fish/types/mining.dm
new file mode 100644
index 0000000000000..9e44e08ae316c
--- /dev/null
+++ b/code/modules/fishing/fish/types/mining.dm
@@ -0,0 +1,216 @@
+/// Commonly found on the mining fishing spots. Can be grown into lobstrosities
+/obj/item/fish/chasm_crab
+ name = "chasm chrab"
+ desc = "The young of the lobstrosity mature in pools below the earth, eating what falls in until large enough to clamber out. Those found near the station are well-fed."
+ icon_state = "chrab"
+ sprite_height = 9
+ sprite_width = 8
+ stable_population = 4
+ feeding_frequency = 10 MINUTES
+ random_case_rarity = FISH_RARITY_RARE
+ fillet_type = /obj/item/food/meat/slab/rawcrab
+ required_temperature_min = MIN_AQUARIUM_TEMP+9
+ required_temperature_max = LAVALAND_MAX_TEMPERATURE+50
+ min_pressure = HAZARD_LOW_PRESSURE
+ safe_air_limits = list(
+ /datum/gas/oxygen = list(2, 100),
+ /datum/gas/nitrogen,
+ /datum/gas/carbon_dioxide = list(0, 20),
+ /datum/gas/water_vapor,
+ /datum/gas/plasma = list(0, 5),
+ /datum/gas/bz = list(0, 5),
+ /datum/gas/miasma = list(0, 5),
+ )
+ evolution_types = list(/datum/fish_evolution/ice_chrab)
+ compatible_types = list(/obj/item/fish/chasm_crab/ice)
+ beauty = FISH_BEAUTY_GOOD
+ favorite_bait = list(/obj/item/fish/lavaloop)
+ ///This value represents how much the crab needs aren't being met. Higher values translate to a more likely hostile lobstrosity.
+ var/anger = 0
+ ///The lobstrosity type this matures into
+ var/lob_type = /mob/living/basic/mining/lobstrosity/juvenile/lava
+
+/obj/item/fish/chasm_crab/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ RegisterSignal(src, COMSIG_FISH_BEFORE_GROWING, PROC_REF(growth_checks))
+ RegisterSignal(src, COMSIG_FISH_FINISH_GROWING, PROC_REF(on_growth))
+
+/obj/item/fish/chasm_crab/get_fish_taste()
+ return list("raw crab" = 2)
+
+/obj/item/fish/chasm_crab/get_fish_taste_cooked()
+ return list("cooked crab" = 2)
+
+///A chasm crab growth speed is determined by its initial weight and size, ergo bigger crabs for faster lobstrosities
+/obj/item/fish/chasm_crab/update_size_and_weight(new_size = average_size, new_weight = average_weight)
+ . = ..()
+ var/multiplier = 1
+ switch(size)
+ if(0 to FISH_SIZE_TINY_MAX)
+ multiplier -= 0.2
+ if(FISH_SIZE_SMALL_MAX to FISH_SIZE_NORMAL_MAX)
+ multiplier += 0.2
+ if(FISH_SIZE_NORMAL_MAX to FISH_SIZE_BULKY_MAX)
+ multiplier += 0.5
+ if(FISH_SIZE_BULKY_MAX to INFINITY)
+ multiplier += 0.8
+
+
+ if(weight <= (average_weight - 200))
+ multiplier -= 0.1 * round((average_weight - weight) / 200)
+ else if(weight >= (average_weight + 500))
+ multiplier += min(0.1 * round((weight - average_weight) / 500), 2)
+ AddComponent(/datum/component/fish_growth, lob_type, 10 MINUTES * multiplier)
+
+/obj/item/fish/chasm_crab/pet_fish(mob/living/user)
+ . = ..()
+ if(.)
+ anger -= min(anger, 6.5)
+
+/obj/item/fish/chasm_crab/proc/growth_checks(datum/source, seconds_per_tick, growth)
+ SIGNAL_HANDLER
+ var/hunger = get_hunger()
+ if(health <= initial(health) * 0.6 || hunger >= 0.6) //if too hurt or hungry, don't grow.
+ anger += growth * 2
+ return COMPONENT_DONT_GROW
+
+ if(hunger >= 0.4) //I'm hungry and angry
+ anger += growth * 0.6
+
+ if(!isaquarium(loc))
+ return
+
+ var/obj/structure/aquarium/aquarium = loc
+ if(!aquarium.reproduction_and_growth) //the aquarium has breeding disabled
+ return COMPONENT_DONT_GROW
+ if(!locate(/obj/item/aquarium_prop) in aquarium) //the aquarium deco is quite barren
+ anger += growth * 0.25
+ var/fish_count = length(aquarium.get_fishes())
+ if(!ISINRANGE(fish_count, 3, AQUARIUM_MAX_BREEDING_POPULATION * 0.5)) //too lonely or overcrowded
+ anger += growth * 0.3
+ if(fish_count > AQUARIUM_MAX_BREEDING_POPULATION * 0.5) //check if there's enough room to maturate.
+ return COMPONENT_DONT_GROW
+
+/obj/item/fish/chasm_crab/proc/on_growth(datum/source, mob/living/basic/mining/lobstrosity/juvenile/result)
+ SIGNAL_HANDLER
+ if(!prob(anger))
+ result.AddElement(/datum/element/ai_retaliate)
+ qdel(result.ai_controller)
+ result.ai_controller = new /datum/ai_controller/basic_controller/lobstrosity/juvenile/calm(result)
+ else if(anger < 30) //not really that mad, just a bit unstable.
+ qdel(result.ai_controller)
+ result.ai_controller = new /datum/ai_controller/basic_controller/lobstrosity/juvenile/capricious(result)
+
+/obj/item/fish/chasm_crab/ice
+ name = "arctic chrab"
+ desc = "A subspecies of chasm chrabs that has adapted to the cold climate and lack of abysmal holes of the icemoon."
+ icon_state = "arctic_chrab"
+ required_temperature_min = ICEBOX_MIN_TEMPERATURE-20
+ required_temperature_max = MIN_AQUARIUM_TEMP+15
+ evolution_types = list(/datum/fish_evolution/chasm_chrab)
+ compatible_types = list(/obj/item/fish/chasm_crab)
+ beauty = FISH_BEAUTY_GREAT
+ lob_type = /mob/living/basic/mining/lobstrosity/juvenile
+
+/obj/item/fish/boned
+ name = "unmarine bonemass"
+ desc = "What one could mistake for fish remains, is in reality a species that chose to discard its weak flesh a long time ago. A living fossil, in its most literal sense."
+ icon_state = "bonemass"
+ sprite_width = 10
+ sprite_height = 7
+ fish_movement_type = /datum/fish_movement/zippy
+ random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
+ required_fluid_type = AQUARIUM_FLUID_ANY_WATER
+ min_pressure = HAZARD_LOW_PRESSURE
+ health = 150
+ stable_population = 3
+ grind_results = list(/datum/reagent/bone_dust = 10)
+ fillet_type = /obj/item/stack/sheet/bone
+ num_fillets = 2
+ fish_traits = list(/datum/fish_trait/revival, /datum/fish_trait/carnivore)
+ average_size = 70
+ average_weight = 2000
+ death_text = "%SRC stops moving." //It's dead... or is it?
+ evolution_types = list(/datum/fish_evolution/mastodon)
+ beauty = FISH_BEAUTY_UGLY
+
+/obj/item/fish/boned/make_edible(weight_val)
+ return //it's all bones and no meat.
+
+/obj/item/fish/lavaloop
+ name = "lavaloop fish"
+ desc = "Due to its curvature, it can be used as make-shift boomerang."
+ icon_state = "lava_loop"
+ sprite_width = 3
+ sprite_height = 5
+ average_size = 30
+ average_weight = 500
+ resistance_flags = FIRE_PROOF | LAVA_PROOF
+ required_fluid_type = AQUARIUM_FLUID_ANY_WATER //if we can survive hot lava and freezing plasrivers, we can survive anything
+ fish_movement_type = /datum/fish_movement/zippy
+ min_pressure = HAZARD_LOW_PRESSURE
+ required_temperature_min = MIN_AQUARIUM_TEMP+30
+ required_temperature_max = MIN_AQUARIUM_TEMP+35
+ fish_traits = list(
+ /datum/fish_trait/carnivore,
+ /datum/fish_trait/heavy,
+ )
+ hitsound = null
+ throwforce = 5
+ beauty = FISH_BEAUTY_GOOD
+ ///maximum bonus damage when winded up
+ var/maximum_bonus = 25
+
+/obj/item/fish/lavaloop/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ add_traits(list(TRAIT_FISHING_BAIT, TRAIT_GOOD_QUALITY_BAIT, TRAIT_BYPASS_RANGED_ARMOR), INNATE_TRAIT)
+ AddComponent(/datum/component/boomerang, throw_range, TRUE)
+ AddComponent(\
+ /datum/component/throwbonus_on_windup,\
+ maximum_bonus = maximum_bonus,\
+ windup_increment_speed = 2,\
+ throw_text = "starts cooking in your hands, it may explode soon!",\
+ pass_maximum_callback = CALLBACK(src, PROC_REF(explode_on_user)),\
+ apply_bonus_callback = CALLBACK(src, PROC_REF(on_fish_land)),\
+ sound_on_success = 'sound/items/weapons/parry.ogg',\
+ effect_on_success = /obj/effect/temp_visual/guardian/phase,\
+ )
+
+/obj/item/fish/lavaloop/get_fish_taste()
+ return list("chewy fish" = 2)
+
+/obj/item/fish/lavaloop/get_food_types()
+ return SEAFOOD|MEAT|GORE //Well-cooked in lava
+
+/obj/item/fish/lavaloop/proc/explode_on_user(mob/living/user)
+ var/obj/item/bodypart/arm/active_arm = user.get_active_hand()
+ active_arm?.dismember()
+ to_chat(user, span_warning("[src] explodes!"))
+ playsound(src, 'sound/effects/explosion/explosion1.ogg', 40, TRUE)
+ user.flash_act(1, 1)
+ qdel(src)
+
+/obj/item/fish/lavaloop/proc/on_fish_land(mob/living/target, bonus_value)
+ if(!istype(target))
+ return FALSE
+ return (target.mob_size >= MOB_SIZE_LARGE)
+
+/obj/item/fish/lavaloop/plasma_river
+ maximum_bonus = 30
+
+/obj/item/fish/lavaloop/plasma_river/explode_on_user(mob/living/user)
+ playsound(src, 'sound/effects/explosion/explosion1.ogg', 40, TRUE)
+ user.flash_act(1, 1)
+ user.apply_status_effect(/datum/status_effect/ice_block_talisman, 5 SECONDS)
+ qdel(src)
+
+/obj/item/fish/lavaloop/plasma_river/on_fish_land(mob/living/target, bonus_value)
+ if(!istype(target))
+ return FALSE
+ if(target.mob_size < MOB_SIZE_LARGE)
+ return FALSE
+ var/freeze_timer = (bonus_value * 0.1)
+ if(freeze_timer <= 0)
+ return FALSE
+ target.apply_status_effect(/datum/status_effect/ice_block_talisman, freeze_timer SECONDS)
+ return FALSE
diff --git a/code/modules/fishing/fish/types/ruins.dm b/code/modules/fishing/fish/types/ruins.dm
new file mode 100644
index 0000000000000..153a5bc3b7b7c
--- /dev/null
+++ b/code/modules/fishing/fish/types/ruins.dm
@@ -0,0 +1,90 @@
+///From oil puddles from the elephant graveyard. Also an evolution of the "unmarine bonemass"
+/obj/item/fish/mastodon
+ name = "unmarine mastodon"
+ desc = "A monster of exposed muscles and innards, wrapped in a fish-like skeleton. You don't remember ever seeing it on the catalog."
+ icon = 'icons/obj/aquarium/wide.dmi'
+ icon_state = "mastodon"
+ base_pixel_x = -16
+ pixel_x = -16
+ sprite_width = 12
+ sprite_height = 7
+ fish_flags = parent_type::fish_flags & ~FISH_FLAG_SHOW_IN_CATALOG
+ random_case_rarity = FISH_RARITY_NOPE
+ fishing_difficulty_modifier = 30
+ required_fluid_type = AQUARIUM_FLUID_ANY_WATER
+ min_pressure = HAZARD_LOW_PRESSURE
+ health = 300
+ stable_population = 1 //This means they can only crossbreed.
+ grind_results = list(/datum/reagent/bone_dust = 5, /datum/reagent/consumable/liquidgibs = 5)
+ fillet_type = /obj/item/stack/sheet/bone
+ num_fillets = 2
+ feeding_frequency = 2 MINUTES
+ breeding_timeout = 5 MINUTES
+ average_size = 180
+ average_weight = 5000
+ death_text = "%SRC stops moving."
+ fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/amphibious, /datum/fish_trait/revival, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/aggressive)
+ beauty = FISH_BEAUTY_BAD
+
+/obj/item/fish/mastodon/make_edible(weight_val)
+ return //it's all bones and gibs.
+
+///From the cursed spring
+/obj/item/fish/soul
+ name = "soulfish"
+ desc = "A distant yet vaguely close critter, like a long lost relative. You feel your soul rejuvenated just from looking at it... Also, what the fuck is this shit?!"
+ icon_state = "soulfish"
+ sprite_width = 7
+ sprite_height = 6
+ average_size = 60
+ average_weight = 1200
+ stable_population = 4
+ fish_flags = parent_type::fish_flags & ~FISH_FLAG_SHOW_IN_CATALOG
+ beauty = FISH_BEAUTY_EXCELLENT
+ fish_movement_type = /datum/fish_movement/choppy //Glideless legacy movement? in my fishing minigame?
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = JUNKFOOD|FRIED,
+ ),
+ )
+ fillet_type = /obj/item/food/meat/cutlet/plain/human
+ required_temperature_min = MIN_AQUARIUM_TEMP+3
+ required_temperature_max = MIN_AQUARIUM_TEMP+38
+ random_case_rarity = FISH_RARITY_NOPE
+
+/obj/item/fish/soul/get_food_types()
+ return MEAT|RAW|GORE //Not-so-quite-seafood
+
+/obj/item/fish/soul/get_fish_taste()
+ return list("meat" = 2, "soulfulness" = 1)
+
+/obj/item/fish/soul/get_fish_taste_cooked()
+ return list("cooked meat" = 2)
+
+///From the cursed spring
+/obj/item/fish/skin_crab
+ name = "skin crab"
+ desc = "\"And on the eighth day, a demential mockery of both humanity and crabity was made.\" Fascinating."
+ icon_state = "skin_crab"
+ sprite_width = 7
+ sprite_height = 6
+ average_size = 40
+ average_weight = 750
+ stable_population = 5
+ fish_flags = parent_type::fish_flags & ~FISH_FLAG_SHOW_IN_CATALOG
+ beauty = FISH_BEAUTY_GREAT
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = JUNKFOOD|FRIED
+ ),
+ )
+ fillet_type = /obj/item/food/meat/slab/rawcrab
+ random_case_rarity = FISH_RARITY_NOPE
+
+/obj/item/fish/skin_crab/get_fish_taste()
+ return list("raw crab" = 2)
+
+/obj/item/fish/skin_crab/get_fish_taste_cooked()
+ return list("cooked crab" = 2)
diff --git a/code/modules/fishing/fish/types/saltwater.dm b/code/modules/fishing/fish/types/saltwater.dm
new file mode 100644
index 0000000000000..74f1d1d32b9e0
--- /dev/null
+++ b/code/modules/fishing/fish/types/saltwater.dm
@@ -0,0 +1,284 @@
+/obj/item/fish/clownfish
+ name = "clownfish"
+ desc = "Clownfish catch prey by swimming onto the reef, attracting larger fish, and luring them back to the anemone. The anemone will sting and eat the larger fish, leaving the remains for the clownfish."
+ icon_state = "clownfish"
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ sprite_width = 7
+ sprite_height = 4
+ average_size = 30
+ average_weight = 500
+ stable_population = 4
+ fish_traits = list(/datum/fish_trait/picky_eater)
+ evolution_types = list(/datum/fish_evolution/lubefish)
+ compatible_types = list(/obj/item/fish/clownfish/lube)
+ required_temperature_min = MIN_AQUARIUM_TEMP+22
+ required_temperature_max = MIN_AQUARIUM_TEMP+30
+
+/obj/item/fish/clownfish/get_fish_taste()
+ return list("raw fish" = 2, "something funny" = 1)
+
+/obj/item/fish/clownfish/lube
+ name = "lubefish"
+ desc = "A clownfish exposed to cherry-flavored lube for far too long. First discovered the days following a cargo incident around the seas of Europa, when thousands of thousands of thousands..."
+ icon_state = "lubefish"
+ random_case_rarity = FISH_RARITY_VERY_RARE
+ fish_traits = list(/datum/fish_trait/picky_eater, /datum/fish_trait/lubed)
+ evolution_types = null
+ compatible_types = list(/obj/item/fish/clownfish)
+ food = /datum/reagent/lube
+ fishing_difficulty_modifier = 5
+ beauty = FISH_BEAUTY_GREAT
+
+/obj/item/fish/cardinal
+ name = "cardinalfish"
+ desc = "Cardinalfish are often found near sea urchins, where the fish hide when threatened."
+ icon_state = "cardinalfish"
+ sprite_width = 6
+ sprite_height = 3
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ average_size = 30
+ average_weight = 500
+ stable_population = 4
+ fish_traits = list(/datum/fish_trait/vegan)
+ required_temperature_min = MIN_AQUARIUM_TEMP+22
+ required_temperature_max = MIN_AQUARIUM_TEMP+30
+
+/obj/item/fish/greenchromis
+ name = "green chromis"
+ desc = "The Chromis can vary in color from blue to green depending on the lighting and distance from the lights."
+ icon_state = "greenchromis"
+ sprite_width = 5
+ sprite_height = 3
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ average_size = 30
+ average_weight = 500
+ stable_population = 5
+ required_temperature_min = MIN_AQUARIUM_TEMP+23
+ required_temperature_max = MIN_AQUARIUM_TEMP+28
+
+ fishing_difficulty_modifier = 5 // Bit harder
+
+/obj/item/fish/firefish
+ name = "firefish goby"
+ desc = "To communicate in the wild, the firefish uses its dorsal fin to alert others of potential danger."
+ icon_state = "firefish"
+ sprite_width = 5
+ sprite_height = 3
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ average_size = 30
+ average_weight = 500
+ stable_population = 3
+ disliked_bait = list(/obj/item/food/bait/worm, /obj/item/food/bait/doughball)
+ fish_movement_type = /datum/fish_movement/zippy
+ required_temperature_min = MIN_AQUARIUM_TEMP+23
+ required_temperature_max = MIN_AQUARIUM_TEMP+28
+
+/obj/item/fish/pufferfish
+ name = "pufferfish"
+ desc = "They say that one pufferfish contains enough toxins to kill 30 people, although in the last few decades they've been genetically engineered en masse to be less poisonous."
+ icon_state = "pufferfish"
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ sprite_width = 8
+ sprite_height = 6
+ average_size = 60
+ average_weight = 1000
+ stable_population = 3
+ required_temperature_min = MIN_AQUARIUM_TEMP+23
+ required_temperature_max = MIN_AQUARIUM_TEMP+28
+ fillet_type = /obj/item/food/fishmeat/quality //Too bad they're poisonous
+ fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/toxic)
+ beauty = FISH_BEAUTY_GOOD
+
+/obj/item/fish/lanternfish
+ name = "lanternfish"
+ desc = "Typically found in areas below 6600 feet below the surface of the ocean, they live in complete darkness."
+ icon_state = "lanternfish"
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ random_case_rarity = FISH_RARITY_VERY_RARE
+ sprite_width = 6
+ sprite_height = 5
+ average_size = 50
+ average_weight = 1000
+ stable_population = 3
+ fish_traits = list(/datum/fish_trait/nocturnal)
+ required_temperature_min = MIN_AQUARIUM_TEMP+2 //My source is that the water at a depth 6600 feet is pretty darn cold.
+ required_temperature_max = MIN_AQUARIUM_TEMP+18
+ beauty = FISH_BEAUTY_NULL
+
+/obj/item/fish/stingray
+ name = "stingray"
+ desc = "A type of ray, most known for its venomous stinger. Despite that, They're normally docile, if not a bit easily frightened."
+ icon_state = "stingray"
+ stable_population = 4
+ sprite_height = 7
+ sprite_width = 8
+ average_size = 60
+ average_weight = 700
+ beauty = FISH_BEAUTY_GREAT
+ random_case_rarity = FISH_RARITY_RARE
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER //Someone ought to add river rays later I guess.
+ fish_traits = list(/datum/fish_trait/stinger, /datum/fish_trait/toxic_barbs, /datum/fish_trait/wary, /datum/fish_trait/carnivore, /datum/fish_trait/predator)
+
+/obj/item/fish/swordfish
+ name = "swordfish"
+ desc = "A large billfish, most famous for its elongated bill, while also fairly popular for cooking, and as a fearsome weapon in the hands of a veteran spess-fisherman."
+ icon = 'icons/obj/aquarium/wide.dmi'
+ icon_state = "swordfish"
+ inhand_icon_state = "swordfish"
+ force = 18
+ sharpness = SHARP_EDGED
+ attack_verb_continuous = list("slashes", "cuts", "pierces")
+ attack_verb_simple = list("slash", "cut", "pierce")
+ block_sound = 'sound/items/weapons/parry.ogg'
+ hitsound = 'sound/items/weapons/rapierhit.ogg'
+ demolition_mod = 0.75
+ attack_speed = 1 SECONDS
+ block_chance = 50
+ wound_bonus = 10
+ bare_wound_bonus = 20
+ armour_penetration = 75
+ base_pixel_x = -18
+ pixel_x = -18
+ sprite_width = 13
+ sprite_height = 6
+ stable_population = 3
+ average_size = 140
+ average_weight = 4000
+ breeding_timeout = 4.5 MINUTES
+ feeding_frequency = 4 MINUTES
+ health = 180
+ beauty = FISH_BEAUTY_EXCELLENT
+ random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ fish_movement_type = /datum/fish_movement/plunger
+ fishing_difficulty_modifier = 25
+ fillet_type = /obj/item/food/fishmeat/quality
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = SEAFOOD,
+ ),
+ /obj/item/fish,
+ )
+ fish_traits = list(/datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/stinger)
+
+/obj/item/fish/swordfish/get_force_rank()
+ switch(w_class)
+ if(WEIGHT_CLASS_TINY)
+ force -= 11
+ attack_speed -= 0.4 SECONDS
+ block_chance -= 45
+ armour_penetration -= 20
+ wound_bonus -= 15
+ bare_wound_bonus -= 20
+ if(WEIGHT_CLASS_SMALL)
+ force -= 8
+ attack_speed -= 0.3 SECONDS
+ block_chance -= 30
+ armour_penetration -= 15
+ wound_bonus -= 10
+ bare_wound_bonus -= 20
+ if(WEIGHT_CLASS_NORMAL)
+ force -= 5
+ attack_speed -= 0.2 SECONDS
+ block_chance -= 20
+ armour_penetration -= 10
+ wound_bonus -= 10
+ bare_wound_bonus -= 15
+ if(WEIGHT_CLASS_BULKY)
+ force -= 3
+ attack_speed -= 0.1 SECONDS
+ block_chance -= 10
+ armour_penetration -= 5
+ wound_bonus -= 5
+ bare_wound_bonus -= 10
+ if(WEIGHT_CLASS_GIGANTIC)
+ force += 5
+ attack_speed += 0.2 SECONDS
+ demolition_mod += 0.15
+ block_chance += 10
+ armour_penetration += 5
+ wound_bonus += 5
+ bare_wound_bonus += 10
+
+ if(status == FISH_DEAD)
+ force -= 4 + w_class
+ block_chance -= 25
+ armour_penetration -= 30
+ wound_bonus -= 10
+ bare_wound_bonus -= 10
+
+/obj/item/fish/swordfish/calculate_fish_force_bonus(bonus_malus)
+ . = ..()
+ armour_penetration += bonus_malus * 5
+ wound_bonus += bonus_malus * 3
+ bare_wound_bonus += bonus_malus * 5
+ block_chance += bonus_malus * 7
+
+/obj/item/fish/squid
+ name = "squid"
+ desc = "An elongated mollusk with eight tentacles, natural camouflage and ink clouds to spray at predators. One of the most intelligent, well-equipped invertebrates out there."
+ icon_state = "squid"
+ sprite_width = 4
+ sprite_height = 5
+ stable_population = 6
+ weight_size_deviation = 0.5 // They vary greatly in size.
+ average_weight = 500 //They're quite lighter than they're long.
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ beauty = FISH_BEAUTY_GOOD
+ required_temperature_min = MIN_AQUARIUM_TEMP+5
+ required_temperature_max = MIN_AQUARIUM_TEMP+26
+ fish_traits = list(/datum/fish_trait/heavy, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/ink, /datum/fish_trait/camouflage, /datum/fish_trait/wary)
+
+/obj/item/fish/squid/get_fish_taste()
+ return list("raw mollusk" = 2)
+
+/obj/item/fish/squid/get_fish_taste_cooked()
+ return list("cooked mollusk" = 2, "tenderness" = 0.5)
+
+/obj/item/fish/monkfish
+ name = "monkfish"
+ desc = "A member of the Lophiid family of anglerfish. It goes by several different names, however none of them will make it look any prettier, nor be any less delicious."
+ icon_state = "monkfish"
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ sprite_height = 7
+ sprite_width = 7
+ beauty = FISH_BEAUTY_UGLY
+ required_temperature_min = MIN_AQUARIUM_TEMP+2
+ required_temperature_max = MIN_AQUARIUM_TEMP+23
+ average_size = 60
+ average_weight = 1400
+ stable_population = 4
+ fish_traits = list(/datum/fish_trait/heavy)
+ fillet_type = /obj/item/food/fishmeat/quality
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = SEAFOOD|BUGS,
+ ),
+ )
+
+/obj/item/fish/monkfish/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ name = pick("monkfish", "fishing-frog", "frog-fish", "sea-devil", "goosefish")
+
+/obj/item/fish/plaice
+ name = "plaice"
+ desc = "Perhaps the most prominent flatfish in the space-market. Nature really pulled out the rolling pin on this one."
+ icon_state = "plaice"
+ sprite_height = 7
+ sprite_width = 6
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ required_temperature_min = MIN_AQUARIUM_TEMP+2
+ required_temperature_max = MIN_AQUARIUM_TEMP+18
+ average_size = 40
+ average_weight = 700
+ stable_population = 5
+ fish_traits = list(/datum/fish_trait/heavy)
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = SEAFOOD|BUGS,
+ ),
+ )
+
diff --git a/code/modules/fishing/fish/types/station.dm b/code/modules/fishing/fish/types/station.dm
new file mode 100644
index 0000000000000..e811996d5af04
--- /dev/null
+++ b/code/modules/fishing/fish/types/station.dm
@@ -0,0 +1,228 @@
+/obj/item/fish/ratfish
+ name = "ratfish"
+ desc = "A rat exposed to the murky waters of maintenance too long. Any higher power, if it revealed itself, would state that the ratfish's continued existence is extremely unwelcome."
+ icon_state = "ratfish"
+ sprite_width = 7
+ sprite_height = 5
+ random_case_rarity = FISH_RARITY_RARE
+ required_fluid_type = AQUARIUM_FLUID_FRESHWATER
+ stable_population = /datum/config_entry/number/mice_roundstart::default //set by New, but this is the default config value
+ fillet_type = /obj/item/food/meat/slab/human/mutant/zombie //eww...
+ fish_traits = list(/datum/fish_trait/necrophage)
+ required_temperature_min = MIN_AQUARIUM_TEMP+15
+ required_temperature_max = MIN_AQUARIUM_TEMP+35
+ fish_movement_type = /datum/fish_movement/zippy
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = DAIRY
+ )
+ )
+ beauty = FISH_BEAUTY_DISGUSTING
+
+/obj/item/fish/ratfish/get_fish_taste()
+ return list("vermin" = 2, "maintenance" = 1)
+
+/obj/item/fish/ratfish/get_fish_taste_cooked()
+ return list("cooked vermin" = 2, "burned fur" = 0.5)
+
+/obj/item/fish/ratfish/get_food_types()
+ return MEAT|RAW|GORE //Not-so-quite-seafood
+
+/obj/item/fish/ratfish/get_base_edible_reagents_to_add()
+ var/list/return_list = ..()
+ return_list[/datum/reagent/rat_spit] = 1
+ return return_list
+
+/obj/item/fish/ratfish/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ //stable pop reflects the config for how many mice migrate. powerful...
+ stable_population = CONFIG_GET(number/mice_roundstart)
+
+/obj/item/fish/sludgefish
+ name = "sludgefish"
+ desc = "A misshapen, fragile, loosely fish-like living goop, the only thing that'd ever thrive in the acidic and claustrophobic cavities of the station's organic waste disposal system."
+ icon_state = "sludgefish"
+ sprite_width = 7
+ sprite_height = 6
+ required_fluid_type = AQUARIUM_FLUID_SULPHWATEVER
+ stable_population = 8
+ average_size = 20
+ average_weight = 400
+ health = 50
+ breeding_timeout = 2.5 MINUTES
+ fish_traits = list(/datum/fish_trait/parthenogenesis, /datum/fish_trait/no_mating)
+ required_temperature_min = MIN_AQUARIUM_TEMP+10
+ required_temperature_max = MIN_AQUARIUM_TEMP+40
+ evolution_types = list(/datum/fish_evolution/purple_sludgefish)
+ beauty = FISH_BEAUTY_NULL
+
+/obj/item/fish/sludgefish/get_fish_taste()
+ return list("raw fish" = 2, "eau de toilet" = 1)
+
+/obj/item/fish/sludgefish/purple
+ name = "purple sludgefish"
+ desc = "A misshapen, fragile, loosely fish-like living goop. This one has developed sexual reproduction mechanisms, and a purple tint to boot."
+ icon_state = "sludgefish_purple"
+ random_case_rarity = FISH_RARITY_NOPE
+ fish_traits = list(/datum/fish_trait/parthenogenesis)
+
+/obj/item/fish/slimefish
+ name = "acquatic slime"
+ desc = "Kids, this is what happens when a slime overcomes its hydrophobic nature. It goes glug glug."
+ icon_state = "slimefish"
+ icon_state_dead = "slimefish_dead"
+ sprite_width = 7
+ sprite_height = 7
+ fish_flags = parent_type::fish_flags & ~FISH_DO_FLOP_ANIM //it already has a cute bouncy wiggle. :3
+ random_case_rarity = FISH_RARITY_VERY_RARE
+ required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
+ stable_population = 4
+ health = 150
+ fillet_type = /obj/item/slime_extract/grey
+ fish_traits = list(/datum/fish_trait/toxin_immunity, /datum/fish_trait/crossbreeder)
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = TOXIC,
+ ),
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_REAGENT,
+ FISH_BAIT_VALUE = /datum/reagent/toxin,
+ FISH_BAIT_AMOUNT = 5,
+ ),
+ )
+ required_temperature_min = MIN_AQUARIUM_TEMP+20
+ beauty = FISH_BEAUTY_GREAT
+
+/obj/item/fish/slimefish/get_food_types()
+ return SEAFOOD|TOXIC
+
+/obj/item/fish/slimefish/get_base_edible_reagents_to_add()
+ return list(/datum/reagent/toxin/slimejelly = 5)
+
+/obj/item/fish/fryish
+ name = "fryish"
+ desc = "A youngling of the Fritterish family of delicious extremophile, piscine lifeforms. Just don't tell 'Mankind for Ethical Animal Treatment' you ate it."
+ icon_state = "fryish"
+ sprite_width = 3
+ sprite_height = 3
+ average_size = 20
+ average_weight = 400
+ random_case_rarity = FISH_RARITY_VERY_RARE
+ required_fluid_type = AQUARIUM_FLUID_ANY_WATER
+ stable_population = 8
+ fillet_type = /obj/item/food/nugget/fish
+ fish_traits = list(/datum/fish_trait/picky_eater, /datum/fish_trait/no_mating)
+ compatible_types = list(/obj/item/fish/fryish/fritterish, /obj/item/fish/fryish/nessie)
+ spawn_types = list(/obj/item/fish/fryish)
+ required_temperature_min = MIN_AQUARIUM_TEMP+50
+ required_temperature_max = MIN_AQUARIUM_TEMP+220
+ fish_movement_type = /datum/fish_movement/zippy
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = FRIED,
+ )
+ )
+ disliked_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = GROSS|RAW,
+ )
+ )
+ beauty = FISH_BEAUTY_GOOD
+ fishing_difficulty_modifier = -5
+ ///Is this a baitfish?
+ var/is_bait = TRUE
+ ///The evolution datum of the next thing we grow into, given time (and food)
+ var/next_type = /datum/fish_evolution/fritterish
+ ///How long does it take for us to grow up?
+ var/growth_time = 3.5 MINUTES
+
+/obj/item/fish/fryish/Initialize(mapload)
+ . = ..()
+ if(is_bait)
+ add_traits(list(TRAIT_FISHING_BAIT, TRAIT_GREAT_QUALITY_BAIT), INNATE_TRAIT)
+ ADD_TRAIT(src, TRAIT_FISH_SURVIVE_COOKING, INNATE_TRAIT)
+
+/obj/item/fish/fryish/update_size_and_weight(new_size = average_size, new_weight = average_weight)
+ . = ..()
+ if(!next_type)
+ return
+ var/multiplier = (size / average_size) * (weight / average_weight)
+
+ AddComponent(/datum/component/fish_growth, next_type, growth_time * multiplier, use_drop_loc = FALSE)
+
+/obj/item/fish/fryish/get_fish_taste()
+ return list("fried fish" = 1)
+
+/obj/item/fish/fryish/get_fish_taste_cooked()
+ return list("extra-fried fish" = 1)
+
+/obj/item/fish/fryish/get_food_types()
+ return FRIED|MEAT|SEAFOOD
+
+/obj/item/fish/fryish/get_base_edible_reagents_to_add()
+ var/list/return_list = list(
+ /datum/reagent/consumable/nutriment/protein = 2,
+ /datum/reagent/consumable/nutriment/fat = 1.5,
+ )
+ return return_list
+
+/obj/item/fish/fryish/fritterish
+ name = "fritterish"
+ desc = "A deliciously extremophile alien fish. This one looks like a taiyaki."
+ icon_state = "fritterish"
+ average_size = 50
+ average_weight = 1000
+ sprite_width = 5
+ sprite_height = 3
+ stable_population = 5
+ fish_traits = list(/datum/fish_trait/picky_eater)
+ compatible_types = list(/obj/item/fish/fryish, /obj/item/fish/fryish/nessie)
+ fish_movement_type = /datum/fish_movement
+ is_bait = FALSE
+ next_type = /datum/fish_evolution/nessie
+ growth_time = 8 MINUTES
+
+/obj/item/fish/fryish/fritterish/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ base_icon_state = icon_state = pick("fritterish", "bernardfish", "matthewfish")
+ switch(icon_state)
+ if("bernardfish")
+ name = "bernard-fish"
+ desc = "A deliciously> extremophile alien fish shaped like a dinosaur. Children love it."
+ sprite_width = 4
+ sprite_height = 6
+ if("matthewfish")
+ desc = "A deliciously> extremophile alien fish shaped like a pterodactyl. Children love it."
+ name = "matthew-fish"
+ sprite_width = 6
+
+/obj/item/fish/fryish/nessie
+ name = "nessie-fish"
+ desc = "A deliciously extremophile alien fish. This one is so big, you could write legends about it."
+ icon = 'icons/obj/aquarium/wide.dmi'
+ icon_state = "nessiefish"
+ base_pixel_x = -16
+ pixel_x = -16
+ sprite_width = 12
+ sprite_height = 4
+ average_size = 150
+ average_weight = 6000
+ health = 125
+ feeding_frequency = 5 MINUTES
+ breeding_timeout = 5 MINUTES
+ random_case_rarity = FISH_RARITY_NOPE
+ stable_population = 3
+ num_fillets = 2
+ fish_traits = list(/datum/fish_trait/picky_eater, /datum/fish_trait/predator)
+ compatible_types = list(/obj/item/fish/fryish, /obj/item/fish/fryish/fritterish)
+ spawn_types = list(/obj/item/fish/fryish/fritterish)
+ fish_movement_type = /datum/fish_movement
+ beauty = FISH_BEAUTY_EXCELLENT
+ fishing_difficulty_modifier = 45
+ fish_flags = parent_type::fish_flags & ~FISH_FLAG_SHOW_IN_CATALOG
+ is_bait = FALSE
+ next_type = null
diff --git a/code/modules/fishing/fish/types/syndicate.dm b/code/modules/fishing/fish/types/syndicate.dm
new file mode 100644
index 0000000000000..8732bb8f0bd3b
--- /dev/null
+++ b/code/modules/fishing/fish/types/syndicate.dm
@@ -0,0 +1,270 @@
+///Contains fish that can be found in the syndicate fishing portal setting as well as the ominous fish case.
+/obj/item/fish/emulsijack
+ name = "toxic emulsijack"
+ desc = "Ah, the terrifying emulsijack. Created in a laboratory, the only real use of this slimey, scaleless fish is for completely ruining a tank."
+ icon_state = "emulsijack"
+ random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
+ required_fluid_type = AQUARIUM_FLUID_ANADROMOUS
+ stable_population = 3
+ sprite_width = 7
+ sprite_height = 3
+ fish_traits = list(/datum/fish_trait/emulsijack)
+ required_temperature_min = MIN_AQUARIUM_TEMP+5
+ required_temperature_max = MIN_AQUARIUM_TEMP+40
+ beauty = FISH_BEAUTY_BAD
+
+/obj/item/fish/emulsijack/get_fish_taste()
+ return list("raw fish" = 2, "acid" = 1) //no scales
+
+/obj/item/fish/donkfish
+ name = "donk co. company patent donkfish"
+ desc = "A lab-grown donkfish. Its invention was an accident for the most part, as it was intended to be consumed in donk pockets. Unfortunately, it tastes horrible, so it has now become a pseudo-mascot."
+ icon_state = "donkfish"
+ random_case_rarity = FISH_RARITY_VERY_RARE
+ stable_population = 4
+ sprite_width = 5
+ sprite_height = 4
+ fillet_type = /obj/item/food/fishmeat/donkfish
+ fish_traits = list(/datum/fish_trait/yucky)
+ required_temperature_min = MIN_AQUARIUM_TEMP+15
+ required_temperature_max = MIN_AQUARIUM_TEMP+28
+ beauty = FISH_BEAUTY_EXCELLENT
+
+/obj/item/fish/jumpercable
+ name = "monocloning jumpercable"
+ desc = "A surprisingly useful if nasty looking creation from the syndicate fish labs. Drop one in a tank, and \
+ watch it self-feed and multiply. Generates more and more power as a growing swarm!"
+ icon_state = "jumpercable"
+ sprite_width = 16
+ sprite_height = 5
+ stable_population = 12
+ average_size = 110
+ average_weight = 6000
+ random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
+ required_temperature_min = MIN_AQUARIUM_TEMP+10
+ required_temperature_max = MIN_AQUARIUM_TEMP+30
+ favorite_bait = list(/obj/item/stock_parts/power_store/cell/lead)
+ fish_traits = list(
+ /datum/fish_trait/parthenogenesis,
+ /datum/fish_trait/mixotroph,
+ /datum/fish_trait/electrogenesis,
+ )
+ electrogenesis_power = 0.9 MEGA JOULES
+ beauty = FISH_BEAUTY_UGLY
+
+/obj/item/fish/chainsawfish
+ name = "chainsawfish"
+ desc = "A very, very angry bioweapon, whose sole purpose is to rip and tear."
+ icon = 'icons/obj/aquarium/wide.dmi'
+ icon_state = "chainsawfish"
+ inhand_icon_state = "chainsawfish"
+ icon_state_dead = "chainsawfish_dead"
+ force = 22
+ demolition_mod = 1.5
+ block_chance = 15
+ attack_verb_continuous = list("saws", "tears", "lacerates", "cuts", "chops", "dices")
+ attack_verb_simple = list("saw", "tear", "lacerate", "cut", "chop", "dice")
+ hitsound = 'sound/items/weapons/chainsawhit.ogg'
+ sharpness = SHARP_EDGED
+ tool_behaviour = TOOL_SAW
+ toolspeed = 0.5
+ base_pixel_x = -16
+ pixel_x = -16
+ sprite_width = 8
+ sprite_height = 5
+ stable_population = 3
+ average_size = 85
+ average_weight = 2500
+ breeding_timeout = 4.25 MINUTES
+ feeding_frequency = 3 MINUTES
+ health = 180
+ beauty = FISH_BEAUTY_GREAT
+ random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
+ required_fluid_type = AQUARIUM_FLUID_FRESHWATER
+ fish_movement_type = /datum/fish_movement/accelerando
+ fishing_difficulty_modifier = 30
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = GORE,
+ ),
+ )
+ fish_traits = list(/datum/fish_trait/aggressive, /datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/stinger)
+ required_temperature_min = MIN_AQUARIUM_TEMP+18
+ required_temperature_max = MIN_AQUARIUM_TEMP+26
+
+/obj/item/fish/chainsawfish/Initialize(mapload)
+ . = ..()
+ AddElement(/datum/element/update_icon_updates_onmob)
+
+/obj/item/fish/chainsawfish/get_fish_taste()
+ return list("raw fish" = 2.5, "anger" = 1)
+
+/obj/item/fish/chainsawfish/update_icon_state()
+ if(status == FISH_DEAD)
+ inhand_icon_state = "chainsawfish_dead"
+ else
+ inhand_icon_state = "chainsawfish"
+ if(HAS_TRAIT(src, TRAIT_WIELDED))
+ inhand_icon_state = "[inhand_icon_state]_wielded"
+ return ..()
+
+/obj/item/fish/chainsawfish/get_force_rank()
+ switch(w_class)
+ if(WEIGHT_CLASS_TINY)
+ force -= 10
+ attack_speed -= 0.2 SECONDS
+ demolition_mod -= 0.4
+ block_chance -= 15
+ armour_penetration -= 10
+ wound_bonus -= 10
+ bare_wound_bonus -= 10
+ toolspeed += 0.6
+ if(WEIGHT_CLASS_SMALL)
+ force -= 8
+ attack_speed -= 0.1 SECONDS
+ demolition_mod -= 0.3
+ block_chance -= 10
+ armour_penetration -= 10
+ wound_bonus -= 10
+ bare_wound_bonus -= 10
+ toolspeed += 0.4
+ if(WEIGHT_CLASS_NORMAL)
+ force -= 5
+ demolition_mod -= 0.15
+ block_chance -= 5
+ armour_penetration -= 5
+ wound_bonus -= 5
+ bare_wound_bonus -= 5
+ toolspeed += 0.2
+ if(WEIGHT_CLASS_HUGE)
+ force += 2
+ attack_speed += 0.2 SECONDS
+ demolition_mod += 0.15
+ armour_penetration += 10
+ block_chance += 10
+ wound_bonus += 10
+ bare_wound_bonus += 5
+ if(WEIGHT_CLASS_GIGANTIC)
+ force += 4
+ attack_speed += 0.4 SECONDS
+ demolition_mod += 0.3
+ block_chance += 20
+ armour_penetration += 20
+ wound_bonus += 15
+ bare_wound_bonus += 10
+ toolspeed -= 0.1
+
+ if(status == FISH_DEAD)
+ force -= 8 + w_class
+ hitsound = SFX_SWING_HIT
+ block_chance -= 25
+ demolition_mod -= 0.3
+ armour_penetration -= 15
+ wound_bonus -= 5
+ bare_wound_bonus -= 5
+ toolspeed += 1
+
+/obj/item/fish/chainsawfish/calculate_fish_force_bonus(bonus_malus)
+ . = ..()
+ armour_penetration += bonus_malus * 3
+ wound_bonus += bonus_malus * 2
+ bare_wound_bonus += bonus_malus * 3
+ block_chance += bonus_malus * 2
+ toolspeed -= bonus_malus * 0.1
+
+/obj/item/fish/pike/armored
+ name = "armored pike"
+ desc = "A long-bodied, metal-clad predator with a snout that almost looks like an halberd. Definitely a weapon to swing around."
+ icon_state = "armored_pike"
+ inhand_icon_state = "armored_pike"
+ attack_verb_continuous = list("attacks", "pokes", "jabs", "tears", "lacerates", "gores")
+ attack_verb_simple = list("attack", "poke", "jab", "tear", "lacerate", "gore")
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
+ block_sound = 'sound/items/weapons/parry.ogg'
+ force = 20
+ sharpness = SHARP_EDGED
+ wound_bonus = -15
+ attack_speed = 1 SECONDS
+ block_chance = 25
+ bare_wound_bonus = 15
+ demolition_mod = 0.8
+ armour_penetration = 10
+ stable_population = 3
+ average_weight = 3000
+ breeding_timeout = 5 MINUTES
+ feeding_frequency = 4 MINUTES
+ health = 180
+ random_case_rarity = FISH_RARITY_GOOD_LUCK_FINDING_THIS
+ beauty = FISH_BEAUTY_GREAT
+ fishing_difficulty_modifier = 20
+ fish_traits = list(/datum/fish_trait/carnivore, /datum/fish_trait/predator, /datum/fish_trait/aggressive, /datum/fish_trait/picky_eater, /datum/fish_trait/stinger)
+ evolution_types = null
+ compatible_types = list(/obj/item/fish/pike)
+ favorite_bait = list(
+ list(
+ FISH_BAIT_TYPE = FISH_BAIT_FOODTYPE,
+ FISH_BAIT_VALUE = SEAFOOD,
+ ),
+ /obj/item/fish,
+ )
+
+/obj/item/fish/pike/armored/get_fish_taste()
+ return list("raw fish" = 2.5, "metal" = 1)
+
+/obj/item/fish/pike/armored/get_fish_taste()
+ return list("cooked fish" = 2.5, "metal" = 1)
+
+/obj/item/fish/pike/armored/get_force_rank()
+ switch(w_class)
+ if(WEIGHT_CLASS_TINY)
+ force -= 11
+ attack_speed -= 0.4 SECONDS
+ block_chance -= 25
+ armour_penetration -= 15
+ wound_bonus -= 15
+ bare_wound_bonus -= 30
+ if(WEIGHT_CLASS_SMALL)
+ force -= 6
+ attack_speed -= 0.3 SECONDS
+ block_chance -= 20
+ armour_penetration -= 10
+ wound_bonus -= 10
+ bare_wound_bonus -= 25
+ if(WEIGHT_CLASS_NORMAL)
+ force -= 4
+ attack_speed -= 0.2 SECONDS
+ block_chance -= 20
+ armour_penetration -= 5
+ wound_bonus -= 10
+ bare_wound_bonus -= 15
+ if(WEIGHT_CLASS_HUGE)
+ force += 3
+ attack_speed += 0.2 SECONDS
+ block_chance += 10
+ demolition_mod += 0.1
+ armour_penetration += 5
+ wound_bonus += 10
+ bare_wound_bonus += 5
+ if(WEIGHT_CLASS_GIGANTIC)
+ force += 7
+ attack_speed += 0.3 SECONDS
+ demolition_mod += 0.2
+ block_chance += 20
+ armour_penetration += 10
+ wound_bonus += 15
+ bare_wound_bonus += 10
+
+ if(status == FISH_DEAD)
+ force -= 5 + w_class
+ block_chance -= 15
+ armour_penetration -= 10
+ wound_bonus -= 5
+ bare_wound_bonus -= 15
+
+/obj/item/fish/pike/armored/calculate_fish_force_bonus(bonus_malus)
+ . = ..()
+ armour_penetration += bonus_malus * 3
+ wound_bonus += bonus_malus * 2
+ bare_wound_bonus += bonus_malus * 4
+ block_chance += bonus_malus * 4
diff --git a/code/modules/fishing/fish/types/tiziran.dm b/code/modules/fishing/fish/types/tiziran.dm
new file mode 100644
index 0000000000000..b6fd43709f2d6
--- /dev/null
+++ b/code/modules/fishing/fish/types/tiziran.dm
@@ -0,0 +1,86 @@
+//Tiziran Fish.
+
+/obj/item/fish/dwarf_moonfish
+ name = "dwarf moonfish"
+ desc = "Ordinarily in the wild, the Zagoskian moonfish is around the size of a tuna, however through selective breeding a smaller breed suitable for being kept as an aquarium pet has been created."
+ icon_state = "dwarf_moonfish"
+ sprite_height = 6
+ sprite_width = 6
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ stable_population = 2
+ fillet_type = /obj/item/food/fishmeat/moonfish
+ average_size = 60
+ average_weight = 1000
+ required_temperature_min = MIN_AQUARIUM_TEMP+20
+ required_temperature_max = MIN_AQUARIUM_TEMP+30
+ beauty = FISH_BEAUTY_GOOD
+
+/obj/item/fish/gunner_jellyfish
+ name = "gunner jellyfish"
+ desc = "So called due to their resemblance to an artillery shell, the gunner jellyfish is native to Tizira, where it is enjoyed as a delicacy. Produces a mild hallucinogen that is destroyed by cooking."
+ icon_state = "gunner_jellyfish"
+ sprite_height = 4
+ sprite_width = 5
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ stable_population = 4
+ fillet_type = /obj/item/food/fishmeat/gunner_jellyfish
+ fish_traits = list(/datum/fish_trait/hallucinogenic)
+ required_temperature_min = MIN_AQUARIUM_TEMP+24
+ required_temperature_max = MIN_AQUARIUM_TEMP+32
+ beauty = FISH_BEAUTY_GOOD
+
+/obj/item/fish/gunner_jellyfish/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ AddElement(/datum/element/quality_food_ingredient, FOOD_COMPLEXITY_2)
+
+/obj/item/fish/gunner_jellyfish/get_fish_taste()
+ return list("cold jelly" = 2)
+
+/obj/item/fish/gunner_jellyfish/get_fish_taste_cooked()
+ return list("crunchy tenderness" = 2)
+
+/obj/item/fish/needlefish
+ name = "needlefish"
+ desc = "A tiny, transparent fish which resides in large schools in the oceans of Tizira. A common food for other, larger fish."
+ icon_state = "needlefish"
+ sprite_height = 3
+ sprite_width = 7
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ stable_population = 12
+ breeding_timeout = 1 MINUTES
+ fillet_type = null
+ average_size = 20
+ average_weight = 300
+ fish_traits = list(/datum/fish_trait/carnivore)
+ required_temperature_min = MIN_AQUARIUM_TEMP+10
+ required_temperature_max = MIN_AQUARIUM_TEMP+32
+
+/obj/item/fish/needlefish/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ add_traits(list(TRAIT_FISHING_BAIT, TRAIT_GOOD_QUALITY_BAIT), INNATE_TRAIT)
+
+/obj/item/fish/armorfish
+ name = "armorfish"
+ desc = "A small shellfish native to Tizira's oceans, known for its exceptionally hard shell. Consumed similarly to prawns."
+ icon_state = "armorfish"
+ sprite_height = 5
+ sprite_width = 6
+ average_size = 25
+ average_weight = 350
+ required_fluid_type = AQUARIUM_FLUID_SALTWATER
+ stable_population = 10
+ breeding_timeout = 1.25 MINUTES
+ fillet_type = /obj/item/food/fishmeat/armorfish
+ fish_movement_type = /datum/fish_movement/slow
+ required_temperature_min = MIN_AQUARIUM_TEMP+10
+ required_temperature_max = MIN_AQUARIUM_TEMP+32
+
+/obj/item/fish/armorfish/Initialize(mapload, apply_qualities = TRUE)
+ . = ..()
+ add_traits(list(TRAIT_FISHING_BAIT, TRAIT_GOOD_QUALITY_BAIT), INNATE_TRAIT)
+
+/obj/item/fish/chasm_crab/get_fish_taste()
+ return list("raw prawn" = 2)
+
+/obj/item/fish/chasm_crab/get_fish_taste_cooked()
+ return list("cooked prawn" = 2)
diff --git a/code/modules/fishing/fish_catalog.dm b/code/modules/fishing/fish_catalog.dm
index 49a84413ded06..4f329861ff93c 100644
--- a/code/modules/fishing/fish_catalog.dm
+++ b/code/modules/fishing/fish_catalog.dm
@@ -3,8 +3,13 @@
name = "Fish Encyclopedia"
desc = "Indexes all fish known to mankind (and related species)."
icon_state = "fishbook"
+ custom_price = PAYCHECK_CREW * 2
starting_content = "Lot of fish stuff" //book wrappers could use cleaning so this is not necessary
+/obj/item/book/manual/fish_catalog/Initialize(mapload)
+ . = ..()
+ AddComponent(/datum/component/adjust_fishing_difficulty, -4, ITEM_SLOT_HANDS)
+
/obj/item/book/manual/fish_catalog/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
@@ -16,11 +21,10 @@
var/static/fish_info
if(!fish_info)
fish_info = list()
- for(var/_fish_type as anything in subtypesof(/obj/item/fish))
- var/obj/item/fish/fish = _fish_type
- var/list/fish_data = list()
- if(!initial(fish.show_in_catalog))
+ for(var/obj/item/fish/fish as anything in subtypesof(/obj/item/fish))
+ if(!(initial(fish.fish_flags) & FISH_FLAG_SHOW_IN_CATALOG))
continue
+ var/list/fish_data = list()
fish_data["name"] = initial(fish.name)
fish_data["desc"] = initial(fish.desc)
fish_data["fluid"] = initial(fish.required_fluid_type)
@@ -37,27 +41,9 @@
else
fish_data["feed"] = "[AQUARIUM_COMPANY] Fish Feed"
fish_data["fishing_tips"] = build_fishing_tips(fish)
- var/beauty_score = initial(fish.beauty)
- switch(beauty_score)
- if(-INFINITY to FISH_BEAUTY_DISGUSTING)
- beauty_score = "OH HELL NAW!"
- if(FISH_BEAUTY_DISGUSTING to FISH_BEAUTY_UGLY)
- beauty_score = "☆☆☆☆☆"
- if(FISH_BEAUTY_UGLY to FISH_BEAUTY_BAD)
- beauty_score = "★☆☆☆☆"
- if(FISH_BEAUTY_BAD to FISH_BEAUTY_NULL)
- beauty_score = "★★☆☆☆"
- if(FISH_BEAUTY_NULL to FISH_BEAUTY_GENERIC)
- beauty_score = "★★★☆☆"
- if(FISH_BEAUTY_GENERIC to FISH_BEAUTY_GOOD)
- beauty_score = "★★★★☆"
- if(FISH_BEAUTY_GOOD to FISH_BEAUTY_GREAT)
- beauty_score = "★★★★★"
- if(FISH_BEAUTY_GREAT to INFINITY)
- beauty_score = "★★★★★★"
- fish_data["beauty"] = beauty_score
+ fish_data["beauty"] = SSfishing.fish_properties[fish][FISH_PROPERTIES_BEAUTY_SCORE]
+
fish_info += list(fish_data)
- // TODO: Custom entries for unusual stuff
.["fish_info"] = fish_info
.["sponsored_by"] = AQUARIUM_COMPANY
@@ -68,12 +54,12 @@
return initial(bait_item.name)
if(islist(bait))
var/list/special_identifier = bait
- switch(special_identifier["Type"])
- if("Foodtype")
- return jointext(bitfield_to_list(special_identifier["Value"], FOOD_FLAGS_IC),",")
- if("Reagent")
- var/datum/reagent/prototype = special_identifier["Value"]
- return "[initial(prototype.name)] (at least [special_identifier["Amount"]]u)"
+ switch(special_identifier[FISH_BAIT_TYPE])
+ if(FISH_BAIT_FOODTYPE)
+ return jointext(bitfield_to_list(special_identifier[FISH_BAIT_VALUE], FOOD_FLAGS_IC),",")
+ if(FISH_BAIT_REAGENT)
+ var/datum/reagent/prototype = special_identifier[FISH_BAIT_VALUE]
+ return "[initial(prototype.name)] (at least [special_identifier[FISH_BAIT_AMOUNT]]u)"
else
stack_trace("Unknown bait identifier in fish favourite/disliked list")
return "SOMETHING VERY WEIRD"
@@ -90,9 +76,9 @@
if(source.catalog_description && (fish_type in source.fish_table))
spot_descriptions += source.catalog_description
.["spots"] = english_list(spot_descriptions, nothing_text = "Unknown")
- var/list/fish_list_properties = collect_fish_properties()
- var/list/fav_bait = fish_list_properties[fishy][NAMEOF(fishy, favorite_bait)]
- var/list/disliked_bait = fish_list_properties[fishy][NAMEOF(fishy, disliked_bait)]
+ var/list/fish_list_properties = SSfishing.fish_properties
+ var/list/fav_bait = fish_list_properties[fishy][FISH_PROPERTIES_FAV_BAIT]
+ var/list/disliked_bait = fish_list_properties[fishy][FISH_PROPERTIES_BAD_BAIT]
var/list/bait_list = list()
// Favourite/Disliked bait
for(var/bait_type_or_trait in fav_bait)
@@ -104,7 +90,7 @@
.["disliked_bait"] = english_list(bait_list, nothing_text = "None")
// Fish traits description
var/list/trait_descriptions = list()
- var/list/fish_traits = fish_list_properties[fishy][NAMEOF(fishy, fish_traits)]
+ var/list/fish_traits = fish_list_properties[fishy][FISH_PROPERTIES_TRAITS]
var/fish_difficulty = initial(fishy.fishing_difficulty_modifier)
for(var/fish_trait in fish_traits)
var/datum/fish_trait/trait = GLOB.fish_traits[fish_trait]
diff --git a/code/modules/fishing/fish_movement.dm b/code/modules/fishing/fish_movement.dm
index a328903617f62..b288161b19237 100644
--- a/code/modules/fishing/fish_movement.dm
+++ b/code/modules/fishing/fish_movement.dm
@@ -53,6 +53,10 @@
else
long_jump_chance *= master.difficulty
+/datum/fish_movement/proc/reset_difficulty_values()
+ short_jump_chance = initial(short_jump_chance)
+ long_jump_chance = initial(long_jump_chance)
+
///The main proc, called by minigame every SSfishing tick while it's in the 'active' phase.
/datum/fish_movement/proc/move_fish(seconds_per_tick)
times_fired++
@@ -190,6 +194,12 @@
plunging_speed += round(plunging_speed * master.difficulty * 0.03)
fish_idle_velocity += plunging_speed //so it can be safely subtracted if the fish starts at the bottom.
+/datum/fish_movement/plunger/reset_difficulty_values()
+ . = ..()
+ if(is_plunging)
+ fish_idle_velocity -= plunging_speed
+ plunging_speed = initial(plunging_speed)
+
/datum/fish_movement/plunger/move_fish(seconds_per_tick)
var/fish_area = FISHING_MINIGAME_AREA - master.fish_height
if(is_plunging)
diff --git a/code/modules/fishing/fishing_equipment.dm b/code/modules/fishing/fishing_equipment.dm
index 5408e411876b9..ccad3b4708017 100644
--- a/code/modules/fishing/fishing_equipment.dm
+++ b/code/modules/fishing/fishing_equipment.dm
@@ -16,6 +16,8 @@
var/list/fishing_line_traits
/// Color of the fishing line
var/line_color = COLOR_GRAY
+ ///The description given to the autowiki
+ var/wiki_desc = "A generic fishing line. Without one, the casting range of the rod will be significantly hampered."
/obj/item/fishing_line/reinforced
name = "reinforced fishing line reel"
@@ -23,6 +25,7 @@
icon_state = "reel_green"
fishing_line_traits = FISHING_LINE_REINFORCED
line_color = "#2b9c2b"
+ wiki_desc = "Allows you to fish in lava and plasma rivers and lakes."
/obj/item/fishing_line/cloaked
name = "cloaked fishing line reel"
@@ -30,6 +33,7 @@
icon_state = "reel_white"
fishing_line_traits = FISHING_LINE_CLOAKED
line_color = "#82cfdd"
+ wiki_desc = "Fishing anxious and wary fish will be easier with this equipped."
/obj/item/fishing_line/bouncy
name = "flexible fishing line reel"
@@ -37,6 +41,7 @@
icon_state = "reel_red"
fishing_line_traits = FISHING_LINE_BOUNCY
line_color = "#99313f"
+ wiki_desc = "It reduces the progression loss during the fishing minigame."
/obj/item/fishing_line/sinew
name = "fishing sinew"
@@ -44,6 +49,7 @@
icon_state = "reel_sinew"
fishing_line_traits = FISHING_LINE_REINFORCED|FISHING_LINE_STIFF
line_color = "#d1cca3"
+ wiki_desc = "Crafted from sinew. It allows you to fish in lava and plasma like the reinforced line, but it'll make the minigame harder."
/**
* A special line reel that let you skip the biting phase of the minigame, netting you a completion bonus,
@@ -52,10 +58,13 @@
*/
/obj/item/fishing_line/auto_reel
name = "fishing line auto-reel"
- desc = "A fishing line that automatically starts reeling in fish the moment they bite. Also good for hurling things at yourself."
+ desc = "A fishing line that automatically spins lures and begins reeling in fish the moment it bites. Also good for hurling things towards you."
icon_state = "reel_auto"
fishing_line_traits = FISHING_LINE_AUTOREEL
line_color = "#F88414"
+ wiki_desc = "Automatically starts the minigame once the fish bites the bait. It also spin fishing lures for you without needing an input. \
+ It can also be used to snag in objects from a distance more rapidly. \
+ It requires the Advanced Fishing Technology Node to be researched to be printed."
/obj/item/fishing_line/auto_reel/Initialize(mapload)
. = ..()
@@ -90,7 +99,7 @@
if(!movable_target.safe_throw_at(destination, source.cast_range, 2, callback = throw_callback, gentle = please_be_gentle))
UnregisterSignal(movable_target, COMSIG_ATOM_PREHITBY)
else
- playsound(src, 'sound/weapons/batonextend.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/batonextend.ogg', 50, TRUE)
/obj/item/fishing_line/auto_reel/proc/catch_it_chucklenut(obj/item/source, atom/hit_atom, datum/thrownthing/throwingdatum)
SIGNAL_HANDLER
@@ -118,7 +127,8 @@
var/rod_overlay_icon_state = "hook_overlay"
/// What subtype of `/obj/item/chasm_detritus` do we fish out of chasms? Defaults to `/obj/item/chasm_detritus`.
var/chasm_detritus_type = /datum/chasm_detritus
-
+ ///The description given to the autowiki
+ var/wiki_desc = "A generic fishing hook. You won't be able to fish without one."
/**
* Simple getter proc for hooks to implement special hook bonuses for
@@ -162,7 +172,22 @@
icon_state = "treasure"
rod_overlay_icon_state = "hook_treasure_overlay"
chasm_detritus_type = /datum/chasm_detritus/restricted/objects
+ wiki_desc = "It vastly improves the chances of catching things other than fish."
+
+/obj/item/fishing_hook/magnet/Initialize(mapload)
+ . = ..()
+ RegisterSignal(src, COMSIG_FISHING_EQUIPMENT_SLOTTED, PROC_REF(hook_equipped))
+
+///We make sure that the fishng rod doesn't need a bait to reliably catch non-fish loot.
+/obj/item/fishing_hook/magnet/proc/hook_equipped(datum/source, obj/item/fishing_rod/rod)
+ SIGNAL_HANDLER
+ ADD_TRAIT(rod, TRAIT_ROD_REMOVE_FISHING_DUD, type)
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_removed))
+/obj/item/fishing_hook/magnet/proc/on_removed(atom/movable/source, atom/old_loc, dir, forced)
+ SIGNAL_HANDLER
+ REMOVE_TRAIT(old_loc, TRAIT_ROD_REMOVE_FISHING_DUD, type)
+ UnregisterSignal(src, COMSIG_MOVABLE_MOVED)
/obj/item/fishing_hook/magnet/get_hook_bonus_multiplicative(fish_type, datum/fish_source/source)
if(fish_type == FISHING_DUD || ispath(fish_type, /obj/item/fish))
@@ -171,19 +196,19 @@
// We multiply the odds by five for everything that's not a fish nor a dud
return MAGNET_HOOK_BONUS_MULTIPLIER
-
/obj/item/fishing_hook/shiny
name = "shiny lure hook"
icon_state = "gold_shiny"
fishing_hook_traits = FISHING_HOOK_SHINY
rod_overlay_icon_state = "hook_shiny_overlay"
+ wiki_desc = "It's used to attract shiny-loving fish and make them easier to catch."
/obj/item/fishing_hook/weighted
name = "weighted hook"
icon_state = "weighted"
fishing_hook_traits = FISHING_HOOK_WEIGHTED
rod_overlay_icon_state = "hook_weighted_overlay"
-
+ wiki_desc = "It reduces the bounce that happens when you hit the boundaries of the minigame bar."
/obj/item/fishing_hook/rescue
name = "rescue hook"
@@ -191,6 +216,8 @@
icon_state = "rescue"
rod_overlay_icon_state = "hook_rescue_overlay"
chasm_detritus_type = /datum/chasm_detritus/restricted/bodies
+ wiki_desc = "A hook used to rescue bodies whom have fallen into chasms. \
+ You won't catch fish with it, nor it can't be used for fishing outside of chasms, though it can still be used to reel in people and items from unreachable locations.."
/obj/item/fishing_hook/rescue/can_be_hooked(atom/target)
return ..() || isliving(target)
@@ -220,6 +247,7 @@
name = "bone hook"
desc = "A simple hook carved from sharpened bone"
icon_state = "hook_bone"
+ wiki_desc = "A generic fishing hook carved out of sharpened bone. Bone fishing rods come pre-equipped with it."
/obj/item/fishing_hook/stabilized
name = "gyro-stabilized hook"
@@ -227,6 +255,8 @@
icon_state = "gyro"
fishing_hook_traits = FISHING_HOOK_BIDIRECTIONAL
rod_overlay_icon_state = "hook_gyro_overlay"
+ wiki_desc = "It allows you to move both up (left-click) and down (right-click) during the minigame while negating gravity. \
+ It requires the Advanced Fishing Technology Node to be researched to be printed."
/obj/item/fishing_hook/stabilized/examine(mob/user)
. = ..()
@@ -239,6 +269,9 @@
w_class = WEIGHT_CLASS_NORMAL
fishing_hook_traits = FISHING_HOOK_NO_ESCAPE|FISHING_HOOK_NO_ESCAPE|FISHING_HOOK_KILL
rod_overlay_icon_state = "hook_jaws_overlay"
+ wiki_desc = "A beartrap-looking hook that makes losing the fishing minigame impossible (Unless you drop the rod or get stunned). However it'll hurt the fish and eventually kill it. \
+ Funnily enough, you can snag in people with it too. It won't hurt them like a actual beartrap, but it'll still slow them down. \
+ It has to be bought from the black market uplink."
/obj/item/fishing_hook/jaws/can_be_hooked(atom/target)
return ..() || isliving(target)
@@ -254,6 +287,9 @@
icon_state = "fishing"
inhand_icon_state = "artistic_toolbox"
material_flags = NONE
+ custom_price = PAYCHECK_CREW * 3
+ ///How much holding this affects fishing difficulty
+ var/fishing_modifier = -2
/obj/item/storage/toolbox/fishing/Initialize(mapload)
. = ..()
@@ -262,6 +298,7 @@
/obj/item/fishing_rod,
))
atom_storage.exception_hold = exception_cache
+ AddComponent(/datum/component/adjust_fishing_difficulty, -2, ITEM_SLOT_HANDS)
/obj/item/storage/toolbox/fishing/PopulateContents()
new /obj/item/bait_can/worm(src)
@@ -292,6 +329,7 @@
desc = "Contains EVERYTHING (almost) you need for your fishing trip."
icon_state = "gold"
inhand_icon_state = "toolbox_gold"
+ fishing_modifier = -7
/obj/item/storage/toolbox/fishing/master/PopulateContents()
new /obj/item/fishing_rod/telescopic/master(src)
@@ -306,6 +344,7 @@
/obj/item/storage/box/fishing_hooks
name = "fishing hook set"
illustration = "fish"
+ custom_price = PAYCHECK_CREW * 2
/obj/item/storage/box/fishing_hooks/PopulateContents()
new /obj/item/fishing_hook/magnet(src)
@@ -322,6 +361,7 @@
/obj/item/storage/box/fishing_lines
name = "fishing line set"
illustration = "fish"
+ custom_price = PAYCHECK_CREW * 2
/obj/item/storage/box/fishing_lines/PopulateContents()
new /obj/item/fishing_line/bouncy(src)
@@ -347,7 +387,7 @@
name = "fishing tip"
desc = "A slip of paper containing a pearl of wisdom about fishing within it, though you wish it were an actual pearl."
-/obj/item/paper/paperslip/fortune/Initialize(mapload)
+/obj/item/paper/paperslip/fishing_tip/Initialize(mapload)
default_raw_text = pick(GLOB.fishing_tips)
return ..()
@@ -364,5 +404,43 @@
new /obj/item/storage/fish_case(src)
new /obj/item/storage/fish_case(src)
+/obj/item/storage/box/fishing_lures
+ name = "fishing lures set"
+ desc = "A small tackle box containing all the fishing lures you will ever need to curb randomness."
+ icon_state = "plasticbox"
+ foldable_result = null
+ illustration = "fish"
+ custom_price = PAYCHECK_CREW * 9
+
+/obj/item/storage/box/fishing_lures/PopulateContents()
+ new /obj/item/paper/lures_instructions(src)
+ var/list/typesof = typesof(/obj/item/fishing_lure)
+ for(var/type in typesof)
+ new type (src)
+ atom_storage.set_holdable(/obj/item/fishing_lure) //can only hold lures
+ //adds an extra slot, so we can put back the lures even if we didn't take out the instructions.
+ atom_storage.max_slots = length(typesof) + 1
+ atom_storage.max_total_storage = WEIGHT_CLASS_SMALL * (atom_storage.max_slots + 1)
+
+/obj/item/paper/lures_instructions
+ name = "instructions paper"
+ icon_state = "slipfull"
+ show_written_words = FALSE
+ desc = "A piece of grey paper with a how-to for dummies about fishing lures printed on it. Smells cheap."
+ default_raw_text = "Thank you for buying this set. \
+ This a simple non-exhaustive set of instructions on how to use fishing lures, some information may \
+ be slightly incorrect or oversimplified.
\
+
+ First and foremost, fishing lures are inedible, artificial baits sturdy enough to not end up being \
+ consumed by the hungry fish. However, they need to be spun at intervals to replicate \
+ the motion of a prey or organic bait and tempt the fish, since a piece of plastic and metal ins't \
+ all that appetitizing by itself. Different lures can be used to catch different fish.
\
+
+ To help you, each lure comes with a small light diode that's attached to the float of your fishing rod. \
+ A float is basically the thing bobbing up'n'down above the fishing spot. \
+ The light will flash green and a sound cue will be played when the lure is ready to be spun. \
+ Do not spin while the light is still red.
\
+ That's all, best of luck to your angling journey."
+
#undef MAGNET_HOOK_BONUS_MULTIPLIER
#undef RESCUE_HOOK_FISH_MULTIPLIER
diff --git a/code/modules/fishing/fishing_minigame.dm b/code/modules/fishing/fishing_minigame.dm
index f53437e355953..30ce2b33ad0cc 100644
--- a/code/modules/fishing/fishing_minigame.dm
+++ b/code/modules/fishing/fishing_minigame.dm
@@ -1,4 +1,4 @@
-// Lure bobbing
+// float bobbing
#define WAIT_PHASE 1
// Click now to start tgui part
#define BITING_PHASE 2
@@ -32,6 +32,8 @@
///The standard pixel height of the fish (minus a pixel on each direction for the sake of a better looking sprite)
#define MINIGAME_FISH_HEIGHT 4
+GLOBAL_LIST_EMPTY(fishing_challenges_by_user)
+
/datum/fishing_challenge
/// When the ui minigame phase started
var/start_time
@@ -53,12 +55,16 @@
var/phase = WAIT_PHASE
// Timer for the next phase
var/next_phase_timer
+ // The last time we clicked during the baiting phase
+ var/last_baiting_click
/// Fishing mob
var/mob/user
/// Rod that is used for the challenge
var/obj/item/fishing_rod/used_rod
- /// Lure visual
- var/obj/effect/fishing_lure/lure
+ /// float visual
+ var/obj/effect/fishing_float/float
+ ///The physical fishing spot our float is hovering
+ var/atom/location
/// Background icon state from fishing_hud.dmi
var/background = "background_default"
/// Fish icon state from fishing_hud.dmi
@@ -105,30 +111,21 @@
///The background as shown in the minigame, and the holder of the other visual overlays
var/atom/movable/screen/fishing_hud/fishing_hud
-/datum/fishing_challenge/New(datum/component/fishing_spot/comp, reward_path, obj/item/fishing_rod/rod, mob/user)
+/datum/fishing_challenge/New(datum/component/fishing_spot/comp, obj/item/fishing_rod/rod, mob/user)
src.user = user
- src.reward_path = reward_path
- src.used_rod = rod
- var/atom/spot = comp.parent
- lure = new(get_turf(spot), spot)
- RegisterSignal(spot, COMSIG_QDELETING, PROC_REF(on_spot_gone))
+ used_rod = rod
+ location = comp.parent
+ float = new(get_turf(location), location)
+ float.spin_frequency = rod.spin_frequency
+ RegisterSignal(location, COMSIG_QDELETING, PROC_REF(on_spot_gone))
+ RegisterSignal(comp, COMSIG_QDELETING, PROC_REF(on_spot_gone))
RegisterSignal(comp.fish_source, COMSIG_FISHING_SOURCE_INTERRUPT_CHALLENGE, PROC_REF(interrupt_challenge))
- comp.fish_source.RegisterSignal(src, COMSIG_FISHING_CHALLENGE_COMPLETED, TYPE_PROC_REF(/datum/fish_source, on_challenge_completed))
+ comp.fish_source.RegisterSignal(src, COMSIG_FISHING_CHALLENGE_ROLL_REWARD, TYPE_PROC_REF(/datum/fish_source, roll_reward_minigame))
+ comp.fish_source.RegisterSignal(src, COMSIG_FISHING_CHALLENGE_GET_DIFFICULTY, TYPE_PROC_REF(/datum/fish_source, calculate_difficulty_minigame))
+ comp.fish_source.RegisterSignal(user, COMSIG_MOB_COMPLETE_FISHING, TYPE_PROC_REF(/datum/fish_source, on_challenge_completed))
background = comp.fish_source.background
-
- /// Fish minigame properties
- if(ispath(reward_path,/obj/item/fish))
- var/obj/item/fish/fish = reward_path
- var/movement_path = initial(fish.fish_movement_type)
- mover = new movement_path(src)
- // Apply fish trait modifiers
- var/list/fish_list_properties = collect_fish_properties()
- var/list/fish_traits = fish_list_properties[fish][NAMEOF(fish, fish_traits)]
- for(var/fish_trait in fish_traits)
- var/datum/fish_trait/trait = GLOB.fish_traits[fish_trait]
- trait.minigame_mod(rod, user, src)
- else
- mover = new /datum/fish_movement(src)
+ SEND_SIGNAL(user, COMSIG_MOB_BEGIN_FISHING, src)
+ GLOB.fishing_challenges_by_user[user] = src
/// Enable special parameters
if(rod.line)
@@ -154,41 +151,24 @@
completion_loss += user.mind?.get_skill_modifier(/datum/skill/fishing, SKILL_VALUE_MODIFIER)/5
- if(special_effects & FISHING_MINIGAME_RULE_KILL && ispath(reward_path,/obj/item/fish))
- RegisterSignal(comp.fish_source, COMSIG_FISH_SOURCE_REWARD_DISPENSED, PROC_REF(hurt_fish))
-
- difficulty += comp.fish_source.calculate_difficulty(reward_path, rod, user, src)
- difficulty = clamp(round(difficulty), FISHING_EASY_DIFFICULTY - 5, 100)
-
- if(difficulty > FISHING_EASY_DIFFICULTY)
- completion -= MAX_FISH_COMPLETION_MALUS * (difficulty * 0.01)
-
- if(HAS_MIND_TRAIT(user, TRAIT_REVEAL_FISH))
- fish_icon = GLOB.specific_fish_icons[reward_path] || FISH_ICON_DEF
-
- mover.adjust_to_difficulty()
-
- bait_height -= round(difficulty * BAIT_HEIGHT_DIFFICULTY_MALUS)
- bait_pixel_height = round(MINIGAME_BAIT_HEIGHT * (bait_height/initial(bait_height)), 1)
-
/datum/fishing_challenge/Destroy(force)
+ GLOB.fishing_challenges_by_user -= user
if(!completed)
complete(win = FALSE)
if(fishing_line)
//Stops the line snapped message from appearing everytime the minigame is over.
UnregisterSignal(fishing_line, COMSIG_QDELETING)
QDEL_NULL(fishing_line)
- if(lure)
- QDEL_NULL(lure)
+ QDEL_NULL(float)
SStgui.close_uis(src)
user = null
used_rod = null
+ location = null
QDEL_NULL(mover)
return ..()
/datum/fishing_challenge/proc/send_alert(message)
- var/turf/lure_turf = get_turf(lure)
- lure_turf?.balloon_alert(user, message)
+ location?.balloon_alert(user, message)
/datum/fishing_challenge/proc/on_spot_gone(datum/source)
SIGNAL_HANDLER
@@ -204,34 +184,66 @@
/datum/fishing_challenge/proc/start(mob/living/user)
/// Create fishing line visuals
if(!used_rod.internal)
- fishing_line = used_rod.create_fishing_line(lure, user, target_py = 5)
+ fishing_line = used_rod.create_fishing_line(float, user, target_py = 5)
+ if(isnull(fishing_line)) //couldn't create a fishing line, probably because we don't have a good line of sight.
+ qdel(src)
+ return
RegisterSignal(fishing_line, COMSIG_QDELETING, PROC_REF(on_line_deleted))
else //if the rod doesnt have a fishing line, then it ends when they move away
- RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_lure_or_user_move))
- RegisterSignal(lure, COMSIG_MOVABLE_MOVED, PROC_REF(on_lure_or_user_move))
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(on_float_or_user_move))
+ RegisterSignal(float, COMSIG_MOVABLE_MOVED, PROC_REF(on_float_or_user_move))
RegisterSignal(user, SIGNAL_ADDTRAIT(TRAIT_HANDS_BLOCKED), PROC_REF(on_hands_blocked))
RegisterSignal(user, SIGNAL_REMOVETRAIT(TRAIT_PROFOUND_FISHER), PROC_REF(no_longer_fishing))
active_effects = bitfield_to_list(special_effects & FISHING_MINIGAME_ACTIVE_EFFECTS)
// If fishing line breaks los / rod gets dropped / deleted
RegisterSignal(used_rod, COMSIG_ITEM_ATTACK_SELF, PROC_REF(on_attack_self))
- ADD_TRAIT(user, TRAIT_GONE_FISHING, WEAKREF(src))
user.add_mood_event("fishing", /datum/mood_event/fishing)
RegisterSignal(user, COMSIG_MOB_CLICKON, PROC_REF(handle_click))
start_baiting_phase()
to_chat(user, span_notice("You start fishing..."))
- playsound(lure, 'sound/effects/splash.ogg', 100)
+ playsound(location, 'sound/effects/splash.ogg', 100)
+
+///Set the timers for lure that need to be spun at intervals.
+/datum/fishing_challenge/proc/set_lure_timers()
+ float.spin_ready = FALSE
+ addtimer(CALLBACK(src, PROC_REF(set_lure_ready)), float.spin_frequency[1], TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_DELETE_ME)
+ addtimer(CALLBACK(src, PROC_REF(missed_lure)), float.spin_frequency[2], TIMER_UNIQUE|TIMER_OVERRIDE|TIMER_DELETE_ME)
+ float.update_appearance(UPDATE_OVERLAYS)
+
+/datum/fishing_challenge/proc/set_lure_ready()
+ if(phase != WAIT_PHASE)
+ return
+ float.spin_ready = TRUE
+ float.update_appearance(UPDATE_OVERLAYS)
+ if(special_effects & FISHING_MINIGAME_AUTOREEL)
+ addtimer(CALLBACK(src, PROC_REF(auto_spin)), 0.2 SECONDS)
+ playsound(float, 'sound/machines/ping.ogg', 10, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+
+/datum/fishing_challenge/proc/auto_spin()
+ if(phase != WAIT_PHASE || !float.spin_ready)
+ return
+ float.spin_ready = FALSE
+ float.update_appearance(UPDATE_OVERLAYS)
+ set_lure_timers()
+ send_alert("spun")
+
+/datum/fishing_challenge/proc/missed_lure()
+ if(phase != WAIT_PHASE)
+ return
+ send_alert("miss!")
+ start_baiting_phase(TRUE) //Add in another 3 to 5 seconds for not spinning the lure.
/datum/fishing_challenge/proc/on_line_deleted(datum/source)
SIGNAL_HANDLER
fishing_line = null
- ///The lure may be out of sight if the user has moed around a corner, so the message should be displayed over him instead.
+ ///The float may be out of sight if the user has moed around a corner, so the message should be displayed over him instead.
user.balloon_alert(user, user.is_holding(used_rod) ? "line snapped" : "rod dropped")
interrupt()
-/datum/fishing_challenge/proc/on_lure_or_user_move(datum/source)
+/datum/fishing_challenge/proc/on_float_or_user_move(datum/source)
SIGNAL_HANDLER
- if(!user.CanReach(lure))
+ if(!user.CanReach(location))
user.balloon_alert(user, "too far!")
interrupt()
@@ -258,8 +270,20 @@
if(!HAS_TRAIT(source, TRAIT_PROFOUND_FISHER) && source.get_active_held_item() != used_rod)
return
if(phase == WAIT_PHASE)
- send_alert("miss!")
- start_baiting_phase(TRUE) //Add in another 3 to 5 seconds for that blunder.
+ if(world.time < last_baiting_click + 0.25 SECONDS)
+ return COMSIG_MOB_CANCEL_CLICKON //Don't punish players if they accidentally double clicked.
+ if(float.spin_frequency)
+ if(!float.spin_ready)
+ send_alert("too early!")
+ start_baiting_phase(TRUE) //Add in another 3 to 5 seconds for that blunder.
+ else
+ send_alert("spun")
+ last_baiting_click = world.time
+ float.spin_ready = FALSE
+ set_lure_timers()
+ else
+ send_alert("miss!")
+ start_baiting_phase(TRUE) //Add in another 3 to 5 seconds for that blunder.
else if(phase == BITING_PHASE)
start_minigame_phase()
return COMSIG_MOB_CANCEL_CLICKON
@@ -280,6 +304,9 @@
send_alert("stopped fishing")
complete(FALSE)
+///The multiplier of the fishing experience malus if the user's level is substantially above the difficulty.
+#define EXPERIENCE_MALUS_MULT 0.08
+
/datum/fishing_challenge/proc/complete(win = FALSE)
if(completed)
return
@@ -287,42 +314,53 @@
completed = TRUE
if(phase == MINIGAME_PHASE)
remove_minigame_hud()
- if(!QDELETED(user))
- UnregisterSignal(user, SIGNAL_REMOVETRAIT(TRAIT_GONE_FISHING))
- user.remove_traits(list(TRAIT_GONE_FISHING, TRAIT_ACTIVELY_FISHING), WEAKREF(src))
- if(start_time)
- var/seconds_spent = (world.time - start_time) * 0.1
- if(!(special_effects & FISHING_MINIGAME_RULE_NO_EXP))
- user.mind?.adjust_experience(/datum/skill/fishing, round(seconds_spent * FISHING_SKILL_EXP_PER_SECOND * experience_multiplier))
- if(user.mind?.get_skill_level(/datum/skill/fishing) >= SKILL_LEVEL_LEGENDARY)
- user.client?.give_award(/datum/award/achievement/skill/legendary_fisher, user)
+ if(!QDELETED(user) && user.mind && start_time && !(special_effects & FISHING_MINIGAME_RULE_NO_EXP))
+ var/seconds_spent = (world.time - start_time) * 0.1
+ var/extra_exp_malus = user.mind.get_skill_level(/datum/skill/fishing) - difficulty * 0.1
+ if(extra_exp_malus > 0)
+ experience_multiplier /= (1 + extra_exp_malus * EXPERIENCE_MALUS_MULT)
+ user.mind.adjust_experience(/datum/skill/fishing, round(seconds_spent * FISHING_SKILL_EXP_PER_SECOND * experience_multiplier))
+ if(user.mind.get_skill_level(/datum/skill/fishing) >= SKILL_LEVEL_LEGENDARY)
+ user.client?.give_award(/datum/award/achievement/skill/legendary_fisher, user)
if(win)
if(reward_path != FISHING_DUD)
- playsound(lure, 'sound/effects/bigsplash.ogg', 100)
- SEND_SIGNAL(src, COMSIG_FISHING_CHALLENGE_COMPLETED, user, win)
+ playsound(location, 'sound/effects/bigsplash.ogg', 100)
+ SEND_SIGNAL(user, COMSIG_MOB_COMPLETE_FISHING, src, win)
if(!QDELETED(src))
qdel(src)
+#undef EXPERIENCE_MALUS_MULT
+
/datum/fishing_challenge/proc/start_baiting_phase(penalty = FALSE)
+ reward_path = null //In case we missed the biting phase, set the path back to null
var/wait_time
+ last_baiting_click = world.time
if(penalty)
wait_time = min(timeleft(next_phase_timer) + rand(3 SECONDS, 5 SECONDS), 30 SECONDS)
else
- wait_time = rand(3 SECONDS, 25 SECONDS)
+ wait_time = float.spin_frequency ? rand(11 SECONDS, 17 SECONDS) : rand(3 SECONDS, 25 SECONDS)
if(special_effects & FISHING_MINIGAME_AUTOREEL && wait_time >= 15 SECONDS)
wait_time = max(wait_time - 7.5 SECONDS, 15 SECONDS)
deltimer(next_phase_timer)
phase = WAIT_PHASE
//Bobbing animation
- animate(lure, pixel_y = 1, time = 1 SECONDS, loop = -1, flags = ANIMATION_RELATIVE)
+ animate(float, pixel_y = 1, time = 1 SECONDS, loop = -1, flags = ANIMATION_RELATIVE)
animate(pixel_y = -1, time = 1 SECONDS, flags = ANIMATION_RELATIVE)
- next_phase_timer = addtimer(CALLBACK(src, PROC_REF(start_biting_phase)), wait_time, TIMER_STOPPABLE)
+ next_phase_timer = addtimer(CALLBACK(src, PROC_REF(start_biting_phase)), wait_time, TIMER_STOPPABLE|TIMER_DELETE_ME)
+ if(float.spin_frequency)
+ set_lure_timers()
/datum/fishing_challenge/proc/start_biting_phase()
phase = BITING_PHASE
- // Trashing animation
- playsound(lure, 'sound/effects/fish_splash.ogg', 100)
+
+ var/list/rewards = list()
+ SEND_SIGNAL(src, COMSIG_FISHING_CHALLENGE_ROLL_REWARD, used_rod, user, location, rewards)
+ if(length(rewards))
+ reward_path = pick(rewards)
+ playsound(location, 'sound/effects/fish_splash.ogg', 100)
+
if(HAS_MIND_TRAIT(user, TRAIT_REVEAL_FISH))
+ fish_icon = GLOB.specific_fish_icons[reward_path] || FISH_ICON_DEF
switch(fish_icon)
if(FISH_ICON_DEF)
send_alert("fish!!!")
@@ -356,13 +394,19 @@
send_alert("bottle!!!")
else
send_alert("!!!")
- animate(lure, pixel_y = 3, time = 5, loop = -1, flags = ANIMATION_RELATIVE)
+ animate(float, pixel_y = 3, time = 5, loop = -1, flags = ANIMATION_RELATIVE)
animate(pixel_y = -3, time = 5, flags = ANIMATION_RELATIVE)
if(special_effects & FISHING_MINIGAME_AUTOREEL)
- start_minigame_phase(auto_reel = TRUE)
- return
+ addtimer(CALLBACK(src, PROC_REF(automatically_start_minigame)), 0.2 SECONDS)
// Setup next phase
- next_phase_timer = addtimer(CALLBACK(src, PROC_REF(start_baiting_phase)), BITING_TIME_WINDOW, TIMER_STOPPABLE)
+ next_phase_timer = addtimer(CALLBACK(src, PROC_REF(start_baiting_phase)), BITING_TIME_WINDOW, TIMER_STOPPABLE|TIMER_DELETE_ME)
+ ///If we're using a lure, we want the float to show a little green light during the minigame phase and not a red one.
+ float.spin_ready = TRUE
+ float.update_appearance(UPDATE_OVERLAYS)
+
+/datum/fishing_challenge/proc/automatically_start_minigame()
+ if(phase == BITING_PHASE)
+ start_minigame_phase(auto_reel = TRUE)
///The damage dealt per second to the fish when FISHING_MINIGAME_RULE_KILL is active.
#define FISH_DAMAGE_PER_SECOND 2
@@ -384,7 +428,58 @@
var/damage = CEILING((world.time - start_time)/10 * FISH_DAMAGE_PER_SECOND, 1)
reward.adjust_health(reward.health - damage)
+/datum/fishing_challenge/proc/get_difficulty()
+ var/list/difficulty_holder = list(0)
+ SEND_SIGNAL(src, COMSIG_FISHING_CHALLENGE_GET_DIFFICULTY, reward_path, used_rod, user, difficulty_holder)
+ difficulty = difficulty_holder[1]
+ //If you manage to be so well-equipped and skilled to completely crush the difficulty, just skip to the reward.
+ if(difficulty <= 0)
+ complete(TRUE)
+ return FALSE
+ difficulty = clamp(round(difficulty), FISHING_MINIMUM_DIFFICULTY, 100)
+ return TRUE
+
+/datum/fishing_challenge/proc/update_difficulty()
+ if(phase != MINIGAME_PHASE)
+ return
+ var/old_difficulty = difficulty
+ //early return if the difficulty is the same or we crush the minigame all the way to 0 difficulty
+ if(!get_difficulty() || difficulty == old_difficulty)
+ return
+ bait_height = initial(bait_height)
+ experience_multiplier -= difficulty * FISHING_SKILL_DIFFIULTY_EXP_MULT
+ mover.reset_difficulty_values()
+ adjust_to_difficulty()
+
+/datum/fishing_challenge/proc/adjust_to_difficulty()
+ mover.adjust_to_difficulty()
+ bait_height -= round(difficulty * BAIT_HEIGHT_DIFFICULTY_MALUS)
+ bait_pixel_height = round(MINIGAME_BAIT_HEIGHT * (bait_height/initial(bait_height)), 1)
+ experience_multiplier += difficulty * FISHING_SKILL_DIFFIULTY_EXP_MULT
+ fishing_hud.hud_bait.adjust_to_difficulty(src)
+
+///Get the difficulty and other variables, than start the minigame
/datum/fishing_challenge/proc/start_minigame_phase(auto_reel = FALSE)
+ SEND_SIGNAL(user, COMSIG_MOB_BEGIN_FISHING_MINIGAME, src)
+ if(!get_difficulty()) //we totalized 0 or less difficulty, instant win.
+ return
+
+ if(difficulty > FISHING_DEFAULT_DIFFICULTY)
+ completion -= MAX_FISH_COMPLETION_MALUS * (difficulty * 0.01)
+
+ /// Fish minigame properties
+ if(ispath(reward_path,/obj/item/fish))
+ var/obj/item/fish/fish = reward_path
+ var/movement_path = initial(fish.fish_movement_type)
+ mover = new movement_path(src)
+ // Apply fish trait modifiers
+ var/list/fish_traits = SSfishing.fish_properties[fish][FISH_PROPERTIES_TRAITS]
+ for(var/fish_trait in fish_traits)
+ var/datum/fish_trait/trait = GLOB.fish_traits[fish_trait]
+ trait.minigame_mod(used_rod, user, src)
+ else
+ mover = new /datum/fish_movement(src)
+
if(auto_reel)
completion *= 1.3
else
@@ -402,17 +497,37 @@
fish_position = rand(0, (FISHING_MINIGAME_AREA - fish_height) * 0.8)
var/diff_dist = 100 + difficulty
bait_position = clamp(round(fish_position + rand(-diff_dist, diff_dist) - bait_height * 0.5), 0, FISHING_MINIGAME_AREA - bait_height)
+
if(!prepare_minigame_hud())
+ get_stack_trace("couldn't prepare minigame hud for a fishing challenge.") //just to be sure. This shouldn't happen.
+ qdel(src)
return
- ADD_TRAIT(user, TRAIT_ACTIVELY_FISHING, WEAKREF(src))
+
+ adjust_to_difficulty()
+
phase = MINIGAME_PHASE
deltimer(next_phase_timer)
if((FISHING_MINIGAME_RULE_KILL in special_effects) && ispath(reward_path,/obj/item/fish))
var/obj/item/fish/fish = reward_path
var/wait_time = (initial(fish.health) / FISH_DAMAGE_PER_SECOND) SECONDS
- addtimer(CALLBACK(src, PROC_REF(win_anyway)), wait_time)
+ addtimer(CALLBACK(src, PROC_REF(win_anyway)), wait_time, TIMER_DELETE_ME)
start_time = world.time
- experience_multiplier += difficulty * FISHING_SKILL_DIFFIULTY_EXP_MULT
+
+///Throws a stack with prefixed text.
+/datum/fishing_challenge/proc/get_stack_trace(init_text)
+ var/text = "[init_text] "
+ text += "used rod: [used_rod || "null"], "
+ if(used_rod)
+ text += "bait: [used_rod.bait || "null"], "
+ text += "reward: [reward_path || "null"], "
+ text += "user: [user || "null"]"
+ if(user)
+ if(QDELING(user))
+ text += ", user qdeling"
+ else if(!user.client)
+ text += ", user clientless"
+ text += "."
+ stack_trace(text)
#undef FISH_DAMAGE_PER_SECOND
@@ -475,6 +590,11 @@
fishing_hud.transform = fishing_hud.transform.Scale(1, -1)
SEND_SOUND(user, sound('sound/effects/boing.ogg'))
COOLDOWN_START(src, active_effect_cd, rand(5, 6) SECONDS)
+ if(FISHING_MINIGAME_RULE_CAMO)
+ fishing_hud.icon_state = "background_camo"
+ SEND_SOUND(user, sound('sound/effects/nightmare_poof.ogg', volume = 15))
+ COOLDOWN_START(src, active_effect_cd, rand(6, 8) SECONDS)
+ animate(fishing_hud.hud_fish, alpha = 7, time = 2 SECONDS)
return
///go back to normal
@@ -487,6 +607,10 @@
if(FISHING_MINIGAME_RULE_FLIP)
fishing_hud.transform = fishing_hud.transform.Scale(1, -1)
COOLDOWN_START(src, active_effect_cd, rand(8, 12) SECONDS)
+ if(FISHING_MINIGAME_RULE_CAMO)
+ COOLDOWN_START(src, active_effect_cd, rand(9, 16) SECONDS)
+ SEND_SOUND(user, sound('sound/effects/nightmare_reappear.ogg', volume = 15))
+ animate(fishing_hud.hud_fish, alpha = 255, time = 1.2 SECONDS)
fishing_hud.icon_state = background
current_active_effect = null
@@ -613,18 +737,24 @@
icon = 'icons/hud/fishing_hud.dmi'
icon_state = "bait"
vis_flags = VIS_INHERIT_ID
+ ///The stored value we used to squish the bar based on the difficulty
+ var/current_vertical_transform
/atom/movable/screen/hud_bait/Initialize(mapload, datum/hud/hud_owner, datum/fishing_challenge/challenge)
. = ..()
if(!challenge || challenge.bait_pixel_height == MINIGAME_BAIT_HEIGHT)
return
- var/static/icon_height
- if(!icon_height)
- var/list/icon_dimensions = get_icon_dimensions(icon)
- icon_height = icon_dimensions["height"]
- var/height_percent_diff = challenge.bait_pixel_height/MINIGAME_BAIT_HEIGHT
- transform = transform.Scale(1, height_percent_diff)
- pixel_z = -icon_height * (1 - height_percent_diff) * 0.5
+ adjust_to_difficulty(challenge)
+
+/atom/movable/screen/hud_bait/proc/adjust_to_difficulty(datum/fishing_challenge/challenge)
+ if(current_vertical_transform)
+ transform = transform.Scale(1, 1/current_vertical_transform)
+ pixel_z = 0
+ var/list/icon_dimensions = get_icon_dimensions(icon)
+ var/icon_height = icon_dimensions["height"]
+ current_vertical_transform = challenge.bait_pixel_height/MINIGAME_BAIT_HEIGHT
+ transform = transform.Scale(1, current_vertical_transform)
+ pixel_z = -icon_height * (1 - current_vertical_transform) * 0.5
/atom/movable/screen/hud_fish
icon = 'icons/hud/fishing_hud.dmi'
@@ -647,21 +777,43 @@
icon_state = "completion_[FLOOR(challenge.completion, 5)]"
/// The visual that appears over the fishing spot
-/obj/effect/fishing_lure
+/obj/effect/fishing_float
+ name = "float"
icon = 'icons/obj/fishing.dmi'
- icon_state = "lure_idle"
+ icon_state = "float"
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ /**
+ * A list with two keys delimiting the spinning interval in which a mouse click has to be pressed while fishing.
+ * If set, an emissive overlay will be added, colored green when the lure is ready to be spun, otherwise red.
+ */
+ var/list/spin_frequency
+ ///Is the bait ready to be spun?
+ var/spin_ready = FALSE
-/obj/effect/fishing_lure/Initialize(mapload, atom/spot)
+/obj/effect/fishing_float/Initialize(mapload, atom/spot)
. = ..()
- if(ismovable(spot)) // we want the lure and therefore the fishing line to stay connected with the fishing spot.
+ if(!spot)
+ return
+ if(ismovable(spot)) // we want the float and therefore the fishing line to stay connected with the fishing spot.
RegisterSignal(spot, COMSIG_MOVABLE_MOVED, PROC_REF(follow_movable))
+ SET_BASE_PIXEL(spot.pixel_x, spot.pixel_y)
+ SET_BASE_VISUAL_PIXEL(spot.pixel_w, spot.pixel_z)
-/obj/effect/fishing_lure/proc/follow_movable(atom/movable/source)
+/obj/effect/fishing_float/proc/follow_movable(atom/movable/source)
SIGNAL_HANDLER
set_glide_size(source.glide_size)
forceMove(source.loc)
+/obj/effect/fishing_float/update_overlays()
+ . = ..()
+ if(!spin_frequency)
+ return
+ var/mutable_appearance/overlay = mutable_appearance(icon, "lure_light")
+ overlay.color = spin_ready ? COLOR_GREEN : COLOR_RED
+ . += overlay
+ . += emissive_appearance(icon, "lure_light_emissive", src, alpha = src.alpha)
+
#undef WAIT_PHASE
#undef BITING_PHASE
#undef MINIGAME_PHASE
diff --git a/code/modules/fishing/fishing_portal_machine.dm b/code/modules/fishing/fishing_portal_machine.dm
index 8b157cbebfff3..e1be9dc909ba9 100644
--- a/code/modules/fishing/fishing_portal_machine.dm
+++ b/code/modules/fishing/fishing_portal_machine.dm
@@ -11,6 +11,46 @@
///The current fishing spot loaded in
var/datum/component/fishing_spot/active
+ ///A list of fishing spot it's linked to with a multitool.
+ var/list/linked_fishing_spots
+ ///The maximum number of fishing spots it can be linked to
+ var/max_fishing_spots = 1
+ ///If true, the fishing portal can stay connected to a linked fishing spot even on different z-levels
+ var/long_range_link = FALSE
+
+/obj/machinery/fishing_portal_generator/Initialize(mapload)
+ . = ..()
+ var/static/list/tool_screentips = list(
+ TOOL_MULTITOOL = list(
+ SCREENTIP_CONTEXT_LMB = "Link",
+ SCREENTIP_CONTEXT_RMB = "Unlink fishing spots"
+ ),
+ )
+ AddElement(/datum/element/contextual_screentip_tools, tool_screentips)
+ ADD_TRAIT(src, TRAIT_UNLINKABLE_FISHING_SPOT, INNATE_TRAIT)
+
+/obj/machinery/fishing_portal_generator/Destroy()
+ deactivate()
+ linked_fishing_spots = null
+ return ..()
+
+///Higher tier parts let you link to more fishing spots at once and eventually let you connect through different zlevels.
+/obj/machinery/fishing_portal_generator/RefreshParts()
+ . = ..()
+ max_fishing_spots = 0
+ long_range_link = FALSE
+ for(var/datum/stock_part/matter_bin/matter_bin in component_parts)
+ max_fishing_spots += matter_bin.tier * 0.5
+ max_fishing_spots = ROUND_UP(max_fishing_spots)
+ for(var/datum/stock_part/capacitor/capacitor in component_parts)
+ if(capacitor.tier >= 3)
+ long_range_link = TRUE
+ if(!long_range_link)
+ check_fishing_spot_z()
+ if(length(linked_fishing_spots) > max_fishing_spots)
+ if(active)
+ deactivate()
+ linked_fishing_spots.len = max_fishing_spots
/obj/machinery/fishing_portal_generator/on_set_panel_open()
update_appearance()
@@ -21,9 +61,94 @@
default_unfasten_wrench(user, tool)
return ITEM_INTERACT_SUCCESS
+/obj/machinery/fishing_portal_generator/multitool_act(mob/living/user, obj/item/multitool/tool)
+ if(machine_stat & NOPOWER)
+ balloon_alert(user, "no power!")
+ return ITEM_INTERACT_BLOCKING
+ var/unlink = tool.buffer == src
+ tool.set_buffer(unlink ? null : src)
+ balloon_alert(user, "fish-porter [unlink ? "un" : ""]linked")
+ if(!unlink)
+ tool.item_flags |= ITEM_HAS_CONTEXTUAL_SCREENTIPS
+ RegisterSignal(tool, COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET, PROC_REF(multitool_context))
+ RegisterSignal(tool, COMSIG_MULTITOOL_REMOVE_BUFFER, PROC_REF(multitool_unbuffered))
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/fishing_portal_generator/multitool_act_secondary(mob/living/user, obj/item/tool)
+ if(machine_stat & NOPOWER)
+ balloon_alert(user, "no power!")
+ return ITEM_INTERACT_BLOCKING
+ if(!length(linked_fishing_spots))
+ balloon_alert(user, "nothing to unlink!")
+ return ITEM_INTERACT_BLOCKING
+ var/list/fishing_list = list()
+ var/id = 1
+ for(var/atom/spot as anything in linked_fishing_spots)
+ var/choice_name = "[spot.name] ([id])"
+ fishing_list[choice_name] = spot
+ id++
+ var/list/choices = list()
+ for(var/radial_name in fishing_list)
+ var/datum/fish_source/source = fishing_list[radial_name]
+ var/mutable_appearance/appearance = mutable_appearance('icons/hud/radial_fishing.dmi', source.radial_state)
+ appearance.add_overlay('icons/hud/radial_fishing.dmi', "minus_sign")
+ choices[radial_name] = appearance
+
+ var/choice = show_radial_menu(user, src, choices, radius = 38, custom_check = CALLBACK(src, TYPE_PROC_REF(/atom, can_interact), user), tooltips = TRUE)
+ if(!choice)
+ return
+ var/atom/spot = fishing_list[choice]
+ if(QDELETED(spot) || !(spot in linked_fishing_spots) || !can_interact(user))
+ return
+ unlink_fishing_spot(spot)
+ balloon_alert(user, "fishing spot unlinked")
+
+/obj/machinery/fishing_portal_generator/proc/multitool_context(obj/item/source, list/context, atom/target, mob/living/user)
+ SIGNAL_HANDLER
+ if(HAS_TRAIT(target, TRAIT_FISHING_SPOT) && !HAS_TRAIT(target, TRAIT_UNLINKABLE_FISHING_SPOT))
+ context[SCREENTIP_CONTEXT_LMB] = "Link to fish-porter"
+ return CONTEXTUAL_SCREENTIP_SET
+ return NONE
+
+/obj/machinery/fishing_portal_generator/proc/multitool_unbuffered(datum/source, datum/buffer)
+ SIGNAL_HANDLER
+ UnregisterSignal(source, list(COMSIG_ITEM_REQUESTING_CONTEXT_FOR_TARGET, COMSIG_MULTITOOL_REMOVE_BUFFER))
+
+///Called when using a multitool on any other fishing source.
+/obj/machinery/fishing_portal_generator/proc/link_fishing_spot(datum/fish_source/source, atom/spot, mob/living/user)
+ if(istype(spot, /obj/machinery/fishing_portal_generator)) //Don't link it to itself or other fishing portals.
+ return
+ if(length(linked_fishing_spots) >= max_fishing_spots)
+ spot.balloon_alert(user, "cannot link more!")
+ return ITEM_INTERACT_BLOCKING
+ for(var/other_spot in linked_fishing_spots)
+ var/datum/fish_source/stored = linked_fishing_spots[other_spot]
+ if(stored == source)
+ spot.balloon_alert(user, "already linked!")
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 15, FALSE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
+ return ITEM_INTERACT_BLOCKING
+ if(HAS_TRAIT(spot, TRAIT_UNLINKABLE_FISHING_SPOT))
+ spot.balloon_alert(user, "unlinkable fishing spot!")
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 15, FALSE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
+ return ITEM_INTERACT_BLOCKING
+ LAZYSET(linked_fishing_spots, spot, source)
+ RegisterSignal(spot, SIGNAL_REMOVETRAIT(TRAIT_FISHING_SPOT), PROC_REF(unlink_fishing_spot))
+ spot.balloon_alert(user, "fishing spot linked")
+ playsound(spot, 'sound/machines/ping.ogg', 15, TRUE, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/fishing_portal_generator/proc/unlink_fishing_spot(atom/spot)
+ SIGNAL_HANDLER
+ var/datum/fish_source/source = linked_fishing_spots[spot]
+ if(active?.fish_source == source)
+ deactivate()
+ LAZYREMOVE(linked_fishing_spots, spot)
+ UnregisterSignal(spot, SIGNAL_REMOVETRAIT(TRAIT_FISHING_SPOT))
+
/obj/machinery/fishing_portal_generator/examine(mob/user)
. = ..()
- . += span_notice("You can unlock further portal settings by completing fish scanning experiments.")
+ . += span_notice("You can unlock further portal settings by completing fish scanning experiments, \
+ or by connecting it to other fishing spots with a multitool.")
/obj/machinery/fishing_portal_generator/emag_act(mob/user, obj/item/card/emag/emag_card)
if(obj_flags & EMAGGED)
@@ -47,19 +172,78 @@
if(!active)
return
. += "portal_on"
- var/datum/fish_source/portal/portal = active.fish_source
+ var/datum/fish_source/portal = active.fish_source
. += portal.overlay_state
. += emissive_appearance(icon, "portal_emissive", src)
-/obj/machinery/fishing_portal_generator/proc/activate(datum/fish_source/selected_source)
+/obj/machinery/fishing_portal_generator/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents)
+ . = ..()
+ check_fishing_spot_z()
+
+/obj/machinery/fishing_portal_generator/proc/check_fishing_spot_z()
+ if(!active || long_range_link || istype(active.fish_source, /datum/fish_source/portal))
+ return
+ var/turf/new_turf = get_turf(src)
+ if(!new_turf)
+ deactivate()
+ return
+ for(var/atom/spot as anything in linked_fishing_spots)
+ if(linked_fishing_spots[spot] != active.fish_source)
+ continue
+ var/turf/turf = get_turf(spot)
+ if(turf.z != new_turf.z && !(is_station_level(turf.z) && is_station_level(new_turf.z)))
+ deactivate()
+
+/obj/machinery/fishing_portal_generator/proc/activate(datum/fish_source/selected_source, mob/user)
+ if(QDELETED(selected_source))
+ return
+ if(machine_stat & NOPOWER)
+ balloon_alert(user, "no power!")
+ return ITEM_INTERACT_BLOCKING
+ if(!istype(selected_source, /datum/fish_source/portal)) //likely from a linked fishing spot
+ var/abort = TRUE
+ for(var/atom/spot as anything in linked_fishing_spots)
+ if(linked_fishing_spots[spot] != selected_source)
+ continue
+ if(long_range_link)
+ abort = FALSE
+ var/turf/spot_turf = get_turf(spot)
+ var/turf/turf = get_turf(src)
+ if(turf.z == spot_turf.z || (is_station_level(turf.z) && is_station_level(spot_turf.z)))
+ abort = FALSE
+ if(!abort)
+ RegisterSignal(spot, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_fishing_spot_z_level_changed))
+ break
+ if(abort)
+ balloon_alert(user, "cannot reach linked!")
+ return
+
active = AddComponent(/datum/component/fishing_spot, selected_source)
- use_power = ACTIVE_POWER_USE
+ ADD_TRAIT(src, TRAIT_CATCH_AND_RELEASE, INNATE_TRAIT)
+ if(use_power != NO_POWER_USE)
+ use_power = ACTIVE_POWER_USE
update_icon()
/obj/machinery/fishing_portal_generator/proc/deactivate()
+ if(!active)
+ return
+ if(!istype(active.fish_source, /datum/fish_source/portal))
+ for(var/atom/spot as anything in linked_fishing_spots)
+ if(linked_fishing_spots[spot] == active.fish_source)
+ UnregisterSignal(spot, COMSIG_MOVABLE_Z_CHANGED)
QDEL_NULL(active)
- use_power = IDLE_POWER_USE
- update_icon()
+
+ REMOVE_TRAIT(src, TRAIT_CATCH_AND_RELEASE, INNATE_TRAIT)
+ if(!QDELETED(src))
+ if(use_power != NO_POWER_USE)
+ use_power = IDLE_POWER_USE
+ update_icon()
+
+/obj/machinery/fishing_portal_generator/proc/on_fishing_spot_z_level_changed(atom/spot, turf/old_turf, turf/new_turf, same_z_layer)
+ SIGNAL_HANDLER
+ var/turf/turf = get_turf(src)
+ if(turf.z != new_turf.z && !(is_station_level(turf.z) && is_station_level(new_turf.z)))
+ deactivate()
/obj/machinery/fishing_portal_generator/on_set_is_operational(old_value)
if(old_value)
@@ -88,18 +272,28 @@
var/datum/fish_source/portal/reward = GLOB.preset_fish_sources[experiment.fish_source_reward]
available_fish_sources[reward.radial_name] = reward
+ var/id = 1
+ for(var/atom/spot as anything in linked_fishing_spots)
+ var/choice_name = "[spot.name] ([id])"
+ available_fish_sources[choice_name] = linked_fishing_spots[spot]
+ id++
+
if(length(available_fish_sources) == 1)
- activate(default)
+ activate(default, user)
return
var/list/choices = list()
for(var/radial_name in available_fish_sources)
- var/datum/fish_source/portal/source = available_fish_sources[radial_name]
- choices[radial_name] = image(icon = 'icons/hud/radial_fishing.dmi', icon_state = source.radial_state)
+ var/datum/fish_source/source = available_fish_sources[radial_name]
+ var/mutable_appearance/radial_icon = mutable_appearance('icons/hud/radial_fishing.dmi', source.radial_state)
+ if(!istype(source, /datum/fish_source/portal))
+ //a little star on the top-left to distinguishs them from standard portals.
+ radial_icon.add_overlay('icons/hud/radial_fishing.dmi', "linked_source")
+ choices[radial_name] = radial_icon
var/choice = show_radial_menu(user, src, choices, radius = 38, custom_check = CALLBACK(src, TYPE_PROC_REF(/atom, can_interact), user), tooltips = TRUE)
if(!choice || !can_interact(user))
return
- activate(available_fish_sources[choice])
+ activate(available_fish_sources[choice], user)
/obj/machinery/fishing_portal_generator/emagged
obj_flags = parent_type::obj_flags | EMAGGED
diff --git a/code/modules/fishing/fishing_rod.dm b/code/modules/fishing/fishing_rod.dm
index 1c47b8f7ee39f..23aabcc3ece7c 100644
--- a/code/modules/fishing/fishing_rod.dm
+++ b/code/modules/fishing/fishing_rod.dm
@@ -17,8 +17,12 @@
var/cast_range = 3
/// Fishing minigame difficulty modifier (additive)
var/difficulty_modifier = 0
- /// Explaination of rod functionality shown in the ui
+ /// Explaination of rod functionality shown in the ui and the autowiki
var/ui_description = "A classic fishing rod, with no special qualities."
+ /// More explaination shown in the wiki after ui_description
+ var/wiki_description = ""
+ /// Is this fishing rod shown in the wiki
+ var/show_in_wiki = TRUE
var/obj/item/bait
var/obj/item/fishing_line/line = /obj/item/fishing_line
@@ -42,6 +46,12 @@
///The name of the icon state of the reel overlay
var/reel_overlay = "reel_overlay"
+ /**
+ * A list with two keys delimiting the spinning interval in which a mouse click has to be pressed while fishing.
+ * Inherited from baits, passed down to the minigame lure.
+ */
+ var/list/spin_frequency
+
///Prevents spamming the line casting, without affecting the player's click cooldown.
COOLDOWN_DECLARE(casting_cd)
@@ -69,9 +79,11 @@
/obj/item/fishing_rod/add_item_context(obj/item/source, list/context, atom/target, mob/living/user)
. = ..()
- if(currently_hooked)
- context[SCREENTIP_CONTEXT_LMB] = "Reel in"
- context[SCREENTIP_CONTEXT_RMB] = "Unhook"
+ var/gone_fishing = GLOB.fishing_challenges_by_user[user]
+ if(currently_hooked || gone_fishing)
+ context[SCREENTIP_CONTEXT_LMB] = (gone_fishing && spin_frequency) ? "Spin" : "Reel in"
+ if(!gone_fishing)
+ context[SCREENTIP_CONTEXT_RMB] = "Unhook"
return CONTEXTUAL_SCREENTIP_SET
return NONE
@@ -130,12 +142,19 @@
/obj/item/fishing_rod/proc/reel(mob/user)
if(DOING_INTERACTION_WITH_TARGET(user, currently_hooked))
return
+
playsound(src, SFX_REEL, 50, vary = FALSE)
- if(!do_after(user, 0.8 SECONDS, currently_hooked, timed_action_flags = IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(fishing_line_check))))
+ var/time = (0.8 - round(user.mind?.get_skill_level(/datum/skill/fishing) * 0.04, 0.1)) SECONDS
+ if(!do_after(user, time, currently_hooked, timed_action_flags = IGNORE_USER_LOC_CHANGE|IGNORE_TARGET_LOC_CHANGE, extra_checks = CALLBACK(src, PROC_REF(fishing_line_check))))
return
+
if(currently_hooked.anchored || currently_hooked.move_resist >= MOVE_FORCE_STRONG)
balloon_alert(user, "[currently_hooked.p_they()] won't budge!")
return
+
+ //About thirty minutes of non-stop reeling to get from zero to master... not worth it but hey, you do what you do.
+ user.mind?.adjust_experience(/datum/skill/fishing, time * 0.13)
+
//Try to move it 'till it's under the user's feet, then try to pick it up
if(isitem(currently_hooked))
var/obj/item/item = currently_hooked
@@ -170,6 +189,8 @@
RegisterSignal(fishing_line, COMSIG_BEAM_BEFORE_DRAW, PROC_REF(check_los))
RegisterSignal(fishing_line, COMSIG_QDELETING, PROC_REF(clear_line))
INVOKE_ASYNC(fishing_line, TYPE_PROC_REF(/datum/beam/, Start))
+ if(QDELETED(fishing_line))
+ return null
firer.update_held_items()
return fishing_line
@@ -181,6 +202,15 @@
fishing_line = null
currently_hooked = null
+/obj/item/fishing_rod/proc/get_cast_range(mob/living/user)
+ . = cast_range
+ if(!user && !isliving(loc))
+ return
+ user = loc
+ if(!user.is_holding(src) || !user.mind)
+ return
+ . += round(user.mind.get_skill_level(/datum/skill/fishing) * 0.3)
+
/obj/item/fishing_rod/dropped(mob/user, silent)
. = ..()
QDEL_NULL(fishing_line)
@@ -201,13 +231,13 @@
SIGNAL_HANDLER
. = NONE
- if(!CheckToolReach(src, source.target, cast_range))
+ if(!CheckToolReach(src, source.target, get_cast_range()))
qdel(source)
return BEAM_CANCEL_DRAW
/obj/item/fishing_rod/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- //this prevent trying to use telekinesis to fish (which would be broken anyway)
- if(!user.contains(src))
+ //this prevent trying to use telekinesis to fish (which would be broken anyway), also whacking people with a rod.
+ if(!user.contains(src) || (user.combat_mode && !isturf(interacting_with)) ||HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION))
return ..()
return ranged_interact_with_atom(interacting_with, user, modifiers)
@@ -246,7 +276,7 @@
return
casting = TRUE
var/obj/projectile/fishing_cast/cast_projectile = new(get_turf(src))
- cast_projectile.range = cast_range
+ cast_projectile.range = get_cast_range(user)
cast_projectile.owner = src
cast_projectile.original = target
cast_projectile.fired_from = src
@@ -285,10 +315,11 @@
/obj/item/fishing_rod/proc/get_fishing_overlays()
. = list()
var/line_color = line?.line_color || default_line_color
- /// Line part by the rod, always visible
- var/mutable_appearance/reel_appearance = mutable_appearance(icon, reel_overlay)
- reel_appearance.color = line_color
- . += reel_appearance
+ /// Line part by the rod.
+ if(reel_overlay)
+ var/mutable_appearance/reel_appearance = mutable_appearance(icon, reel_overlay)
+ reel_appearance.color = line_color
+ . += reel_appearance
// Line & hook is also visible when only bait is equipped but it uses default appearances then
if(hook || bait)
@@ -396,7 +427,7 @@
/// Ideally this will be replaced with generic slotted storage datum + display
/obj/item/fishing_rod/proc/use_slot(slot, mob/user, obj/item/new_item)
- if(fishing_line || HAS_TRAIT(user, TRAIT_GONE_FISHING))
+ if(fishing_line || GLOB.fishing_challenges_by_user[user])
return
var/obj/item/current_item
switch(slot)
@@ -419,23 +450,20 @@
if(user.transferItemToLoc(new_item,src))
set_slot(new_item, slot)
balloon_alert(user, "[slot] installed")
+ else
+ balloon_alert(user, "stuck to your hands!")
+ return
/// Trying to swap item
else if(new_item && current_item)
if(!slot_check(new_item,slot))
return
- if(user.transferItemToLoc(new_item,src))
- switch(slot)
- if(ROD_SLOT_BAIT)
- bait = new_item
- if(ROD_SLOT_HOOK)
- hook = new_item
- if(ROD_SLOT_LINE)
- line = new_item
- user.put_in_hands(current_item)
- balloon_alert(user, "[slot] swapped")
-
- if(new_item)
- SEND_SIGNAL(new_item, COMSIG_FISHING_EQUIPMENT_SLOTTED, src)
+ if(user.transferItemToLoc(new_item, src))
+ user.put_in_hands(current_item)
+ set_slot(new_item, slot)
+ balloon_alert(user, "[slot] swapped")
+ else
+ balloon_alert(user, "stuck to your hands!")
+ return
update_icon()
playsound(src, 'sound/items/click.ogg', 50, TRUE)
@@ -445,16 +473,23 @@
switch(slot)
if(ROD_SLOT_BAIT)
bait = equipment
+ if(!HAS_TRAIT(bait, TRAIT_BAIT_ALLOW_FISHING_DUD))
+ ADD_TRAIT(src, TRAIT_ROD_REMOVE_FISHING_DUD, INNATE_TRAIT)
if(ROD_SLOT_HOOK)
hook = equipment
if(ROD_SLOT_LINE)
line = equipment
cast_range += FISHING_ROD_REEL_CAST_RANGE
+ else
+ CRASH("set_slot called with an undefined slot: [slot]")
+
+ SEND_SIGNAL(equipment, COMSIG_FISHING_EQUIPMENT_SLOTTED, src)
/obj/item/fishing_rod/Exited(atom/movable/gone, direction)
. = ..()
if(gone == bait)
bait = null
+ REMOVE_TRAIT(src, TRAIT_ROD_REMOVE_FISHING_DUD, INNATE_TRAIT)
if(gone == line)
cast_range -= FISHING_ROD_REEL_CAST_RANGE
line = null
@@ -466,10 +501,12 @@
/obj/item/fishing_rod/unslotted
hook = null
line = null
+ show_in_wiki = FALSE
/obj/item/fishing_rod/bone
name = "bone fishing rod"
desc = "A humble rod, made with whatever happened to be on hand."
+ ui_description = "A fishing rod crafted with leather, sinew and bones."
icon_state = "fishing_rod_bone"
reel_overlay = "reel_bone"
default_line_color = "red"
@@ -481,9 +518,11 @@
icon_state = "fishing_rod_telescopic"
desc = "A lightweight, ergonomic, easy to store telescopic fishing rod. "
inhand_icon_state = null
+ custom_price = PAYCHECK_CREW * 9
force = 0
w_class = WEIGHT_CLASS_NORMAL
ui_description = "A collapsible fishing rod that can fit within a backpack."
+ wiki_description = "It has to be bought from Cargo."
reel_overlay = "reel_telescopic"
///The force of the item when extended.
var/active_force = 8
@@ -522,7 +561,7 @@
if(HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE))
return
//the fishing minigame uses the attack_self signal to let the user end it early without having to drop the rod.
- if(HAS_TRAIT(user, TRAIT_GONE_FISHING))
+ if(GLOB.fishing_challenges_by_user[user])
return COMPONENT_BLOCK_TRANSFORM
///Gives feedback to the user, makes it show up inhand, toggles whether it can be used for fishing.
@@ -532,16 +571,21 @@
inhand_icon_state = active ? "rod" : null // When inactive, there is no inhand icon_state.
if(user)
balloon_alert(user, active ? "extended" : "collapsed")
- playsound(src, 'sound/weapons/batonextend.ogg', 50, TRUE)
- update_appearance(UPDATE_OVERLAYS)
+ playsound(src, 'sound/items/weapons/batonextend.ogg', 50, TRUE)
+ update_appearance()
QDEL_NULL(fishing_line)
return COMPONENT_NO_DEFAULT_MESSAGE
+/obj/item/fishing_rod/telescopic/update_icon_state()
+ . = ..()
+ icon_state = "[initial(icon_state)][!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE) ? "_collapsed" : ""]"
+
/obj/item/fishing_rod/telescopic/master
name = "master fishing rod"
desc = "The mythical rod of a lost fisher king. Said to be imbued with un-paralleled fishing power. There's writing on the back of the pole. \"中国航天制造\""
difficulty_modifier = -10
- ui_description = "This rod makes fishing easy even for an absolute beginner."
+ ui_description = "A mythical telescopic fishing rod that makes fishing quite easier."
+ wiki_description = null
icon_state = "fishing_rod_master"
reel_overlay = "reel_master"
active_force = 13 //It's that sturdy
@@ -552,7 +596,8 @@
/obj/item/fishing_rod/tech
name = "advanced fishing rod"
desc = "An embedded universal constructor along with micro-fusion generator makes this marvel of technology never run out of bait. Interstellar treaties prevent using it outside of recreational fishing. And you can fish with this. "
- ui_description = "This rod has an infinite supply of synth-bait. Also doubles as an Experi-Scanner for fish."
+ ui_description = "A rod with an infinite supply of synthetic bait. Doubles as an Experi-Scanner for fish."
+ wiki_description = "It requires the Advanced Fishing Technology Node to be researched to be printed."
icon_state = "fishing_rod_science"
reel_overlay = "reel_science"
bait = /obj/item/food/bait/doughball/synthetic/unconsumable
@@ -676,4 +721,7 @@
override_origin_pixel_x = lefthand ? lefthand_n_px : righthand_n_px
override_origin_pixel_y = lefthand ? lefthand_n_py : righthand_n_py
+ override_origin_pixel_x += origin.pixel_x
+ override_origin_pixel_y += origin.pixel_y
+
#undef FISHING_ROD_REEL_CAST_RANGE
diff --git a/code/modules/fishing/sources/_fish_source.dm b/code/modules/fishing/sources/_fish_source.dm
index 5aa03ae8c0c04..38455068ce22e 100644
--- a/code/modules/fishing/sources/_fish_source.dm
+++ b/code/modules/fishing/sources/_fish_source.dm
@@ -31,6 +31,7 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
/obj/item/fish/jumpercable = FISH_ICON_ELECTRIC,
/obj/item/fish/lavaloop = FISH_ICON_WEAPON,
/obj/item/fish/mastodon = FISH_ICON_BONE,
+ /obj/item/fish/pike/armored = FISH_ICON_WEAPON,
/obj/item/fish/pufferfish = FISH_ICON_CHUNKY,
/obj/item/fish/sand_crab = FISH_ICON_CRAB,
/obj/item/fish/skin_crab = FISH_ICON_CRAB,
@@ -71,28 +72,46 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
var/list/currently_on_regen
/// Text shown as baloon alert when you roll a dud in the table
var/duds = list("it was nothing", "the hook is empty")
- /// Baseline difficulty for fishing in this spot
+ /// Baseline difficulty for fishing in this spot. THIS IS ADDED TO THE DEFAULT DIFFICULTY OF THE MINIGAME (15)
var/fishing_difficulty = FISHING_DEFAULT_DIFFICULTY
/// How the spot type is described in fish catalog section about fish sources, will be skipped if null
var/catalog_description
/// Background image name from /datum/asset/simple/fishing_minigame
var/background = "background_default"
- /// It true, repeated and large explosions won't be as efficient. This is usually meant for global fish sources.
+ /// It true, repeated and large explosions won't be as efficient. This is usually for fish sources that cover multiple turfs (i.e. rivers, oceans).
var/explosive_malus = FALSE
/// If explosive_malus is true, this will be used to keep track of the turfs where an explosion happened for when we'll spawn the loot.
var/list/exploded_turfs
+ ///When linked to a fishing portal, this will be the icon_state of this option in the radial menu
+ var/radial_state = "default"
+ ///When selected by the fishing portal, this will be the icon_state of the overlay shown on the machine.
+ var/overlay_state = "portal_aquarium"
/// Mindless mobs that can fish will never pull up items on this list
var/static/list/profound_fisher_blacklist = typecacheof(list(
/mob/living/basic/mining/lobstrosity,
/obj/structure/closet/crate/necropolis/tendril,
))
+
+ ///List of multipliers used to make fishes more common compared to everything else depending on bait quality, indexed from best to worst.
+ var/static/weight_result_multiplier = list(
+ TRAIT_GREAT_QUALITY_BAIT = 9,
+ TRAIT_GOOD_QUALITY_BAIT = 3.5,
+ TRAIT_BASIC_QUALITY_BAIT = 2,
+ )
+ ///List of exponents used to level out the table weight differences between fish depending on bait quality.
+ var/static/weight_leveling_exponents = list(
+ TRAIT_GREAT_QUALITY_BAIT = 0.7,
+ TRAIT_GOOD_QUALITY_BAIT = 0.55,
+ TRAIT_BASIC_QUALITY_BAIT = 0.4,
+ )
+
/datum/fish_source/New()
if(!PERFORM_ALL_TESTS(focus_only/fish_sources_tables))
return
for(var/path in fish_counts)
if(!(path in fish_table))
- stack_trace("path [path] found in the 'fish_counts' list but not in the fish_table one of [type]")
+ stack_trace("path [path] found in the 'fish_counts' list but not in the 'fish_table'")
/datum/fish_source/Destroy()
exploded_turfs = null
@@ -113,6 +132,19 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
/datum/fish_source/proc/on_start_fishing(obj/item/fishing_rod/rod, mob/fisherman, atom/parent)
return
+///Comsig proc from the fishing minigame for 'calculate_difficulty'
+/datum/fish_source/proc/calculate_difficulty_minigame(datum/fishing_challenge/challenge, reward_path, obj/item/fishing_rod/rod, mob/fisherman, list/difficulty_holder)
+ SIGNAL_HANDLER
+ SHOULD_NOT_OVERRIDE(TRUE)
+ difficulty_holder[1] += calculate_difficulty(reward_path, rod, fisherman)
+
+ // Difficulty modifier added by the fisher's skill level
+ if(!(challenge.special_effects & FISHING_MINIGAME_RULE_NO_EXP))
+ difficulty_holder[1] += fisherman.mind?.get_skill_modifier(/datum/skill/fishing, SKILL_VALUE_MODIFIER)
+
+ if(challenge.special_effects & FISHING_MINIGAME_RULE_KILL)
+ challenge.RegisterSignal(src, COMSIG_FISH_SOURCE_REWARD_DISPENSED, TYPE_PROC_REF(/datum/fishing_challenge, hurt_fish))
+
/**
* Calculates the difficulty of the minigame:
*
@@ -139,8 +171,8 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
// In the future non-fish rewards can have variable difficulty calculated here
return
- var/list/fish_list_properties = collect_fish_properties()
var/obj/item/fish/caught_fish = result
+ var/list/fish_properties = SSfishing.fish_properties[caught_fish]
// Baseline fish difficulty
. += initial(caught_fish.fishing_difficulty_modifier)
@@ -148,18 +180,18 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
if(rod.bait)
var/obj/item/bait = rod.bait
//Fav bait makes it easier
- var/list/fav_bait = fish_list_properties[caught_fish][NAMEOF(caught_fish, favorite_bait)]
+ var/list/fav_bait = fish_properties[FISH_PROPERTIES_FAV_BAIT]
for(var/bait_identifer in fav_bait)
if(is_matching_bait(bait, bait_identifer))
. += FAV_BAIT_DIFFICULTY_MOD
//Disliked bait makes it harder
- var/list/disliked_bait = fish_list_properties[caught_fish][NAMEOF(caught_fish, disliked_bait)]
+ var/list/disliked_bait = fish_properties[FISH_PROPERTIES_BAD_BAIT]
for(var/bait_identifer in disliked_bait)
if(is_matching_bait(bait, bait_identifer))
. += DISLIKED_BAIT_DIFFICULTY_MOD
// Matching/not matching fish traits and equipment
- var/list/fish_traits = fish_list_properties[caught_fish][NAMEOF(caught_fish, fish_traits)]
+ var/list/fish_traits = fish_properties[FISH_PROPERTIES_TRAITS]
var/additive_mod = 0
var/multiplicative_mod = 1
@@ -172,9 +204,15 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
. += additive_mod
. *= multiplicative_mod
-/// In case you want more complex rules for specific spots
-/datum/fish_source/proc/roll_reward(obj/item/fishing_rod/rod, mob/fisherman)
- return pick_weight(get_modified_fish_table(rod,fisherman))
+///Comsig proc from the fishing minigame for 'roll_reward'
+/datum/fish_source/proc/roll_reward_minigame(datum/source, obj/item/fishing_rod/rod, mob/fisherman, atom/location, list/rewards)
+ SIGNAL_HANDLER
+ SHOULD_NOT_OVERRIDE(TRUE)
+ rewards += roll_reward(rod, fisherman, location)
+
+/// Returns a typepath or a special value which we use for spawning dispensing a reward later.
+/datum/fish_source/proc/roll_reward(obj/item/fishing_rod/rod, mob/fisherman, atom/location)
+ return pick_weight(get_modified_fish_table(rod, fisherman, location)) || FISHING_DUD
/**
* Used to register signals or add traits and the such right after conditions have been cleared
@@ -188,21 +226,21 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
SEND_SIGNAL(src, COMSIG_FISHING_SOURCE_INTERRUPT_CHALLENGE, reason)
/**
- * Proc called when the COMSIG_FISHING_CHALLENGE_COMPLETED signal is sent.
+ * Proc called when the COMSIG_MOB_COMPLETE_FISHING signal is sent.
* Check if we've succeeded. If so, write into memory and dispense the reward.
*/
-/datum/fish_source/proc/on_challenge_completed(datum/fishing_challenge/source, mob/user, success)
+/datum/fish_source/proc/on_challenge_completed(mob/user, datum/fishing_challenge/challenge, success)
SIGNAL_HANDLER
SHOULD_CALL_PARENT(TRUE)
+ UnregisterSignal(user, COMSIG_MOB_COMPLETE_FISHING)
if(!success)
return
- var/obj/item/fish/caught = source.reward_path
- user.add_mob_memory(/datum/memory/caught_fish, protagonist = user, deuteragonist = initial(caught.name))
- var/turf/fishing_spot = get_turf(source.lure)
- var/atom/movable/reward = dispense_reward(source.reward_path, user, fishing_spot)
- if(source.used_rod)
- SEND_SIGNAL(source.used_rod, COMSIG_FISHING_ROD_CAUGHT_FISH, reward, user)
- source.used_rod.consume_bait(reward)
+ var/turf/fishing_spot = get_turf(challenge.float)
+ var/atom/movable/reward = dispense_reward(challenge.reward_path, user, fishing_spot)
+ if(reward)
+ user.add_mob_memory(/datum/memory/caught_fish, protagonist = user, deuteragonist = reward.name)
+ SEND_SIGNAL(challenge.used_rod, COMSIG_FISHING_ROD_CAUGHT_FISH, reward, user)
+ challenge.used_rod.consume_bait(reward)
/// Gives out the reward if possible
/datum/fish_source/proc/dispense_reward(reward_path, mob/fisherman, turf/fishing_spot)
@@ -225,7 +263,11 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
/datum/fish_source/proc/simple_dispense_reward(reward_path, atom/spawn_location, turf/fishing_spot)
if(isnull(reward_path))
return null
- if(reward_path in fish_counts) // This is limited count result
+ var/area/area = get_area(fishing_spot)
+ if(!(area.area_flags & UNLIMITED_FISHING) && !isnull(fish_counts[reward_path])) // This is limited count result
+ //Somehow, we're trying to spawn an expended reward.
+ if(fish_counts[reward_path] <= 0)
+ return null
fish_counts[reward_path] -= 1
var/regen_time = fish_count_regen?[reward_path]
if(regen_time)
@@ -237,13 +279,16 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
SEND_SIGNAL(src, COMSIG_FISH_SOURCE_REWARD_DISPENSED, reward)
return reward
-/datum/fish_source/proc/regen_count(reward_path, regen_time)
+/datum/fish_source/proc/regen_count(reward_path)
+ if(!LAZYACCESS(currently_on_regen, reward_path))
+ return
fish_counts[reward_path] += 1
currently_on_regen[reward_path] -= 1
- if(!currently_on_regen[reward_path])
+ if(currently_on_regen[reward_path] <= 0)
LAZYREMOVE(currently_on_regen, reward_path)
- else
- addtimer(CALLBACK(src, PROC_REF(regen_count), reward_path), regen_time)
+ return
+ var/regen_time = fish_count_regen[reward_path]
+ addtimer(CALLBACK(src, PROC_REF(regen_count), reward_path), regen_time)
/// Spawns a reward from a atom path right where the fisherman is. Part of the dispense_reward() logic.
/datum/fish_source/proc/spawn_reward(reward_path, atom/spawn_location, turf/fishing_spot)
@@ -259,55 +304,35 @@ GLOBAL_LIST_INIT(specific_fish_icons, generate_specific_fish_icons())
caught_fish.randomize_size_and_weight()
return reward
-/// Cached fish list properties so we don't have to initalize fish every time, init deffered
-GLOBAL_LIST(fishing_property_cache)
-
-/// Awful workaround around initial(x.list_variable) not being a thing while trying to keep some semblance of being structured
-/proc/collect_fish_properties()
- if(GLOB.fishing_property_cache == null)
- var/list/fish_property_table = list()
- for(var/fish_type in subtypesof(/obj/item/fish))
- var/obj/item/fish/fish = new fish_type(null, FALSE)
- fish_property_table[fish_type] = list()
- fish_property_table[fish_type][NAMEOF(fish, favorite_bait)] = fish.favorite_bait.Copy()
- fish_property_table[fish_type][NAMEOF(fish, disliked_bait)] = fish.disliked_bait.Copy()
- fish_property_table[fish_type][NAMEOF(fish, fish_traits)] = fish.fish_traits.Copy()
- QDEL_NULL(fish)
- GLOB.fishing_property_cache = fish_property_table
- return GLOB.fishing_property_cache
-
/// Returns the fish table, with with the unavailable items from fish_counts removed.
/datum/fish_source/proc/get_fish_table()
var/list/table = fish_table.Copy()
for(var/result in table)
- if(fish_counts[result] == 0)
+ if(!isnull(fish_counts[result]) && fish_counts[result] <= 0)
table -= result
return table
/// Builds a fish weights table modified by bait/rod/user properties
-/datum/fish_source/proc/get_modified_fish_table(obj/item/fishing_rod/rod, mob/fisherman)
+/datum/fish_source/proc/get_modified_fish_table(obj/item/fishing_rod/rod, mob/fisherman, atom/location)
var/obj/item/bait = rod.bait
- ///An exponent used to level out the difference in probabilities between fishes/mobs on the table depending on bait quality.
+ ///An exponent used to level out the table weight differences between fish depending on bait quality.
var/leveling_exponent = 0
///Multiplier used to make fishes more common compared to everything else.
var/result_multiplier = 1
- var/list/final_table = fish_table.Copy()
+ var/list/final_table = get_fish_table()
if(bait)
- if(HAS_TRAIT(bait, TRAIT_GREAT_QUALITY_BAIT))
- result_multiplier = 9
- leveling_exponent = 0.5
- else if(HAS_TRAIT(bait, TRAIT_GOOD_QUALITY_BAIT))
- result_multiplier = 3.5
- leveling_exponent = 0.25
- else if(HAS_TRAIT(bait, TRAIT_BASIC_QUALITY_BAIT))
- result_multiplier = 2
- leveling_exponent = 0.1
- final_table -= FISHING_DUD
+ for(var/trait in weight_result_multiplier)
+ if(HAS_TRAIT(bait, trait))
+ result_multiplier = weight_result_multiplier[trait]
+ leveling_exponent = weight_leveling_exponents[trait]
+ break
- var/list/fish_list_properties = collect_fish_properties()
+
+ if(HAS_TRAIT(rod, TRAIT_ROD_REMOVE_FISHING_DUD))
+ final_table -= FISHING_DUD
if(HAS_TRAIT(fisherman, TRAIT_PROFOUND_FISHER) && !fisherman.client)
@@ -317,60 +342,101 @@ GLOBAL_LIST(fishing_property_cache)
final_table[result] += rod.hook?.get_hook_bonus_additive(result)//Decide on order here so it can be multiplicative
if(ispath(result, /obj/item/fish))
- //Modify fish roll chance
- var/obj/item/fish/caught_fish = result
-
if(bait)
final_table[result] = round(final_table[result] * result_multiplier, 1)
- if(!HAS_TRAIT(bait, TRAIT_OMNI_BAIT))
- //Bait matching likes doubles the chance
- var/list/fav_bait = fish_list_properties[result][NAMEOF(caught_fish, favorite_bait)]
- for(var/bait_identifer in fav_bait)
- if(is_matching_bait(bait, bait_identifer))
- final_table[result] *= 2
- //Bait matching dislikes
- var/list/disliked_bait = fish_list_properties[result][NAMEOF(caught_fish, disliked_bait)]
- for(var/bait_identifer in disliked_bait)
- if(is_matching_bait(bait, bait_identifer))
- final_table[result] = round(final_table[result] * 0.5, 1)
+ var/mult = bait.check_bait(result)
+ final_table[result] = round(final_table[result] * mult, 1)
+ if(mult > 1 && HAS_TRAIT(bait, TRAIT_BAIT_ALLOW_FISHING_DUD))
+ final_table -= FISHING_DUD
else
- final_table[result] = round(final_table[result] * 0.15, 1) //Fishing without bait is not going to be easy
+ final_table[result] = round(final_table[result] * FISH_WEIGHT_MULT_WITHOUT_BAIT, 1) //Fishing without bait is not going to be easy
// Apply fish trait modifiers
- var/list/fish_traits = fish_list_properties[caught_fish][NAMEOF(caught_fish, fish_traits)]
- var/additive_mod = 0
- var/multiplicative_mod = 1
- for(var/fish_trait in fish_traits)
- var/datum/fish_trait/trait = GLOB.fish_traits[fish_trait]
- var/list/mod = trait.catch_weight_mod(rod, fisherman)
- additive_mod += mod[ADDITIVE_FISHING_MOD]
- multiplicative_mod *= mod[MULTIPLICATIVE_FISHING_MOD]
-
- final_table[result] += additive_mod
- final_table[result] = round(final_table[result] * multiplicative_mod, 1)
+ final_table[result] = get_fish_trait_catch_mods(final_table[result], result, rod, fisherman, location)
if(final_table[result] <= 0)
final_table -= result
- ///here we even out the chances of fishie based on bait quality: better baits lead rarer fishes being more common.
+
if(leveling_exponent)
- var/highest_fish_weight
- var/list/collected_fish_weights = list()
- for(var/fishable in final_table)
- if(ispath(fishable, /obj/item/fish))
- var/fish_weight = fish_table[fishable]
- collected_fish_weights[fishable] = fish_weight
- if(fish_weight > highest_fish_weight)
- highest_fish_weight = fish_weight
-
- for(var/fish in collected_fish_weights)
- var/difference = highest_fish_weight - collected_fish_weights[fish]
- if(!difference)
- continue
- final_table[fish] += round(difference**leveling_exponent, 1)
+ level_out_fish(final_table, leveling_exponent)
return final_table
+///A proc that levels out the weights of various fish, leading to rarer fishes being more common.
+/datum/fish_source/proc/level_out_fish(list/table, exponent)
+ var/highest_fish_weight
+ var/list/collected_fish_weights = list()
+ for(var/fishable in table)
+ if(ispath(fishable, /obj/item/fish))
+ var/fish_weight = table[fishable]
+ collected_fish_weights[fishable] = fish_weight
+ if(fish_weight > highest_fish_weight)
+ highest_fish_weight = fish_weight
+
+ for(var/fish in collected_fish_weights)
+ var/difference = highest_fish_weight - collected_fish_weights[fish]
+ if(!difference)
+ continue
+ table[fish] += round(difference**exponent, 1)
+
+/datum/fish_source/proc/get_fish_trait_catch_mods(weight, obj/item/fish/fish, obj/item/fishing_rod/rod, mob/user, atom/location)
+ if(!ispath(fish, /obj/item/fish))
+ return weight
+ var/multiplier = 1
+ for(var/fish_trait in SSfishing.fish_properties[fish][FISH_PROPERTIES_TRAITS])
+ var/datum/fish_trait/trait = GLOB.fish_traits[fish_trait]
+ var/list/mod = trait.catch_weight_mod(rod, user, location, fish)
+ weight += mod[ADDITIVE_FISHING_MOD]
+ multiplier *= mod[MULTIPLICATIVE_FISHING_MOD]
+
+ return round(weight * multiplier, 1)
+
+///returns true if this fishing spot has fish that are shown in the catalog.
+/datum/fish_source/proc/has_known_fishes()
+ for(var/reward in fish_table)
+ if(!ispath(reward, /obj/item/fish))
+ continue
+ var/obj/item/fish/prototype = reward
+ if(initial(prototype.fish_flags) & FISH_FLAG_SHOW_IN_CATALOG)
+ return TRUE
+ return FALSE
+
+///Add a string with the names of catchable fishes to the examine text.
+/datum/fish_source/proc/get_catchable_fish_names(mob/user, atom/location, list/examine_text)
+ var/list/known_fishes = list()
+
+ var/obj/item/fishing_rod/rod = user.get_active_held_item()
+ if(!istype(rod))
+ rod = null
+
+ for(var/reward in fish_table)
+ if(!ispath(reward, /obj/item/fish))
+ continue
+ var/obj/item/fish/prototype = reward
+ if(initial(prototype.fish_flags) & FISH_FLAG_SHOW_IN_CATALOG)
+ var/init_name = initial(prototype.name)
+ if(rod)
+ var/init_weight = fish_table[reward]
+ var/weight = (rod.bait ? rod.bait.check_bait(prototype) : 1)
+ weight = get_fish_trait_catch_mods(weight, reward, rod, user, location)
+ if(weight > init_weight)
+ init_name = span_bold(init_name)
+ if(weight/init_weight >= 3.5)
+ init_name = "init_name"
+ else if(weight < init_weight)
+ init_name = span_small(init_name)
+ known_fishes += init_name
+
+ if(!length(known_fishes))
+ return
+
+ var/info = "You can catch the following fish here"
+
+ if(rod)
+ info = span_tooltip("boldened are the fish you're more likely to catch with your current setup. The opposite is true for smaller names", info)
+ examine_text += span_info("[info]: [english_list(known_fishes)].")
+
/datum/fish_source/proc/spawn_reward_from_explosion(atom/location, severity)
if(!explosive_malus)
explosive_spawn(location, severity)
@@ -388,7 +454,7 @@ GLOBAL_LIST(fishing_property_cache)
explosive_spawn(turf, exploded_turfs[turf], multiplier)
exploded_turfs = null
-/datum/fish_source/proc/explosive_spawn(location, severity, multiplier = 1)
+/datum/fish_source/proc/explosive_spawn(atom/location, severity, multiplier = 1)
for(var/i in 1 to (severity + 2))
if(!prob((100 + 100 * severity)/i * multiplier))
continue
@@ -404,3 +470,119 @@ GLOBAL_LIST(fishing_property_cache)
reward.pixel_y = rand(-9, 9)
if(severity >= EXPLODE_DEVASTATE)
reward.ex_act(EXPLODE_LIGHT)
+
+///Called when releasing a fish in a fishing spot with the TRAIT_CATCH_AND_RELEASE trait.
+/datum/fish_source/proc/readd_fish(obj/item/fish/fish, mob/living/releaser)
+ var/is_morbid = HAS_MIND_TRAIT(releaser, TRAIT_MORBID)
+ var/is_naive = HAS_MIND_TRAIT(releaser, TRAIT_NAIVE)
+ if(fish.status == FISH_DEAD) //ded fish won't repopulate the sea.
+ if(is_naive || is_morbid)
+ releaser.add_mood_event("fish_released", /datum/mood_event/fish_released, is_morbid && !is_naive, fish)
+ return
+ if(((fish.type in fish_table) != is_morbid) || is_naive)
+ releaser.add_mood_event("fish_released", /datum/mood_event/fish_released, is_morbid && !is_naive, fish)
+ if(isnull(fish_counts[fish.type])) //This fish can be caught indefinitely so it won't matter.
+ return
+ //If this fish population isn't recovering from recent losses, we just increase it.
+ if(!LAZYACCESS(currently_on_regen, fish.type))
+ fish_counts[fish.type] += 1
+ else
+ regen_count(fish.type)
+
+/**
+ * Called by /datum/autowiki/fish_sources unless the catalog entry for this fish source is null.
+ * It should Return a list of entries with keys named "name", "icon", "weight" and "notes"
+ * detailing the contents of this fish source.
+ */
+/datum/fish_source/proc/generate_wiki_contents(datum/autowiki/fish_sources/wiki)
+ var/list/data = list()
+ var/list/only_fish = list()
+
+ var/total_weight = 0
+ var/total_weight_without_bait = 0
+ var/total_weight_no_fish = 0
+
+ var/list/tables_by_quality = list()
+ var/list/total_weight_by_quality = list()
+ var/list/total_weight_by_quality_no_fish = list()
+
+ for(var/obj/item/fish/fish as anything in fish_table)
+ var/weight = fish_table[fish]
+ if(fish != FISHING_DUD)
+ total_weight += weight
+ if(!ispath(fish, /obj/item/fish))
+ total_weight_without_bait += weight
+ total_weight_no_fish += weight
+ continue
+ if(initial(fish.fish_flags) & FISH_FLAG_SHOW_IN_CATALOG)
+ only_fish += fish
+ total_weight_without_bait += round(fish_table[fish] * FISH_WEIGHT_MULT_WITHOUT_BAIT, 1)
+
+ for(var/trait in weight_result_multiplier)
+ var/list/table_copy = fish_table.Copy()
+ table_copy -= FISHING_DUD
+ var/exponent = weight_leveling_exponents[trait]
+ var/multiplier = weight_result_multiplier[trait]
+ for(var/fish as anything in table_copy)
+ if(!ispath(fish, /obj/item/fish))
+ continue
+ table_copy[fish] = round(table_copy[fish] * multiplier, 1)
+
+ level_out_fish(table_copy, exponent)
+ tables_by_quality[trait] = table_copy
+
+ var/tot_weight = 0
+ var/tot_weight_no_fish = 0
+ for(var/result in table_copy)
+ var/weight = table_copy[result]
+ tot_weight += weight
+ if(!ispath(result, /obj/item/fish))
+ tot_weight_no_fish += weight
+ total_weight_by_quality[trait] = tot_weight
+ total_weight_by_quality_no_fish[trait] = tot_weight_no_fish
+
+ //show the improved weights in ascending orders for fish.
+ tables_by_quality = reverseList(tables_by_quality)
+
+ if(FISHING_DUD in fish_table)
+ data += LIST_VALUE_WRAP_LISTS(list(
+ FISH_SOURCE_AUTOWIKI_NAME = FISH_SOURCE_AUTOWIKI_DUD,
+ FISH_SOURCE_AUTOWIKI_ICON = "",
+ FISH_SOURCE_AUTOWIKI_WEIGHT = PERCENT(fish_table[FISHING_DUD]/total_weight_without_bait),
+ FISH_SOURCE_AUTOWIKI_WEIGHT_SUFFIX = "WITHOUT A BAIT",
+ FISH_SOURCE_AUTOWIKI_NOTES = "Unless you have a magnet or rescue hook or you know what you're doing, always use a bait",
+ ))
+
+ for(var/obj/item/fish/fish as anything in only_fish)
+ var/weight = fish_table[fish]
+ var/deets = "Can be caught indefinitely"
+ if(fish in fish_counts)
+ deets = "It's quite rare and can only be caught up to [fish_counts[fish]] times"
+ if(fish in fish_count_regen)
+ deets += " every [DisplayTimeText(fish::breeding_timeout)]"
+ var/list/weight_deets = list()
+ for(var/trait in tables_by_quality)
+ weight_deets += "[round(PERCENT(tables_by_quality[trait][fish]/total_weight_by_quality[trait]), 0.1)]%"
+ var/weight_suffix = "([english_list(weight_deets, and_text = ", ")])"
+ data += LIST_VALUE_WRAP_LISTS(list(
+ FISH_SOURCE_AUTOWIKI_NAME = wiki.escape_value(full_capitalize(initial(fish.name))),
+ FISH_SOURCE_AUTOWIKI_ICON = FISH_AUTOWIKI_FILENAME(fish),
+ FISH_SOURCE_AUTOWIKI_WEIGHT = PERCENT(weight/total_weight),
+ FISH_SOURCE_AUTOWIKI_WEIGHT_SUFFIX = weight_suffix,
+ FISH_SOURCE_AUTOWIKI_NOTES = deets,
+ ))
+
+ if(total_weight_no_fish) //There are things beside fish that we can catch.
+ var/list/weight_deets = list()
+ for(var/trait in tables_by_quality)
+ weight_deets += "[round(PERCENT(total_weight_by_quality_no_fish[trait]/total_weight_by_quality[trait]), 0.1)]%"
+ var/weight_suffix = "([english_list(weight_deets, and_text = ", ")])"
+ data += LIST_VALUE_WRAP_LISTS(list(
+ FISH_SOURCE_AUTOWIKI_NAME = FISH_SOURCE_AUTOWIKI_OTHER,
+ FISH_SOURCE_AUTOWIKI_ICON = FISH_SOURCE_AUTOWIKI_QUESTIONMARK,
+ FISH_SOURCE_AUTOWIKI_WEIGHT = PERCENT(total_weight_no_fish/total_weight),
+ FISH_SOURCE_AUTOWIKI_WEIGHT_SUFFIX = weight_suffix,
+ FISH_SOURCE_AUTOWIKI_NOTES = "Who knows what it may be. Try and find out",
+ ))
+
+ return data
diff --git a/code/modules/fishing/sources/source_types.dm b/code/modules/fishing/sources/source_types.dm
index 52388685687c4..2f56ffaad3cd1 100644
--- a/code/modules/fishing/sources/source_types.dm
+++ b/code/modules/fishing/sources/source_types.dm
@@ -1,17 +1,24 @@
/datum/fish_source/ocean
+ radial_state = "seaboat"
+ overlay_state = "portal_ocean"
fish_table = list(
- FISHING_DUD = 11,
+ FISHING_DUD = 10,
/obj/effect/spawner/message_in_a_bottle = 4,
- /obj/item/coin/gold = 7,
- /obj/item/fish/clownfish = 15,
- /obj/item/fish/pufferfish = 15,
- /obj/item/fish/cardinal = 15,
- /obj/item/fish/greenchromis = 15,
+ /obj/item/coin/gold = 6,
+ /obj/item/fish/clownfish = 11,
+ /obj/item/fish/pufferfish = 11,
+ /obj/item/fish/cardinal = 11,
+ /obj/item/fish/greenchromis = 11,
+ /obj/item/fish/squid = 11,
+ /obj/item/fish/stingray = 8,
+ /obj/item/fish/plaice = 8,
+ /obj/item/fish/monkfish = 5,
/obj/item/fish/stingray = 10,
/obj/item/fish/lanternfish = 7,
/obj/item/fish/zipzap = 7,
/obj/item/fish/clownfish/lube = 5,
/obj/item/fish/swordfish = 5,
+ /obj/item/fish/swordfish = 3,
/obj/structure/mystery_box/fishing = 2,
)
fish_counts = list(
@@ -28,9 +35,13 @@
/datum/fish_source/ocean/beach
catalog_description = "Beach shore water"
+ radial_state = "palm_beach"
+ overlay_state = "portal_beach"
/datum/fish_source/ice_fishing
catalog_description = "Ice-covered water"
+ radial_state = "ice"
+ overlay_state = "portal_ocean"
fish_table = list(
FISHING_DUD = 4,
/obj/item/fish/arctic_char = 5,
@@ -42,21 +53,34 @@
/datum/fish_source/river
catalog_description = "River water"
+ radial_state = "river"
+ overlay_state = "portal_river"
fish_table = list(
FISHING_DUD = 4,
/obj/item/fish/goldfish = 5,
/obj/item/fish/guppy = 5,
+ /obj/item/fish/perch = 4,
/obj/item/fish/angelfish = 4,
/obj/item/fish/catfish = 4,
+ /obj/item/fish/perch = 5,
/obj/item/fish/slimefish = 2,
/obj/item/fish/sockeye_salmon = 1,
/obj/item/fish/arctic_char = 1,
+ /obj/item/fish/pike = 1,
/obj/item/fish/goldfish/three_eyes = 1,
)
+ fish_counts = list(
+ /obj/item/fish/pike = 3,
+ )
+ fish_count_regen = list(
+ /obj/item/fish/pike = 4 MINUTES,
+ )
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 5
+ explosive_malus = TRUE
/datum/fish_source/sand
catalog_description = "Sand"
+ radial_state = "palm_beach"
fish_table = list(
FISHING_DUD = 8,
/obj/item/fish/sand_crab = 10,
@@ -65,9 +89,11 @@
/obj/item/coin/gold = 3,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 20
+ explosive_malus = TRUE
/datum/fish_source/cursed_spring
catalog_description = null //it's a secret (sorta, I know you're reading this)
+ radial_state = "cursed"
fish_table = list(
FISHING_DUD = 2,
/obj/item/fish/soul = 3,
@@ -78,6 +104,7 @@
/obj/item/fishing_rod/telescopic/master = 1,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 25
+ explosive_malus = TRUE
/datum/fish_source/portal
fish_table = list(
@@ -85,15 +112,13 @@
/obj/item/fish/goldfish = 10,
/obj/item/fish/guppy = 10,
/obj/item/fish/angelfish = 10,
+ /obj/item/fish/perch = 5,
/obj/item/fish/goldfish/three_eyes = 3,
)
catalog_description = "Aquarium dimension (Fishing portal generator)"
+ radial_state = "fish_tank"
///The name of this option shown in the radial menu on the fishing portal generator
var/radial_name = "Aquarium"
- ///The icon state shown for this option in the radial menu
- var/radial_state = "fish_tank"
- ///The icon state of the overlay shown on the machine when active.
- var/overlay_state = "portal_aquarium"
/datum/fish_source/portal/beach
fish_table = list(
@@ -103,6 +128,8 @@
/obj/item/fish/pufferfish = 10,
/obj/item/fish/cardinal = 10,
/obj/item/fish/greenchromis = 10,
+ /obj/item/fish/squid = 8,
+ /obj/item/fish/plaice = 8,
)
catalog_description = "Beach dimension (Fishing portal generator)"
radial_name = "Beach"
@@ -141,6 +168,7 @@
/obj/item/fish/armorfish = 5,
/obj/item/fish/zipzap = 5,
/obj/item/fish/stingray = 4,
+ /obj/item/fish/monkfish = 4,
/obj/item/fish/swordfish = 3,
)
fish_counts = list(
@@ -185,13 +213,16 @@
/obj/item/fish/donkfish = 5,
/obj/item/fish/emulsijack = 5,
/obj/item/fish/jumpercable = 5,
- /obj/item/fish/chainsawfish = 3,
+ /obj/item/fish/chainsawfish = 2,
+ /obj/item/fish/pike/armored = 2,
)
fish_counts = list(
/obj/item/fish/chainsawfish = 1,
+ /obj/item/fish/pike/armored = 1,
)
fish_count_regen = list(
/obj/item/fish/chainsawfish = 7 MINUTES,
+ /obj/item/fish/pike/armored = 7 MINUTES,
)
catalog_description = "Syndicate dimension (Fishing portal generator)"
radial_name = "Syndicate"
@@ -234,7 +265,7 @@
fish_table[reward_path] = rand(1, 4)
///Difficulty has to be calculated before the rest, because of how it influences jump chances
-/datum/fish_source/portal/random/calculate_difficulty(result, obj/item/fishing_rod/rod, mob/fisherman, datum/fishing_challenge/challenge)
+/datum/fish_source/portal/random/calculate_difficulty(datum/fishing_challenge/challenge, result, obj/item/fishing_rod/rod, mob/fisherman)
. = ..()
. += rand(-10, 15)
@@ -274,12 +305,13 @@
/datum/fish_source/chasm
catalog_description = "Chasm depths"
background = "background_lavaland"
+ radial_state = "ground_hole"
+ overlay_state = "portal_chasm"
fish_table = list(
FISHING_DUD = 5,
/obj/item/fish/chasm_crab = 15,
/datum/chasm_detritus = 30,
)
-
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 5
/datum/fish_source/chasm/on_start_fishing(obj/item/fishing_rod/rod, mob/fisherman, atom/parent)
@@ -303,6 +335,8 @@
/datum/fish_source/lavaland
catalog_description = "Lava vents"
background = "background_lavaland"
+ radial_state = "lava"
+ overlay_state = "portal_lava"
fish_table = list(
FISHING_DUD = 5,
/obj/item/stack/ore/slag = 20,
@@ -346,15 +380,17 @@
/datum/fish_source/moisture_trap
catalog_description = "Moisture trap basins"
+ radial_state = "garbage"
fish_table = list(
FISHING_DUD = 20,
/obj/item/fish/ratfish = 10,
- /obj/item/fish/slimefish = 4
+ /obj/item/fish/slimefish = 4,
)
fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 10
/datum/fish_source/toilet
catalog_description = "Station toilets"
+ radial_state = "toilet"
duds = list("ewww... nothing", "it was nothing", "it was toilet paper", "it was flushed away", "the hook is empty", "where's the damn money?!")
fish_table = list(
FISHING_DUD = 18,
@@ -380,6 +416,21 @@
)
fishing_difficulty = FISHING_EASY_DIFFICULTY
+/datum/fish_source/holographic/on_fishing_spot_init(datum/component/fishing_spot/spot)
+ ADD_TRAIT(spot.parent, TRAIT_UNLINKABLE_FISHING_SPOT, REF(src)) //You would have to be inside the holodeck anyway...
+
+/datum/fish_source/holographic/on_fishing_spot_del(datum/component/fishing_spot/spot)
+ REMOVE_TRAIT(spot.parent, TRAIT_UNLINKABLE_FISHING_SPOT, REF(src))
+
+/datum/fish_source/holographic/generate_wiki_contents(datum/autowiki/fish_sources/wiki)
+ var/obj/item/fish/prototype = /obj/item/fish/holo/checkered
+ return LIST_VALUE_WRAP_LISTS(list(
+ FISH_SOURCE_AUTOWIKI_NAME = "Holographic Fish",
+ FISH_SOURCE_AUTOWIKI_ICON = FISH_AUTOWIKI_FILENAME(prototype),
+ FISH_SOURCE_AUTOWIKI_WEIGHT = 100,
+ FISH_SOURCE_AUTOWIKI_NOTES = "Holographic fish disappears outside the Holodeck",
+ ))
+
/datum/fish_source/holographic/reason_we_cant_fish(obj/item/fishing_rod/rod, mob/fisherman, atom/parent)
. = ..()
if(!istype(get_area(fisherman), /area/station/holodeck))
@@ -399,6 +450,8 @@
/datum/fish_source/oil_well
catalog_description = "Oil wells"
+ radial_state = "oil"
+ overlay_state = "portal_chasm" //close enough to pitch black
fish_table = list(
FISHING_DUD = 5,
/obj/item/fish/boned = 10,
@@ -421,10 +474,11 @@
/datum/fish_source/hydro_tray
catalog_description = "Hydroponics trays"
+ radial_state = "hydro"
fish_table = list(
FISHING_DUD = 25,
/obj/item/food/grown/grass = 25,
- RANDOM_SEED = 16,
+ FISHING_RANDOM_SEED = 16,
/obj/item/seeds/grass = 6,
/obj/item/seeds/random = 1,
/mob/living/basic/frog = 1,
@@ -433,13 +487,55 @@
fish_counts = list(
/obj/item/food/grown/grass = 10,
/obj/item/seeds/grass = 4,
- RANDOM_SEED = 4,
+ FISHING_RANDOM_SEED = 4,
/obj/item/seeds/random = 1,
/mob/living/basic/frog = 1,
/mob/living/basic/axolotl = 1,
)
fishing_difficulty = FISHING_EASY_DIFFICULTY - 5
+/datum/fish_source/hydro_tray/generate_wiki_contents(datum/autowiki/fish_sources/wiki)
+ var/list/data = list()
+ var/total_weight = 0
+ var/critter_weight = 0
+ var/seed_weight = 0
+ var/other_weight = 0
+ var/dud_weight = fish_table[FISHING_DUD]
+ for(var/content in fish_table)
+ var/weight = fish_table[content]
+ total_weight += weight
+ if(ispath(content, /mob/living))
+ critter_weight += weight
+ else if(ispath(content, /obj/item/food/grown) || ispath(content, /obj/item/seeds) || content == FISHING_RANDOM_SEED)
+ seed_weight += weight
+ else if(content != FISHING_DUD)
+ other_weight += weight
+
+ data += LIST_VALUE_WRAP_LISTS(list(
+ FISH_SOURCE_AUTOWIKI_NAME = FISH_SOURCE_AUTOWIKI_DUD,
+ FISH_SOURCE_AUTOWIKI_DUD = "",
+ FISH_SOURCE_AUTOWIKI_WEIGHT = PERCENT(dud_weight/total_weight),
+ FISH_SOURCE_AUTOWIKI_WEIGHT_SUFFIX = "WITHOUT A BAIT",
+ FISH_SOURCE_AUTOWIKI_NOTES = "",
+ ))
+
+ data += LIST_VALUE_WRAP_LISTS(list(
+ FISH_SOURCE_AUTOWIKI_NAME = "Critter",
+ FISH_SOURCE_AUTOWIKI_DUD = "",
+ FISH_SOURCE_AUTOWIKI_WEIGHT = PERCENT(critter_weight/total_weight),
+ FISH_SOURCE_AUTOWIKI_NOTES = "A small creature, usually a frog or an axolotl",
+ ))
+
+ if(other_weight)
+ data += LIST_VALUE_WRAP_LISTS(list(
+ FISH_SOURCE_AUTOWIKI_NAME = "Other Stuff",
+ FISH_SOURCE_AUTOWIKI_DUD = "",
+ FISH_SOURCE_AUTOWIKI_WEIGHT = PERCENT(other_weight/total_weight),
+ FISH_SOURCE_AUTOWIKI_NOTES = "Other stuff, who knows...",
+ ))
+
+ return data
+
/datum/fish_source/hydro_tray/reason_we_cant_fish(obj/item/fishing_rod/rod, mob/fisherman, atom/parent)
if(!istype(parent, /obj/machinery/hydroponics/constructable))
return ..()
@@ -483,3 +579,24 @@
var/picked_path = pick(seeds_to_draw_from)
return new picked_path(get_turf(fishing_spot))
+
+/datum/fish_source/deepfryer
+ catalog_description = "Deep Fryers"
+ radial_state = "fryer"
+ fish_table = list(
+ /obj/item/food/badrecipe = 15,
+ /obj/item/food/nugget = 5,
+ /obj/item/fish/fryish = 40,
+ /obj/item/fish/fryish/fritterish = 4,
+ /obj/item/fish/fryish/nessie = 1,
+ )
+ fish_counts = list(
+ /obj/item/fish/fryish = 10,
+ /obj/item/fish/fryish/fritterish = 4,
+ /obj/item/fish/fryish/nessie = 1,
+ )
+ fish_count_regen = list(
+ /obj/item/fish/fryish = 2 MINUTES,
+ /obj/item/fish/fryish/fritterish = 6 MINUTES,
+ )
+ fishing_difficulty = FISHING_DEFAULT_DIFFICULTY + 13
diff --git a/code/modules/food_and_drinks/machinery/deep_fryer.dm b/code/modules/food_and_drinks/machinery/deep_fryer.dm
index 322c0a42c5557..313bc29d199dc 100644
--- a/code/modules/food_and_drinks/machinery/deep_fryer.dm
+++ b/code/modules/food_and_drinks/machinery/deep_fryer.dm
@@ -63,6 +63,8 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
reagents.add_reagent(/datum/reagent/consumable/nutriment/fat/oil, 25)
fry_loop = new(src, FALSE)
RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, PROC_REF(on_cleaned))
+ AddElement(/datum/element/lazy_fishing_spot, /datum/fish_source/deepfryer)
+ AddElement(/datum/element/fish_safe_storage) //Prevents fryish and fritterish from dying inside the deepfryer.
/obj/machinery/deepfryer/Destroy()
QDEL_NULL(fry_loop)
@@ -154,7 +156,7 @@ GLOBAL_LIST_INIT(oilfry_blacklisted_items, typecacheof(list(
reagents.trans_to(frying, oil_use * seconds_per_tick, multiplier = fry_speed * 3) //Fried foods gain more of the reagent thanks to space magic
grease_level += prob(grease_increase_chance) * grease_Increase_amount
- cook_time += fry_speed * seconds_per_tick
+ cook_time += fry_speed * seconds_per_tick SECONDS
if(cook_time >= DEEPFRYER_COOKTIME && !frying_fried)
frying_fried = TRUE //frying... frying... fried
playsound(src.loc, 'sound/machines/ding.ogg', 50, TRUE)
diff --git a/code/modules/food_and_drinks/machinery/food_cart.dm b/code/modules/food_and_drinks/machinery/food_cart.dm
index a14ea3593c51a..9549bcc7ae1d3 100644
--- a/code/modules/food_and_drinks/machinery/food_cart.dm
+++ b/code/modules/food_and_drinks/machinery/food_cart.dm
@@ -86,7 +86,7 @@
return
var/obj/item/card/id/id_card = user.get_idcard(hand_first = TRUE)
if(!check_access(id_card))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
return
to_chat(user, span_notice("You attempt to [unpacked ? "pack up" :"unpack"] [src]..."))
if(!do_after(user, 5 SECONDS, src))
diff --git a/code/modules/food_and_drinks/machinery/griddle.dm b/code/modules/food_and_drinks/machinery/griddle.dm
index e0c45e6c9af10..ed1884c89af27 100644
--- a/code/modules/food_and_drinks/machinery/griddle.dm
+++ b/code/modules/food_and_drinks/machinery/griddle.dm
@@ -73,8 +73,8 @@
return
if(user.transferItemToLoc(I, src, silent = FALSE))
//Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf)
- I.pixel_x = clamp(text2num(LAZYACCESS(modifiers, ICON_X)) - 16, -(world.icon_size/2), world.icon_size/2)
- I.pixel_y = clamp(text2num(LAZYACCESS(modifiers, ICON_Y)) - 16, -(world.icon_size/2), world.icon_size/2)
+ I.pixel_x = clamp(text2num(LAZYACCESS(modifiers, ICON_X)) - 16, -(ICON_SIZE_X/2), ICON_SIZE_X/2)
+ I.pixel_y = clamp(text2num(LAZYACCESS(modifiers, ICON_Y)) - 16, -(ICON_SIZE_Y/2), ICON_SIZE_Y/2)
to_chat(user, span_notice("You place [I] on [src]."))
AddToGrill(I, user)
else
diff --git a/code/modules/food_and_drinks/machinery/microwave.dm b/code/modules/food_and_drinks/machinery/microwave.dm
index 4fa586401ff56..ae6e3945c94b4 100644
--- a/code/modules/food_and_drinks/machinery/microwave.dm
+++ b/code/modules/food_and_drinks/machinery/microwave.dm
@@ -381,12 +381,20 @@
if(operating)
return NONE
- if (item.item_flags & ABSTRACT)
+ if(item.item_flags & ABSTRACT)
+ return NONE
+
+ if(dirty >= MAX_MICROWAVE_DIRTINESS) // The microwave is all dirty so can't be used!
+ if(IS_EDIBLE(item))
+ balloon_alert(user, "it's too dirty!")
+ return ITEM_INTERACT_BLOCKING
return NONE
if(broken > NOT_BROKEN)
- balloon_alert(user, "it's broken!")
- return ITEM_INTERACT_BLOCKING
+ if(IS_EDIBLE(item))
+ balloon_alert(user, "it's broken!")
+ return ITEM_INTERACT_BLOCKING
+ return NONE
if(istype(item, /obj/item/stock_parts/power_store/cell) && cell_powered)
var/swapped = FALSE
@@ -405,12 +413,10 @@
return ITEM_INTERACT_SUCCESS
if(!anchored)
- balloon_alert(user, "not secured!")
- return ITEM_INTERACT_BLOCKING
-
- if(dirty >= MAX_MICROWAVE_DIRTINESS) // The microwave is all dirty so can't be used!
- balloon_alert(user, "it's too dirty!")
- return ITEM_INTERACT_BLOCKING
+ if(IS_EDIBLE(item))
+ balloon_alert(user, "not secured!")
+ return ITEM_INTERACT_BLOCKING
+ return NONE
if(vampire_charging_capable && istype(item, /obj/item/modular_computer) && ingredients.len > 0)
balloon_alert(user, "max 1 device!")
@@ -483,7 +489,7 @@
vampire_charging_enabled = !vampire_charging_enabled
balloon_alert(user, "set to [vampire_charging_enabled ? "charge" : "cook"]")
- playsound(src, 'sound/machines/twobeep_high.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/beep/twobeep_high.ogg', 50, FALSE)
if(HAS_SILICON_ACCESS(user))
visible_message(span_notice("[user] sets \the [src] to [vampire_charging_enabled ? "charge" : "cook"]."), blind_message = span_notice("You hear \the [src] make an informative beep!"))
return CLICK_ACTION_SUCCESS
@@ -582,11 +588,11 @@
if(wire_disabled)
audible_message("[src] buzzes.")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
return
if(cell_powered && cell?.charge < TIER_1_CELL_CHARGE_RATE * efficiency)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
balloon_alert(cooker, "no power draw!")
return
@@ -622,7 +628,7 @@
/obj/machinery/microwave/proc/wzhzhzh()
if(cell_powered && !isnull(cell))
if(!cell.use(TIER_1_CELL_CHARGE_RATE * efficiency))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
return
visible_message(span_notice("\The [src] turns on."), null, span_hear("You hear a microwave humming."))
@@ -802,13 +808,13 @@
/obj/machinery/microwave/proc/vampire(mob/cooker)
var/obj/item/modular_computer/vampire_pda = LAZYACCESS(ingredients, 1)
if(isnull(vampire_pda))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
after_finish_loop()
return
vampire_cell = vampire_pda.internal_cell
if(isnull(vampire_cell))
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
after_finish_loop()
return
@@ -819,7 +825,7 @@
/obj/machinery/microwave/proc/charge(mob/cooker)
if(!vampire_charging_capable)
balloon_alert(cooker, "needs upgrade!")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
return
if(operating || broken > 0 || panel_open || dirty >= MAX_MICROWAVE_DIRTINESS)
@@ -827,14 +833,14 @@
if(wire_disabled)
audible_message("[src] buzzes.")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
return
// We should only be charging PDAs
for(var/atom/movable/potential_item as anything in ingredients)
if(!istype(potential_item, /obj/item/modular_computer))
balloon_alert(cooker, "pda only!")
- playsound(src, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 50, FALSE)
eject()
return
diff --git a/code/modules/food_and_drinks/machinery/smartfridge.dm b/code/modules/food_and_drinks/machinery/smartfridge.dm
index 1dca29b950422..0481d1c491092 100644
--- a/code/modules/food_and_drinks/machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/machinery/smartfridge.dm
@@ -203,7 +203,7 @@
/// Returns details related to the fridge structure
/obj/machinery/smartfridge/proc/structure_examine()
- . = ""
+ . = list()
if(welded_down)
. += span_info("It's moorings are firmly [EXAMINE_HINT("welded")] to the floor.")
@@ -253,9 +253,9 @@
/obj/machinery/smartfridge/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
if(BRUTE)
- playsound(src.loc, 'sound/effects/glasshit.ogg', 75, TRUE)
+ playsound(src.loc, 'sound/effects/glass/glasshit.ogg', 75, TRUE)
if(BURN)
- playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
+ playsound(src.loc, 'sound/items/tools/welder.ogg', 100, TRUE)
/obj/machinery/smartfridge/atom_break(damage_flag)
playsound(src, SFX_SHATTER, 50, TRUE)
@@ -313,7 +313,7 @@
to_chat(user, span_warning("\The [src]'s magnetic door won't open without power!"))
return FALSE
- if(!user.combat_mode)
+ if(!user.combat_mode || (weapon.item_flags & NOBLUDGEON))
to_chat(user, span_warning("\The [src] smartly refuses [weapon]."))
return FALSE
diff --git a/code/modules/food_and_drinks/machinery/stove_component.dm b/code/modules/food_and_drinks/machinery/stove_component.dm
index fcbabafc2d12c..76f52345c8c35 100644
--- a/code/modules/food_and_drinks/machinery/stove_component.dm
+++ b/code/modules/food_and_drinks/machinery/stove_component.dm
@@ -132,7 +132,7 @@
real_parent.balloon_alert_to_viewers("burners [on ? "on" : "off"]")
playsound(real_parent, 'sound/machines/click.ogg', 30, TRUE)
- playsound(real_parent, on ? 'sound/items/welderactivate.ogg' : 'sound/items/welderdeactivate.ogg', 15, TRUE)
+ playsound(real_parent, on ? 'sound/items/tools/welderactivate.ogg' : 'sound/items/tools/welderdeactivate.ogg', 15, TRUE)
/datum/component/stove/proc/on_attackby(obj/machinery/source, obj/item/attacking_item, mob/user, params)
SIGNAL_HANDLER
@@ -267,7 +267,7 @@
return
// this gets badly murdered by sidemap
soup_smoke = new(parent, particle_type)
- soup_smoke.set_particle_position(container_x, round(world.icon_size * 0.66), 0)
+ soup_smoke.set_particle_position(container_x, round(ICON_SIZE_Y * 0.66), 0)
return
QDEL_NULL(soup_smoke)
diff --git a/code/modules/food_and_drinks/plate.dm b/code/modules/food_and_drinks/plate.dm
index add7eecaf92e0..ef88a4758115b 100644
--- a/code/modules/food_and_drinks/plate.dm
+++ b/code/modules/food_and_drinks/plate.dm
@@ -121,7 +121,7 @@
icon = 'icons/obj/service/kitchen.dmi'
icon_state = "plate_shard1"
base_icon_state = "plate_shard"
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
w_class = WEIGHT_CLASS_TINY
force = 5
throwforce = 5
diff --git a/code/modules/food_and_drinks/recipes/drinks/drinks_alcoholic.dm b/code/modules/food_and_drinks/recipes/drinks/drinks_alcoholic.dm
index 77606f5e37210..78b9623c3a016 100644
--- a/code/modules/food_and_drinks/recipes/drinks/drinks_alcoholic.dm
+++ b/code/modules/food_and_drinks/recipes/drinks/drinks_alcoholic.dm
@@ -224,7 +224,7 @@
/datum/chemical_reaction/drink/moscow_mule
results = list(/datum/reagent/consumable/ethanol/moscow_mule = 10)
required_reagents = list(/datum/reagent/consumable/sol_dry = 5, /datum/reagent/consumable/ethanol/vodka = 5, /datum/reagent/consumable/limejuice = 1, /datum/reagent/consumable/ice = 1)
- mix_sound = 'sound/effects/bubbles2.ogg'
+ mix_sound = 'sound/effects/bubbles/bubbles2.ogg'
/datum/chemical_reaction/drink/painkiller
results = list(/datum/reagent/consumable/ethanol/painkiller = 10)
@@ -361,7 +361,7 @@
/datum/chemical_reaction/drink/bacchus_blessing
results = list(/datum/reagent/consumable/ethanol/bacchus_blessing = 4)
required_reagents = list(/datum/reagent/consumable/ethanol/hooch = 1, /datum/reagent/consumable/ethanol/absinthe = 1, /datum/reagent/consumable/ethanol/manly_dorf = 1, /datum/reagent/consumable/ethanol/syndicatebomb = 1)
- mix_message = "The mixture turns to a sickening froth."
+ mix_message = span_warning("The mixture turns to a sickening froth.")
/datum/chemical_reaction/drink/eggnog
results = list(/datum/reagent/consumable/ethanol/eggnog = 15)
@@ -378,7 +378,7 @@
results = list(/datum/reagent/consumable/ethanol/quadruple_sec = 15)
required_reagents = list(/datum/reagent/consumable/ethanol/triple_sec = 5, /datum/reagent/consumable/triple_citrus = 5, /datum/reagent/consumable/grenadine = 5)
mix_message = "The snap of a taser emanates clearly from the mixture as it settles."
- mix_sound = 'sound/weapons/taser.ogg'
+ mix_sound = 'sound/items/weapons/taser.ogg'
reaction_tags = REACTION_TAG_DRINK | REACTION_TAG_EASY | REACTION_TAG_OTHER
/datum/chemical_reaction/drink/grasshopper
@@ -394,7 +394,7 @@
results = list(/datum/reagent/consumable/ethanol/quintuple_sec = 15)
required_reagents = list(/datum/reagent/consumable/ethanol/quadruple_sec = 5, /datum/reagent/consumable/nutriment/soup/clown_tears = 5, /datum/reagent/consumable/ethanol/syndicatebomb = 5)
mix_message = "Judgement is upon you."
- mix_sound = 'sound/items/airhorn2.ogg'
+ mix_sound = 'sound/items/airhorn/airhorn2.ogg'
reaction_tags = REACTION_TAG_DRINK | REACTION_TAG_EASY | REACTION_TAG_OTHER
/datum/chemical_reaction/drink/bastion_bourbon
@@ -481,14 +481,14 @@
results = list(/datum/reagent/consumable/ethanol/wizz_fizz = 3)
required_reagents = list(/datum/reagent/consumable/ethanol/triple_sec = 1, /datum/reagent/consumable/sodawater = 1, /datum/reagent/consumable/ethanol/champagne = 1)
mix_message = "The beverage starts to froth with an almost mystical zeal!"
- mix_sound = 'sound/effects/bubbles2.ogg'
+ mix_sound = 'sound/effects/bubbles/bubbles2.ogg'
reaction_tags = REACTION_TAG_DRINK | REACTION_TAG_EASY | REACTION_TAG_OTHER
/datum/chemical_reaction/drink/bug_spray
results = list(/datum/reagent/consumable/ethanol/bug_spray = 5)
required_reagents = list(/datum/reagent/consumable/ethanol/triple_sec = 2, /datum/reagent/consumable/lemon_lime = 1, /datum/reagent/consumable/ethanol/rum = 2, /datum/reagent/consumable/ethanol/vodka = 1)
mix_message = "The faint aroma of summer camping trips wafts through the air; but what's that buzzing noise?"
- mix_sound = 'sound/creatures/bee.ogg'
+ mix_sound = 'sound/mobs/non-humanoids/bee/bee.ogg'
reaction_tags = REACTION_TAG_DRINK | REACTION_TAG_EASY | REACTION_TAG_OTHER
/datum/chemical_reaction/drink/jack_rose
@@ -579,7 +579,7 @@
results = list(/datum/reagent/consumable/ethanol/pod_tesla = 15)
required_reagents = list(/datum/reagent/consumable/ethanol/telepole = 5, /datum/reagent/consumable/ethanol/brave_bull = 3, /datum/reagent/consumable/ethanol/admiralty = 5)
mix_message = "Arcs of lightning fly from the mixture."
- mix_sound = 'sound/weapons/zapbang.ogg'
+ mix_sound = 'sound/items/weapons/zapbang.ogg'
/datum/chemical_reaction/drink/yuyakita
results = list(/datum/reagent/consumable/ethanol/yuyakita = 4)
diff --git a/code/modules/food_and_drinks/recipes/food_mixtures.dm b/code/modules/food_and_drinks/recipes/food_mixtures.dm
index c04edf6fee79d..e4ef45bda7fde 100644
--- a/code/modules/food_and_drinks/recipes/food_mixtures.dm
+++ b/code/modules/food_and_drinks/recipes/food_mixtures.dm
@@ -1,5 +1,6 @@
/datum/crafting_recipe/food
mass_craftable = TRUE
+ crafting_flags = parent_type::crafting_flags | CRAFT_TRANSFERS_REAGENTS | CRAFT_CLEARS_REAGENTS
/datum/crafting_recipe/food/on_craft_completion(mob/user, atom/result)
SHOULD_CALL_PARENT(TRUE)
@@ -273,7 +274,7 @@
results = list(/datum/reagent/consumable/salt = 2)
required_reagents = list(/datum/reagent/consumable/liquidelectricity/enriched = 2, /datum/reagent/consumable/grounding_solution = 1)
mix_message = "The mixture lets off a sharp snap as the electricity discharges."
- mix_sound = 'sound/weapons/taser.ogg'
+ mix_sound = 'sound/items/weapons/taser.ogg'
reaction_flags = REACTION_INSTANT
/datum/chemical_reaction/food/martian_batter
diff --git a/code/modules/food_and_drinks/recipes/processor_recipes.dm b/code/modules/food_and_drinks/recipes/processor_recipes.dm
index 3ff29e194c719..14f93b45cd8dc 100644
--- a/code/modules/food_and_drinks/recipes/processor_recipes.dm
+++ b/code/modules/food_and_drinks/recipes/processor_recipes.dm
@@ -139,3 +139,7 @@
output = /obj/item/popsicle_stick
food_multiplier = 3
preserve_materials = FALSE
+
+/datum/food_processor_process/canned_ink
+ input = /obj/item/food/ink_sac
+ output = /obj/item/food/canned/squid_ink
diff --git a/code/modules/food_and_drinks/recipes/soup_mixtures.dm b/code/modules/food_and_drinks/recipes/soup_mixtures.dm
index 446782d00cbf4..c69de62fbfc97 100644
--- a/code/modules/food_and_drinks/recipes/soup_mixtures.dm
+++ b/code/modules/food_and_drinks/recipes/soup_mixtures.dm
@@ -245,7 +245,7 @@
// Everything else will just get fried
if(isnull(ingredient.reagents) && !is_type_in_list(ingredient, required_ingredients))
- ingredient.AddElement(/datum/element/fried_item, 30)
+ ingredient.AddElement(/datum/element/fried_item, 30 SECONDS)
continue
// Things that had reagents or ingredients in the soup will get deleted
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
index c965526bcb1c6..8798f7cd8806c 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm
@@ -13,6 +13,14 @@
result = /obj/item/food/donut/plain
category = CAT_PASTRY
+// It is so stupid that we have to do this but because food crafting clears all reagents that got added during init,
+// here we are adding it again (but only for crafting, maploaded and spawned donuts work fine).
+// Until the issues with crafted items' reagents are resolved this will have to do
+/datum/crafting_recipe/food/donut/on_craft_completion(mob/user, atom/result)
+ . = ..()
+ var/obj/item/food/donut/donut_result = result
+ if(donut_result.is_decorated)
+ donut_result.reagents.add_reagent(/datum/reagent/consumable/sprinkles, 1)
/datum/crafting_recipe/food/donut/chaos
name = "Chaos donut"
diff --git a/code/modules/food_and_drinks/restaurant/customers/_customer.dm b/code/modules/food_and_drinks/restaurant/customers/_customer.dm
index 646f31f80c67b..653ce81b7d461 100644
--- a/code/modules/food_and_drinks/restaurant/customers/_customer.dm
+++ b/code/modules/food_and_drinks/restaurant/customers/_customer.dm
@@ -39,7 +39,7 @@
///Base icon state for the customer
var/base_icon_state = "amerifat"
///Sound to use when this robot type speaks
- var/speech_sound = 'sound/creatures/tourist/tourist_talk.ogg'
+ var/speech_sound = 'sound/mobs/non-humanoids/tourist/tourist_talk.ogg'
/// Is this unique once per venue?
var/is_unique = FALSE
@@ -159,7 +159,7 @@
first_warning_line = "Get your hands off of me!"
second_warning_line = "Do not touch me you filthy animal, last warning!"
self_defense_line = "I will break you like a baguette!"
- speech_sound = 'sound/creatures/tourist/tourist_talk_french.ogg'
+ speech_sound = 'sound/mobs/non-humanoids/tourist/tourist_talk_french.ogg'
orderable_objects = list(
VENUE_RESTAURANT = list(
/obj/item/food/baguette = 20,
@@ -203,7 +203,7 @@
first_warning_line = "Don't touch me you pervert!"
second_warning_line = "I'm going to go super saiyan if you touch me again! Last warning!"
self_defense_line = "OMAE WA MO, SHINDEROU!"
- speech_sound = 'sound/creatures/tourist/tourist_talk_japanese1.ogg'
+ speech_sound = 'sound/mobs/non-humanoids/tourist/tourist_talk_japanese1.ogg'
orderable_objects = list(
VENUE_RESTAURANT = list(
/datum/custom_order/icecream = 4,
@@ -247,7 +247,7 @@
first_warning_line = "Hey, only my employer gets to mess with me like that."
second_warning_line = "Leave me be, I'm trying to focus. Last warning!"
self_defense_line = "I didn't want it to end up like this."
- speech_sound = 'sound/creatures/tourist/tourist_talk_japanese2.ogg'
+ speech_sound = 'sound/mobs/non-humanoids/tourist/tourist_talk_japanese2.ogg'
orderable_objects = list(
VENUE_RESTAURANT = list(
/datum/reagent/consumable/nutriment/soup/miso = 6,
@@ -282,7 +282,7 @@
second_warning_line = "Last warning! I'll destroy you!"
self_defense_line = "Flap attack!"
- speech_sound = 'sound/creatures/tourist/tourist_talk_moth.ogg'
+ speech_sound = 'sound/mobs/non-humanoids/tourist/tourist_talk_moth.ogg'
orderable_objects = list(
VENUE_RESTAURANT = list(
@@ -342,7 +342,7 @@
/datum/customer_data/mexican
base_icon_state = "mexican"
prefix_file = "strings/names/mexican_prefix.txt"
- speech_sound = 'sound/creatures/tourist/tourist_talk_mexican.ogg'
+ speech_sound = 'sound/mobs/non-humanoids/tourist/tourist_talk_mexican.ogg'
clothing_sets = list("mexican_poncho")
orderable_objects = list(
VENUE_RESTAURANT = list(
@@ -382,7 +382,7 @@
/datum/customer_data/british
base_icon_state = "british"
prefix_file = "strings/names/british_prefix.txt"
- speech_sound = 'sound/creatures/tourist/tourist_talk_british.ogg'
+ speech_sound = 'sound/mobs/non-humanoids/tourist/tourist_talk_british.ogg'
friendly_pull_line = "I don't enjoy being pulled around like this."
first_warning_line = "Our sovereign lord the Queen chargeth and commandeth all persons, being assembled, immediately to disperse themselves."
diff --git a/code/modules/hallucination/battle.dm b/code/modules/hallucination/battle.dm
index 4bbf9729cdeb6..2a50093e3a015 100644
--- a/code/modules/hallucination/battle.dm
+++ b/code/modules/hallucination/battle.dm
@@ -11,9 +11,9 @@
/// The upper end to how many shots we'll fire.
var/shots_to_fire_upper_range = 6
/// The sound effect we play when we "fire" a shot.
- var/fire_sound = 'sound/weapons/gun/shotgun/shot.ogg'
+ var/fire_sound = 'sound/items/weapons/gun/shotgun/shot.ogg'
/// The sound we make when our shot actually "hits" "someone".
- var/hit_person_sound = 'sound/weapons/pierce.ogg'
+ var/hit_person_sound = 'sound/items/weapons/pierce.ogg'
/// The sound we make when our shot misses someone and "hits" a "wall".
var/hit_wall_sound = SFX_RICOCHET
/// The number of successful hits required to "down" the "someone" we're firing at.
@@ -60,9 +60,9 @@
/datum/hallucination/battle/gun/disabler
shots_to_fire_lower_range = 5
shots_to_fire_upper_range = 10
- fire_sound = 'sound/weapons/taser2.ogg'
- hit_person_sound = 'sound/weapons/tap.ogg'
- hit_wall_sound = 'sound/weapons/effects/searwall.ogg'
+ fire_sound = 'sound/items/weapons/taser2.ogg'
+ hit_person_sound = 'sound/items/weapons/tap.ogg'
+ hit_wall_sound = 'sound/items/weapons/effects/searwall.ogg'
number_of_hits_to_end = 3
chance_to_fall = 70
@@ -70,9 +70,9 @@
/datum/hallucination/battle/gun/laser
shots_to_fire_lower_range = 5
shots_to_fire_upper_range = 10
- fire_sound = 'sound/weapons/laser.ogg'
- hit_person_sound = 'sound/weapons/sear.ogg'
- hit_wall_sound = 'sound/weapons/effects/searwall.ogg'
+ fire_sound = 'sound/items/weapons/laser.ogg'
+ hit_person_sound = 'sound/items/weapons/sear.ogg'
+ hit_wall_sound = 'sound/items/weapons/effects/searwall.ogg'
number_of_hits_to_end = 4
chance_to_fall = 70
@@ -82,7 +82,7 @@
/datum/hallucination/battle/stun_prod/start()
var/turf/source = random_far_turf()
- hallucinator.playsound_local(source, 'sound/weapons/egloves.ogg', 40, TRUE)
+ hallucinator.playsound_local(source, 'sound/items/weapons/egloves.ogg', 40, TRUE)
hallucinator.playsound_local(source, SFX_BODYFALL, 25, TRUE)
addtimer(CALLBACK(src, PROC_REF(fake_cuff), source), 2 SECONDS)
return TRUE
@@ -92,7 +92,7 @@
if(QDELETED(src) || QDELETED(hallucinator) || !source)
return
- hallucinator.playsound_local(source, 'sound/weapons/cablecuff.ogg', 15, TRUE)
+ hallucinator.playsound_local(source, 'sound/items/weapons/cablecuff.ogg', 15, TRUE)
qdel(src)
/// A hallucination of someone being stun batonned, and subsequently harmbatonned.
@@ -101,7 +101,7 @@
/datum/hallucination/battle/harm_baton/start()
var/turf/source = random_far_turf()
- hallucinator.playsound_local(source, 'sound/weapons/egloves.ogg', 40, TRUE)
+ hallucinator.playsound_local(source, 'sound/items/weapons/egloves.ogg', 40, TRUE)
hallucinator.playsound_local(source, SFX_BODYFALL, 25, TRUE)
addtimer(CALLBACK(src, PROC_REF(harmbaton_loop), source, rand(5, 12)), 2 SECONDS)
@@ -126,7 +126,7 @@
/datum/hallucination/battle/e_sword/start()
var/turf/source = random_far_turf()
- hallucinator.playsound_local(source, 'sound/weapons/saberon.ogg', 15, 1)
+ hallucinator.playsound_local(source, 'sound/items/weapons/saberon.ogg', 15, 1)
addtimer(CALLBACK(src, PROC_REF(stab_loop), source, rand(4, 8)), CLICK_CD_MELEE)
return TRUE
@@ -136,10 +136,10 @@
return
if(stabs_remaining >= 1)
- hallucinator.playsound_local(source, 'sound/weapons/blade1.ogg', 50, TRUE)
+ hallucinator.playsound_local(source, 'sound/items/weapons/blade1.ogg', 50, TRUE)
else
- hallucinator.playsound_local(source, 'sound/weapons/saberoff.ogg', 15, TRUE)
+ hallucinator.playsound_local(source, 'sound/items/weapons/saberoff.ogg', 15, TRUE)
qdel(src)
return
diff --git a/code/modules/hallucination/bolted_airlocks.dm b/code/modules/hallucination/bolted_airlocks.dm
index 9fb180dfcae11..a275c775d66ec 100644
--- a/code/modules/hallucination/bolted_airlocks.dm
+++ b/code/modules/hallucination/bolted_airlocks.dm
@@ -102,11 +102,11 @@
/obj/effect/client_image_holder/hallucination/fake_door_lock/show_image_to(mob/show_to)
. = ..()
- show_to.playsound_local(get_turf(src), 'sound/machines/boltsdown.ogg', 30, FALSE, 3)
+ show_to.playsound_local(get_turf(src), 'sound/machines/airlock/boltsdown.ogg', 30, FALSE, 3)
/obj/effect/client_image_holder/hallucination/fake_door_lock/hide_image_from(mob/show_to)
. = ..()
- show_to.playsound_local(get_turf(src), 'sound/machines/boltsup.ogg', 30, FALSE, 3)
+ show_to.playsound_local(get_turf(src), 'sound/machines/airlock/boltsup.ogg', 30, FALSE, 3)
/obj/effect/client_image_holder/hallucination/fake_door_lock/proc/on_airlock_deleted(datum/source)
SIGNAL_HANDLER
diff --git a/code/modules/hallucination/delusions.dm b/code/modules/hallucination/delusions.dm
index 106988f73277b..0760d05ff46c6 100644
--- a/code/modules/hallucination/delusions.dm
+++ b/code/modules/hallucination/delusions.dm
@@ -94,7 +94,7 @@
if(play_wabbajack)
to_chat(hallucinator, span_hear("...wabbajack...wabbajack..."))
- hallucinator.playsound_local(get_turf(hallucinator), 'sound/magic/staff_change.ogg', 50, TRUE)
+ hallucinator.playsound_local(get_turf(hallucinator), 'sound/effects/magic/staff_change.ogg', 50, TRUE)
if(duration > 0)
QDEL_IN(src, duration)
@@ -191,7 +191,7 @@
/datum/hallucination/delusion/preset/cyborg/make_delusion_image(mob/over_who)
. = ..()
- hallucinator.playsound_local(get_turf(over_who), 'sound/voice/liveagain.ogg', 75, TRUE)
+ hallucinator.playsound_local(get_turf(over_who), 'sound/mobs/non-humanoids/cyborg/liveagain.ogg', 75, TRUE)
/datum/hallucination/delusion/preset/ghost
delusion_icon_file = 'icons/mob/simple/mob.dmi'
diff --git a/code/modules/hallucination/fake_sound.dm b/code/modules/hallucination/fake_sound.dm
index f5d750a114427..1d93be83f947c 100644
--- a/code/modules/hallucination/fake_sound.dm
+++ b/code/modules/hallucination/fake_sound.dm
@@ -34,40 +34,40 @@
/datum/hallucination/fake_sound/normal/airlock
volume = 30
- sound_type = 'sound/machines/airlock.ogg'
+ sound_type = 'sound/machines/airlock/airlock.ogg'
/datum/hallucination/fake_sound/normal/airlock_pry
volume = 100
- sound_type = 'sound/machines/airlock_alien_prying.ogg'
+ sound_type = 'sound/machines/airlock/airlock_alien_prying.ogg'
/datum/hallucination/fake_sound/normal/airlock_pry/play_fake_sound(turf/source, sound_to_play)
. = ..()
- queue_fake_sound(source, 'sound/machines/airlockforced.ogg', 50, TRUE, delay = 5 SECONDS)
+ queue_fake_sound(source, 'sound/machines/airlock/airlockforced.ogg', 50, TRUE, delay = 5 SECONDS)
/datum/hallucination/fake_sound/normal/console
volume = 25
- sound_type = 'sound/machines/terminal_prompt.ogg'
+ sound_type = 'sound/machines/terminal/terminal_prompt.ogg'
/datum/hallucination/fake_sound/normal/boom
- sound_type = list('sound/effects/explosion1.ogg', 'sound/effects/explosion2.ogg')
+ sound_type = list('sound/effects/explosion/explosion1.ogg', 'sound/effects/explosion/explosion2.ogg')
/datum/hallucination/fake_sound/normal/distant_boom
- sound_type = 'sound/effects/explosionfar.ogg'
+ sound_type = 'sound/effects/explosion/explosionfar.ogg'
/datum/hallucination/fake_sound/normal/glass
- sound_type = list('sound/effects/glassbr1.ogg', 'sound/effects/glassbr2.ogg', 'sound/effects/glassbr3.ogg')
+ sound_type = list('sound/effects/glass/glassbr1.ogg', 'sound/effects/glass/glassbr2.ogg', 'sound/effects/glass/glassbr3.ogg')
/datum/hallucination/fake_sound/normal/alarm
- volume = 100
- sound_type = 'sound/machines/alarm.ogg'
+ volume = 70
+ sound_type = 'sound/announcer/alarm/nuke_alarm.ogg'
/datum/hallucination/fake_sound/normal/beepsky
volume = 35
- sound_type = 'sound/voice/beepsky/freeze.ogg'
+ sound_type = 'sound/mobs/non-humanoids/beepsky/freeze.ogg'
/datum/hallucination/fake_sound/normal/mech
volume = 40
- sound_type = 'sound/mecha/mechstep.ogg'
+ sound_type = 'sound/vehicles/mecha/mechstep.ogg'
/// The turf the mech started walking from.
var/turf/mech_source
/// What dir is the mech walking?
@@ -106,15 +106,15 @@
addtimer(CALLBACK(src, PROC_REF(mech_walk)), 1 SECONDS)
/datum/hallucination/fake_sound/normal/wall_deconstruction
- sound_type = 'sound/items/welder.ogg'
+ sound_type = 'sound/items/tools/welder.ogg'
/datum/hallucination/fake_sound/normal/wall_deconstruction/play_fake_sound(turf/source, sound_to_play)
. = ..()
- queue_fake_sound(source, 'sound/items/welder2.ogg', delay = 10.5 SECONDS)
- queue_fake_sound(source, 'sound/items/ratchet.ogg', delay = 12 SECONDS)
+ queue_fake_sound(source, 'sound/items/tools/welder2.ogg', delay = 10.5 SECONDS)
+ queue_fake_sound(source, 'sound/items/tools/ratchet.ogg', delay = 12 SECONDS)
/datum/hallucination/fake_sound/normal/door_hacking
- sound_type = 'sound/items/screwdriver.ogg'
+ sound_type = 'sound/items/tools/screwdriver.ogg'
volume = 30
/datum/hallucination/fake_sound/normal/door_hacking/play_fake_sound(turf/source, sound_to_play)
@@ -124,20 +124,20 @@
var/hacking_time = rand(4 SECONDS, 8 SECONDS)
// Multitool sound.
- queue_fake_sound(source, 'sound/weapons/empty.ogg', delay = 0.8 SECONDS)
+ queue_fake_sound(source, 'sound/items/weapons/empty.ogg', delay = 0.8 SECONDS)
if(hacking_time > 4.5 SECONDS)
// Another multitool sound if the hacking time is long.
- queue_fake_sound(source, 'sound/weapons/empty.ogg', delay = 3 SECONDS)
+ queue_fake_sound(source, 'sound/items/weapons/empty.ogg', delay = 3 SECONDS)
if(prob(50))
// Bonus multitool sound, rapidly after the last.
- queue_fake_sound(source, 'sound/weapons/empty.ogg', delay = 3.5 SECONDS)
+ queue_fake_sound(source, 'sound/items/weapons/empty.ogg', delay = 3.5 SECONDS)
if(hacking_time > 5.5 SECONDS)
// A final multitool sound if the hacking time is very long.
- queue_fake_sound(source, 'sound/weapons/empty.ogg', delay = 5 SECONDS)
+ queue_fake_sound(source, 'sound/items/weapons/empty.ogg', delay = 5 SECONDS)
// Crowbarring it open.
- queue_fake_sound(source, 'sound/machines/airlockforced.ogg', delay = hacking_time)
+ queue_fake_sound(source, 'sound/machines/airlock/airlockforced.ogg', delay = hacking_time)
/datum/hallucination/fake_sound/normal/steam
volume = 75
@@ -146,7 +146,7 @@
/datum/hallucination/fake_sound/normal/flash
random_hallucination_weight = 2 // "it's revs"
volume = 90
- sound_type = 'sound/weapons/flash.ogg'
+ sound_type = 'sound/items/weapons/flash.ogg'
/datum/hallucination/fake_sound/weird
abstract_hallucination_parent = /datum/hallucination/fake_sound/weird
@@ -167,24 +167,24 @@
sound_vary = FALSE
no_source = TRUE
sound_type = list(
- 'sound/ambience/antag/bloodcult/bloodcult_gain.ogg',
- 'sound/ambience/antag/clockcultalr.ogg',
- 'sound/ambience/antag/heretic/heretic_gain.ogg',
- 'sound/ambience/antag/ling_alert.ogg',
- 'sound/ambience/antag/malf.ogg',
- 'sound/ambience/antag/ops.ogg',
- 'sound/ambience/antag/spy.ogg',
- 'sound/ambience/antag/tatoralert.ogg',
+ 'sound/music/antag/bloodcult/bloodcult_gain.ogg',
+ 'sound/music/antag/clockcultalr.ogg',
+ 'sound/music/antag/heretic/heretic_gain.ogg',
+ 'sound/music/antag/ling_alert.ogg',
+ 'sound/music/antag/malf.ogg',
+ 'sound/music/antag/ops.ogg',
+ 'sound/music/antag/spy.ogg',
+ 'sound/music/antag/traitor/tatoralert.ogg',
)
/datum/hallucination/fake_sound/weird/chimp_event
volume = 90
sound_vary = FALSE
no_source = TRUE
- sound_type = 'sound/ambience/antag/monkey.ogg'
+ sound_type = 'sound/music/antag/monkey.ogg'
/datum/hallucination/fake_sound/weird/colossus
- sound_type = 'sound/magic/clockwork/invoke_general.ogg'
+ sound_type = 'sound/effects/magic/clockwork/invoke_general.ogg'
/datum/hallucination/fake_sound/weird/creepy
@@ -197,11 +197,11 @@
volume = 40
sound_vary = FALSE
no_source = TRUE
- sound_type = 'sound/magic/curse.ogg'
+ sound_type = 'sound/effects/magic/curse.ogg'
/datum/hallucination/fake_sound/weird/game_over
sound_vary = FALSE
- sound_type = 'sound/misc/compiler-failure.ogg'
+ sound_type = 'sound/machines/compiler/compiler-failure.ogg'
/datum/hallucination/fake_sound/weird/hallelujah
sound_vary = FALSE
@@ -219,15 +219,15 @@
/datum/hallucination/fake_sound/weird/laugher
sound_type = list(
- 'sound/voice/human/womanlaugh.ogg',
- 'sound/voice/human/manlaugh1.ogg',
- 'sound/voice/human/manlaugh2.ogg',
+ 'sound/mobs/humanoids/human/laugh/womanlaugh.ogg',
+ 'sound/mobs/humanoids/human/laugh/manlaugh1.ogg',
+ 'sound/mobs/humanoids/human/laugh/manlaugh2.ogg',
)
/datum/hallucination/fake_sound/weird/phone
volume = 15
sound_vary = FALSE
- sound_type = 'sound/weapons/ring.ogg'
+ sound_type = 'sound/items/weapons/ring.ogg'
/datum/hallucination/fake_sound/weird/phone/play_fake_sound(turf/source, sound_to_play)
for(var/next_ring in 1 to 3)
@@ -237,25 +237,25 @@
/datum/hallucination/fake_sound/weird/spell
sound_type = list(
- 'sound/magic/disintegrate.ogg',
- 'sound/magic/ethereal_enter.ogg',
- 'sound/magic/ethereal_exit.ogg',
- 'sound/magic/fireball.ogg',
- 'sound/magic/forcewall.ogg',
- 'sound/magic/teleport_app.ogg',
- 'sound/magic/teleport_diss.ogg',
+ 'sound/effects/magic/disintegrate.ogg',
+ 'sound/effects/magic/ethereal_enter.ogg',
+ 'sound/effects/magic/ethereal_exit.ogg',
+ 'sound/effects/magic/fireball.ogg',
+ 'sound/effects/magic/forcewall.ogg',
+ 'sound/effects/magic/teleport_app.ogg',
+ 'sound/effects/magic/teleport_diss.ogg',
)
/datum/hallucination/fake_sound/weird/spell/just_jaunt // A few antags use jaunts, so this sound specifically is fun to isolate
- sound_type = 'sound/magic/ethereal_enter.ogg'
+ sound_type = 'sound/effects/magic/ethereal_enter.ogg'
/datum/hallucination/fake_sound/weird/summon_sound // Heretic circle sound, notably
volume = 75
- sound_type = 'sound/magic/castsummon.ogg'
+ sound_type = 'sound/effects/magic/castsummon.ogg'
/datum/hallucination/fake_sound/weird/tesloose
volume = 35
- sound_type = 'sound/magic/lightningbolt.ogg'
+ sound_type = 'sound/effects/magic/lightningbolt.ogg'
/datum/hallucination/fake_sound/weird/tesloose/play_fake_sound(turf/source, sound_to_play)
. = ..()
@@ -266,21 +266,21 @@
random_hallucination_weight = 2 // Some of these are ambience sounds too
volume = 25
sound_type = list(
- 'sound/voice/lowHiss1.ogg',
- 'sound/voice/lowHiss2.ogg',
- 'sound/voice/lowHiss3.ogg',
- 'sound/voice/lowHiss4.ogg',
- 'sound/voice/hiss1.ogg',
- 'sound/voice/hiss2.ogg',
- 'sound/voice/hiss3.ogg',
- 'sound/voice/hiss4.ogg',
+ 'sound/mobs/non-humanoids/hiss/lowHiss1.ogg',
+ 'sound/mobs/non-humanoids/hiss/lowHiss2.ogg',
+ 'sound/mobs/non-humanoids/hiss/lowHiss3.ogg',
+ 'sound/mobs/non-humanoids/hiss/lowHiss4.ogg',
+ 'sound/mobs/non-humanoids/hiss/hiss1.ogg',
+ 'sound/mobs/non-humanoids/hiss/hiss2.ogg',
+ 'sound/mobs/non-humanoids/hiss/hiss3.ogg',
+ 'sound/mobs/non-humanoids/hiss/hiss4.ogg',
)
/datum/hallucination/fake_sound/weird/radio_static
volume = 75
no_source = TRUE
sound_vary = FALSE
- sound_type = 'sound/hallucinations/radio_static.ogg'
+ sound_type = 'sound/effects/hallucinations/radio_static.ogg'
/datum/hallucination/fake_sound/weird/ice_crack
random_hallucination_weight = 2
diff --git a/code/modules/hallucination/inhand_fake_item.dm b/code/modules/hallucination/inhand_fake_item.dm
index ba791f3a56c7c..de3b6b99411e9 100644
--- a/code/modules/hallucination/inhand_fake_item.dm
+++ b/code/modules/hallucination/inhand_fake_item.dm
@@ -72,7 +72,7 @@
var/obj/item/melee/energy/sword/saber/sabre_color = pick(subtypesof(/obj/item/melee/energy/sword/saber))
// Yes this can break if someone changes esword icon stuff
hallucinated_item.icon_state = "[hallucinated_item.icon_state]_on_[initial(sabre_color.sword_color_icon)]"
- hallucinator.playsound_local(get_turf(hallucinator), 'sound/weapons/saberon.ogg', 35, TRUE)
+ hallucinator.playsound_local(get_turf(hallucinator), 'sound/items/weapons/saberon.ogg', 35, TRUE)
return hallucinated_item
@@ -109,7 +109,7 @@
if(prob(15))
// Yes this can break if someone changse grenade icon stuff
hallucinated_item.icon_state = "[hallucinated_item.icon_state]_active"
- hallucinator.playsound_local(get_turf(hallucinator), 'sound/weapons/armbomb.ogg', 60, TRUE)
+ hallucinator.playsound_local(get_turf(hallucinator), 'sound/items/weapons/armbomb.ogg', 60, TRUE)
to_chat(hallucinator, span_warning("You prime [hallucinated_item]! 5 seconds!"))
return hallucinated_item
diff --git a/code/modules/hallucination/mother.dm b/code/modules/hallucination/mother.dm
index d9cd7f1983119..7d407e43d8eb1 100644
--- a/code/modules/hallucination/mother.dm
+++ b/code/modules/hallucination/mother.dm
@@ -37,7 +37,7 @@
var/obj/visual = image('icons/hud/screen_gen.dmi', mother.loc, "arrow", FLY_LAYER)
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay_global), visual, list(hallucinator.client), 2.5 SECONDS)
- animate(visual, pixel_x = (tile.x - mother.x) * world.icon_size, pixel_y = (tile.y - mother.y) * world.icon_size, time = 1.7, easing = EASE_OUT)
+ animate(visual, pixel_x = (tile.x - mother.x) * ICON_SIZE_X, pixel_y = (tile.y - mother.y) * ICON_SIZE_Y, time = 1.7, easing = EASE_OUT)
/datum/hallucination/your_mother/proc/talk(text)
var/plus_runechat = hallucinator.client?.prefs.read_preference(/datum/preference/toggle/enable_runechat)
diff --git a/code/modules/hallucination/nearby_fake_item.dm b/code/modules/hallucination/nearby_fake_item.dm
index 4864594c9a50b..10d08ee47c96f 100644
--- a/code/modules/hallucination/nearby_fake_item.dm
+++ b/code/modules/hallucination/nearby_fake_item.dm
@@ -67,12 +67,12 @@
image_icon_state = "e_sword_on_red"
/datum/hallucination/nearby_fake_item/e_sword/generate_fake_image(mob/living/carbon/human/holder, file)
- hallucinator.playsound_local(get_turf(holder), 'sound/weapons/saberon.ogg', 35, TRUE)
+ hallucinator.playsound_local(get_turf(holder), 'sound/items/weapons/saberon.ogg', 35, TRUE)
return ..()
/datum/hallucination/nearby_fake_item/e_sword/remove_image(mob/living/carbon/human/holder)
if(!QDELETED(holder))
- hallucinator.playsound_local(get_turf(holder), 'sound/weapons/saberoff.ogg', 35, TRUE)
+ hallucinator.playsound_local(get_turf(holder), 'sound/items/weapons/saberoff.ogg', 35, TRUE)
return ..()
/datum/hallucination/nearby_fake_item/e_sword/double_bladed
@@ -115,12 +115,12 @@
image_icon_state = "arm_blade"
/datum/hallucination/nearby_fake_item/armblade/generate_fake_image(mob/living/carbon/human/holder, file)
- hallucinator.playsound_local(get_turf(holder), 'sound/effects/blobattack.ogg', 35, TRUE)
+ hallucinator.playsound_local(get_turf(holder), 'sound/effects/blob/blobattack.ogg', 35, TRUE)
return ..()
/datum/hallucination/nearby_fake_item/armblade/remove_image(mob/living/carbon/human/holder)
if(!QDELETED(holder))
- hallucinator.playsound_local(get_turf(holder), 'sound/effects/blobattack.ogg', 35, TRUE)
+ hallucinator.playsound_local(get_turf(holder), 'sound/effects/blob/blobattack.ogg', 35, TRUE)
return ..()
/datum/hallucination/nearby_fake_item/ttv
diff --git a/code/modules/hallucination/station_message.dm b/code/modules/hallucination/station_message.dm
index 976b88f662097..9441cdeb42a5a 100644
--- a/code/modules/hallucination/station_message.dm
+++ b/code/modules/hallucination/station_message.dm
@@ -42,23 +42,23 @@
var/static/list/ascension_bodies = list(
list(
"text" = "Fear the blaze, for the Ashlord, %FAKENAME% has ascended! The flames shall consume all!",
- "sound" = 'sound/ambience/antag/heretic/ascend_blade.ogg',
+ "sound" = 'sound/music/antag/heretic/ascend_blade.ogg',
),
list(
"text" = "Master of blades, the Torn Champion's disciple, %FAKENAME% has ascended! Their steel is that which will cut reality in a maelstom of silver!",
- "sound" = 'sound/ambience/antag/heretic/ascend_blade.ogg',
+ "sound" = 'sound/music/antag/heretic/ascend_blade.ogg',
),
list(
"text" = "Ever coiling vortex. Reality unfolded. ARMS OUTREACHED, THE LORD OF THE NIGHT, %FAKENAME% has ascended! Fear the ever twisting hand!",
- "sound" = 'sound/ambience/antag/heretic/ascend_flesh.ogg',
+ "sound" = 'sound/music/antag/heretic/ascend_flesh.ogg',
),
list(
"text" = "Fear the decay, for the Rustbringer, %FAKENAME% has ascended! None shall escape the corrosion!",
- "sound" = 'sound/ambience/antag/heretic/ascend_rust.ogg',
+ "sound" = 'sound/music/antag/heretic/ascend_rust.ogg',
),
list(
"text" = "The nobleman of void %FAKENAME% has arrived, stepping along the Waltz that ends worlds!",
- "sound" = 'sound/ambience/antag/heretic/ascend_void.ogg',
+ "sound" = 'sound/music/antag/heretic/ascend_void.ogg',
)
)
@@ -95,7 +95,7 @@
priority_announce(
text = "Figments from an eldritch god are being summoned by [totally_real_cult_leader.real_name] into [fake_summon_area] from an unknown dimension. Disrupt the ritual at all costs!",
title = "[command_name()] Higher Dimensional Affairs",
- sound = 'sound/ambience/antag/bloodcult/bloodcult_scribe.ogg',
+ sound = 'sound/music/antag/bloodcult/bloodcult_scribe.ogg',
has_important_message = TRUE,
players = list(hallucinator),
)
@@ -111,7 +111,7 @@
/datum/hallucination/station_message/supermatter_delam
/datum/hallucination/station_message/supermatter_delam/start()
- SEND_SOUND(hallucinator, 'sound/magic/charge.ogg')
+ SEND_SOUND(hallucinator, 'sound/effects/magic/charge.ogg')
to_chat(hallucinator, span_boldannounce("You feel reality distort for a moment..."))
return ..()
@@ -129,5 +129,5 @@
if(QDELETED(src))
return
- hallucinator.playsound_local(get_turf(hallucinator), 'sound/effects/explosion_distant.ogg', 50, FALSE, pressure_affected = FALSE)
+ hallucinator.playsound_local(get_turf(hallucinator), 'sound/effects/explosion/explosion_distant.ogg', 50, FALSE, pressure_affected = FALSE)
qdel(src)
diff --git a/code/modules/hallucination/stray_bullet.dm b/code/modules/hallucination/stray_bullet.dm
index 63e19c1bb89f2..13ace2933350a 100644
--- a/code/modules/hallucination/stray_bullet.dm
+++ b/code/modules/hallucination/stray_bullet.dm
@@ -189,7 +189,7 @@
name = "bullet"
hal_icon_state = "bullet"
hal_fire_sound = "gunshot"
- hal_hitsound = 'sound/weapons/pierce.ogg'
+ hal_hitsound = 'sound/items/weapons/pierce.ogg'
hal_hitsound_wall = SFX_RICOCHET
hal_impact_effect = "impact_bullet"
hal_impact_effect_wall = "impact_bullet"
@@ -203,9 +203,9 @@
name = "laser"
damage_type = BURN
hal_icon_state = "laser"
- hal_fire_sound = 'sound/weapons/laser.ogg'
- hal_hitsound = 'sound/weapons/sear.ogg'
- hal_hitsound_wall = 'sound/weapons/effects/searwall.ogg'
+ hal_fire_sound = 'sound/items/weapons/laser.ogg'
+ hal_hitsound = 'sound/items/weapons/sear.ogg'
+ hal_hitsound_wall = 'sound/items/weapons/effects/searwall.ogg'
hal_impact_effect = "impact_laser"
hal_impact_effect_wall = "impact_laser_wall"
hit_duration = 4
@@ -225,8 +225,8 @@
damage_type = BURN
hal_icon_state = "spark"
color = COLOR_YELLOW
- hal_fire_sound = 'sound/weapons/taser.ogg'
- hal_hitsound = 'sound/weapons/taserhit.ogg'
+ hal_fire_sound = 'sound/items/weapons/taser.ogg'
+ hal_hitsound = 'sound/items/weapons/taserhit.ogg'
hal_hitsound_wall = null
hal_impact_effect = null
hal_impact_effect_wall = null
@@ -250,9 +250,9 @@
name = "disabler beam"
damage_type = STAMINA
hal_icon_state = "omnilaser"
- hal_fire_sound = 'sound/weapons/taser2.ogg'
- hal_hitsound = 'sound/weapons/tap.ogg'
- hal_hitsound_wall = 'sound/weapons/effects/searwall.ogg'
+ hal_fire_sound = 'sound/items/weapons/taser2.ogg'
+ hal_hitsound = 'sound/items/weapons/tap.ogg'
+ hal_hitsound_wall = 'sound/items/weapons/effects/searwall.ogg'
hal_impact_effect = "impact_laser_blue"
hal_impact_effect_wall = null
hit_duration = 4
@@ -269,7 +269,7 @@
name = "bolt"
damage_type = TOX
hal_icon_state = "cbbolt"
- hal_fire_sound = 'sound/weapons/genhit.ogg'
+ hal_fire_sound = 'sound/items/weapons/genhit.ogg'
hal_hitsound = null
hal_hitsound_wall = null
hal_impact_effect = null
@@ -285,7 +285,7 @@
name = "bolt of change"
damage_type = BURN
hal_icon_state = "ice_1"
- hal_fire_sound = 'sound/magic/staff_change.ogg'
+ hal_fire_sound = 'sound/effects/magic/staff_change.ogg'
hal_hitsound = null
hal_hitsound_wall = null
hal_impact_effect = null
@@ -307,7 +307,7 @@
name = "bolt of death"
damage_type = BURN
hal_icon_state = "pulse1_bl"
- hal_fire_sound = 'sound/magic/wandodeath.ogg'
+ hal_fire_sound = 'sound/effects/magic/wandodeath.ogg'
hal_hitsound = null
hal_hitsound_wall = null
hal_impact_effect = null
diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm
index 6fe7fdbc57d94..d5a9457141294 100644
--- a/code/modules/holiday/holidays.dm
+++ b/code/modules/holiday/holidays.dm
@@ -257,7 +257,7 @@
/datum/holiday/april_fools/celebrate()
. = ..()
SSjob.set_overflow_role(/datum/job/clown)
- SSticker.login_music = 'sound/ambience/clown.ogg'
+ SSticker.login_music = 'sound/music/lobby_music/clown.ogg'
for(var/i in GLOB.new_player_list)
var/mob/dead/new_player/P = i
if(P.client)
@@ -694,6 +694,10 @@
begin_day = 14
begin_month = DECEMBER
+/datum/holiday/monkey/celebrate()
+ . = ..()
+ SSstation.setup_trait(/datum/station_trait/job/pun_pun)
+
/datum/holiday/doomsday
name = "Mayan Doomsday Anniversary"
begin_day = 21
diff --git a/code/modules/hydroponics/beekeeping/bee_smoker.dm b/code/modules/hydroponics/beekeeping/bee_smoker.dm
index 91195dacc84d7..3daa75f89e63a 100644
--- a/code/modules/hydroponics/beekeeping/bee_smoker.dm
+++ b/code/modules/hydroponics/beekeeping/bee_smoker.dm
@@ -37,13 +37,17 @@
return TRUE
/obj/item/bee_smoker/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!istype(interacting_with, /obj/structure/beebox) && !isturf(interacting_with) && !istype(interacting_with, /mob/living/basic/bee))
+ return NONE
+
+ . = ITEM_INTERACT_BLOCKING
if(!activated)
user.balloon_alert(user, "not activated!")
- return ITEM_INTERACT_BLOCKING
+ return .
if(current_herb_fuel < single_use_cost)
user.balloon_alert(user, "not enough fuel!")
- return ITEM_INTERACT_BLOCKING
+ return .
current_herb_fuel -= single_use_cost
playsound(src, 'sound/effects/spray2.ogg', 100, TRUE)
@@ -53,16 +57,19 @@
if(friend.flags_1 & HOLOGRAM_1)
continue
friend.befriend(user)
+ . = ITEM_INTERACT_SUCCESS
if(!istype(interacting_with, /obj/structure/beebox))
- return ITEM_INTERACT_BLOCKING
+ return .
var/obj/structure/beebox/hive = interacting_with
for(var/mob/living/bee as anything in hive.bees)
if(bee.flags_1 & HOLOGRAM_1)
continue
bee.befriend(user)
- return ITEM_INTERACT_SUCCESS
+ . = ITEM_INTERACT_SUCCESS
+
+ return .
/obj/item/bee_smoker/attackby(obj/item/herb, mob/living/carbon/human/user, list/modifiers)
. = ..()
@@ -89,7 +96,7 @@
/obj/item/bee_smoker/proc/alter_state()
activated = !activated
- playsound(src, 'sound/items/welderdeactivate.ogg', 50, TRUE)
+ playsound(src, 'sound/items/tools/welderdeactivate.ogg', 50, TRUE)
if(!activated)
beesmoke_loop.stop()
diff --git a/code/modules/hydroponics/fermenting_barrel.dm b/code/modules/hydroponics/fermenting_barrel.dm
index 49b7056c9e7e8..993173b7efc52 100644
--- a/code/modules/hydroponics/fermenting_barrel.dm
+++ b/code/modules/hydroponics/fermenting_barrel.dm
@@ -23,7 +23,7 @@
/// The sound of fermentation
var/datum/looping_sound/boiling/soundloop
/// Sound played when the lid is opened.
- var/lid_open_sound = 'sound/items/handling/cardboardbox_pickup.ogg'
+ var/lid_open_sound = 'sound/items/handling/cardboard_box/cardboardbox_pickup.ogg'
/// Sound played when the lid is closed.
var/lid_close_sound = 'sound/effects/footstep/woodclaw2.ogg'
diff --git a/code/modules/hydroponics/grown/beans.dm b/code/modules/hydroponics/grown/beans.dm
index 885eb77c0c695..62b18c8eea703 100644
--- a/code/modules/hydroponics/grown/beans.dm
+++ b/code/modules/hydroponics/grown/beans.dm
@@ -53,7 +53,7 @@
//Now squeezable for imitation carpmeat
/obj/item/food/grown/koibeans/attack_self(mob/living/user)
user.visible_message(span_notice("[user] crushes [src] into a slab of carplike meat."), span_notice("You crush [src] into something that resembles a slab of carplike meat."))
- playsound(user, 'sound/effects/blobattack.ogg', 50, TRUE)
+ playsound(user, 'sound/effects/blob/blobattack.ogg', 50, TRUE)
var/obj/item/food/fishmeat/carp/imitation/fishie = new(null)
fishie.reagents.set_all_reagents_purity(seed.get_reagent_purity())
qdel(src)
@@ -85,7 +85,7 @@
/obj/item/food/grown/butterbeans/attack_self(mob/living/user)
user.visible_message(span_notice("[user] crushes [src] into a pat of butter."), span_notice("You crush [src] into something that resembles butter."))
- playsound(user, 'sound/effects/blobattack.ogg', 50, TRUE)
+ playsound(user, 'sound/effects/blob/blobattack.ogg', 50, TRUE)
var/obj/item/food/butterslice/butties = new(null)
butties.reagents.set_all_reagents_purity(seed.get_reagent_purity())
qdel(src)
diff --git a/code/modules/hydroponics/grown/cereals.dm b/code/modules/hydroponics/grown/cereals.dm
index b304382d2dc47..f1fdfe807ef1d 100644
--- a/code/modules/hydroponics/grown/cereals.dm
+++ b/code/modules/hydroponics/grown/cereals.dm
@@ -100,7 +100,7 @@
/obj/item/food/grown/meatwheat/attack_self(mob/living/user)
user.visible_message(span_notice("[user] crushes [src] into meat."), span_notice("You crush [src] into something that resembles meat."))
- playsound(user, 'sound/effects/blobattack.ogg', 50, TRUE)
+ playsound(user, 'sound/effects/blob/blobattack.ogg', 50, TRUE)
var/obj/item/food/meat/slab/meatwheat/meaties = new(null)
meaties.reagents.set_all_reagents_purity(seed.get_reagent_purity())
qdel(src)
diff --git a/code/modules/hydroponics/grown/onion.dm b/code/modules/hydroponics/grown/onion.dm
index 0d33c3e1f395d..4287bf9eb3ec9 100644
--- a/code/modules/hydroponics/grown/onion.dm
+++ b/code/modules/hydroponics/grown/onion.dm
@@ -48,7 +48,7 @@
/obj/item/food/grown/onion/red/make_processable()
AddElement(/datum/element/processable, TOOL_KNIFE, /obj/item/food/onion_slice/red, 2, 15, screentip_verb = "Cut")
-/obj/item/food/grown/onion/UsedforProcessing(mob/living/user, obj/item/I, list/chosen_option)
+/obj/item/food/grown/onion/UsedforProcessing(mob/living/user, obj/item/I, list/chosen_option, list/created_atoms)
var/datum/effect_system/fluid_spread/smoke/chem/cry_about_it = new //Since the onion is destroyed when it's sliced,
var/splat_location = get_turf(src) //we need to set up the smoke beforehand
cry_about_it.attach(splat_location)
diff --git a/code/modules/hydroponics/grown/pineapple.dm b/code/modules/hydroponics/grown/pineapple.dm
index 5de85e9168f1f..577befaadfaa8 100644
--- a/code/modules/hydroponics/grown/pineapple.dm
+++ b/code/modules/hydroponics/grown/pineapple.dm
@@ -23,7 +23,7 @@
bite_consumption_mod = 2
force = 4
throwforce = 8
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
attack_verb_continuous = list("stings", "pines")
attack_verb_simple = list("sting", "pine")
throw_speed = 1
diff --git a/code/modules/hydroponics/grown/weeds/nettle.dm b/code/modules/hydroponics/grown/weeds/nettle.dm
index 33a0f6288912d..dec1e8b119e2b 100644
--- a/code/modules/hydroponics/grown/weeds/nettle.dm
+++ b/code/modules/hydroponics/grown/weeds/nettle.dm
@@ -43,7 +43,7 @@
righthand_file = 'icons/mob/inhands/weapons/plants_righthand.dmi'
damtype = BURN
force = 15
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
throwforce = 5
w_class = WEIGHT_CLASS_NORMAL
throw_speed = 1
diff --git a/code/modules/hydroponics/hydroitemdefines.dm b/code/modules/hydroponics/hydroitemdefines.dm
index 9b2cbdfae40fb..1251cb54d32b9 100644
--- a/code/modules/hydroponics/hydroitemdefines.dm
+++ b/code/modules/hydroponics/hydroitemdefines.dm
@@ -428,7 +428,7 @@
custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT*0.5)
attack_verb_continuous = list("slashes", "slices", "cuts", "claws")
attack_verb_simple = list("slash", "slice", "cut", "claw")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
/obj/item/cultivator/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is scratching [user.p_their()] back as hard as [user.p_they()] can with \the [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
@@ -460,7 +460,7 @@
if(has_gravity(loc) && HAS_TRAIT(H, TRAIT_CLUMSY) && !H.resting)
H.set_confusion_if_lower(10 SECONDS)
H.Stun(20)
- playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/punch4.ogg', 50, TRUE)
H.visible_message(span_warning("[H] steps on [src] causing the handle to hit [H.p_them()] right in the face!"), \
span_userdanger("You step on [src] causing the handle to hit you right in the face!"))
@@ -482,7 +482,7 @@
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*7.5)
attack_verb_continuous = list("chops", "tears", "lacerates", "cuts")
attack_verb_simple = list("chop", "tear", "lacerate", "cut")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
sharpness = SHARP_EDGED
/datum/embed_data/hatchet
@@ -499,7 +499,7 @@
/obj/item/hatchet/suicide_act(mob/living/user)
user.visible_message(span_suicide("[user] is chopping at [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!"))
- playsound(src, 'sound/weapons/bladeslice.ogg', 50, TRUE, -1)
+ playsound(src, 'sound/items/weapons/bladeslice.ogg', 50, TRUE, -1)
return BRUTELOSS
/obj/item/hatchet/wooden
@@ -528,7 +528,7 @@
slot_flags = ITEM_SLOT_BACK
attack_verb_continuous = list("chops", "slices", "cuts", "reaps")
attack_verb_simple = list("chop", "slice", "cut", "reap")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
sharpness = SHARP_EDGED
item_flags = CRUEL_IMPLEMENT //maybe they want to use it in surgery
var/swiping = FALSE
@@ -589,7 +589,7 @@
custom_materials = list(/datum/material/iron= SHEET_MATERIAL_AMOUNT*2)
attack_verb_continuous = list("slashes", "slices", "cuts", "claws")
attack_verb_simple = list("slash", "slice", "cut", "claw")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
///Catch right clicks so we can stylize!
/obj/item/secateurs/pre_attack_secondary(atom/target, mob/living/user, params)
@@ -603,7 +603,7 @@
SEND_SIGNAL(target, COMSIG_ATOM_RESTYLE, user, target, user.zone_selected, EXTERNAL_RESTYLE_PLANT, 6 SECONDS)
/obj/item/geneshears
- name = "Botanogenetic Plant Shears"
+ name = "botanogenetic plant shears"
desc = "A high tech, high fidelity pair of plant shears, capable of cutting genetic traits out of a plant."
icon = 'icons/obj/service/hydroponics/equipment.dmi'
icon_state = "genesheers"
@@ -619,7 +619,7 @@
custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT*2, /datum/material/uranium=HALF_SHEET_MATERIAL_AMOUNT * 1.5, /datum/material/gold=SMALL_MATERIAL_AMOUNT*5)
attack_verb_continuous = list("slashes", "slices", "cuts")
attack_verb_simple = list("slash", "slice", "cut")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
// *************************************
// Nutrient defines for hydroponics
diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm
index 041ff97b73cee..3a1a6d83e7dbf 100644
--- a/code/modules/hydroponics/hydroponics.dm
+++ b/code/modules/hydroponics/hydroponics.dm
@@ -139,8 +139,8 @@
context[SCREENTIP_CONTEXT_LMB] = "Lock mutation"
return CONTEXTUAL_SCREENTIP_SET
- // Edibles and pills can be composted.
- if(IS_EDIBLE(held_item) || istype(held_item, /obj/item/reagent_containers/pill))
+ // Edibles can be composted (most of the times).
+ if(IS_EDIBLE(held_item) && HAS_TRAIT(held_item, TRAIT_UNCOMPOSTABLE))
context[SCREENTIP_CONTEXT_LMB] = "Compost"
return CONTEXTUAL_SCREENTIP_SET
@@ -532,6 +532,7 @@
if(myseed && myseed.loc != src)
myseed.forceMove(src)
SEND_SIGNAL(src, COMSIG_HYDROTRAY_SET_SEED, new_seed)
+ age = 0
update_appearance()
if(isnull(myseed))
particles = null
@@ -700,7 +701,6 @@
else
new_seed = new /obj/item/seeds/starthistle(src)
set_seed(new_seed)
- age = 0
lastcycle = world.time
set_plant_health(myseed.endurance, update_icon = FALSE)
set_weedlevel(0, update_icon = FALSE) // Reset
@@ -724,7 +724,6 @@
set_seed(new mutantseed(src))
hardmutate()
- age = 0
set_plant_health(myseed.endurance, update_icon = FALSE)
lastcycle = world.time
set_weedlevel(0, update_icon = FALSE)
@@ -741,7 +740,6 @@
set_seed(new polymorph_seed(src))
hardmutate()
- age = 0
set_plant_health(myseed.endurance, update_icon = FALSE)
lastcycle = world.time
set_weedlevel(0, update_icon = FALSE)
@@ -755,7 +753,6 @@
var/newWeed = pick(/obj/item/seeds/liberty, /obj/item/seeds/angel, /obj/item/seeds/nettle/death, /obj/item/seeds/kudzu)
set_seed(new newWeed(src))
hardmutate()
- age = 0
set_plant_health(myseed.endurance, update_icon = FALSE)
lastcycle = world.time
set_weedlevel(0, update_icon = FALSE) // Reset
@@ -855,7 +852,10 @@
var/visi_msg = ""
var/transfer_amount
- if(IS_EDIBLE(reagent_source) || istype(reagent_source, /obj/item/reagent_containers/pill))
+ if(IS_EDIBLE(reagent_source))
+ if(HAS_TRAIT(reagent_source, TRAIT_UNCOMPOSTABLE))
+ to_chat(user, "[reagent_source] cannot be composted in its current state")
+ return
visi_msg="[user] composts [reagent_source], spreading it through [target]"
transfer_amount = reagent_source.reagents.total_volume
SEND_SIGNAL(reagent_source, COMSIG_ITEM_ON_COMPOSTED, user)
@@ -907,7 +907,6 @@
SEND_SIGNAL(O, COMSIG_SEED_ON_PLANTED, src)
to_chat(user, span_notice("You plant [O]."))
set_seed(O)
- age = 1
set_plant_health(myseed.endurance)
lastcycle = world.time
return
@@ -1012,7 +1011,6 @@
if(O.use_tool(src, user, 50, volume=50) || (!myseed && !weedlevel))
user.visible_message(span_notice("[user] digs out the plants in [src]!"), span_notice("You dig out all of [src]'s plants!"))
if(myseed) //Could be that they're just using it as a de-weeder
- age = 0
set_plant_health(0, update_icon = FALSE, forced = TRUE)
lastproduce = 0
set_seed(null)
@@ -1087,10 +1085,14 @@
/obj/machinery/hydroponics/click_ctrl(mob/user)
if(!anchored)
return NONE
+
+ update_use_power(ACTIVE_POWER_USE)
+
if(!powered())
to_chat(user, span_warning("[name] has no power."))
update_use_power(NO_POWER_USE)
return CLICK_ACTION_BLOCKING
+
set_self_sustaining(!self_sustaining)
to_chat(user, span_notice("You [self_sustaining ? "activate" : "deactivated"] [src]'s autogrow function[self_sustaining ? ", maintaining the tray's health while using high amounts of power" : ""]."))
return CLICK_ACTION_SUCCESS
diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm
index f59adad783968..79dd725b6e354 100644
--- a/code/modules/hydroponics/plant_genes.dm
+++ b/code/modules/hydroponics/plant_genes.dm
@@ -202,7 +202,7 @@
return
RegisterSignal(our_plant, COMSIG_PLANT_ON_SLIP, PROC_REF(squash_plant))
- RegisterSignal(our_plant, COMSIG_MOVABLE_IMPACT, PROC_REF(squash_plant))
+ RegisterSignal(our_plant, COMSIG_MOVABLE_IMPACT, PROC_REF(squash_plant_if_not_caught))
RegisterSignal(our_plant, COMSIG_ITEM_ATTACK_SELF, PROC_REF(squash_plant))
/*
@@ -239,6 +239,10 @@
qdel(our_plant)
+/datum/plant_gene/trait/squash/proc/squash_plant_if_not_caught(datum/source, atom/hit_atom, datum/thrownthing/throwing_datum, caught)
+ if(!caught)
+ squash_plant(source, hit_atom)
+
/*
* Makes plant slippery, unless it has a grown-type trash. Then the trash gets slippery.
* Applies other trait effects (teleporting, etc) to the target by signal.
@@ -798,7 +802,7 @@
icon = "face-laugh-squint"
mutability_flags = PLANT_GENE_REMOVABLE | PLANT_GENE_MUTATABLE | PLANT_GENE_GRAFTABLE
/// Sounds that play when this trait triggers
- var/list/sounds = list('sound/items/SitcomLaugh1.ogg', 'sound/items/SitcomLaugh2.ogg', 'sound/items/SitcomLaugh3.ogg')
+ var/list/sounds = list('sound/items/sitcom_laugh/sitcomLaugh1.ogg', 'sound/items/sitcom_laugh/sitcomLaugh2.ogg', 'sound/items/sitcom_laugh/sitcomLaugh3.ogg')
/datum/plant_gene/trait/plant_laughter/on_new_plant(obj/item/our_plant, newloc)
. = ..()
diff --git a/code/modules/hydroponics/seeds.dm b/code/modules/hydroponics/seeds.dm
index 18197c006b51e..86847f011738b 100644
--- a/code/modules/hydroponics/seeds.dm
+++ b/code/modules/hydroponics/seeds.dm
@@ -470,7 +470,7 @@
return
switch(choice)
if("Plant Name")
- var/newplantname = reject_bad_text(tgui_input_text(user, "Write a new plant name", "Plant Name", plantname, 20))
+ var/newplantname = reject_bad_text(tgui_input_text(user, "Write a new plant name", "Plant Name", plantname, max_length = MAX_NAME_LEN))
if(isnull(newplantname))
return
if(!user.can_perform_action(src))
@@ -478,7 +478,7 @@
name = "[LOWER_TEXT(newplantname)]"
plantname = newplantname
if("Seed Description")
- var/newdesc = tgui_input_text(user, "Write a new seed description", "Seed Description", desc, 180)
+ var/newdesc = tgui_input_text(user, "Write a new seed description", "Seed Description", desc, max_length = MAX_DESC_LEN)
if(isnull(newdesc))
return
if(!user.can_perform_action(src))
@@ -487,7 +487,7 @@
if("Product Description")
if(product && !productdesc)
productdesc = initial(product.desc)
- var/newproductdesc = tgui_input_text(user, "Write a new product description", "Product Description", productdesc, 180)
+ var/newproductdesc = tgui_input_text(user, "Write a new product description", "Product Description", productdesc, max_length = MAX_DESC_LEN)
if(isnull(newproductdesc))
return
if(!user.can_perform_action(src))
diff --git a/code/modules/hydroponics/unique_plant_genes.dm b/code/modules/hydroponics/unique_plant_genes.dm
index c3855ff6939e8..eef79ded735c5 100644
--- a/code/modules/hydroponics/unique_plant_genes.dm
+++ b/code/modules/hydroponics/unique_plant_genes.dm
@@ -593,7 +593,7 @@
else
our_plant.color = COLOR_RED
- playsound(our_plant.drop_location(), 'sound/weapons/armbomb.ogg', 75, TRUE, -3)
+ playsound(our_plant.drop_location(), 'sound/items/weapons/armbomb.ogg', 75, TRUE, -3)
addtimer(CALLBACK(src, PROC_REF(detonate), our_plant), rand(1 SECONDS, 6 SECONDS))
/datum/plant_gene/trait/bomb_plant/potency_based/detonate(obj/item/our_plant)
diff --git a/code/modules/instruments/instrument_data/fun.dm b/code/modules/instruments/instrument_data/fun.dm
index 68a88683fccd3..52b88295ea861 100644
--- a/code/modules/instruments/instrument_data/fun.dm
+++ b/code/modules/instruments/instrument_data/fun.dm
@@ -38,11 +38,11 @@
/datum/instrument/fun/mothscream
name = "Moth Scream"
id = "mothscream"
- real_samples = list("60"='sound/voice/moth/scream_moth.ogg')
+ real_samples = list("60"='sound/mobs/humanoids/moth/scream_moth.ogg')
admin_only = TRUE
/datum/instrument/fun/bilehorn
name = "Bilehorn"
id = "bilehorn"
- real_samples = list("60"='sound/creatures/bileworm/bileworm_spit.ogg')
+ real_samples = list("60"='sound/mobs/non-humanoids/bileworm/bileworm_spit.ogg')
admin_only = TRUE
diff --git a/code/modules/instruments/items.dm b/code/modules/instruments/items.dm
index f0176e6453092..4cf7df5a671ce 100644
--- a/code/modules/instruments/items.dm
+++ b/code/modules/instruments/items.dm
@@ -59,7 +59,7 @@
inhand_icon_state = "banjo"
attack_verb_continuous = list("scruggs-styles", "hum-diggitys", "shin-digs", "clawhammers")
attack_verb_simple = list("scruggs-style", "hum-diggity", "shin-dig", "clawhammer")
- hitsound = 'sound/weapons/banjoslap.ogg'
+ hitsound = 'sound/items/weapons/banjoslap.ogg'
allowed_instrument_ids = "banjo"
/obj/item/instrument/guitar
@@ -69,7 +69,7 @@
inhand_icon_state = "guitar"
attack_verb_continuous = list("plays metal on", "serenades", "crashes", "smashes")
attack_verb_simple = list("play metal on", "serenade", "crash", "smash")
- hitsound = 'sound/weapons/stringsmash.ogg'
+ hitsound = 'sound/items/weapons/stringsmash.ogg'
allowed_instrument_ids = list("guitar","csteelgt","cnylongt", "ccleangt", "cmutedgt")
/obj/item/instrument/eguitar
@@ -80,7 +80,7 @@
force = 12
attack_verb_continuous = list("plays metal on", "shreds", "crashes", "smashes")
attack_verb_simple = list("play metal on", "shred", "crash", "smash")
- hitsound = 'sound/weapons/stringsmash.ogg'
+ hitsound = 'sound/items/weapons/stringsmash.ogg'
allowed_instrument_ids = "eguitar"
/obj/item/instrument/glockenspiel
@@ -243,6 +243,6 @@
attack_verb_simple = list("flutter", "flap")
w_class = WEIGHT_CLASS_TINY
force = 0
- hitsound = 'sound/voice/moth/scream_moth.ogg'
+ hitsound = 'sound/mobs/humanoids/moth/scream_moth.ogg'
custom_price = PAYCHECK_COMMAND * 2.37
custom_premium_price = PAYCHECK_COMMAND * 2.37
diff --git a/code/modules/instruments/songs/editor.dm b/code/modules/instruments/songs/editor.dm
index 651b3d6f3b647..4029e5c395419 100644
--- a/code/modules/instruments/songs/editor.dm
+++ b/code/modules/instruments/songs/editor.dm
@@ -111,7 +111,7 @@
tempo = sanitize_tempo(5) // default 120 BPM
return TRUE
if("add_new_line")
- var/newline = tgui_input_text(user, "Enter your line", parent.name)
+ var/newline = tgui_input_text(user, "Enter your line", parent.name, max_length = MUSIC_MAXLINECHARS)
if(!newline || !in_range(parent, user))
return
if(lines.len > MUSIC_MAXLINES)
@@ -129,7 +129,7 @@
var/line_to_edit = params["line_editing"]
if(line_to_edit > lines.len || line_to_edit < 1)
return FALSE
- var/new_line_text = tgui_input_text(user, "Enter your line ", parent.name, lines[line_to_edit], MUSIC_MAXLINECHARS)
+ var/new_line_text = tgui_input_text(user, "Enter your line ", parent.name, lines[line_to_edit], max_length = MUSIC_MAXLINECHARS)
if(isnull(new_line_text) || !in_range(parent, user))
return FALSE
lines[line_to_edit] = new_line_text
diff --git a/code/modules/instruments/stationary.dm b/code/modules/instruments/stationary.dm
index ca0e7e2d9e770..c9b8263924023 100644
--- a/code/modules/instruments/stationary.dm
+++ b/code/modules/instruments/stationary.dm
@@ -52,7 +52,7 @@
if(BRUTE)
playsound(src, 'sound/effects/piano_hit.ogg', 100, TRUE)
if(BURN)
- playsound(src, 'sound/items/welder.ogg', 100, TRUE)
+ playsound(src, 'sound/items/tools/welder.ogg', 100, TRUE)
/obj/structure/musician/piano/atom_break(damage_flag)
. = ..()
diff --git a/code/modules/jobs/job_types/_job.dm b/code/modules/jobs/job_types/_job.dm
index 81d42b051dff8..41ff0c058cebc 100644
--- a/code/modules/jobs/job_types/_job.dm
+++ b/code/modules/jobs/job_types/_job.dm
@@ -220,10 +220,20 @@
dna.species.pre_equip_species_outfit(equipping, src, visual_only)
equip_outfit_and_loadout(equipping.get_outfit(consistent), player_client?.prefs, visual_only)
-/datum/job/proc/announce_head(mob/living/carbon/human/H, channels) //tells the given channel that the given mob is the new department head. See communications.dm for valid channels.
- if(H && GLOB.announcement_systems.len)
- //timer because these should come after the captain announcement
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_addtimer), CALLBACK(pick(GLOB.announcement_systems), TYPE_PROC_REF(/obj/machinery/announcement_system, announce), "NEWHEAD", H.real_name, H.job, channels), 1))
+/datum/job/proc/announce_head(mob/living/carbon/human/human, channels) //tells the given channel that the given mob is the new department head. See communications.dm for valid channels.
+ if(!human)
+ return
+ var/obj/machinery/announcement_system/system
+ var/list/available_machines = list()
+ for(var/obj/machinery/announcement_system/announce as anything in GLOB.announcement_systems)
+ if(announce.newhead_toggle)
+ available_machines += announce
+ break
+ if(!length(available_machines))
+ return
+ system = pick(available_machines)
+ //timer because these should come after the captain announcement
+ SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_addtimer), CALLBACK(system, TYPE_PROC_REF(/obj/machinery/announcement_system, announce), AUTO_ANNOUNCE_NEWHEAD, human.real_name, human.job, channels), 1))
//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1
/datum/job/proc/player_old_enough(client/player)
@@ -381,10 +391,10 @@
if(visualsOnly)
return
- var/datum/job/equipped_job = SSjob.GetJobType(jobtype)
+ var/datum/job/equipped_job = SSjob.get_job_type(jobtype)
if(!equipped_job)
- equipped_job = SSjob.GetJob(equipped.job)
+ equipped_job = SSjob.get_job(equipped.job)
var/obj/item/card/id/card = equipped.wear_id
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_costumes.dm b/code/modules/jobs/job_types/chaplain/chaplain_costumes.dm
index 637177adffbcd..6af8c844555da 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_costumes.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_costumes.dm
@@ -40,7 +40,7 @@
icon_state = "holidaypriest"
inhand_icon_state = "w_suit"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
- flags_inv = HIDEJUMPSUIT
+ flags_inv = HIDEJUMPSUIT|HIDEBELT
/obj/item/clothing/suit/chaplainsuit/nun
name = "nun robe"
@@ -48,7 +48,7 @@
icon_state = "nun"
inhand_icon_state = "nun"
body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS
- flags_inv = HIDEJUMPSUIT
+ flags_inv = HIDEJUMPSUIT|HIDEBELT
/obj/item/clothing/suit/chaplainsuit/habit
name = "religious tunic"
@@ -56,7 +56,7 @@
icon_state = "habit"
alternate_worn_layer = GLOVES_LAYER // since the sleeves cover a part of the hands, this way it looks better while retaining glove overlay correctly.
body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS
- flags_inv = HIDEJUMPSUIT
+ flags_inv = HIDEJUMPSUIT|HIDEBELT
/obj/item/clothing/suit/chaplainsuit/bishoprobe
name = "bishop's robes"
@@ -64,7 +64,7 @@
icon_state = "bishoprobe"
inhand_icon_state = "bishoprobe"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
- flags_inv = HIDEJUMPSUIT
+ flags_inv = HIDEJUMPSUIT|HIDEBELT
/obj/item/clothing/suit/chaplainsuit/armor/studentuni
name = "student robe"
@@ -106,7 +106,7 @@
icon_state = "monkrobeeast"
inhand_icon_state = null
body_parts_covered = GROIN|LEGS
- flags_inv = HIDEJUMPSUIT
+ flags_inv = HIDEJUMPSUIT|HIDEBELT
/obj/item/clothing/suit/chaplainsuit/whiterobe
name = "white robe"
@@ -114,7 +114,7 @@
icon_state = "whiterobe"
inhand_icon_state = null
body_parts_covered = CHEST|GROIN|LEGS|ARMS
- flags_inv = HIDEJUMPSUIT
+ flags_inv = HIDEJUMPSUIT|HIDEBELT
/obj/item/clothing/suit/chaplainsuit/clownpriest
name = "Robes of the Honkmother"
@@ -122,7 +122,7 @@
icon_state = "clownpriest"
inhand_icon_state = "clownpriest"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
- flags_inv = HIDEJUMPSUIT
+ flags_inv = HIDEJUMPSUIT|HIDEBELT
allowed = list(/obj/item/megaphone/clown, /obj/item/soap, /obj/item/food/pie/cream, /obj/item/bikehorn, /obj/item/bikehorn/golden, /obj/item/bikehorn/airhorn, /obj/item/instrument/bikehorn, /obj/item/reagent_containers/cup/soda_cans/canned_laughter, /obj/item/toy/crayon, /obj/item/toy/crayon/spraycan, /obj/item/toy/crayon/spraycan/lubecan, /obj/item/grown/bananapeel, /obj/item/food/grown/banana)
/obj/item/clothing/head/helmet/chaplain/clock
@@ -222,7 +222,7 @@
inhand_icon_state = null
/obj/item/clothing/suit/chaplainsuit/armor/crusader
- name = "Crusader's Armour"
+ name = "crusader's armour"
desc = "Armour that's comprised of metal and cloth."
icon_state = "crusader"
w_class = WEIGHT_CLASS_BULKY
@@ -287,4 +287,4 @@
icon_state = "shrinehand"
inhand_icon_state = "shrinehand"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
- flags_inv = HIDEJUMPSUIT
+ flags_inv = HIDEJUMPSUIT|HIDEBELT
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
index dd7208d170618..f8e94746d6030 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_nullrod.dm
@@ -22,6 +22,8 @@
var/menu_description = "A standard chaplain's weapon. Fits in pockets. Can be worn on the belt."
/// Lazylist, tracks refs()s to all cultists which have been crit or killed by this nullrod.
var/list/cultists_slain
+ /// Affects GLOB.holy_weapon_type. Disable to allow null rods to change at will and without affecting the station's type.
+ var/station_holy_item = TRUE
/obj/item/nullrod/Initialize(mapload)
. = ..()
@@ -35,7 +37,7 @@
)
AddElement(/datum/element/bane, target_type = /mob/living/basic/revenant, damage_multiplier = 0, added_damage = 25, requires_combat_mode = FALSE)
- if(!GLOB.holy_weapon_type && type == /obj/item/nullrod)
+ if((!GLOB.holy_weapon_type || !station_holy_item) && type == /obj/item/nullrod)
var/list/rods = list()
for(var/obj/item/nullrod/nullrod_type as anything in typesof(/obj/item/nullrod))
if(!initial(nullrod_type.chaplain_spawnable))
@@ -52,6 +54,8 @@
AddComponent(/datum/component/subtype_picker, rods, CALLBACK(src, PROC_REF(on_holy_weapon_picked)))
/obj/item/nullrod/proc/on_holy_weapon_picked(obj/item/nullrod/holy_weapon_type)
+ if(!station_holy_item)
+ return
GLOB.holy_weapon_type = holy_weapon_type
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NULLROD_PICKED)
SSblackbox.record_feedback("tally", "chaplain_weapon", 1, "[initial(holy_weapon_type.name)]")
@@ -90,6 +94,10 @@
. += span_cult_italic("It has the blood of [num_slain] fallen cultist[num_slain == 1 ? "" : "s"] on it. \
Offering it to Nar'sie will transform it into a [num_slain >= 3 ? "powerful" : "standard"] cult weapon.")
+/obj/item/nullrod/non_station
+ station_holy_item = FALSE
+ chaplain_spawnable = FALSE
+
/// Claymore Variant
/// This subtype possesses a block chance and is sharp.
@@ -105,9 +113,9 @@
w_class = WEIGHT_CLASS_BULKY
slot_flags = ITEM_SLOT_BACK|ITEM_SLOT_BELT
block_chance = 30
- block_sound = 'sound/weapons/parry.ogg'
+ block_sound = 'sound/items/weapons/parry.ogg'
sharpness = SHARP_EDGED
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
menu_description = "A sharp claymore which provides a low chance of blocking incoming melee attacks. Can be worn on the back or belt."
@@ -128,7 +136,7 @@
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
inhand_x_dimension = 64
inhand_y_dimension = 64
- hitsound = 'sound/hallucinations/growl1.ogg'
+ hitsound = 'sound/effects/hallucinations/growl1.ogg'
menu_description = "A sharp blade which provides a low chance of blocking incoming melee attacks. Can be worn on the back or belt."
/obj/item/nullrod/claymore/chainsaw_sword
@@ -140,7 +148,7 @@
slot_flags = ITEM_SLOT_BELT
attack_verb_continuous = list("saws", "tears", "lacerates", "cuts", "chops", "dices")
attack_verb_simple = list("saw", "tear", "lacerate", "cut", "chop", "dice")
- hitsound = 'sound/weapons/chainsawhit.ogg'
+ hitsound = 'sound/items/weapons/chainsawhit.ogg'
tool_behaviour = TOOL_SAW
toolspeed = 1.5 //slower than a real saw
menu_description = "A sharp chainsaw sword which provides a low chance of blocking incoming melee attacks. Can be used as a slower saw tool. Can be worn on the belt."
@@ -185,8 +193,8 @@
inhand_icon_state = "e_sword_on_blue"
worn_icon_state = "swordblue"
slot_flags = ITEM_SLOT_BELT
- hitsound = 'sound/weapons/blade1.ogg'
- block_sound = 'sound/weapons/block_blade.ogg'
+ hitsound = 'sound/items/weapons/blade1.ogg'
+ block_sound = 'sound/items/weapons/block_blade.ogg'
menu_description = "A sharp energy sword which provides a low chance of blocking incoming melee attacks. Can be worn on the belt."
/obj/item/nullrod/claymore/saber/red
@@ -221,7 +229,7 @@
sharpness = SHARP_EDGED
attack_verb_continuous = list("chops", "slices", "cuts", "zandatsu's")
attack_verb_simple = list("chop", "slice", "cut", "zandatsu")
- hitsound = 'sound/weapons/rapierhit.ogg'
+ hitsound = 'sound/items/weapons/rapierhit.ogg'
menu_description = "A sharp blade which partially penetrates armor. Very effective at butchering bodies. Can be worn on the back."
/obj/item/nullrod/vibro/Initialize(mapload)
@@ -241,7 +249,7 @@
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
worn_icon_state = "spellblade"
- hitsound = 'sound/weapons/rapierhit.ogg'
+ hitsound = 'sound/items/weapons/rapierhit.ogg'
menu_description = "A sharp blade which partially penetrates armor. Very effective at butchering bodies. Can be worn on the back."
/obj/item/nullrod/vibro/talking
@@ -255,7 +263,7 @@
worn_icon_state = "talking_sword"
attack_verb_continuous = list("chops", "slices", "cuts")
attack_verb_simple= list("chop", "slice", "cut")
- hitsound = 'sound/weapons/rapierhit.ogg'
+ hitsound = 'sound/items/weapons/rapierhit.ogg'
menu_description = "A sharp blade which partially penetrates armor. Able to awaken a friendly spirit to provide guidance. Very effective at butchering bodies. Can be worn on the back."
/obj/item/nullrod/vibro/talking/Initialize(mapload)
@@ -272,7 +280,7 @@
slot_flags = ITEM_SLOT_BELT
attack_verb_continuous = list("saws", "tears", "lacerates", "cuts", "chops", "dices")
attack_verb_simple = list("saw", "tear", "lacerate", "cut", "chop", "dice")
- hitsound = 'sound/weapons/chainsawhit.ogg'
+ hitsound = 'sound/items/weapons/chainsawhit.ogg'
tool_behaviour = TOOL_SAW
toolspeed = 0.5 //same speed as an active chainsaw
chaplain_spawnable = FALSE //prevents being pickable as a chaplain weapon (it has 30 force)
@@ -293,7 +301,7 @@
slot_flags = null
item_flags = ABSTRACT | DROPDEL
w_class = WEIGHT_CLASS_HUGE
- hitsound = 'sound/weapons/sear.ogg'
+ hitsound = 'sound/items/weapons/sear.ogg'
damtype = BURN
attack_verb_continuous = list("punches", "cross counters", "pummels")
attack_verb_simple = list(SFX_PUNCH, "cross counter", "pummel")
@@ -317,7 +325,7 @@
force = 5
slot_flags = ITEM_SLOT_BACK
block_chance = 50
- block_sound = 'sound/weapons/genhit.ogg'
+ block_sound = 'sound/items/weapons/genhit.ogg'
menu_description = "A red staff which provides a medium chance of blocking incoming attacks via a protective red aura around its user, but deals very low amount of damage. Can be worn only on the back."
/// The icon which appears over the mob holding the item
var/shield_icon = "shield-red"
@@ -348,7 +356,7 @@
force = 4.13
throwforce = 1
slot_flags = ITEM_SLOT_BELT
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
menu_description = "An odd s(w)ord dealing a laughable amount of damage. Fits in pockets. Can be worn on the belt."
@@ -394,7 +402,7 @@
sharpness = SHARP_EDGED
attack_verb_continuous = list("saws", "tears", "lacerates", "cuts", "chops", "dices")
attack_verb_simple = list("saw", "tear", "lacerate", "cut", "chop", "dice")
- hitsound = 'sound/weapons/chainsawhit.ogg'
+ hitsound = 'sound/items/weapons/chainsawhit.ogg'
tool_behaviour = TOOL_SAW
toolspeed = 2 //slower than a real saw
menu_description = "An undroppable sharp chainsaw hand. Can be used as a very slow saw tool. Capable of slowly butchering bodies. Disappears if the arm holding it is cut off."
@@ -445,7 +453,7 @@
slot_flags = ITEM_SLOT_BACK
attack_verb_continuous = list("attacks", "smashes", "crushes", "splatters", "cracks")
attack_verb_simple = list("attack", "smash", "crush", "splatter", "crack")
- hitsound = 'sound/weapons/blade1.ogg'
+ hitsound = 'sound/items/weapons/blade1.ogg'
menu_description = "A hammer dealing a little less damage due to its user's pride. Has a low chance of transferring some of the user's reagents to the target. Capable of tapping knees to measure brain health. Can be worn on the back."
/obj/item/nullrod/pride_hammer/Initialize(mapload)
@@ -474,7 +482,7 @@
slot_flags = ITEM_SLOT_BELT
attack_verb_continuous = list("whips", "lashes")
attack_verb_simple = list("whip", "lash")
- hitsound = 'sound/weapons/chainhit.ogg'
+ hitsound = 'sound/items/weapons/chainhit.ogg'
menu_description = "A whip. Deals extra damage to vampires. Fits in pockets. Can be worn on the belt."
// Atheist's Fedora - Wear it on your head. No melee damage, massive throw force.
@@ -552,7 +560,7 @@
force = 15
attack_verb_continuous = list("bites", "eats", "fin slaps")
attack_verb_simple = list("bite", "eat", "fin slap")
- hitsound = 'sound/weapons/bite.ogg'
+ hitsound = 'sound/items/weapons/bite.ogg'
menu_description = "A plushie dealing a little less damage due to its cute form. Capable of blessing one person with the Carp-Sie favor, which grants friendship of all wild space carps. Fits in pockets. Can be worn on the belt."
/obj/item/nullrod/carp/Initialize(mapload)
@@ -566,7 +574,7 @@
desc = "A long, tall staff made of polished wood. Traditionally used in ancient old-Earth martial arts, it is now used to harass the clown."
force = 14
block_chance = 40
- block_sound = 'sound/weapons/genhit.ogg'
+ block_sound = 'sound/items/weapons/genhit.ogg'
slot_flags = ITEM_SLOT_BACK
w_class = WEIGHT_CLASS_BULKY
hitsound = SFX_SWING_HIT
@@ -606,7 +614,7 @@
w_class = WEIGHT_CLASS_HUGE
sharpness = SHARP_EDGED
slot_flags = null
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
item_flags = SLOWS_WHILE_IN_HAND
@@ -646,7 +654,7 @@
slot_flags = ITEM_SLOT_BACK
attack_verb_continuous = list("pokes", "impales", "pierces", "jabs")
attack_verb_simple = list("poke", "impale", "pierce", "jab")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
sharpness = SHARP_EDGED
menu_description = "A sharp pitchfork. Can be worn on the back."
@@ -683,7 +691,7 @@
armour_penetration = 35
attack_verb_continuous = list("pulses", "mends", "cuts")
attack_verb_simple = list("pulse", "mend", "cut")
- hitsound = 'sound/effects/sparks4.ogg'
+ hitsound = 'sound/effects/sparks/sparks4.ogg'
menu_description = "A tool dealing brain damage which partially penetrates armor. Fits in pockets. Can be worn on the belt."
// Ancient Spear - Slight armor penetration, based on the Brass Spear from the Clockcult game mode.
@@ -702,7 +710,7 @@
w_class = WEIGHT_CLASS_HUGE
attack_verb_continuous = list("stabs", "pokes", "slashes", "clocks")
attack_verb_simple = list("stab", "poke", "slash", "clock")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
menu_description = "A pointy spear which penetrates armor a little. Can be worn only on the belt."
// Unholy version of above, since the gamemode is dead in the water
@@ -725,7 +733,7 @@
w_class = WEIGHT_CLASS_HUGE
attack_verb_continuous = list("stabs", "pokes", "slashes", "clocks")
attack_verb_simple = list("stab", "poke", "slash", "clock")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
// Nullblade - For when you really want to feel like rolling dice during combat
@@ -743,9 +751,9 @@
wound_bonus = 10
bare_wound_bonus = 30
slot_flags = ITEM_SLOT_BELT
- block_sound = 'sound/weapons/parry.ogg'
+ block_sound = 'sound/items/weapons/parry.ogg'
sharpness = SHARP_POINTY
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
attack_verb_continuous = list("attacks", "punctures", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
attack_verb_simple = list("attack", "puncture", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
menu_description = "A blade that deals variable, low amounts of damage, but does easily inflict wounds. \
@@ -896,4 +904,4 @@
// We got a sneak attack!
living_target.apply_damage(round(sneak_attack_dice, DAMAGE_PRECISION), BRUTE, def_zone = affecting, blocked = armor_block, wound_bonus = bare_wound_bonus, sharpness = SHARP_EDGED)
living_target.balloon_alert(user, "sneak attack!")
- playsound(living_target, 'sound/weapons/guillotine.ogg', 50, TRUE)
+ playsound(living_target, 'sound/items/weapons/guillotine.ogg', 50, TRUE)
diff --git a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
index 4b07baaa05890..e6f34b894b3de 100644
--- a/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
+++ b/code/modules/jobs/job_types/chaplain/chaplain_vorpal_scythe.dm
@@ -25,7 +25,7 @@ If the scythe isn't empowered when you sheath it, you take a heap of damage and
return ..()
to_chat(owner, span_userdanger("[scythe] tears into you for your unworthy display of arrogance!"))
- playsound(owner, 'sound/magic/demon_attack1.ogg', 50, TRUE)
+ playsound(owner, 'sound/effects/magic/demon_attack1.ogg', 50, TRUE)
part.receive_damage(brute = 25, wound_bonus = 10, sharpness = SHARP_EDGED)
return ..()
@@ -152,7 +152,7 @@ If the scythe isn't empowered when you sheath it, you take a heap of damage and
log_combat(user, potential_reaping, "prepared to use [src] to decapitate")
if(do_after(user, 15 SECONDS * death_knell_speed_mod, target = potential_reaping))
- playsound(get_turf(potential_reaping), 'sound/weapons/bladeslice.ogg', 250, TRUE)
+ playsound(get_turf(potential_reaping), 'sound/items/weapons/bladeslice.ogg', 250, TRUE)
reaped_head.dismember()
user.visible_message(span_danger("[user] swings [src] down, slicing [potential_reaping]'s [head_name] clean off! You think [src] may have grown stronger!"), span_notice("As you perform the death knell on [potential_reaping], [src] gains power! For a time..."))
if(potential_empowerment == SCYTHE_SATED) //We don't want actual player heads to go wandering off, but it'll be funny if a bunch of monkeyhuman heads started floating around
diff --git a/code/modules/jobs/job_types/cook.dm b/code/modules/jobs/job_types/cook.dm
index 26a43fa775192..dc36796c4d1cf 100644
--- a/code/modules/jobs/job_types/cook.dm
+++ b/code/modules/jobs/job_types/cook.dm
@@ -82,7 +82,7 @@
/datum/outfit/job/cook/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
..()
- var/datum/job/cook/other_chefs = SSjob.GetJobType(jobtype)
+ var/datum/job/cook/other_chefs = SSjob.get_job_type(jobtype)
if(other_chefs) // If there's other Chefs, you're a Cook
if(other_chefs.cooks > 0)//Cooks
id_trim = /datum/id_trim/job/cook
diff --git a/code/modules/jobs/job_types/detective.dm b/code/modules/jobs/job_types/detective.dm
index 00bd8790d9bd0..258b2b322f1b9 100644
--- a/code/modules/jobs/job_types/detective.dm
+++ b/code/modules/jobs/job_types/detective.dm
@@ -29,13 +29,14 @@
mail_goodies = list(
/obj/item/storage/fancy/cigarettes = 25,
- /obj/item/ammo_box/c38 = 25,
+ /obj/item/ammo_box/c38 = 20,
/obj/item/ammo_box/c38/dumdum = 5,
/obj/item/ammo_box/c38/hotshot = 5,
/obj/item/ammo_box/c38/iceblox = 5,
/obj/item/ammo_box/c38/match = 5,
/obj/item/ammo_box/c38/trac = 5,
- /obj/item/storage/belt/holster/detective/full = 1
+ /obj/item/card/id/advanced/plainclothes = 5,
+ /obj/item/storage/belt/holster/detective/full = 1,
)
family_heirlooms = list(/obj/item/reagent_containers/cup/glass/bottle/whiskey)
@@ -49,6 +50,8 @@
name = "Detective"
jobtype = /datum/job/detective
+ id = /obj/item/card/id/advanced/plainclothes
+
id_trim = /datum/id_trim/job/detective
uniform = /obj/item/clothing/under/rank/security/detective
suit = /obj/item/clothing/suit/jacket/det_suit
diff --git a/code/modules/jobs/job_types/station_trait/human_ai.dm b/code/modules/jobs/job_types/station_trait/human_ai.dm
index 032ad08af5a60..a6e77d77a510c 100644
--- a/code/modules/jobs/job_types/station_trait/human_ai.dm
+++ b/code/modules/jobs/job_types/station_trait/human_ai.dm
@@ -161,6 +161,6 @@
user.balloon_alert(user, "unpacking...")
if(!do_after(user, 5 SECONDS, src))
return
- playsound(src, 'sound/items/drill_use.ogg', 40, TRUE)
+ playsound(src, 'sound/items/tools/drill_use.ogg', 40, TRUE)
new /obj/machinery/computer/camera_advanced/human_ai(get_turf(src))
qdel(src)
diff --git a/code/modules/jobs/job_types/station_trait/pun_pun.dm b/code/modules/jobs/job_types/station_trait/pun_pun.dm
new file mode 100644
index 0000000000000..eca4861e77ef0
--- /dev/null
+++ b/code/modules/jobs/job_types/station_trait/pun_pun.dm
@@ -0,0 +1,53 @@
+///Special job, active during monkey day.
+/datum/job/pun_pun
+ title = JOB_PUN_PUN
+ description = "Assist the service department by serving drinks and food and entertaining the crew."
+ department_head = list(JOB_HEAD_OF_PERSONNEL)
+ faction = FACTION_STATION
+ total_positions = 0
+ spawn_positions = 0
+ supervisors = "the Bartender"
+ spawn_type = /mob/living/carbon/human/species/monkey/punpun
+ outfit = /datum/outfit/job/pun_pun
+ config_tag = "PUN_PUN"
+ random_spawns_possible = FALSE
+ paycheck = PAYCHECK_LOWER
+ paycheck_department = ACCOUNT_CIV
+ display_order = JOB_DISPLAY_ORDER_PUN_PUN
+ departments_list = list(/datum/job_department/service)
+ exclusive_mail_goodies = TRUE
+ mail_goodies = list(
+ /obj/item/food/grown/banana = 4,
+ /obj/effect/spawner/random/entertainment/money_medium = 3,
+ /obj/item/clothing/head/helmet/monkey_sentience = 1,
+ /obj/item/book/granter/sign_language = 1,
+ /obj/item/food/monkeycube = 1,
+ )
+ rpg_title = "Homunculus"
+ allow_bureaucratic_error = FALSE
+ job_flags = (STATION_JOB_FLAGS|STATION_TRAIT_JOB_FLAGS)&~JOB_ASSIGN_QUIRKS
+
+/datum/job/pun_pun/get_spawn_mob(client/player_client, atom/spawn_point)
+ if (!player_client)
+ return
+ var/mob/living/monky = new spawn_type(get_turf(spawn_point))
+ if(!GLOB.the_one_and_only_punpun)
+ GLOB.the_one_and_only_punpun = monky
+ return monky
+
+/datum/job/pun_pun/after_spawn(mob/living/carbon/human/monkey, client/player_client)
+ . = ..()
+ monkey.make_clever_and_no_dna_scramble()
+
+/datum/outfit/job/pun_pun
+ name = "Pun Pun"
+ jobtype = /datum/job/pun_pun
+
+ id_trim = /datum/id_trim/job/pun_pun
+ belt = /obj/item/modular_computer/pda/pun_pun
+ uniform = /obj/item/clothing/under/suit/waiter
+ backpack_contents = list(
+ /obj/item/gun/ballistic/shotgun/monkey = 1,
+ /obj/item/storage/box/beanbag = 1,
+ )
+ shoes = null //monkeys cannot equip shoes
diff --git a/code/modules/jobs/job_types/station_trait/veteran_advisor.dm b/code/modules/jobs/job_types/station_trait/veteran_advisor.dm
index 6fcb8d94707f5..f8a0f2f801d31 100644
--- a/code/modules/jobs/job_types/station_trait/veteran_advisor.dm
+++ b/code/modules/jobs/job_types/station_trait/veteran_advisor.dm
@@ -37,10 +37,19 @@
allow_bureaucratic_error = FALSE
job_flags = STATION_JOB_FLAGS | STATION_TRAIT_JOB_FLAGS
-/datum/job/veteran_advisor/get_roundstart_spawn_point() //Spawning at Brig where Officers spawn
- if (length(GLOB.start_landmarks_list["Security Officer"]))
- return pick(GLOB.start_landmarks_list["Security Officer"])
- return ..()
+/datum/job/veteran_advisor/get_default_roundstart_spawn_point()
+ for(var/obj/effect/landmark/start/spawn_point as anything in GLOB.start_landmarks_list)
+ if(spawn_point.name != "Security Officer")
+ continue
+ . = spawn_point
+ if(spawn_point.used) //so we can revert to spawning them on top of eachother if something goes wrong
+ continue
+ spawn_point.used = TRUE
+ break
+ if(!.) // Try to fall back to "our" landmark
+ . = ..()
+ if(!.)
+ log_mapping("Job [title] ([type]) couldn't find a round start spawn point.")
/datum/job/veteran_advisor/after_spawn(mob/living/spawned, client/player_client)
. = ..()
diff --git a/code/modules/language/_language_holder.dm b/code/modules/language/_language_holder.dm
index b6dea2d4e0e28..b48a1ab1530ab 100644
--- a/code/modules/language/_language_holder.dm
+++ b/code/modules/language/_language_holder.dm
@@ -37,10 +37,10 @@ Key procs
/datum/language_holder
/// Lazyassoclist of all understood languages
- var/list/understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
+ var/list/understood_languages
/// Lazyassoclist of languages that can be spoken.
/// Tongue organ may also set limits beyond this list.
- var/list/spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
+ var/list/spoken_languages
/// Lazyassoclist of blocked languages.
/// Used to prevent understanding and speaking certain languages, ie for certain mobs, mutations etc.
var/list/blocked_languages
@@ -503,14 +503,17 @@ GLOBAL_LIST_INIT(prototype_language_holders, init_language_holder_prototypes())
/datum/language/nekomimetic = list(LANGUAGE_ATOM),
)
+// Given to atoms by default
+/datum/language_holder/atom_basic
+ understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
+ spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM))
+
+// Explicitly empty one for readability
/datum/language_holder/empty
- understood_languages = null
- spoken_languages = null
+// Has all the languages known (via "mind")
/datum/language_holder/universal
- understood_languages = null
- spoken_languages = null
/datum/language_holder/universal/New()
. = ..()
- grant_all_languages()
+ grant_all_languages(source = LANGUAGE_MIND)
diff --git a/code/modules/library/bibles.dm b/code/modules/library/bibles.dm
index 0c6a1aad71d63..eda1f18f8e7eb 100644
--- a/code/modules/library/bibles.dm
+++ b/code/modules/library/bibles.dm
@@ -91,7 +91,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
/// Destroy the bible when it's shot by a bullet
/obj/item/book/bible/proc/on_intercepted_bullet(mob/living/victim, obj/projectile/bullet)
victim.add_mood_event("blessing", /datum/mood_event/blessing)
- playsound(victim, 'sound/magic/magic_block_holy.ogg', 50, TRUE)
+ playsound(victim, 'sound/effects/magic/magic_block_holy.ogg', 50, TRUE)
victim.visible_message(span_warning("[src] takes [bullet] in [victim]'s place!"))
var/obj/structure/fluff/paper/stack/pages = new(get_turf(src))
pages.setDir(pick(GLOB.alldirs))
@@ -269,9 +269,6 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
playsound(target_mob, SFX_PUNCH, 25, TRUE, -1)
log_combat(user, target_mob, "attacked", src)
-/obj/item/book/bible/storage_insert_on_interaction(datum/storage, atom/storage_holder, mob/user)
- return !istype(storage_holder, /obj/item/book/bible)
-
/obj/item/book/bible/interact_with_atom(atom/bible_smacked, mob/living/user, list/modifiers)
if(SEND_SIGNAL(bible_smacked, COMSIG_BIBLE_SMACKED, user) & COMSIG_END_BIBLE_CHAIN)
return ITEM_INTERACT_SUCCESS
@@ -313,7 +310,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
if(istype(bible_smacked, /obj/item/melee/cultblade/haunted) && !IS_CULTIST(user))
var/obj/item/melee/cultblade/haunted/sword = bible_smacked
sword.balloon_alert(user, "exorcising...")
- playsound(src,'sound/hallucinations/veryfar_noise.ogg',40,TRUE)
+ playsound(src,'sound/effects/hallucinations/veryfar_noise.ogg',40,TRUE)
if(do_after(user, 4 SECONDS, target = sword))
playsound(src,'sound/effects/pray_chaplain.ogg',60,TRUE)
new /obj/item/nullrod/nullblade(get_turf(sword))
@@ -332,7 +329,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
new /obj/item/reagent_containers/cup/glass/bottle/whiskey(src)
/obj/item/book/bible/syndicate
- name = "Syndicate Tome"
+ name = "syndicate tome"
desc = "A very ominous tome resembling a bible."
icon_state ="ebook"
item_flags = NO_BLOOD_ON_ITEM
@@ -340,7 +337,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list(
throw_range = 7
throwforce = 18
force = 18
- hitsound = 'sound/weapons/sear.ogg'
+ hitsound = 'sound/items/weapons/sear.ogg'
damtype = BURN
attack_verb_continuous = list("attacks", "burns", "blesses", "damns", "scorches", "curses", "smites")
attack_verb_simple = list("attack", "burn", "bless", "damn", "scorch", "curses", "smites")
diff --git a/code/modules/library/book.dm b/code/modules/library/book.dm
index 5ae9afcdcbe49..7f5f010563a5a 100644
--- a/code/modules/library/book.dm
+++ b/code/modules/library/book.dm
@@ -133,7 +133,7 @@
name = newtitle
book_data.set_title(html_decode(newtitle)) //Don't want to double encode here
if("Contents")
- var/content = tgui_input_text(user, "Write your book's contents (HTML NOT allowed)", "Book Contents", multiline = TRUE)
+ var/content = tgui_input_text(user, "Write your book's contents (HTML NOT allowed)", "Book Contents", max_length = MAX_PAPER_LENGTH, multiline = TRUE)
if(!user.can_perform_action(src) || !user.can_write(attacking_item))
return
if(!content)
@@ -141,7 +141,7 @@
return
book_data.set_content(html_decode(content))
if("Author")
- var/author = tgui_input_text(user, "Write the author's name", "Author Name")
+ var/author = tgui_input_text(user, "Write the author's name", "Author Name", max_length = MAX_NAME_LEN)
if(!user.can_perform_action(src) || !user.can_write(attacking_item))
return
if(!author)
diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm
index 35def980eb922..d7102fe96005a 100644
--- a/code/modules/library/lib_machines.dm
+++ b/code/modules/library/lib_machines.dm
@@ -753,7 +753,7 @@ GLOBAL_VAR_INIT(library_table_modified, 0)
return
cache = held_book.book_data.return_copy()
flick("bigscanner1", src)
- playsound(src, 'sound/machines/scanner.ogg', vol = 50, vary = TRUE)
+ playsound(src, 'sound/machines/scanner/scanner.ogg', vol = 50, vary = TRUE)
return TRUE
if("clear")
cache = null
diff --git a/code/modules/library/skill_learning/generic_skillchips/rod_suplex.dm b/code/modules/library/skill_learning/generic_skillchips/rod_suplex.dm
index bff83423be73e..07bc945e1d180 100644
--- a/code/modules/library/skill_learning/generic_skillchips/rod_suplex.dm
+++ b/code/modules/library/skill_learning/generic_skillchips/rod_suplex.dm
@@ -5,8 +5,8 @@
skill_name = "True Strength"
skill_description = "The knowledge and strength to resolve the most ancient conumdrum; what happens when an unstoppable force meets an immovable object."
skill_icon = "dumbbell"
- activate_message = "You realise if you apply the correct force, at the correct angle, it is possible to make the immovable permanently movable. And... damn, you look huge."
- deactivate_message = "You forget how to permanently anchor a paradoxical object. Also, you should really hit the gym..."
+ activate_message = span_notice("You realise if you apply the correct force, at the correct angle, it is possible to make the immovable permanently movable. And... damn, you look huge.")
+ deactivate_message = span_notice("You forget how to permanently anchor a paradoxical object. Also, you should really hit the gym...")
chip_category = SKILLCHIP_CATEGORY_GENERAL
skillchip_flags = NONE
slot_use = 1
diff --git a/code/modules/library/skill_learning/job_skillchips/chef.dm b/code/modules/library/skill_learning/job_skillchips/chef.dm
index 75bc494543c36..e457d8773a09f 100644
--- a/code/modules/library/skill_learning/job_skillchips/chef.dm
+++ b/code/modules/library/skill_learning/job_skillchips/chef.dm
@@ -4,8 +4,8 @@
skill_name = "Close Quarters Cooking"
skill_description = "A specialised form of self defence, developed by skilled sous-chef de cuisines. No man fights harder than a chef to defend his kitchen."
skill_icon = "utensils"
- activate_message = "You can visualize how to defend your kitchen with martial arts."
- deactivate_message = "You forget how to control your muscles to execute kicks, slams and restraints while in a kitchen environment."
+ activate_message = span_notice("You can visualize how to defend your kitchen with martial arts.")
+ deactivate_message = span_notice("You forget how to control your muscles to execute kicks, slams and restraints while in a kitchen environment.")
/// The Chef CQC given by the skillchip.
var/datum/martial_art/cqc/under_siege/style
diff --git a/code/modules/library/skill_learning/job_skillchips/psychologist.dm b/code/modules/library/skill_learning/job_skillchips/psychologist.dm
index 6450d13b89a37..be0fe7502f63d 100644
--- a/code/modules/library/skill_learning/job_skillchips/psychologist.dm
+++ b/code/modules/library/skill_learning/job_skillchips/psychologist.dm
@@ -5,5 +5,5 @@
skill_name = "Supermatter Cognition Theory"
skill_description = "Understand the correct mental patterns to keep in mind around matter in a hyperfractal state, causing immunity to visions and making the matter in question \"calmer\"."
skill_icon = "spa"
- activate_message = "You start thinking in patterns that will render you immune to visions from, and act as a calming influence for, matter in a hyperfractal state."
- deactivate_message = "Your thoughts become more disordered and jumbled. You are no longer immune to the abyss."
+ activate_message = span_notice("You start thinking in patterns that will render you immune to visions from, and act as a calming influence for, matter in a hyperfractal state.")
+ deactivate_message = span_notice("Your thoughts become more disordered and jumbled. You are no longer immune to the abyss.")
diff --git a/code/modules/library/skill_learning/job_skillchips/roboticist.dm b/code/modules/library/skill_learning/job_skillchips/roboticist.dm
index 401315e265abc..aa43bafbe8b52 100644
--- a/code/modules/library/skill_learning/job_skillchips/roboticist.dm
+++ b/code/modules/library/skill_learning/job_skillchips/roboticist.dm
@@ -5,5 +5,5 @@
skill_name = "Cyborg Circuitry"
skill_description = "Recognise cyborg wire layouts and understand their functionality at a glance."
skill_icon = "sitemap"
- activate_message = "You suddenly comprehend the secrets behind cyborg circuitry."
- deactivate_message = "Cyborg circuitry stops making sense as images of coloured wires fade from your mind."
+ activate_message = span_notice("You suddenly comprehend the secrets behind cyborg circuitry.")
+ deactivate_message = span_notice("Cyborg circuitry stops making sense as images of coloured wires fade from your mind.")
diff --git a/code/modules/library/skill_learning/job_skillchips/station_engineer.dm b/code/modules/library/skill_learning/job_skillchips/station_engineer.dm
index 0ed2edb5ccda9..08ab6ee61e3f1 100644
--- a/code/modules/library/skill_learning/job_skillchips/station_engineer.dm
+++ b/code/modules/library/skill_learning/job_skillchips/station_engineer.dm
@@ -5,5 +5,5 @@
skill_name = "Engineering Circuitry"
skill_description = "Recognise airlock and APC wire layouts and understand their functionality at a glance."
skill_icon = "sitemap"
- activate_message = "You suddenly comprehend the secrets behind airlock and APC circuitry."
- deactivate_message = "Airlock and APC circuitry stops making sense as images of coloured wires fade from your mind."
+ activate_message = span_notice("You suddenly comprehend the secrets behind airlock and APC circuitry.")
+ deactivate_message = span_notice("Airlock and APC circuitry stops making sense as images of coloured wires fade from your mind.")
diff --git a/code/modules/library/skill_learning/skillchip.dm b/code/modules/library/skill_learning/skillchip.dm
index b8903e5bde09a..10139585dd9a7 100644
--- a/code/modules/library/skill_learning/skillchip.dm
+++ b/code/modules/library/skill_learning/skillchip.dm
@@ -377,8 +377,8 @@
skill_name = "Underwater Basketweaving"
skill_description = "Master intricate art of using twine to create perfect baskets while submerged."
skill_icon = "shopping-basket"
- activate_message = "You're one with the twine and the sea."
- deactivate_message = "Higher mysteries of underwater basketweaving leave your mind."
+ activate_message = span_notice("You're one with the twine and the sea.")
+ deactivate_message = span_notice("Higher mysteries of underwater basketweaving leave your mind.")
/obj/item/skillchip/wine_taster
name = "WINE skillchip"
@@ -387,8 +387,8 @@
skill_name = "Wine Tasting"
skill_description = "Recognize wine vintage from taste alone. Never again lack an opinion when presented with an unknown drink."
skill_icon = "wine-bottle"
- activate_message = "You recall wine taste."
- deactivate_message = "Your memories of wine evaporate."
+ activate_message = span_notice("You recall wine taste.")
+ deactivate_message = span_notice("Your memories of wine evaporate.")
/obj/item/skillchip/bonsai
name = "Hedge 3 skillchip"
@@ -396,16 +396,16 @@
skill_name = "Hedgetrimming"
skill_description = "Trim hedges and potted plants into marvelous new shapes with any old knife. Not applicable to plastic plants."
skill_icon = "spa"
- activate_message = "Your mind is filled with plant arrangments."
- deactivate_message = "You can't remember what a hedge looks like anymore."
+ activate_message = span_notice("Your mind is filled with plant arrangments.")
+ deactivate_message = span_notice("You can't remember what a hedge looks like anymore.")
/obj/item/skillchip/useless_adapter
name = "Skillchip adapter"
skill_name = "Useless adapter"
skill_description = "Allows you to insert another skillchip into this adapter after it has been inserted into your brain..."
skill_icon = "plug"
- activate_message = "You can now activate another chip through this adapter, but you're not sure why you did this..."
- deactivate_message = "You no longer have the useless skillchip adapter."
+ activate_message = span_notice("You can now activate another chip through this adapter, but you're not sure why you did this...")
+ deactivate_message = span_notice("You no longer have the useless skillchip adapter.")
skillchip_flags = SKILLCHIP_ALLOWS_MULTIPLE
// Literally does nothing.
complexity = 0
@@ -417,8 +417,8 @@
skill_name = "Lightbulb Removing"
skill_description = "Stop failing taking out lightbulbs today, no gloves needed!"
skill_icon = "lightbulb"
- activate_message = "Your feel like your pain receptors are less sensitive to hot objects."
- deactivate_message = "You feel like hot objects could stop you again..."
+ activate_message = span_notice("Your feel like your pain receptors are less sensitive to hot objects.")
+ deactivate_message = span_notice("You feel like hot objects could stop you again...")
/obj/item/skillchip/disk_verifier
name = "K33P-TH4T-D15K skillchip"
@@ -426,8 +426,8 @@
skill_name = "Nuclear Disk Verification"
skill_description = "Nuclear authentication disks have an extremely long serial number for verification. This skillchip stores that number, which allows the user to automatically spot forgeries."
skill_icon = "save"
- activate_message = "You feel your mind automatically verifying long serial numbers on disk shaped objects."
- deactivate_message = "The innate recognition of absurdly long disk-related serial numbers fades from your mind."
+ activate_message = span_notice("You feel your mind automatically verifying long serial numbers on disk shaped objects.")
+ deactivate_message = span_notice("The innate recognition of absurdly long disk-related serial numbers fades from your mind.")
/obj/item/skillchip/entrails_reader
name = "3NTR41LS skillchip"
@@ -435,8 +435,8 @@
skill_name = "Entrails Reader"
skill_description = "Be able to learn about a person's life, by looking at their internal organs. Not to be confused with looking into the future."
skill_icon = "lungs"
- activate_message = "You feel that you know a lot about interpreting organs."
- deactivate_message = "Knowledge of liver damage, heart strain and lung scars fades from your mind."
+ activate_message = span_notice("You feel that you know a lot about interpreting organs.")
+ deactivate_message = span_notice("Knowledge of liver damage, heart strain and lung scars fades from your mind.")
/obj/item/skillchip/appraiser
name = "GENUINE ID Appraisal Now! skillchip"
diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm
index e3f72da5bbffd..21676d1741e74 100644
--- a/code/modules/lighting/lighting_atom.dm
+++ b/code/modules/lighting/lighting_atom.dm
@@ -201,8 +201,8 @@
var/list/hand_back
if(!(get_offset.light_flags & LIGHT_IGNORE_OFFSET))
hand_back = get_visual_offset(get_offset)
- hand_back[1] = -hand_back[1] / world.icon_size
- hand_back[2] = -hand_back[2] / world.icon_size
+ hand_back[1] = -hand_back[1] / ICON_SIZE_X
+ hand_back[2] = -hand_back[2] / ICON_SIZE_Y
else
hand_back = list(0, 0)
diff --git a/code/modules/loadout/categories/heads.dm b/code/modules/loadout/categories/heads.dm
index 6b939495684ba..ad23f0b2dfb7b 100644
--- a/code/modules/loadout/categories/heads.dm
+++ b/code/modules/loadout/categories/heads.dm
@@ -132,6 +132,26 @@
name = "Rose"
item_path = /obj/item/food/grown/rose
+/datum/loadout_item/head/sunflower
+ name = "Sunflower"
+ item_path = /obj/item/food/grown/sunflower
+
+/datum/loadout_item/head/poppy
+ name = "Poppy"
+ item_path = /obj/item/food/grown/poppy
+
+/datum/loadout_item/head/lily
+ name = "Lily"
+ item_path = /obj/item/food/grown/poppy/lily
+
+/datum/loadout_item/head/geranium
+ name = "Geranium"
+ item_path = /obj/item/food/grown/poppy/geranium
+
+/datum/loadout_item/head/harebell
+ name = "Harebell"
+ item_path = /obj/item/food/grown/harebell
+
/datum/loadout_item/head/wig
name = "Wig"
item_path = /obj/item/clothing/head/wig/natural
diff --git a/code/modules/mafia/controller.dm b/code/modules/mafia/controller.dm
index f6b46c3430dc9..d4edbb37f411e 100644
--- a/code/modules/mafia/controller.dm
+++ b/code/modules/mafia/controller.dm
@@ -602,13 +602,18 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment())
outfit_to_distribute = player_outfit
for(var/datum/mafia_role/role as anything in all_roles)
- var/mob/living/carbon/human/H = new(get_turf(role.assigned_landmark))
- H.add_traits(list(TRAIT_NOFIRE, TRAIT_NOBREATH, TRAIT_CANNOT_CRYSTALIZE, TRAIT_PERMANENTLY_MORTAL), MAFIA_TRAIT)
- H.equipOutfit(outfit_to_distribute)
- H.status_flags |= GODMODE
- RegisterSignal(H, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(display_votes))
+ var/mob/living/carbon/human/human = new(get_turf(role.assigned_landmark))
+ human.add_traits(list(
+ TRAIT_NOFIRE,
+ TRAIT_NOBREATH,
+ TRAIT_CANNOT_CRYSTALIZE,
+ TRAIT_PERMANENTLY_MORTAL,
+ TRAIT_GODMODE,
+ ), MAFIA_TRAIT)
+ human.equipOutfit(outfit_to_distribute)
+ RegisterSignal(human, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(display_votes))
var/obj/item/modular_computer/modpc = role.player_pda
- role.register_body(H)
+ role.register_body(human)
if(modpc)
player_role_lookup[modpc] = role
else
diff --git a/code/modules/mafia/roles/roles.dm b/code/modules/mafia/roles/roles.dm
index 4cfd7662d843e..b035b618ec4e7 100644
--- a/code/modules/mafia/roles/roles.dm
+++ b/code/modules/mafia/roles/roles.dm
@@ -145,7 +145,7 @@
/datum/mafia_role/proc/greet()
mafia_alert = new(body, src)
- SEND_SOUND(body, 'sound/ambience/ambifailure.ogg')
+ SEND_SOUND(body, 'sound/ambience/misc/ambifailure.ogg')
to_chat(body, span_danger("You are the [name]."))
to_chat(body, span_danger("[desc]"))
switch(team)
diff --git a/code/modules/manufactorio/_manufacturing.dm b/code/modules/manufactorio/_manufacturing.dm
new file mode 100644
index 0000000000000..236c05dd86910
--- /dev/null
+++ b/code/modules/manufactorio/_manufacturing.dm
@@ -0,0 +1,123 @@
+#define MANUFACTURING_FAIL_FULL -1
+#define MANUFACTURING_FAIL 0
+#define MANUFACTURING_SUCCESS 1
+
+#define POCKET_INPUT "Input"
+#define POCKET_OUTPUT "Output"
+
+#define MANUFACTURING_TURF_LAG_LIMIT 10 // max items on a turf before we consider it full
+
+/obj/machinery/power/manufacturing
+ icon = 'icons/obj/machines/manufactorio.dmi'
+ name = "base manufacture receiving type"
+ desc = "this shouldnt exist"
+ density = TRUE
+ /// Do we add the simple_rotation component and a text that we are powered by cable? Also allows unwrenching
+ var/may_be_moved = TRUE
+ /// Allow taking in mobs from conveyors?
+ var/allow_mob_bump_intake = FALSE
+
+/obj/machinery/power/manufacturing/Initialize(mapload)
+ . = ..()
+ if(may_be_moved)
+ AddComponent(/datum/component/simple_rotation)
+ if(anchored)
+ connect_to_network()
+
+/obj/machinery/power/manufacturing/examine(mob/user)
+ . = ..()
+ if(may_be_moved)
+ . += "It receives power via cable, but certain buildings do not need power."
+ . += length(contents - circuit) ? "It contains:" : "Its empty."
+ for(var/atom/movable/thing as anything in contents - circuit)
+ var/text = thing.name
+ var/obj/item/stack/possible_stack = thing
+ if(istype(possible_stack))
+ text = "[possible_stack.amount] [text]"
+ . += text
+
+
+/obj/machinery/power/manufacturing/Bumped(atom/movable/bumped_atom) //attempt to put in whatever is pushed into us via conveyor
+ . = ..()
+ if((!allow_mob_bump_intake && ismob(bumped_atom)) || !anchored) //only uncomment if youre brave
+ return
+ var/conveyor = locate(/obj/machinery/conveyor) in bumped_atom.loc
+ if(isnull(conveyor))
+ return
+ receive_resource(bumped_atom, bumped_atom.loc, get_dir(src, bumped_atom))
+
+/obj/machinery/power/manufacturing/wrench_act(mob/living/user, obj/item/tool)
+ . = ..()
+ if(!may_be_moved)
+ return
+ default_unfasten_wrench(user, tool)
+ if(anchored)
+ connect_to_network()
+ else
+ disconnect_from_network()
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/power/manufacturing/screwdriver_act(mob/living/user, obj/item/tool)
+ if(default_deconstruction_screwdriver(user, icon_state, icon_state, tool))
+ return ITEM_INTERACT_SUCCESS
+ return ITEM_INTERACT_BLOCKING
+
+/obj/machinery/power/manufacturing/crowbar_act(mob/living/user, obj/item/tool)
+ . = ITEM_INTERACT_BLOCKING
+ if(default_deconstruction_crowbar(tool))
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/power/manufacturing/proc/generate_io_overlays(direction, color, offsets_override)
+ var/list/dir_offset
+ if(islist(offsets_override))
+ dir_offset = offsets_override
+ else
+ dir_offset = dir2offset(direction)
+ dir_offset[1] *= 32
+ dir_offset[2] *= 32
+ var/image/nonemissive = image(icon='icons/obj/doors/airlocks/station/overlays.dmi', icon_state="unres_[direction]")
+ nonemissive.pixel_x = dir_offset[1]
+ nonemissive.pixel_y = dir_offset[2]
+ nonemissive.color = color
+ var/mutable_appearance/emissive = emissive_appearance(nonemissive.icon, nonemissive.icon_state, offset_spokesman = src, alpha = nonemissive.alpha)
+ emissive.pixel_y = nonemissive.pixel_y
+ emissive.pixel_x = nonemissive.pixel_x
+ return list(nonemissive, emissive)
+
+/// Returns whatever object it may output, or null if it cant do that
+/obj/machinery/power/manufacturing/proc/request_resource()
+
+
+/obj/machinery/power/manufacturing/proc/receive_resource(atom/movable/receiving, atom/from, receive_dir)
+ CRASH("Unimplemented!") //check can_receive_resource here
+
+//use dir please
+/obj/machinery/power/manufacturing/proc/send_resource(atom/movable/sending, atom/what_or_dir)
+ if(isobj(what_or_dir))
+ var/obj/machinery/power/manufacturing/target = what_or_dir
+ return target.receive_resource(sending, src, get_step(src, what_or_dir))
+ var/turf/next_turf = isturf(what_or_dir) ? what_or_dir : get_step(src, what_or_dir)
+ var/obj/machinery/power/manufacturing/manufactury = locate(/obj/machinery/power/manufacturing) in next_turf
+ if(!isnull(manufactury))
+ if(!manufactury.anchored)
+ return MANUFACTURING_FAIL
+ return manufactury.receive_resource(sending, src, isturf(what_or_dir) ? get_dir(src, what_or_dir) : what_or_dir)
+ if(next_turf.is_blocked_turf(exclude_mobs = TRUE, source_atom = sending))
+ return MANUFACTURING_FAIL
+ if(length(next_turf.contents) >= MANUFACTURING_TURF_LAG_LIMIT)
+ return MANUFACTURING_FAIL_FULL
+ if(isnull(sending))
+ return MANUFACTURING_SUCCESS // for the sake of being used as a check
+ if(isnull(sending.loc) || !sending.Move(next_turf, get_dir(src, next_turf)))
+ sending.forceMove(next_turf)
+ return MANUFACTURING_SUCCESS
+
+/// Checks if this stack (if not a stack does not do anything) can merge WITHOUT creating two stacks in contents
+/obj/machinery/power/manufacturing/proc/may_merge_in_contents(obj/item/stack/stack)
+ if(!istype(stack))
+ return
+ for(var/obj/item/stack/other in contents - circuit)
+ if(!other.can_merge(stack))
+ continue
+ if(other.amount + stack.amount <= other.max_amount)
+ return other
diff --git a/code/modules/manufactorio/machines/crafter.dm b/code/modules/manufactorio/machines/crafter.dm
new file mode 100644
index 0000000000000..ee794d2930121
--- /dev/null
+++ b/code/modules/manufactorio/machines/crafter.dm
@@ -0,0 +1,139 @@
+/obj/machinery/power/manufacturing/crafter
+ name = "manufacturing assembling machine"
+ desc = "Assembles (crafts) the set recipe until it runs out of resources. Inputs irrelevant to the recipe are ignored."
+ icon_state = "crafter"
+ circuit = /obj/item/circuitboard/machine/manucrafter
+ /// power used per process() spent crafting
+ var/power_cost = 5 KILO WATTS
+ /// our output, if the way out was blocked is held here
+ var/atom/movable/withheld
+ /// current recipe
+ var/datum/crafting_recipe/recipe
+ /// crafting component
+ var/datum/component/personal_crafting/machine/craftsman
+ /// current timer for our crafting
+ var/craft_timer
+ /// do we use cooking recipes instead
+ var/cooking = FALSE
+
+/obj/machinery/power/manufacturing/crafter/Initialize(mapload)
+ . = ..()
+ craftsman = AddComponent(/datum/component/personal_crafting/machine)
+
+/obj/machinery/power/manufacturing/crafter/examine(mob/user)
+ . = ..()
+ . += span_notice("It is currently manufacturing [isnull(recipe) ? "nothing. Use a multitool to set it" : recipe.name].")
+ if(isnull(recipe))
+ return
+ . += span_notice("It needs:")
+ for(var/valid_type in recipe.reqs)
+ // Check if they're datums, specifically reagents.
+ var/datum/reagent/reagent_ingredient = valid_type
+ if(istype(reagent_ingredient))
+ var/amount = recipe.reqs[reagent_ingredient]
+ . += "[amount] unit[amount > 1 ? "s" : ""] of [initial(reagent_ingredient.name)]"
+
+ var/atom/ingredient = valid_type
+ var/amount = recipe.reqs[ingredient]
+
+ . += "[amount > 1 ? ("[amount]" + " of") : "a"] [initial(ingredient.name)]"
+
+/obj/machinery/power/manufacturing/crafter/update_overlays()
+ . = ..()
+ . += generate_io_overlays(dir, COLOR_ORANGE)
+ for(var/target_dir in GLOB.cardinals - dir)
+ . += generate_io_overlays(target_dir, COLOR_MODERATE_BLUE)
+
+/obj/machinery/power/manufacturing/crafter/proc/valid_for_recipe(obj/item/checking)
+ . = FALSE
+ for(var/requirement_path in recipe.reqs)
+ if(!ispath(checking.type, requirement_path) || recipe.blacklist.Find(checking.type))
+ continue
+ return TRUE
+
+/obj/machinery/power/manufacturing/crafter/proc/contains_type(path)
+ . = FALSE
+ for(var/content in contents - circuit)
+ if(!istype(content, path))
+ continue
+ return TRUE
+
+/obj/machinery/power/manufacturing/crafter/receive_resource(obj/receiving, atom/from, receive_dir)
+ if(isnull(recipe) || !isitem(receiving) || surplus() < power_cost)
+ return MANUFACTURING_FAIL
+ if(receive_dir == dir || !valid_for_recipe(receiving))
+ return MANUFACTURING_FAIL
+ if(!may_merge_in_contents(receiving) && contains_type(receiving.type))
+ return MANUFACTURING_FAIL_FULL
+ receiving.Move(src, get_dir(receiving, src))
+ START_PROCESSING(SSmanufacturing, src)
+ return MANUFACTURING_SUCCESS
+
+/obj/machinery/power/manufacturing/crafter/multitool_act(mob/living/user, obj/item/tool)
+ . = NONE
+ var/list/unavailable = list()
+ for(var/datum/crafting_recipe/potential_recipe as anything in cooking ? GLOB.cooking_recipes : GLOB.crafting_recipes)
+ if(craftsman.is_recipe_available(potential_recipe, user))
+ continue
+ var/obj/result = initial(potential_recipe.result)
+ if(istype(result) && initial(result.anchored))
+ continue
+ unavailable += potential_recipe
+ var/result = tgui_input_list(usr, "Recipe", "Select Recipe", (cooking ? GLOB.cooking_recipes : GLOB.crafting_recipes) - unavailable)
+ if(isnull(result) || result == recipe || !user.can_perform_action(src))
+ return ITEM_INTERACT_FAILURE
+ var/dump_target = get_step(src, get_dir(src, user))
+ for(var/atom/movable/thing as anything in contents - circuit)
+ thing.Move(dump_target)
+ recipe = result
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/power/manufacturing/crafter/Exited(atom/movable/gone, direction)
+ . = ..()
+ if(gone == withheld)
+ withheld = null
+
+/obj/machinery/power/manufacturing/crafter/atom_destruction(damage_flag)
+ . = ..()
+ withheld?.Move(drop_location(src))
+
+/obj/machinery/power/manufacturing/crafter/Destroy()
+ . = ..()
+ recipe = null
+ craftsman = null
+ QDEL_NULL(withheld)
+
+/obj/machinery/power/manufacturing/crafter/process(seconds_per_tick)
+ if(!isnull(withheld) && !send_resource(withheld, dir))
+ return
+ if(!isnull(craft_timer))
+ if(surplus() >= power_cost)
+ add_load()
+ else
+ deltimer(craft_timer)
+ craft_timer = null
+ say("Power failure!")
+ return
+ if(isnull(recipe) || !craftsman.check_contents(src, recipe, craftsman.get_surroundings(src)))
+ return
+ flick_overlay_view(mutable_appearance(icon, "crafter_printing"), recipe.time)
+ craft_timer = addtimer(CALLBACK(src, PROC_REF(craft), recipe), recipe.time, TIMER_STOPPABLE)
+
+/obj/machinery/power/manufacturing/crafter/proc/craft(datum/crafting_recipe/recipe)
+ if(QDELETED(src))
+ return
+ craft_timer = null
+ var/atom/movable/result = craftsman.construct_item(src, recipe)
+ if(istype(result))
+ if(isitem(result))
+ result.pixel_x += rand(-4, 4)
+ result.pixel_y += rand(-4, 4)
+ result.Move(src)
+ send_resource(result, dir)
+ else
+ say(result)
+
+/obj/machinery/power/manufacturing/crafter/cooker
+ name = "manufacturing cooking machine" // maybe this shouldnt be available dont wanna make chef useless, though otherwise it would need a sprite
+ desc = "Cooks the set recipe until it runs out of resources. Inputs irrelevant to the recipe are ignored."
+ cooking = TRUE
diff --git a/code/modules/manufactorio/machines/crusher.dm b/code/modules/manufactorio/machines/crusher.dm
new file mode 100644
index 0000000000000..f0f18c10ae8c5
--- /dev/null
+++ b/code/modules/manufactorio/machines/crusher.dm
@@ -0,0 +1,83 @@
+/obj/machinery/power/manufacturing/crusher //todo make it work for other stuff
+ name = "manufacturing crusher"
+ desc = "Crushes any item put into it, boulders and such. Materials below a sheet are stored in the machine."
+ icon_state = "crusher"
+ circuit = /obj/item/circuitboard/machine/manucrusher
+ /// power used to crush
+ var/crush_cost = 3 KILO WATTS
+ /// how much can we hold
+ var/capacity = 5
+ /// withheld output because output is either blocked or full
+ var/atom/movable/withholding
+ /// list of held mats
+ var/list/obj/item/stack/held_mats = list()
+
+/obj/machinery/power/manufacturing/crusher/update_overlays()
+ . = ..()
+ . += generate_io_overlays(dir, COLOR_ORANGE) // OUT - stuff in it
+ . += generate_io_overlays(REVERSE_DIR(dir), COLOR_MODERATE_BLUE) // IN - to crush
+
+/obj/machinery/power/manufacturing/crusher/Destroy()
+ . = ..()
+ QDEL_NULL(withholding)
+
+/obj/machinery/power/manufacturing/crusher/atom_destruction(damage_flag)
+ withholding?.Move(drop_location())
+ return ..()
+
+/obj/machinery/power/manufacturing/crusher/receive_resource(obj/receiving, atom/from, receive_dir)
+ if(istype(receiving, /obj/item/stack/ore) || receiving.resistance_flags & INDESTRUCTIBLE || !isitem(receiving) || surplus() < crush_cost || receive_dir != REVERSE_DIR(dir))
+ return MANUFACTURING_FAIL
+ if(!may_merge_in_contents(receiving) && length(contents - circuit) >= capacity)
+ return MANUFACTURING_FAIL_FULL
+ receiving.Move(src, get_dir(receiving, src))
+ START_PROCESSING(SSmanufacturing, src)
+ return MANUFACTURING_SUCCESS
+
+/obj/machinery/power/manufacturing/crusher/Exited(atom/movable/gone, direction)
+ . = ..()
+ if(gone == withholding)
+ withholding = null
+
+/obj/machinery/power/manufacturing/crusher/process(seconds_per_tick) //noot functional
+ if(!isnull(withholding) && !send_resource(withholding, dir))
+ return
+ for(var/material in held_mats)
+ if(held_mats[material] >= 1)
+ var/new_amount = floor(held_mats[material])
+ held_mats[material] -= new_amount
+ if(held_mats[material] <= 0)
+ held_mats -= material
+ withholding = new material(null, new_amount)
+ return
+ var/list/poor_saps = contents - circuit
+ if(!length(poor_saps))
+ return PROCESS_KILL
+ if(surplus() < crush_cost)
+ return
+ var/obj/victim = poor_saps[length(poor_saps)]
+ if(istype(victim)) //todo handling for other things
+ if(!length(victim.custom_materials))
+ add_load(crush_cost)
+ victim.atom_destruction()
+ for(var/obj/object in victim.contents+victim)
+ for(var/datum/material/possible_mat as anything in object.custom_materials)
+ var/quantity = object.custom_materials[possible_mat]
+ object.set_custom_materials(object.custom_materials.Copy() - possible_mat, 1)
+ var/type_to_use = istype(victim, /obj/item/boulder) ? possible_mat.ore_type : possible_mat.sheet_type
+ if(quantity < SHEET_MATERIAL_AMOUNT)
+ if(!(type_to_use in held_mats))
+ held_mats[type_to_use] = quantity / SHEET_MATERIAL_AMOUNT
+ continue
+ held_mats[type_to_use] += quantity / SHEET_MATERIAL_AMOUNT
+ continue
+ var/obj/item/stack/sheet/new_item = new type_to_use(src, quantity / SHEET_MATERIAL_AMOUNT)
+ if(!send_resource(new_item, dir))
+ withholding = new_item
+ return
+ else if(isliving(victim))
+ var/mob/living/poor_sap = victim
+ poor_sap.adjustBruteLoss(95, TRUE)
+ if(!send_resource(poor_sap, dir))
+ withholding = poor_sap
+ return
diff --git a/code/modules/manufactorio/machines/debug.dm b/code/modules/manufactorio/machines/debug.dm
new file mode 100644
index 0000000000000..7c21cf4e989a7
--- /dev/null
+++ b/code/modules/manufactorio/machines/debug.dm
@@ -0,0 +1,18 @@
+/obj/loop_spawner
+ name = "testing loop spawner"
+ icon = 'icons/obj/machines/mining_machines.dmi'
+ icon_state = "unloader"
+ anchored = TRUE
+ color = COLOR_PURPLE
+ /// directions we can output to right now
+ var/to_spawn = /obj/item/screwdriver
+ /// the subsystem to process us
+ var/subsystem_to_process_us = /datum/controller/subsystem/processing/obj
+
+/obj/loop_spawner/Initialize(mapload)
+ . = ..()
+ var/datum/controller/subsystem/processing/subsystem = locate(subsystem_to_process_us) in Master.subsystems
+ START_PROCESSING(subsystem, src)
+
+/obj/loop_spawner/process(seconds_per_tick)
+ new to_spawn(get_step(src, dir))
diff --git a/code/modules/manufactorio/machines/lathe.dm b/code/modules/manufactorio/machines/lathe.dm
new file mode 100644
index 0000000000000..2669e851b931f
--- /dev/null
+++ b/code/modules/manufactorio/machines/lathe.dm
@@ -0,0 +1,145 @@
+/obj/machinery/power/manufacturing/lathe // this is a heavily gutted autolathe
+ name = "manufacturing lathe"
+ desc = "Lathes the set recipe until it runs out of resources. Only accepts sheets or other kinds of material stacks."
+ icon_state = "lathe"
+ circuit = /obj/item/circuitboard/machine/manulathe
+ /// power cost for lathing
+ var/power_cost = 5 KILO WATTS
+ /// design id we print
+ var/design_id
+ ///The container to hold materials
+ var/datum/component/material_container/materials
+ //looping sound for printing items
+ var/datum/looping_sound/lathe_print/print_sound
+ ///Designs related to the autolathe
+ var/datum/techweb/autounlocking/stored_research
+ /// timer id of printing
+ var/busy = FALSE
+ /// our output, if the way out was blocked is held here
+ var/atom/movable/withheld
+
+/obj/machinery/power/manufacturing/lathe/Initialize(mapload)
+ . = ..()
+ print_sound = new(src, FALSE)
+ materials = AddComponent( \
+ /datum/component/material_container, \
+ SSmaterials.materials_by_category[MAT_CATEGORY_ITEM_MATERIAL], \
+ SHEET_MATERIAL_AMOUNT * MAX_STACK_SIZE * 2, \
+ MATCONTAINER_EXAMINE|MATCONTAINER_NO_INSERT, \
+ )
+ if(!GLOB.autounlock_techwebs[/datum/techweb/autounlocking/autolathe])
+ GLOB.autounlock_techwebs[/datum/techweb/autounlocking/autolathe] = new /datum/techweb/autounlocking/autolathe
+ stored_research = GLOB.autounlock_techwebs[/datum/techweb/autounlocking/autolathe]
+
+/obj/machinery/power/manufacturing/lathe/examine(mob/user)
+ . = ..()
+ var/datum/design/design
+ if(!isnull(design_id))
+ design = SSresearch.techweb_design_by_id(design_id)
+ . += span_notice("It is set to print [!isnull(design) ? design.name : "nothing, set with a multitool"].")
+ if(isnull(design))
+ return
+ . += span_notice("It needs:")
+ for(var/valid_type in design.materials)
+ var/atom/ingredient = valid_type
+ var/amount = design.materials[ingredient] / SHEET_MATERIAL_AMOUNT
+
+ . += "[amount] sheets of [initial(ingredient.name)]"
+
+/obj/machinery/power/manufacturing/lathe/update_overlays()
+ . = ..()
+ . += generate_io_overlays(dir, COLOR_ORANGE) // OUT - stuff in it
+ . += generate_io_overlays(REVERSE_DIR(dir), COLOR_MODERATE_BLUE) // IN - to crush
+
+/obj/machinery/power/manufacturing/lathe/Destroy()
+ . = ..()
+ stored_research = null
+ QDEL_NULL(print_sound)
+ materials = null
+ QDEL_NULL(withheld)
+
+/obj/machinery/power/manufacturing/lathe/atom_destruction(damage_flag)
+ withheld?.Move(drop_location())
+ return ..()
+
+/obj/machinery/power/manufacturing/lathe/receive_resource(atom/movable/receiving, atom/from, receive_dir)
+ if(!isstack(receiving) || receiving.resistance_flags & INDESTRUCTIBLE || receive_dir != REVERSE_DIR(dir))
+ return MANUFACTURING_FAIL
+ materials.insert_item(receiving)
+ return MANUFACTURING_SUCCESS
+
+/obj/machinery/power/manufacturing/lathe/multitool_act(mob/living/user, obj/item/tool)
+ . = ..()
+ var/list/name_to_id = list()
+ for(var/id in stored_research.researched_designs)
+ var/datum/design/design = SSresearch.techweb_design_by_id(id)
+ name_to_id[design.name] = id
+ var/result = tgui_input_list(user, "Select Design", "Select Design", sort_list(name_to_id))
+ if(isnull(result))
+ return ITEM_INTERACT_FAILURE
+ design_id = name_to_id[result]
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/power/manufacturing/lathe/process()
+ if(!isnull(withheld) && !send_resource(withheld, dir))
+ return
+
+ var/datum/design/design = SSresearch.techweb_design_by_id(design_id)
+ if(isnull(design) || !(design.build_type & AUTOLATHE))
+ return
+ if(surplus() < power_cost)
+ finalize_build()
+ return
+ //check for materials required. For custom material items decode their required materials
+ var/list/materials_needed = list()
+ for(var/material in design.materials)
+ var/amount_needed = design.materials[material]
+ if(istext(material)) // category
+ for(var/datum/material/valid_candidate as anything in SSmaterials.materials_by_category[material])
+ if(materials.get_material_amount(valid_candidate) < amount_needed)
+ continue
+ material = valid_candidate
+ break
+ if(isnull(material))
+ return
+ materials_needed[material] = amount_needed
+
+ if(!materials.has_materials(materials_needed))
+ return
+
+ var/craft_time = (design.construction_time * design.lathe_time_factor) ** 0.8
+ flick_overlay_view(mutable_appearance(icon, "crafter_printing"), craft_time)
+ print_sound.start()
+ add_load(power_cost)
+ busy = addtimer(CALLBACK(src, PROC_REF(do_make_item), design, materials_needed), craft_time, TIMER_UNIQUE | TIMER_STOPPABLE | TIMER_DELETE_ME)
+
+/obj/machinery/power/manufacturing/lathe/proc/do_make_item(datum/design/design, list/materials_needed)
+ finalize_build()
+ if(surplus() < power_cost)
+ return
+
+ var/is_stack = ispath(design.build_path, /obj/item/stack)
+ if(!materials.has_materials(materials_needed))
+ return
+ materials.use_materials(materials_needed)
+
+ var/atom/movable/created
+ if(is_stack)
+ var/obj/item/stack/stack_item = initial(design.build_path)
+ created = new stack_item(null, 1)
+ else
+ created = new design.build_path(null)
+ split_materials_uniformly(materials_needed, target_object = created)
+ if(isitem(created))
+ created.pixel_x = created.base_pixel_x + rand(-6, 6)
+ created.pixel_y = created.base_pixel_y + rand(-6, 6)
+ SSblackbox.record_feedback("nested tally", "lathe_printed_items", 1, list("[type]", "[created.type]"))
+
+ if(!send_resource(created, dir))
+ withheld = created
+
+
+/obj/machinery/power/manufacturing/lathe/proc/finalize_build()
+ print_sound.stop()
+ deltimer(busy)
+ busy = null
diff --git a/code/modules/manufactorio/machines/router.dm b/code/modules/manufactorio/machines/router.dm
new file mode 100644
index 0000000000000..7c57a930bd3a6
--- /dev/null
+++ b/code/modules/manufactorio/machines/router.dm
@@ -0,0 +1,66 @@
+/obj/machinery/power/manufacturing/router // Basically a splitter
+ name = "manufacturing router"
+ desc = "Distributes input to 3 output directions equally. Stacks are split, and you may toggle outputs with a multitool. May not receive from other routers."
+ allow_mob_bump_intake = TRUE
+ icon_state = "splitter"
+ circuit = /obj/item/circuitboard/machine/manurouter
+ /// outputs disabled with a multitool
+ var/list/disabled_dirs = list()
+ /// directions we can output to right now
+ var/list/directions
+
+/obj/machinery/power/manufacturing/router/Initialize(mapload)
+ . = ..()
+ directions = GLOB.cardinals.Copy()
+
+/obj/machinery/power/manufacturing/router/multitool_act(mob/living/user, obj/item/tool)
+ . = ..()
+ var/to_toggle = get_dir(src, user)
+ if(!(to_toggle in GLOB.cardinals))
+ balloon_alert(user, "stand inline!")
+ return ITEM_INTERACT_FAILURE
+ if(to_toggle in disabled_dirs)
+ disabled_dirs -= to_toggle
+ else
+ disabled_dirs += to_toggle
+ update_appearance(UPDATE_OVERLAYS)
+ balloon_alert(user, "toggled output")
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/power/manufacturing/router/update_overlays()
+ . = ..()
+ for(var/direction in GLOB.cardinals)
+ var/variant
+ if(disabled_dirs.Find(direction))
+ variant = "bl"
+ else
+ variant = (direction == dir) ? "in" : "out"
+ var/image/new_overlay = image(icon, "splitter_[variant]", layer = layer+0.001, dir = direction)
+ . += new_overlay
+
+/obj/machinery/power/manufacturing/router/receive_resource(obj/receiving, atom/from, receive_dir)
+ if(istype(from, /obj/machinery/power/manufacturing/router))
+ return MANUFACTURING_FAIL
+ var/list/filtered = directions - receive_dir - disabled_dirs
+ if(!length(filtered))
+ directions = GLOB.cardinals.Copy()
+ for(var/target in filtered)
+ directions -= target
+ if(isstack(receiving))
+ receiving = handle_stack(receiving, receive_dir)
+ if(send_resource(receiving, target))
+ dir = receive_dir
+ update_appearance(UPDATE_OVERLAYS) // im sorry
+ return MANUFACTURING_SUCCESS
+ return MANUFACTURING_FAIL_FULL
+
+/obj/machinery/power/manufacturing/router/proc/handle_stack(obj/item/stack/stack, direction)
+ . = stack
+ var/potential_output_count = length(GLOB.cardinals - direction - disabled_dirs)
+ if(potential_output_count <= 1)
+ return
+ var/split_amount = round(stack.amount / potential_output_count, 1)
+ if(stack.amount == potential_output_count)
+ return
+ var/atom/movable/new_stack = stack.split_stack(amount = min(stack.amount, split_amount))
+ return new_stack
diff --git a/code/modules/manufactorio/machines/smelter.dm b/code/modules/manufactorio/machines/smelter.dm
new file mode 100644
index 0000000000000..1a7beca66f49c
--- /dev/null
+++ b/code/modules/manufactorio/machines/smelter.dm
@@ -0,0 +1,59 @@
+/obj/machinery/power/manufacturing/smelter
+ name = "manufacturing smelter"
+ desc = "Pretty much incinerates whatever is put into it. Refines ore (not boulders)."
+ icon_state = "smelter"
+ circuit = /obj/item/circuitboard/machine/manusmelter
+ /// power used to smelt
+ var/power_cost = 4 KILO WATTS
+ /// our output, if the way out was blocked is held here
+ var/atom/movable/withheld
+
+/obj/machinery/power/manufacturing/smelter/update_overlays()
+ . = ..()
+ . += generate_io_overlays(dir, COLOR_ORANGE) // OUT - stuff in it
+ . += generate_io_overlays(REVERSE_DIR(dir), COLOR_MODERATE_BLUE) // IN - to crush
+
+/obj/machinery/power/manufacturing/smelter/receive_resource(obj/receiving, atom/from, receive_dir)
+ if(!isitem(receiving) || surplus() < power_cost || receive_dir != REVERSE_DIR(dir))
+ return MANUFACTURING_FAIL
+ var/list/stacks = contents - circuit
+ if(!may_merge_in_contents(receiving) && length(stacks) >= 5)
+ return MANUFACTURING_FAIL_FULL
+ receiving.Move(src, get_dir(receiving, src))
+ START_PROCESSING(SSmanufacturing, src)
+ return MANUFACTURING_SUCCESS
+
+/obj/machinery/power/manufacturing/smelter/Destroy()
+ . = ..()
+ QDEL_NULL(withheld)
+
+/obj/machinery/power/manufacturing/smelter/atom_destruction(damage_flag)
+ withheld?.Move(drop_location())
+ return ..()
+
+/obj/machinery/power/manufacturing/smelter/process(seconds_per_tick)
+ var/list/stacks = contents - circuit
+ if(!length(stacks))
+ return
+
+ var/list/stacks_preprocess = contents - circuit
+ var/obj/item/stack/ore/ore = stacks_preprocess[length(stacks_preprocess)]
+ if(isnull(ore))
+ return
+ if(isnull(withheld) && surplus() >= power_cost)
+ icon_state="smelter_on"
+ add_load(power_cost)
+ if(istype(ore))
+ var/obj/item/stack/new_stack = new ore.refined_type(null, min(5, ore.amount), FALSE)
+ new_stack.moveToNullspace()
+ ore.use(min(5, ore.amount))
+ ore = new_stack
+ else
+ ore.fire_act(1400)
+ withheld = ore
+ else if(surplus() < power_cost)
+ icon_state = "smelter"
+ if(send_resource(withheld, dir))
+ withheld = null // nullspace thumbs down
+ if(!length(contents - circuit))
+ return PROCESS_KILL //we finished
diff --git a/code/modules/manufactorio/machines/sorter.dm b/code/modules/manufactorio/machines/sorter.dm
new file mode 100644
index 0000000000000..b749b14c6d893
--- /dev/null
+++ b/code/modules/manufactorio/machines/sorter.dm
@@ -0,0 +1,149 @@
+/obj/machinery/power/manufacturing/sorter
+ icon_state = "router"
+ name = "conveyor sort-router"
+ desc = "Pushes things on it to its sides following set criteria, set via multitool."
+ layer = BELOW_OPEN_DOOR_LAYER
+ density = FALSE
+ interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND
+ circuit = /obj/item/circuitboard/machine/manusorter
+ /// for mappers; filter path = list(direction, value), otherwise a list of initialized filters
+ var/list/sort_filters = list()
+ /// dir to push to if there is no criteria
+ var/dir_if_not_met
+ /// timer id of the thing that makes stuff move
+ var/delay_timerid
+ /// max filters
+ var/max_filters = 10
+
+/obj/machinery/power/manufacturing/sorter/Initialize(mapload)
+ . = ..()
+ if(isnull(dir_if_not_met))
+ dir_if_not_met = dir
+ var/static/list/loc_connections = list(
+ COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
+ )
+ AddElement(/datum/element/connect_loc, loc_connections)
+ for(var/i in 1 to length(sort_filters))
+ var/creating_type = sort_filters[i]
+ var/list/values = sort_filters[creating_type]
+ var/datum/sortrouter_filter/new_type = new creating_type(src)
+ new_type.dir_target = values[1]
+ new_type.value = values[2]
+ sort_filters[i] = new_type
+ START_PROCESSING(SSobj, src)
+
+/obj/machinery/power/manufacturing/sorter/Destroy()
+ . = ..()
+ QDEL_LIST(sort_filters)
+
+/obj/machinery/power/manufacturing/sorter/multitool_act(mob/living/user, obj/item/tool)
+ . = ..()
+ ui_interact(user)
+
+/obj/machinery/power/manufacturing/sorter/receive_resource(atom/movable/receiving, atom/from, receive_dir)
+ if(length(loc.contents) >= MANUFACTURING_TURF_LAG_LIMIT)
+ return MANUFACTURING_FAIL_FULL
+ receiving.Move(loc)
+ return MANUFACTURING_SUCCESS
+
+
+/obj/machinery/power/manufacturing/sorter/ui_data(mob/user)
+ . = list()
+ .["unmet_dir"] = dir_if_not_met
+ .["filters"] = list()
+ for(var/datum/sortrouter_filter/sorting as anything in sort_filters)
+ .["filters"] += list(list(
+ "name" = sorting.return_name(),
+ "ref" = REF(sorting),
+ "inverted" = sorting.inverted,
+ "dir" = sorting.dir_target,
+ ))
+
+/obj/machinery/power/manufacturing/sorter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+ switch(action)
+ if("del_filter")
+ var/datum/sortrouter_filter/filter = locate(params["ref"])
+ if(isnull(filter))
+ return
+ sort_filters -= filter
+ qdel(filter)
+ return TRUE
+ if("new_filter")
+ if(length(sort_filters) >= max_filters)
+ return
+ var/static/list/filter_by_name
+ if(!length(filter_by_name))
+ filter_by_name = list()
+ for(var/datum/sortrouter_filter/to_do as anything in subtypesof(/datum/sortrouter_filter))
+ filter_by_name[initial(to_do.name)] = to_do
+ filter_by_name = sort_list(filter_by_name)
+ var/target_type = tgui_input_list(usr, "Select a filter", "New Filter", filter_by_name)
+ if(isnull(target_type)|| !usr.can_perform_action(src, ALLOW_SILICON_REACH))
+ return
+ target_type = filter_by_name[target_type]
+ sort_filters += new target_type(src)
+ return TRUE
+ if("rotate")
+ var/datum/sortrouter_filter/filter = locate(params["ref"])
+ if(isnull(filter))
+ return
+ var/next_ind = GLOB.cardinals.Find(filter.dir_target) + 1
+ filter.dir_target = GLOB.cardinals[WRAP(next_ind, 1, 5)]
+ return TRUE
+ if("rotate_unmet")
+ var/next_ind = GLOB.cardinals.Find(dir_if_not_met) + 1
+ dir_if_not_met = GLOB.cardinals[WRAP(next_ind, 1, 5)]
+ return TRUE
+ if("edit")
+ var/datum/sortrouter_filter/filter = locate(params["ref"])
+ if(isnull(filter))
+ return
+ filter.edit(usr)
+ return TRUE
+ if("shift")
+ var/datum/sortrouter_filter/filter = locate(params["ref"])
+ if(isnull(filter))
+ return
+ var/next_ind = WRAP(sort_filters.Find(filter) + text2num(params["amount"]), 1, length(sort_filters)+1)
+ sort_filters -= filter
+ sort_filters.Insert(next_ind, filter)
+ return TRUE
+
+/obj/machinery/power/manufacturing/sorter/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ManufacturingSorter")
+ ui.open()
+
+/obj/machinery/power/manufacturing/sorter/proc/send_nomobs(atom/movable/moving, dir)
+ var/mutable_appearance/operate = mutable_appearance(icon, "router_operate")
+ operate.dir = dir
+ flick_overlay_view(operate, 1 SECONDS)
+ return ismob(moving) ? moving.Move(get_step(src,dir), dir) : send_resource(moving, dir)
+
+/obj/machinery/power/manufacturing/sorter/process()
+ if(delay_timerid || !length(loc?.contents - 1))
+ return
+ launch_everything()
+
+/obj/machinery/power/manufacturing/sorter/proc/on_entered(datum/source, atom/movable/mover)
+ SIGNAL_HANDLER
+ if(mover == src || !istype(mover) || mover.anchored || delay_timerid)
+ return
+ delay_timerid = addtimer(CALLBACK(src, PROC_REF(launch_everything)), 0.2 SECONDS)
+
+/obj/machinery/power/manufacturing/sorter/proc/launch_everything()
+ delay_timerid = null
+ var/turf/where_we_at = get_turf(src)
+ for(var/atom/movable/mover as anything in where_we_at.contents)
+ if(mover.anchored)
+ continue
+ for(var/datum/sortrouter_filter/sorting as anything in sort_filters)
+ if(sorting.meets_conditions(mover) == sorting.inverted)
+ continue
+ send_nomobs(mover, sorting.dir_target)
+ return
+ send_nomobs(mover, dir_if_not_met)
diff --git a/code/modules/manufactorio/machines/sorter_filters.dm b/code/modules/manufactorio/machines/sorter_filters.dm
new file mode 100644
index 0000000000000..cb7e31cc41ed4
--- /dev/null
+++ b/code/modules/manufactorio/machines/sorter_filters.dm
@@ -0,0 +1,120 @@
+/datum/sortrouter_filter
+ /// name of the filter shown in UI
+ var/name
+ /// if it meets criteria, item is pushed to this direction
+ var/dir_target = NORTH
+ /// value of our filter, checked by us
+ var/value = ""
+ /// is our output inverted? checked by sorter
+ var/inverted = FALSE
+ /// the sorter we belong to
+ var/obj/machinery/power/manufacturing/sorter/sorter
+
+/datum/sortrouter_filter/New(sorter)
+ . = ..()
+ if(isnull(sorter))
+ return
+ src.sorter = sorter
+
+/datum/sortrouter_filter/Destroy()
+ . = ..()
+ if(isnull(sorter))
+ return
+ sorter = null
+
+/datum/sortrouter_filter
+
+/datum/sortrouter_filter/proc/return_name()
+ return name
+
+/datum/sortrouter_filter/proc/edit(mob/user)
+ to_chat(user, "This filter is not editable.")
+
+/datum/sortrouter_filter/proc/meets_conditions(atom/checking)
+
+/datum/sortrouter_filter/is_stack
+ name = "input is stack"
+
+/datum/sortrouter_filter/is_stack/meets_conditions(atom/checking)
+ return isstack(checking)
+
+/datum/sortrouter_filter/is_ore
+ name = "input is ore"
+
+/datum/sortrouter_filter/is_ore/meets_conditions(atom/checking)
+ return istype(checking, /obj/item/stack/ore)
+
+/datum/sortrouter_filter/is_mail
+ name = "input is mail"
+
+/datum/sortrouter_filter/is_mail/meets_conditions(atom/checking)
+ return istype(checking, /obj/item/mail)
+
+/datum/sortrouter_filter/is_tagged
+ name = "input is tagged X"
+
+/datum/sortrouter_filter/is_tagged/edit(mob/user)
+ var/target = tgui_input_list(user, "Select a tag", "Tag", sort_list(GLOB.TAGGERLOCATIONS))
+ if(isnull(target) || !user.can_perform_action(sorter, ALLOW_SILICON_REACH))
+ return
+ value = GLOB.TAGGERLOCATIONS.Find(target)
+
+/datum/sortrouter_filter/is_tagged/return_name()
+ return "input is tagged [value ? GLOB.TAGGERLOCATIONS[value] : ""]"
+
+/datum/sortrouter_filter/is_tagged/meets_conditions(checking)
+ var/obj/item/delivery/mail_or_delivery = checking
+ var/sort_tag
+ if(istype(checking, /obj/item/delivery) || istype(checking, /obj/item/mail))
+ sort_tag = mail_or_delivery.sort_tag
+
+ return value == sort_tag
+
+/datum/sortrouter_filter/name_contains
+ name = "input's name contains"
+
+/datum/sortrouter_filter/name_contains/edit(mob/user)
+ var/target = tgui_input_text(user, "What should it contain?", "Name", value, 12)
+ if(isnull(target)|| !user.can_perform_action(sorter, ALLOW_SILICON_REACH))
+ return
+ value = target
+
+/datum/sortrouter_filter/name_contains/return_name()
+ return "input's name contains [value]"
+
+/datum/sortrouter_filter/name_contains/meets_conditions(atom/checking)
+ return findtext(LOWER_TEXT(checking.name), value)
+
+/datum/sortrouter_filter/is_path_specific
+ name = "input is specific item"
+ /// are we currently listening for an item to set as our filter?
+ var/currently_listening = FALSE
+
+/datum/sortrouter_filter/is_path_specific/edit(mob/user)
+ name = initial(name)
+ if(!currently_listening)
+ name = "awaiting item"
+ to_chat(user, "Hit the sorter with the item of choice to set the filter.")
+ sorter.balloon_alert(user, "awaiting item!")
+ currently_listening = TRUE
+ RegisterSignal(sorter, COMSIG_ATOM_ATTACKBY, PROC_REF(sorter_hit))
+ else
+ currently_listening = FALSE
+ UnregisterSignal(sorter, COMSIG_ATOM_ATTACKBY)
+
+/datum/sortrouter_filter/is_path_specific/proc/sorter_hit(datum/source, obj/item/attacking_item, user, params)
+ currently_listening = FALSE
+ value = attacking_item.type
+ name = attacking_item.name
+ sorter.balloon_alert(user, "filter set")
+ UnregisterSignal(sorter, COMSIG_ATOM_ATTACKBY)
+ return COMPONENT_NO_AFTERATTACK
+
+/datum/sortrouter_filter/is_path_specific/meets_conditions(atom/checking)
+ return checking.type == value
+
+/datum/sortrouter_filter/is_path_specific/subtypes
+ name = "input is specific kind of item"
+
+/datum/sortrouter_filter/is_path_specific/subtypes/meets_conditions(atom/checking)
+ return istype(checking.type, value)
diff --git a/code/modules/manufactorio/machines/storagebox.dm b/code/modules/manufactorio/machines/storagebox.dm
new file mode 100644
index 0000000000000..21957871cf803
--- /dev/null
+++ b/code/modules/manufactorio/machines/storagebox.dm
@@ -0,0 +1,46 @@
+/obj/machinery/power/manufacturing/storagebox
+ name = "manufacturing storage unit"
+ desc = "Its basically a box. Receives resources (if anchored). Needs a machine to take stuff out of without dumping everything out."
+ icon_state = "box"
+ /// how much can we hold
+ var/max_stuff = 16
+
+/obj/machinery/power/manufacturing/request_resource() //returns last inserted item
+ var/list/real_contents = contents - circuit
+ if(!length(real_contents))
+ return
+ return (real_contents)[length(real_contents)]
+
+/obj/machinery/power/manufacturing/storagebox/receive_resource(atom/movable/receiving, atom/from, receive_dir)
+ if(iscloset(receiving) && length(receiving.contents))
+ return MANUFACTURING_FAIL
+ if(!may_merge_in_contents(receiving) && length(contents - circuit) >= max_stuff)
+ return MANUFACTURING_FAIL_FULL
+ receiving.Move(src,receive_dir)
+ return MANUFACTURING_SUCCESS
+
+/obj/machinery/power/manufacturing/storagebox/container_resist_act(mob/living/user)
+ . = ..()
+ user.Move(drop_location())
+
+/obj/machinery/power/manufacturing/storagebox/screwdriver_act(mob/living/user, obj/item/tool)
+ . = NONE
+ balloon_alert(user, "disassembling...")
+ if(!do_after(user, 5 SECONDS, src))
+ return ITEM_INTERACT_FAILURE
+ atom_destruction()
+ return ITEM_INTERACT_SUCCESS
+
+/obj/machinery/power/manufacturing/storagebox/atom_destruction(damage_flag)
+ new /obj/item/stack/sheet/iron(drop_location(), 10)
+ dump_inventory_contents()
+ return ..()
+
+/obj/machinery/power/manufacturing/storagebox/attack_hand(mob/living/user, list/modifiers)
+ . = ..()
+ if(user.combat_mode)
+ return
+ balloon_alert(user, "dumping..")
+ if(!do_after(user, 1.25 SECONDS, src))
+ return
+ dump_inventory_contents()
diff --git a/code/modules/manufactorio/machines/unloader.dm b/code/modules/manufactorio/machines/unloader.dm
new file mode 100644
index 0000000000000..982c33582684e
--- /dev/null
+++ b/code/modules/manufactorio/machines/unloader.dm
@@ -0,0 +1,78 @@
+/obj/machinery/power/manufacturing/unloader
+ name = "manufacturing crate unloader"
+ desc = "Unloads crates (and ore boxes) passed into it, ejecting the empty crate to the side and its contents forwards. Use a multitool to flip the crate output."
+ icon = 'icons/obj/machines/mining_machines.dmi'
+ icon_state = "unloader-corner"
+ circuit = /obj/item/circuitboard/machine/manuunloader
+ /// power used per attempt to unload a crate
+ var/power_to_unload_crate = 2 KILO WATTS
+ /// whether the side we output unloaded crates is flipped
+ var/flip_side = FALSE
+
+/obj/machinery/power/manufacturing/unloader/update_overlays()
+ . = ..()
+ . += generate_io_overlays(dir, COLOR_ORANGE) // OUT - stuff in it
+ . += generate_io_overlays(REVERSE_DIR(dir), COLOR_MODERATE_BLUE) // IN - crate
+ . += generate_io_overlays(turn(dir, flip_side ? 90 : -90), COLOR_ORANGE) // OUT -- empty crate
+
+/obj/machinery/power/manufacturing/unloader/request_resource() //returns held crate if someone wants to do that for some reason
+ var/list/real_contents = contents - circuit
+ if(!length(real_contents))
+ return
+ return (real_contents)[1]
+
+/obj/machinery/power/manufacturing/unloader/multitool_act(mob/living/user, obj/item/tool)
+ . = ..()
+ balloon_alert(user, "flipped")
+ flip_side = !flip_side
+ update_appearance()
+
+/obj/machinery/power/manufacturing/unloader/receive_resource(obj/receiving, atom/from, receive_dir)
+ if(surplus() < power_to_unload_crate || receive_dir != REVERSE_DIR(dir))
+ return MANUFACTURING_FAIL
+ var/list/real_contents = contents - circuit
+ if(length(real_contents))
+ return MANUFACTURING_FAIL_FULL
+
+ var/obj/structure/closet/as_closet = receiving
+ var/obj/structure/ore_box/as_orebox = receiving
+ if(istype(as_closet))
+ if(!as_closet.can_open())
+ return MANUFACTURING_FAIL
+ else if(!istype(as_orebox))
+ return MANUFACTURING_FAIL
+ receiving.Move(src, get_dir(receiving, src))
+ START_PROCESSING(SSfastprocess, src)
+ return MANUFACTURING_SUCCESS
+
+/obj/machinery/power/manufacturing/unloader/process(seconds_per_tick)
+ var/list/real_contents = contents - circuit
+ if(!length(real_contents))
+ return PROCESS_KILL
+ if(surplus() < power_to_unload_crate)
+ return
+ add_load(power_to_unload_crate)
+ var/obj/structure/closet/closet = real_contents[1]
+ if(istype(closet))
+ return unload_crate(closet)
+ else
+ return unload_orebox(closet)
+
+/obj/machinery/power/manufacturing/unloader/proc/unload_crate(obj/structure/closet/closet)
+ if (!closet.contents_initialized)
+ closet.contents_initialized = TRUE
+ closet.PopulateContents()
+ SEND_SIGNAL(closet, COMSIG_CLOSET_CONTENTS_INITIALIZED)
+ for(var/atom/thing as anything in closet.contents)
+ if(ismob(thing))
+ continue
+ send_resource(thing, dir)
+ if(!length(closet.contents) && send_resource(closet, turn(dir, flip_side ? 90 : -90)))
+ closet.open(force = TRUE)
+ return PROCESS_KILL
+
+/obj/machinery/power/manufacturing/unloader/proc/unload_orebox(obj/structure/ore_box/box)
+ for(var/atom/thing as anything in box.contents)
+ send_resource(thing, dir)
+ if(!length(box.contents) && send_resource(box, turn(dir, flip_side ? 90 : -90)))
+ return PROCESS_KILL
diff --git a/code/modules/mapfluff/ruins/icemoonruin_code/hotsprings.dm b/code/modules/mapfluff/ruins/icemoonruin_code/hotsprings.dm
index 7c674e98e24a1..6952dd0af588b 100644
--- a/code/modules/mapfluff/ruins/icemoonruin_code/hotsprings.dm
+++ b/code/modules/mapfluff/ruins/icemoonruin_code/hotsprings.dm
@@ -11,6 +11,7 @@
*/
/turf/open/water/cursed_spring
+ name = "cursed spring"
baseturfs = /turf/open/water/cursed_spring
planetary_atmos = TRUE
initial_gas_mix = ICEMOON_DEFAULT_ATMOS
diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/biodome_winter.dm b/code/modules/mapfluff/ruins/lavalandruin_code/biodome_winter.dm
index 7f1c8d781f4f8..a5b1492a1520b 100644
--- a/code/modules/mapfluff/ruins/lavalandruin_code/biodome_winter.dm
+++ b/code/modules/mapfluff/ruins/lavalandruin_code/biodome_winter.dm
@@ -35,7 +35,7 @@
addtimer(CALLBACK(src, TYPE_PROC_REF(/atom/movable, throw_at), thrown_by, throw_range+2, throw_speed, null, TRUE), 0.1 SECONDS)
/obj/item/freeze_cube/proc/freeze_hit_atom(atom/movable/hit_atom)
- playsound(src, 'sound/effects/glassbr3.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/glass/glassbr3.ogg', 50, TRUE)
COOLDOWN_START(src, freeze_cooldown, cooldown_time)
if(isobj(hit_atom))
var/obj/hit_object = hit_atom
diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/puzzle.dm b/code/modules/mapfluff/ruins/lavalandruin_code/puzzle.dm
index ef6dc902c9f08..8501c21cbccf7 100644
--- a/code/modules/mapfluff/ruins/lavalandruin_code/puzzle.dm
+++ b/code/modules/mapfluff/ruins/lavalandruin_code/puzzle.dm
@@ -160,10 +160,10 @@
var/y = width - round((id - 1) / width)
var/x = ((id - 1) % width) + 1
- var/x_start = 1 + (x - 1) * world.icon_size
- var/x_end = x_start + world.icon_size - 1
- var/y_start = 1 + ((y - 1) * world.icon_size)
- var/y_end = y_start + world.icon_size - 1
+ var/x_start = 1 + (x - 1) * ICON_SIZE_X
+ var/x_end = x_start + ICON_SIZE_X - 1
+ var/y_start = 1 + ((y - 1) * ICON_SIZE_Y)
+ var/y_end = y_start + ICON_SIZE_Y - 1
var/icon/T = new(base_icon)
T.Crop(x_start,y_start,x_end,y_end)
diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/syndicate_base.dm b/code/modules/mapfluff/ruins/lavalandruin_code/syndicate_base.dm
index 26fdfcbb90bb0..c49b5c1399625 100644
--- a/code/modules/mapfluff/ruins/lavalandruin_code/syndicate_base.dm
+++ b/code/modules/mapfluff/ruins/lavalandruin_code/syndicate_base.dm
@@ -3,7 +3,6 @@
/obj/machinery/vending/syndichem
name = "\improper SyndiChem"
desc = "A vending machine full of grenades and grenade accessories. Sponsored by Donk Co."
- req_access = list(ACCESS_SYNDICATE)
products = list(/obj/item/stack/cable_coil = 5,
/obj/item/assembly/igniter = 20,
/obj/item/assembly/prox_sensor = 5,
diff --git a/code/modules/mapfluff/ruins/lavalandruin_code/watcher_grave.dm b/code/modules/mapfluff/ruins/lavalandruin_code/watcher_grave.dm
index f3b321b88b223..6d3ef03c3f028 100644
--- a/code/modules/mapfluff/ruins/lavalandruin_code/watcher_grave.dm
+++ b/code/modules/mapfluff/ruins/lavalandruin_code/watcher_grave.dm
@@ -153,7 +153,7 @@
/// Type of projectile we fire
var/projectile_type = /obj/projectile/baby_watcher_blast
/// Sound to make when we shoot
- var/projectile_sound = 'sound/weapons/pierce.ogg'
+ var/projectile_sound = 'sound/items/weapons/pierce.ogg'
/// Time between taking potshots at goliaths
var/fire_delay = 5 SECONDS
/// How much faster do we shoot when avenging our parent?
diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/ash_walker_den.dm b/code/modules/mapfluff/ruins/objects_and_mobs/ash_walker_den.dm
index 21c96f0aeaa10..7ef451ddc303a 100644
--- a/code/modules/mapfluff/ruins/objects_and_mobs/ash_walker_den.dm
+++ b/code/modules/mapfluff/ruins/objects_and_mobs/ash_walker_den.dm
@@ -65,7 +65,7 @@
else
deadmind = offeredmob.get_ghost(FALSE, TRUE)
to_chat(deadmind, "Your body has been returned to the nest. You are being remade anew, and will awaken shortly. Your memories will remain intact in your new body, as your soul is being salvaged")
- SEND_SOUND(deadmind, sound('sound/magic/enter_blood.ogg',volume=100))
+ SEND_SOUND(deadmind, sound('sound/effects/magic/enter_blood.ogg',volume=100))
addtimer(CALLBACK(src, PROC_REF(remake_walker), offeredmob), 20 SECONDS)
offeredmob.forceMove(src)
return
@@ -75,7 +75,7 @@
else
meat_counter++
visible_message(span_warning("Serrated tendrils eagerly pull [offeredmob] to [src], tearing the body apart as its blood seeps over the eggs."))
- playsound(get_turf(src),'sound/magic/demon_consume.ogg', 100, TRUE)
+ playsound(get_turf(src),'sound/effects/magic/demon_consume.ogg', 100, TRUE)
var/deliverykey = offeredmob.fingerprintslast //ckey of whoever brought the body
var/mob/living/deliverymob = get_mob_by_key(deliverykey) //mob of said ckey
//there is a 40% chance that the Lava Lizard unlocks their respawn with each sacrifice
@@ -103,7 +103,7 @@
oldmob.mind.transfer_to(newwalker)
newwalker.mind.grab_ghost()
to_chat(newwalker, "You have been pulled back from beyond the grave, with a new body and renewed purpose. Glory to the Necropolis!")
- playsound(get_turf(newwalker),'sound/magic/exit_blood.ogg', 100, TRUE)
+ playsound(get_turf(newwalker),'sound/effects/magic/exit_blood.ogg', 100, TRUE)
qdel(oldmob)
/obj/structure/lavaland/ash_walker/proc/spawn_mob()
diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/cursed_slot_machine.dm b/code/modules/mapfluff/ruins/objects_and_mobs/cursed_slot_machine.dm
index ab6b2bb1825c9..16b63f37b2a37 100644
--- a/code/modules/mapfluff/ruins/objects_and_mobs/cursed_slot_machine.dm
+++ b/code/modules/mapfluff/ruins/objects_and_mobs/cursed_slot_machine.dm
@@ -44,7 +44,7 @@
icon_screen = "slots_screen_working"
update_appearance()
- playsound(src, 'sound/lavaland/cursed_slot_machine.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/lavaland/cursed_slot_machine.ogg', 50, FALSE)
addtimer(CALLBACK(src, PROC_REF(determine_victor), user), 5 SECONDS)
/obj/structure/cursed_slot_machine/update_overlays()
@@ -84,11 +84,11 @@
user.apply_status_effect(/datum/status_effect/grouped/cursed)
SEND_SIGNAL(user, COMSIG_CURSED_SLOT_MACHINE_LOST)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, TRUE)
balloon_alert_to_viewers("you lost!")
return
- playsound(src, 'sound/lavaland/cursed_slot_machine_jackpot.ogg', 50, FALSE)
+ playsound(src, 'sound/machines/lavaland/cursed_slot_machine_jackpot.ogg', 50, FALSE)
new prize(get_turf(src))
if(user)
to_chat(user, span_boldwarning("You've hit the jackpot!!! Laughter echoes around you as your reward appears in the machine's place."))
diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/museum.dm b/code/modules/mapfluff/ruins/objects_and_mobs/museum.dm
index 57f20abb1aa19..c8b504b72e572 100644
--- a/code/modules/mapfluff/ruins/objects_and_mobs/museum.dm
+++ b/code/modules/mapfluff/ruins/objects_and_mobs/museum.dm
@@ -64,7 +64,7 @@
max_integrity = 5 //one tap
/obj/structure/fluff/balloon_nuke/atom_destruction()
- playsound(loc, 'sound/effects/cartoon_pop.ogg', 75, vary = TRUE)
+ playsound(loc, 'sound/effects/cartoon_sfx/cartoon_pop.ogg', 75, vary = TRUE)
..()
/obj/structure/fluff/fake_camera
@@ -159,7 +159,7 @@
mask = /obj/item/clothing/mask/fakemoustache/italian
/obj/machinery/vending/hotdog/museum
- onstation_override = TRUE
+ all_products_free = TRUE
/obj/machinery/vending/hotdog/museum/screwdriver_act(mob/living/user, obj/item/attack_item)
return NONE
@@ -199,6 +199,6 @@
var/obj/structure/toilet/destination = pick(partners)
forceMove(destination)
destination.w_items += w_class
- destination.contents += src
+ LAZYADD(destination.cistern_items, src)
#undef CAFE_KEYCARD_TOILETS
diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm b/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm
index 646de6a2186ef..7fda1df5951f0 100644
--- a/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm
+++ b/code/modules/mapfluff/ruins/objects_and_mobs/necropolis_gate.dm
@@ -122,7 +122,7 @@
sight_blocker.pixel_y = initial(sight_blocker.pixel_y) - (32 * sight_blocker_distance)
sight_blocker.forceMove(sight_blocker_turf)
sleep(0.25 SECONDS)
- playsound(T, 'sound/magic/clockwork/invoke_general.ogg', 30, TRUE, frequency = 15000)
+ playsound(T, 'sound/effects/magic/clockwork/invoke_general.ogg', 30, TRUE, frequency = 15000)
add_overlay(door_overlay)
open = FALSE
else
@@ -183,7 +183,7 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate)
message_admins("[user ? ADMIN_LOOKUPFLW(user):"Unknown"] has released Legion!")
user.log_message("released Legion.", LOG_GAME)
- var/sound/legion_sound = sound('sound/creatures/legion_spawn.ogg')
+ var/sound/legion_sound = sound('sound/mobs/non-humanoids/legion/legion_spawn.ogg')
for(var/mob/M in GLOB.player_list)
if(is_valid_z_level(get_turf(M), T))
to_chat(M, span_userdanger("Discordant whispers flood your mind in a thousand voices. Each one speaks your name, over and over. Something horrible has been released."))
diff --git a/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm b/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm
index 15566603a9322..6e67c0831d398 100644
--- a/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm
+++ b/code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm
@@ -34,7 +34,7 @@
force = 18
throwforce = 10
w_class = WEIGHT_CLASS_NORMAL
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
/obj/item/knife/envy/afterattack(atom/target, mob/living/carbon/human/user, click_parameters)
if(!istype(user) || !ishuman(target))
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/forgottenship.dm b/code/modules/mapfluff/ruins/spaceruin_code/forgottenship.dm
index e06c9bbb03608..21bf3da443180 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/forgottenship.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/forgottenship.dm
@@ -13,41 +13,46 @@ GLOBAL_VAR_INIT(fscpassword, generate_password())
. = ..()
password = "[GLOB.fscpassword]"
-/obj/machinery/vending/medical/syndicate_access/cybersun
+/obj/machinery/vending/medical/syndicate/cybersun
name = "\improper CyberMed ++"
desc = "An advanced vendor that dispenses medical drugs, both recreational and medicinal."
- products = list(/obj/item/reagent_containers/syringe = 4,
- /obj/item/healthanalyzer = 4,
- /obj/item/reagent_containers/pill/patch/libital = 5,
- /obj/item/reagent_containers/pill/patch/aiuri = 5,
- /obj/item/reagent_containers/cup/bottle/multiver = 1,
- /obj/item/reagent_containers/cup/bottle/syriniver = 1,
- /obj/item/reagent_containers/cup/bottle/epinephrine = 3,
- /obj/item/reagent_containers/cup/bottle/morphine = 3,
- /obj/item/reagent_containers/cup/bottle/potass_iodide = 1,
- /obj/item/reagent_containers/cup/bottle/salglu_solution = 3,
- /obj/item/reagent_containers/syringe/antiviral = 5,
- /obj/item/reagent_containers/medigel/libital = 2,
- /obj/item/reagent_containers/medigel/aiuri = 2,
- /obj/item/reagent_containers/medigel/sterilizine = 1)
+ products = list(
+ /obj/item/reagent_containers/syringe = 4,
+ /obj/item/healthanalyzer = 4,
+ /obj/item/reagent_containers/pill/patch/libital = 5,
+ /obj/item/reagent_containers/pill/patch/aiuri = 5,
+ /obj/item/reagent_containers/cup/bottle/multiver = 1,
+ /obj/item/reagent_containers/cup/bottle/syriniver = 1,
+ /obj/item/reagent_containers/cup/bottle/epinephrine = 3,
+ /obj/item/reagent_containers/cup/bottle/morphine = 3,
+ /obj/item/reagent_containers/cup/bottle/potass_iodide = 1,
+ /obj/item/reagent_containers/cup/bottle/salglu_solution = 3,
+ /obj/item/reagent_containers/syringe/antiviral = 5,
+ /obj/item/reagent_containers/medigel/libital = 2,
+ /obj/item/reagent_containers/medigel/aiuri = 2,
+ /obj/item/reagent_containers/medigel/sterilizine = 1,
+ )
contraband = list(/obj/item/reagent_containers/cup/bottle/cold = 2,
- /obj/item/restraints/handcuffs = 4,
- /obj/item/storage/backpack/duffelbag/syndie/surgery = 1,
- /obj/item/storage/medkit/tactical = 1)
- premium = list(/obj/item/storage/pill_bottle/psicodine = 2,
- /obj/item/reagent_containers/hypospray/medipen = 3,
- /obj/item/reagent_containers/hypospray/medipen/atropine = 2,
- /obj/item/storage/medkit/regular = 3,
- /obj/item/storage/medkit/brute = 1,
- /obj/item/storage/medkit/fire = 1,
- /obj/item/storage/medkit/toxin = 1,
- /obj/item/storage/medkit/o2 = 1,
- /obj/item/storage/medkit/advanced = 1,
- /obj/item/defibrillator/loaded = 1,
- /obj/item/wallframe/defib_mount = 1,
- /obj/item/sensor_device = 2,
- /obj/item/pinpointer/crew = 2,
- /obj/item/shears = 1)
+ /obj/item/restraints/handcuffs = 4,
+ /obj/item/storage/backpack/duffelbag/syndie/surgery = 1,
+ /obj/item/storage/medkit/tactical = 1,
+ )
+ premium = list(
+ /obj/item/storage/pill_bottle/psicodine = 2,
+ /obj/item/reagent_containers/hypospray/medipen = 3,
+ /obj/item/reagent_containers/hypospray/medipen/atropine = 2,
+ /obj/item/storage/medkit/regular = 3,
+ /obj/item/storage/medkit/brute = 1,
+ /obj/item/storage/medkit/fire = 1,
+ /obj/item/storage/medkit/toxin = 1,
+ /obj/item/storage/medkit/o2 = 1,
+ /obj/item/storage/medkit/advanced = 1,
+ /obj/item/defibrillator/loaded = 1,
+ /obj/item/wallframe/defib_mount = 1,
+ /obj/item/sensor_device = 2,
+ /obj/item/pinpointer/crew = 2,
+ /obj/item/shears = 1,
+ )
/////////// forgottenship lore
@@ -112,15 +117,15 @@ GLOBAL_VAR_INIT(fscpassword, generate_password())
/area/ruin/space/has_grav/syndicate_forgotten_ship
name = "Syndicate Forgotten Ship"
icon_state = "syndie-ship"
- ambientsounds = list('sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambigen8.ogg', 'sound/ambience/ambigen9.ogg')
+ ambientsounds = list('sound/ambience/misc/ambidanger.ogg', 'sound/ambience/misc/ambidanger2.ogg', 'sound/ambience/general/ambigen8.ogg', 'sound/ambience/general/ambigen9.ogg')
/area/ruin/space/has_grav/syndicate_forgotten_cargopod
name = "Syndicate Forgotten Cargo pod"
icon_state = "syndie-ship"
- ambientsounds = list('sound/ambience/ambigen3.ogg', 'sound/ambience/signal.ogg')
+ ambientsounds = list('sound/ambience/general/ambigen3.ogg', 'sound/ambience/misc/signal.ogg')
/area/ruin/space/has_grav/powered/syndicate_forgotten_vault
name = "Syndicate Forgotten Vault"
icon_state = "syndie-ship"
- ambientsounds = list('sound/ambience/ambitech2.ogg', 'sound/ambience/ambitech3.ogg')
+ ambientsounds = list('sound/ambience/engineering/ambitech2.ogg', 'sound/ambience/engineering/ambitech3.ogg')
area_flags = NOTELEPORT | UNIQUE_AREA
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm b/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm
index 03bd6c224deda..04404dc630872 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/hauntedtradingpost.dm
@@ -115,16 +115,14 @@
// [Hazards & Traps]
//cyborg holobarriers that die when the boss dies, how exciting
+#define SELFDESTRUCT_QUEUE "hauntedtradingpost_sd" //make sure it matches the AI cores ID
/obj/structure/holosign/barrier/cyborg/cybersun_ai_shield
desc = "A fragile holographic energy field projected by an AI core. It keeps unwanted humanoids at safe distance."
/obj/structure/holosign/barrier/cyborg/cybersun_ai_shield/Initialize(mapload)
. = ..()
- GLOB.selfdestructs_when_boss_dies += src
-
-/obj/structure/holosign/barrier/cyborg/cybersun_ai_shield/Destroy()
- GLOB.selfdestructs_when_boss_dies -= src
- return ..()
+ if(mapload) //shouldnt queue when we arent even part of a ruin, probably admin shitspawned
+ SSqueuelinks.add_to_queue(src, SELFDESTRUCT_QUEUE)
//smes that produces power, until the boss dies then it self destructs and you gotta make your own power
/obj/machinery/power/smes/magical/cybersun
@@ -136,12 +134,7 @@
/obj/machinery/power/smes/magical/cybersun/Initialize(mapload)
. = ..()
if(donk_ai_slave)
- GLOB.selfdestructs_when_boss_dies += src
-
-/obj/machinery/power/smes/magical/cybersun/Destroy()
- if(donk_ai_slave)
- GLOB.selfdestructs_when_boss_dies -= src
- return ..()
+ SSqueuelinks.add_to_queue(src, SELFDESTRUCT_QUEUE)
//this is a trigger for traps involving doors and shutters
//doors get closed and bolted, shutters get cycled open/closed
@@ -165,19 +158,31 @@
var/suicide_pact = FALSE
//id of the suicide pact this tripwire is in
var/suicide_pact_id
-GLOBAL_LIST_EMPTY(tripwire_suicide_pact)
/obj/machinery/button/door/invisible_tripwire/Initialize(mapload)
. = ..()
- if(donk_ai_slave == TRUE)
- GLOB.selfdestructs_when_boss_dies += src
- if(suicide_pact == TRUE && suicide_pact_id != null)
- GLOB.tripwire_suicide_pact += src
+ if(donk_ai_slave)
+ SSqueuelinks.add_to_queue(src, SELFDESTRUCT_QUEUE)
+ if(suicide_pact && suicide_pact_id != null)
+ SSqueuelinks.add_to_queue(src, suicide_pact_id)
+ . = INITIALIZE_HINT_LATELOAD
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
AddElement(/datum/element/connect_loc, loc_connections)
+/obj/machinery/button/door/invisible_tripwire/post_machine_initialize()
+ . = ..()
+ if(!suicide_pact || isnull(SSqueuelinks.queues[suicide_pact_id]))
+ return // we got beat to it
+ SSqueuelinks.pop_link(suicide_pact_id)
+
+/obj/machinery/button/door/invisible_tripwire/MatchedLinks(id, list/partners)
+ if(id != suicide_pact_id)
+ return
+ for(var/partner in partners)
+ RegisterSignal(partner, COMSIG_PUZZLE_COMPLETED, TYPE_PROC_REF(/datum, selfdelete))
+
/obj/machinery/button/door/invisible_tripwire/proc/on_entered(atom/source, atom/movable/victim)
SIGNAL_HANDLER
if(!isliving(victim))
@@ -194,19 +199,9 @@ GLOBAL_LIST_EMPTY(tripwire_suicide_pact)
INVOKE_ASYNC(src, TYPE_PROC_REF(/atom, interact), victim)
if(multiuse && uses_remaining != 1)
return
- if(suicide_pact&& suicide_pact_id)
- for (var/obj/machinery/button/door/invisible_tripwire/pact_member in GLOB.tripwire_suicide_pact)
- if(src.suicide_pact_id == pact_member.suicide_pact_id)
- qdel(pact_member)
- qdel(src)
-
-
-/obj/machinery/button/door/invisible_tripwire/Destroy()
- if(donk_ai_slave)
- GLOB.selfdestructs_when_boss_dies -= src
if(suicide_pact && suicide_pact_id)
- GLOB.tripwire_suicide_pact -= src
- return ..()
+ SEND_SIGNAL(src, COMSIG_PUZZLE_COMPLETED)
+ qdel(src)
//door button that destroys itself when it is pressed
/obj/machinery/button/door/selfdestructs
@@ -271,13 +266,8 @@ GLOBAL_LIST_EMPTY(tripwire_suicide_pact)
proximity_monitor?.set_range(trigger_range)
my_turf = get_turf(src)
host_machine = locate(/obj/machinery) in loc
- if(donk_ai_slave == TRUE)
- GLOB.selfdestructs_when_boss_dies += src
-
-/obj/effect/overloader_trap/Destroy()
- if(donk_ai_slave == TRUE)
- GLOB.selfdestructs_when_boss_dies -= src
- return ..()
+ if(donk_ai_slave)
+ SSqueuelinks.add_to_queue(src, SELFDESTRUCT_QUEUE)
/obj/effect/overloader_trap/proc/check_faction(mob/target)
for(var/faction1 in faction)
@@ -337,8 +327,8 @@ GLOBAL_LIST_EMPTY(tripwire_suicide_pact)
base_icon_state = "donk"
stun_projectile = /obj/projectile/bullet/foam_dart/riot
lethal_projectile = /obj/projectile/bullet/c9mm/blunttip
- lethal_projectile_sound = 'sound/weapons/gun/pistol/shot.ogg'
- stun_projectile_sound = 'sound/weapons/gun/pistol/shot.ogg'
+ lethal_projectile_sound = 'sound/items/weapons/gun/pistol/shot.ogg'
+ stun_projectile_sound = 'sound/items/weapons/gun/pistol/shot.ogg'
desc = "A ballistic machine gun auto-turret with Donk Co. branding. It uses 9mm rounds."
armor_type = /datum/armor/donk_turret
scan_range = 6
@@ -363,9 +353,9 @@ GLOBAL_LIST_EMPTY(tripwire_suicide_pact)
icon_state = "red_lethal"
base_icon_state = "red"
stun_projectile = /obj/projectile/energy/electrode
- stun_projectile_sound = 'sound/weapons/taser.ogg'
+ stun_projectile_sound = 'sound/items/weapons/taser.ogg'
lethal_projectile = /obj/projectile/beam/laser/cybersun
- lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
+ lethal_projectile_sound = 'sound/items/weapons/lasercannonfire.ogg'
desc = "An energy gun auto-turret with Cybersun branding. It fires high-energy plasma beams that do a lot of damage, but it can be fairly slow."
armor_type = /datum/armor/syndicate_shuttle
scan_range = 6
@@ -381,3 +371,4 @@ GLOBAL_LIST_EMPTY(tripwire_suicide_pact)
damage = 30
wound_bonus = -50
+#undef SELFDESTRUCT_QUEUE
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm
index b42618f94ee21..0e77140422525 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm
@@ -395,7 +395,7 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999))
has_gravity = TRUE
area_flags = NOTELEPORT | HIDDEN_AREA
static_lighting = TRUE
- ambientsounds = list('sound/ambience/servicebell.ogg')
+ ambientsounds = list('sound/ambience/ruin/servicebell.ogg')
var/roomnumber = 0
var/obj/item/hilbertshotel/parentSphere
var/datum/turf_reservation/reservation
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/meatderelict.dm b/code/modules/mapfluff/ruins/spaceruin_code/meatderelict.dm
index 0db718e399bf6..2b2216641488d 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/meatderelict.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/meatderelict.dm
@@ -131,7 +131,7 @@
/obj/lightning_thrower/process(seconds_per_tick)
var/list/dirs = throw_diagonals ? GLOB.diagonals : GLOB.cardinals
throw_diagonals = !throw_diagonals
- playsound(src, 'sound/magic/lightningbolt.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, ignore_walls = FALSE)
+ playsound(src, 'sound/effects/magic/lightningbolt.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE, ignore_walls = FALSE)
for(var/direction in dirs)
var/victim_turf = get_step(src, direction)
if(isclosedturf(victim_turf))
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm
index 59998bb53c8f2..a91d033f1ee7b 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/meateor.dm
@@ -56,7 +56,7 @@
switch(damage_type)
if(BRUTE)
if(damage_amount)
- playsound(loc, 'sound/effects/attackblob.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
+ playsound(loc, 'sound/effects/blob/attackblob.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
else
playsound(loc, 'sound/effects/meatslap.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
if(BURN)
diff --git a/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_mod.dm b/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_mod.dm
index 77b8aa0bbce28..994162345dae2 100644
--- a/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_mod.dm
+++ b/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_mod.dm
@@ -35,7 +35,7 @@
update_appearance()
/obj/machinery/mod_installer/proc/play_install_sound()
- playsound(src, 'sound/items/rped.ogg', 30, FALSE)
+ playsound(src, 'sound/items/tools/rped.ogg', 30, FALSE)
/obj/machinery/mod_installer/update_icon_state()
icon_state = busy ? busy_icon_state : "[base_icon_state][state_open ? "_open" : null]"
diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm
index 587a62ec0e1b7..55b802cc26c1d 100644
--- a/code/modules/mapping/mapping_helpers.dm
+++ b/code/modules/mapping/mapping_helpers.dm
@@ -326,9 +326,6 @@
if(target.syndicate_access + target.away_general_access + target.engine_access + target.mixingchamber_access + target.all_access > 1)
CRASH("Tried to combine incompatible air alarm access helpers!")
- if(target.air_sensor_chamber_id)
- target.setup_chamber_link()
-
target.update_appearance()
qdel(src)
@@ -418,6 +415,7 @@
/obj/effect/mapping_helpers/airalarm/link
name = "airalarm link helper"
icon_state = "airalarm_link_helper"
+ late = TRUE
var/chamber_id = ""
var/allow_link_change = FALSE
@@ -427,13 +425,15 @@
log_mapping("[src] spawned outside of mapload!")
return INITIALIZE_HINT_QDEL
+/obj/effect/mapping_helpers/airalarm/link/LateInitialize(mapload)
var/obj/machinery/airalarm/alarm = locate(/obj/machinery/airalarm) in loc
if(!isnull(alarm))
alarm.air_sensor_chamber_id = chamber_id
alarm.allow_link_change = allow_link_change
+ alarm.setup_chamber_link()
else
log_mapping("[src] failed to find air alarm at [AREACOORD(src)].")
- return INITIALIZE_HINT_QDEL
+ qdel(src)
//apc helpers
/obj/effect/mapping_helpers/apc
diff --git a/code/modules/meteors/meteor_dark_matteor.dm b/code/modules/meteors/meteor_dark_matteor.dm
index 18dbaf007024f..acf21a3c434b8 100644
--- a/code/modules/meteors/meteor_dark_matteor.dm
+++ b/code/modules/meteors/meteor_dark_matteor.dm
@@ -6,7 +6,7 @@
hits = 15
hitpwr = EXPLODE_DEVASTATE
heavy = TRUE
- meteorsound = 'sound/effects/curse1.ogg'
+ meteorsound = 'sound/effects/curse/curse1.ogg'
meteordrop = list(/obj/singularity/dark_matter) //what the FUCK
dropamt = 1
threat = 100
diff --git a/code/modules/meteors/meteor_spawning.dm b/code/modules/meteors/meteor_spawning.dm
index d19d3aff0a5a6..83b1c9533c577 100644
--- a/code/modules/meteors/meteor_spawning.dm
+++ b/code/modules/meteors/meteor_spawning.dm
@@ -104,7 +104,7 @@
player_mind.transfer_to(new_changeling)
player_mind.special_role = ROLE_CHANGELING_MIDROUND
player_mind.add_antag_datum(/datum/antagonist/changeling/space)
- SEND_SOUND(new_changeling, 'sound/magic/mutate.ogg')
+ SEND_SOUND(new_changeling, 'sound/effects/magic/mutate.ogg')
message_admins("[ADMIN_LOOKUPFLW(new_changeling)] has been made into a space changeling by an event.")
new_changeling.log_message("was spawned as a midround space changeling by an event.", LOG_GAME)
diff --git a/code/modules/meteors/meteor_types.dm b/code/modules/meteors/meteor_types.dm
index 199f6517abb1e..74e5ecb78c106 100644
--- a/code/modules/meteors/meteor_types.dm
+++ b/code/modules/meteors/meteor_types.dm
@@ -203,7 +203,7 @@
pass_flags = PASSTABLE | PASSGRILLE
hits = 1
hitpwr = EXPLODE_LIGHT
- meteorsound = 'sound/weapons/gun/smg/shot.ogg'
+ meteorsound = 'sound/items/weapons/gun/smg/shot.ogg'
meteordrop = list(/obj/item/stack/ore/glass)
threat = 1
@@ -301,7 +301,7 @@
icon_state = "carp"
desc = "Am I glad he's frozen in there, and that we're out here."
hits = 4
- meteorsound = 'sound/effects/ethereal_revive_fail.ogg'
+ meteorsound = 'sound/mobs/humanoids/ethereal/ethereal_revive_fail.ogg'
meteordrop = list(/mob/living/basic/carp)
dropamt = 1
threat = 5
@@ -342,7 +342,7 @@
/obj/effect/meteor/banana/meteor_effect()
..()
- playsound(src, 'sound/items/AirHorn.ogg', 100, TRUE, -1)
+ playsound(src, 'sound/items/airhorn/AirHorn.ogg', 100, TRUE, -1)
for(var/atom/movable/object in view(4, get_turf(src)))
var/turf/throwtarget = get_edge_target_turf(get_turf(src), get_dir(get_turf(src), get_step_away(object, get_turf(src))))
object.safe_throw_at(throwtarget, 5, 1, force = MOVE_FORCE_STRONG)
@@ -368,7 +368,7 @@
/obj/effect/meteor/emp/meteor_effect()
..()
- playsound(src, 'sound/weapons/zapbang.ogg', 100, TRUE, -1)
+ playsound(src, 'sound/items/weapons/zapbang.ogg', 100, TRUE, -1)
empulse(src, 3, 8)
//Meaty Ore
@@ -378,7 +378,7 @@
desc = "Just... don't think too hard about where this thing came from."
hits = 2
heavy = TRUE
- meteorsound = 'sound/effects/blobattack.ogg'
+ meteorsound = 'sound/effects/blob/blobattack.ogg'
meteordrop = list(/obj/item/food/meat/slab/human, /obj/item/food/meat/slab/human/mutant, /obj/item/organ/internal/heart, /obj/item/organ/internal/lungs, /obj/item/organ/internal/tongue, /obj/item/organ/internal/appendix/)
var/meteorgibs = /obj/effect/gibspawner/generic
threat = 2
@@ -464,6 +464,6 @@
/obj/effect/meteor/pumpkin/Initialize(mapload)
. = ..()
- meteorsound = pick('sound/hallucinations/im_here1.ogg','sound/hallucinations/im_here2.ogg')
+ meteorsound = pick('sound/effects/hallucinations/im_here1.ogg','sound/effects/hallucinations/im_here2.ogg')
#undef DEFAULT_METEOR_LIFETIME
diff --git a/code/modules/mining/abandoned_crates.dm b/code/modules/mining/abandoned_crates.dm
index 40cb967d3a0ab..10b2fbe71d062 100644
--- a/code/modules/mining/abandoned_crates.dm
+++ b/code/modules/mining/abandoned_crates.dm
@@ -246,7 +246,7 @@
new /obj/item/clothing/mask/balaclava(src)
new /obj/item/gun/ballistic/shotgun/toy(src)
new /obj/item/gun/ballistic/automatic/pistol/toy(src)
- new /obj/item/gun/ballistic/automatic/toy/unrestricted(src)
+ new /obj/item/gun/ballistic/automatic/toy(src)
new /obj/item/gun/ballistic/automatic/l6_saw/toy/unrestricted(src)
new /obj/item/ammo_box/foambox(src)
if(98)
diff --git a/code/modules/mining/boulder_processing/_boulder_processing.dm b/code/modules/mining/boulder_processing/_boulder_processing.dm
index ab72e4ebae57b..28d8f9b6ea462 100644
--- a/code/modules/mining/boulder_processing/_boulder_processing.dm
+++ b/code/modules/mining/boulder_processing/_boulder_processing.dm
@@ -81,14 +81,14 @@
for(var/obj/item/boulder/potential_boulder in contents)
boulder_count += 1
. += span_notice("Storage capacity = [boulder_count]/[boulders_held_max] boulders.")
- . += span_notice("Can process upto [boulders_processing_count] boulders at a time.")
+ . += span_notice("Can process up to [boulders_processing_count] boulders at a time.")
if(anchored)
- . += span_notice("Its [EXAMINE_HINT("anchored")] in place.")
+ . += span_notice("It's [EXAMINE_HINT("anchored")] in place.")
else
. += span_warning("It needs to be [EXAMINE_HINT("anchored")] to start operations.")
- . += span_notice("Its maintainence panel can be [EXAMINE_HINT("screwed")] [panel_open ? "closed" : "open"].")
+ . += span_notice("Its maintenance panel can be [EXAMINE_HINT("screwed")] [panel_open ? "closed" : "open"].")
if(panel_open)
. += span_notice("The whole machine can be [EXAMINE_HINT("pried")] apart.")
@@ -127,7 +127,7 @@
if(!istype(new_boulder) || QDELETED(new_boulder))
return FALSE
- //someone just processed this
+ //someone is still processing this
if(new_boulder.processed_by)
return FALSE
@@ -149,7 +149,6 @@
*/
/obj/machinery/bouldertech/proc/accept_boulder(obj/item/boulder/new_boulder)
PRIVATE_PROC(TRUE)
-
if(!can_process_boulder(new_boulder))
return FALSE
@@ -262,7 +261,7 @@
if(!COOLDOWN_FINISHED(src, sound_cooldown))
return ITEM_INTERACT_BLOCKING
COOLDOWN_START(src, sound_cooldown, 1.5 SECONDS)
- playsound(src, 'sound/machines/buzz-sigh.ogg', 30, FALSE)
+ playsound(src, 'sound/machines/buzz/buzz-sigh.ogg', 30, FALSE)
return ITEM_INTERACT_BLOCKING
var/obj/item/card/id/id_card = tool
@@ -304,7 +303,7 @@
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || panel_open)
return
if(!anchored)
- balloon_alert(user, "anchor first!")
+ balloon_alert(user, "anchor it first!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
if(panel_open)
balloon_alert(user, "close panel!")
@@ -408,6 +407,9 @@
return TRUE
if(locate(/obj/item/boulder) in loc) //There is an boulder in our loc. it has be removed so we don't clog up our loc with even more boulders
return FALSE
+ if(!length(specific_boulder.custom_materials))
+ specific_boulder.break_apart()
+ return TRUE
//Reset durability to little random lower value cause we have crushed it so many times
var/size = specific_boulder.boulder_size
diff --git a/code/modules/mining/boulder_processing/boulder.dm b/code/modules/mining/boulder_processing/boulder.dm
index 555ea50dff3bf..57a143a0d33a5 100644
--- a/code/modules/mining/boulder_processing/boulder.dm
+++ b/code/modules/mining/boulder_processing/boulder.dm
@@ -62,9 +62,9 @@
icon_state = "[boulder_string]_small"
/obj/item/boulder/CanAllowThrough(atom/movable/mover, border_dir)
- . = ..()
if(istype(mover, /obj/item/boulder)) //This way, boulders can only go one at a time on conveyor belts, but everyone else can go through.
return FALSE
+ return ..()
/obj/item/boulder/attack_self(mob/user, list/modifiers)
. = ..()
@@ -127,7 +127,7 @@
process_speed = override_speed_multiplier
else
process_speed = INATE_BOULDER_SPEED_MULTIPLIER
- playsound(src, 'sound/effects/rocktap1.ogg', 50)
+ playsound(src, 'sound/effects/rock/rocktap1.ogg', 50)
if(!continued)
to_chat(user, span_notice("You scrape away at \the [src]..."))
else
@@ -142,7 +142,7 @@
if(durability <= 0)
convert_to_ore()
to_chat(user, span_notice("You finish working on \the [src], and it crumbles into ore."))
- playsound(src, 'sound/effects/rock_break.ogg', 50)
+ playsound(src, 'sound/effects/rock/rock_break.ogg', 50)
user.mind?.adjust_experience(/datum/skill/mining, MINING_SKILL_BOULDER_SIZE_XP * 0.2)
qdel(src)
return
@@ -171,7 +171,7 @@
if(length(contents))
var/list/quips = list("Clang!", "Crack!", "Bang!", "Clunk!", "Clank!")
visible_message(span_notice("[pick(quips)] Something falls out of \the [src]!"))
- playsound(loc, 'sound/effects/picaxe1.ogg', 60, FALSE)
+ playsound(loc, 'sound/effects/pickaxe/picaxe1.ogg', 60, FALSE)
for(var/obj/item/content as anything in contents)
content.forceMove(get_turf(src))
qdel(src)
diff --git a/code/modules/mining/boulder_processing/boulder_types.dm b/code/modules/mining/boulder_processing/boulder_types.dm
index 8f6889b7c8470..366c5b21c4259 100644
--- a/code/modules/mining/boulder_processing/boulder_types.dm
+++ b/code/modules/mining/boulder_processing/boulder_types.dm
@@ -25,6 +25,10 @@
artifact_inside = null
return ..()
+/obj/item/boulder/artifact/update_icon_state()
+ . = ..()
+ icon_state = "boulder_artifact" // Hardset to artifact sprites for consistency
+
///Boulders usually spawned in lavaland labour camp area
/obj/item/boulder/gulag
name = "low-quality boulder"
diff --git a/code/modules/mining/boulder_processing/brm.dm b/code/modules/mining/boulder_processing/brm.dm
index 9b9186968918b..bb7b7f650338a 100644
--- a/code/modules/mining/boulder_processing/brm.dm
+++ b/code/modules/mining/boulder_processing/brm.dm
@@ -58,15 +58,15 @@
/obj/machinery/brm/examine(mob/user)
. = ..()
. += span_notice("The small screen reads there are [span_boldnotice("[SSore_generation.available_boulders.len] boulders")] available to teleport.")
- . += span_notice("Can collect upto [boulders_processing_max] boulders at a time.")
- . += span_notice("Automatic boulder retrival can be toggled [EXAMINE_HINT("[toggled_on ? "Off" : "On"]")] with [EXAMINE_HINT("Right Click")].")
+ . += span_notice("Can collect up to [boulders_processing_max] boulders at a time.")
+ . += span_notice("Automatic boulder retrieval can be toggled [EXAMINE_HINT("[toggled_on ? "Off" : "On"]")] with [EXAMINE_HINT("Right Click")].")
if(anchored)
- . += span_notice("Its [EXAMINE_HINT("anchored")] in place.")
+ . += span_notice("It's [EXAMINE_HINT("anchored")] in place.")
else
. += span_warning("It needs to be [EXAMINE_HINT("anchored")] to start operations.")
- . += span_notice("Its maintainence panel can be [EXAMINE_HINT("screwed")] [panel_open ? "Closed" : "Open"].")
+ . += span_notice("Its maintenance panel can be [EXAMINE_HINT("screwed")] [panel_open ? "closed" : "open"].")
if(panel_open)
. += span_notice("The whole machine can be [EXAMINE_HINT("pried")] apart.")
@@ -127,7 +127,9 @@
var/result = pre_collect_boulder()
if(result == TURF_BLOCKED_BY_BOULDER)
- balloon_alert(user, "no space")
+ balloon_alert(user, "no space!")
+ else if(result)
+ balloon_alert(user, "teleporting...")
COOLDOWN_START(src, manual_teleport_cooldown, TELEPORTATION_TIME)
return TRUE
@@ -162,9 +164,9 @@
var/result = pre_collect_boulder()
if(result == TURF_BLOCKED_BY_BOULDER)
- balloon_alert(user, "no space")
+ balloon_alert(user, "no space!")
else if(result)
- balloon_alert(user, "teleporting")
+ balloon_alert(user, "teleporting...")
COOLDOWN_START(src, manual_teleport_cooldown, TELEPORTATION_TIME)
@@ -179,9 +181,9 @@
var/result = pre_collect_boulder()
if(result == TURF_BLOCKED_BY_BOULDER)
- balloon_alert(user, "no space")
+ balloon_alert(user, "no space!")
else if(result)
- balloon_alert(user, "teleporting")
+ balloon_alert(user, "teleporting...")
COOLDOWN_START(src, manual_teleport_cooldown, TELEPORTATION_TIME)
@@ -192,7 +194,7 @@
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || panel_open)
return
if(!anchored)
- balloon_alert(user, "anchor first!")
+ balloon_alert(user, "anchor it first!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
toggle_auto_on(user)
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
@@ -211,7 +213,7 @@
balloon_alert(user, "close panel first!")
return
if(!anchored)
- balloon_alert(user, "anchor first!")
+ balloon_alert(user, "anchor it first!")
return
if(!is_operational || machine_stat & (BROKEN | NOPOWER))
return
@@ -228,7 +230,7 @@
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || panel_open)
return
if(!anchored)
- balloon_alert(user, "anchor first!")
+ balloon_alert(user, "unanchored!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
toggle_auto_on(user)
@@ -239,7 +241,7 @@
if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || panel_open)
return
if(!anchored)
- balloon_alert(user, "anchor first!")
+ balloon_alert(user, "unanchored!")
return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN
toggle_auto_on(user)
@@ -282,7 +284,7 @@
//no more boulders
if(!SSore_generation.available_boulders.len)
if(feedback)
- playsound(loc, 'sound/machines/synth_no.ogg', 30 , TRUE)
+ playsound(loc, 'sound/machines/synth/synth_no.ogg', 30 , TRUE)
balloon_alert_to_viewers("no boulders to collect!")
batch_processing = FALSE
return FALSE
diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm
index ecaa17321c486..af31d32719f14 100644
--- a/code/modules/mining/equipment/explorer_gear.dm
+++ b/code/modules/mining/equipment/explorer_gear.dm
@@ -332,7 +332,7 @@
COOLDOWN_START(src, effect_cooldown, effect_cooldown_time) //This needs to happen first, otherwise there's an infinite loop
user.heal_ordered_damage(heal_amount, damage_heal_order)
user.visible_message(span_notice("[user] suddenly revives, as their armor swirls with demonic energy!"), span_notice("You suddenly feel invigorated!"))
- playsound(user.loc, 'sound/magic/clockwork/ratvar_attack.ogg', 50)
+ playsound(user.loc, 'sound/effects/magic/clockwork/ratvar_attack.ogg', 50)
/obj/item/clothing/suit/hooded/explorer/syndicate
name = "syndicate explorer suit"
diff --git a/code/modules/mining/equipment/grapple_gun.dm b/code/modules/mining/equipment/grapple_gun.dm
index 76da071ec6e2d..0247d0164b025 100644
--- a/code/modules/mining/equipment/grapple_gun.dm
+++ b/code/modules/mining/equipment/grapple_gun.dm
@@ -64,7 +64,7 @@
if(user.CanReach(attacked_atom))
return ITEM_INTERACT_BLOCKING
- var/atom/bullet = fire_projectile(/obj/projectile/grapple_hook, attacked_atom, 'sound/weapons/zipline_fire.ogg')
+ var/atom/bullet = fire_projectile(/obj/projectile/grapple_hook, attacked_atom, 'sound/items/weapons/zipline_fire.ogg')
zipline = user.Beam(bullet, icon_state = "zipline_hook", maxdistance = 9, layer = BELOW_MOB_LAYER)
hooked = FALSE
RegisterSignal(bullet, COMSIG_PROJECTILE_SELF_ON_HIT, PROC_REF(on_grapple_hit))
@@ -181,6 +181,6 @@
range = 9
speed = 0.1
can_hit_turfs = TRUE
- hitsound = 'sound/weapons/zipline_hit.ogg'
+ hitsound = 'sound/items/weapons/zipline_hit.ogg'
#undef DAMAGE_ON_IMPACT
diff --git a/code/modules/mining/equipment/kheiral_cuffs.dm b/code/modules/mining/equipment/kheiral_cuffs.dm
index eaaf11616cc71..27b3a1c42a70d 100644
--- a/code/modules/mining/equipment/kheiral_cuffs.dm
+++ b/code/modules/mining/equipment/kheiral_cuffs.dm
@@ -42,13 +42,13 @@
if(!(slot & ITEM_SLOT_GLOVES))
return
on_wrist = TRUE
- playsound(loc, 'sound/weapons/handcuffs.ogg', 30, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ playsound(loc, 'sound/items/weapons/handcuffs.ogg', 30, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
connect_kheiral_network(user)
/obj/item/kheiral_cuffs/dropped(mob/user, silent)
. = ..()
if(on_wrist)
- playsound(loc, 'sound/weapons/handcuffs.ogg', 30, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ playsound(loc, 'sound/items/weapons/handcuffs.ogg', 30, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
on_wrist = FALSE
remove_kheiral_network(user)
diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm
index 6a276e830726b..d35ef3c2a9499 100644
--- a/code/modules/mining/equipment/kinetic_crusher.dm
+++ b/code/modules/mining/equipment/kinetic_crusher.dm
@@ -23,7 +23,7 @@
throw_speed = 4
armour_penetration = 10
custom_materials = list(/datum/material/iron=HALF_SHEET_MATERIAL_AMOUNT*1.15, /datum/material/glass=HALF_SHEET_MATERIAL_AMOUNT*2.075)
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
attack_verb_continuous = list("smashes", "crushes", "cleaves", "chops", "pulps")
attack_verb_simple = list("smash", "crush", "cleave", "chop", "pulp")
sharpness = SHARP_EDGED
@@ -49,7 +49,6 @@
)
//technically it's huge and bulky, but this provides an incentive to use it
AddComponent(/datum/component/two_handed, force_unwielded=0, force_wielded=20)
- RegisterSignal(src, COMSIG_HIT_BY_SABOTEUR, PROC_REF(on_saboteur))
/obj/item/kinetic_crusher/Destroy()
QDEL_LIST(trophies)
@@ -127,7 +126,7 @@
if((user.dir & backstab_dir) && (target.dir & backstab_dir) || boosted_mark)
backstabbed = TRUE
combined_damage += backstab_bonus
- playsound(user, 'sound/weapons/kinetic_accel.ogg', 100, TRUE) //Seriously who spelled it wrong
+ playsound(user, 'sound/items/weapons/kinetic_accel.ogg', 100, TRUE) //Seriously who spelled it wrong
if(!QDELETED(crusher_damage_effect))
crusher_damage_effect.total_damage += combined_damage
SEND_SIGNAL(user, COMSIG_LIVING_CRUSHER_DETONATE, target, src, backstabbed)
@@ -158,7 +157,7 @@
attached_trophy.on_projectile_fire(destabilizer, user)
destabilizer.preparePixelProjectile(target, user, modifiers)
destabilizer.firer = user
- playsound(user, 'sound/weapons/plasma_cutter.ogg', 100, TRUE)
+ playsound(user, 'sound/items/weapons/plasma_cutter.ogg', 100, TRUE)
destabilizer.fire()
charged = FALSE
update_appearance()
@@ -168,17 +167,18 @@
if(!charged)
charged = TRUE
update_appearance()
- playsound(src.loc, 'sound/weapons/kinetic_reload.ogg', 60, TRUE)
+ playsound(src.loc, 'sound/items/weapons/kinetic_reload.ogg', 60, TRUE)
/obj/item/kinetic_crusher/ui_action_click(mob/user, actiontype)
set_light_on(!light_on)
- playsound(user, 'sound/weapons/empty.ogg', 100, TRUE)
+ playsound(user, 'sound/items/weapons/empty.ogg', 100, TRUE)
update_appearance()
-/obj/item/kinetic_crusher/proc/on_saboteur(datum/source, disrupt_duration)
+/obj/item/kinetic_crusher/on_saboteur(datum/source, disrupt_duration)
+ . = ..()
set_light_on(FALSE)
- playsound(src, 'sound/weapons/empty.ogg', 100, TRUE)
- return COMSIG_SABOTEUR_SUCCESS
+ playsound(src, 'sound/items/weapons/empty.ogg', 100, TRUE)
+ return TRUE
/obj/item/kinetic_crusher/update_icon_state()
inhand_icon_state = "crusher[HAS_TRAIT(src, TRAIT_WIELDED)]" // this is not icon_state and not supported by 2hcomponent
@@ -362,7 +362,7 @@
for(var/mob/living/living_target in oview(2, user))
if(user.faction_check_atom(living_target) || living_target.stat == DEAD)
continue
- playsound(living_target, 'sound/magic/fireball.ogg', 20, TRUE)
+ playsound(living_target, 'sound/effects/magic/fireball.ogg', 20, TRUE)
new /obj/effect/temp_visual/fire(living_target.loc)
addtimer(CALLBACK(src, PROC_REF(pushback), living_target, user), 1) //no free backstabs, we push AFTER module stuff is done
living_target.adjustFireLoss(bonus_value, forced = TRUE)
diff --git a/code/modules/mining/equipment/marker_beacons.dm b/code/modules/mining/equipment/marker_beacons.dm
index c33181dd8068d..5433c85977b41 100644
--- a/code/modules/mining/equipment/marker_beacons.dm
+++ b/code/modules/mining/equipment/marker_beacons.dm
@@ -39,8 +39,7 @@ GLOBAL_LIST_INIT(marker_beacon_colors, sort_list(list(
/obj/item/stack/marker_beacon/examine(mob/user)
. = ..()
- . += "Use in-hand to place a [singular_name].\n"+\
- "Alt-click to select a color. Current color is [picked_color]."
+ . += span_notice("Use in-hand to place a [singular_name].\nAlt-click to select a color. Current color is [picked_color].")
/obj/item/stack/marker_beacon/update_icon_state()
icon_state = "[initial(icon_state)][LOWER_TEXT(picked_color)]"
@@ -148,7 +147,7 @@ GLOBAL_LIST_INIT(marker_beacon_colors, sort_list(list(
var/obj/effect/decal/cleanable/ash/A = new /obj/effect/decal/cleanable/ash(drop_location())
A.desc += "\nLooks like this used to be \a [src] some time ago."
visible_message(span_danger("[src] is disintegrated by [I]!"))
- playsound(src, 'sound/items/welder.ogg', 50, TRUE)
+ playsound(src, 'sound/items/tools/welder.ogg', 50, TRUE)
qdel(src)
return
return ..()
diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm
index 0685dda148dee..85cb66ae8676c 100644
--- a/code/modules/mining/equipment/mining_tools.dm
+++ b/code/modules/mining/equipment/mining_tools.dm
@@ -15,7 +15,7 @@
custom_materials = list(/datum/material/iron=SHEET_MATERIAL_AMOUNT)
tool_behaviour = TOOL_MINING
toolspeed = 1
- usesound = list('sound/effects/picaxe1.ogg', 'sound/effects/picaxe2.ogg', 'sound/effects/picaxe3.ogg')
+ usesound = list('sound/effects/pickaxe/picaxe1.ogg', 'sound/effects/pickaxe/picaxe2.ogg', 'sound/effects/pickaxe/picaxe3.ogg')
attack_verb_continuous = list("hits", "pierces", "slices", "attacks")
attack_verb_simple = list("hit", "pierce", "slice", "attack")
@@ -67,8 +67,8 @@
inhand_icon_state = "handdrill"
slot_flags = ITEM_SLOT_BELT
toolspeed = 0.6 //available from roundstart, faster than a pickaxe.
- usesound = 'sound/weapons/drill.ogg'
- hitsound = 'sound/weapons/drill.ogg'
+ usesound = 'sound/items/weapons/drill.ogg'
+ hitsound = 'sound/items/weapons/drill.ogg'
desc = "An electric mining drill for the especially scrawny."
/obj/item/pickaxe/drill/cyborg
@@ -98,8 +98,8 @@
icon_state = "jackhammer"
inhand_icon_state = "jackhammer"
toolspeed = 0.1 //the epitome of powertools. extremely fast mining
- usesound = 'sound/weapons/sonic_jackhammer.ogg'
- hitsound = 'sound/weapons/sonic_jackhammer.ogg'
+ usesound = 'sound/items/weapons/sonic_jackhammer.ogg'
+ hitsound = 'sound/items/weapons/sonic_jackhammer.ogg'
desc = "Cracks rocks with sonic blasts."
/obj/item/pickaxe/improvised
@@ -213,7 +213,7 @@
w_class = WEIGHT_CLASS_SMALL
tool_behaviour = TOOL_WRENCH
toolspeed = 0.75
- usesound = 'sound/items/ratchet.ogg'
+ usesound = 'sound/items/tools/ratchet.ogg'
attack_verb_continuous = list("bashes", "bludgeons", "thrashes", "whacks")
attack_verb_simple = list("bash", "bludgeon", "thrash", "whack")
wound_bonus = 10
@@ -260,7 +260,7 @@
sharpness = NONE
toolspeed = 0.75
update_weight_class(WEIGHT_CLASS_SMALL)
- usesound = 'sound/items/ratchet.ogg'
+ usesound = 'sound/items/tools/ratchet.ogg'
attack_verb_continuous = list("bashes", "bludgeons", "thrashes", "whacks")
attack_verb_simple = list("bash", "bludgeon", "thrash", "whack")
if("Shovel")
@@ -276,10 +276,10 @@
sharpness = SHARP_POINTY
toolspeed = 0.5
update_weight_class(WEIGHT_CLASS_NORMAL)
- usesound = 'sound/effects/picaxe1.ogg'
+ usesound = 'sound/effects/pickaxe/picaxe1.ogg'
attack_verb_continuous = list("hits", "pierces", "slices", "attacks")
attack_verb_simple = list("hit", "pierce", "slice", "attack")
- playsound(src, 'sound/items/ratchet.ogg', 50, vary = TRUE)
+ playsound(src, 'sound/items/tools/ratchet.ogg', 50, vary = TRUE)
update_appearance(UPDATE_ICON)
/obj/item/trench_tool/proc/check_menu(mob/user)
@@ -312,10 +312,10 @@
wound_bonus = -10
attack_verb_continuous = list("bonks", "bludgeons", "pounds")
attack_verb_simple = list("bonk", "bludgeon", "pound")
- drop_sound = 'sound/weapons/sonic_jackhammer.ogg'
- pickup_sound = 'sound/items/handling/crowbar_pickup.ogg'
- hitsound = 'sound/weapons/sonic_jackhammer.ogg'
- block_sound = 'sound/weapons/sonic_jackhammer.ogg'
+ drop_sound = 'sound/items/weapons/sonic_jackhammer.ogg'
+ pickup_sound = 'sound/items/handling/tools/crowbar_pickup.ogg'
+ hitsound = 'sound/items/weapons/sonic_jackhammer.ogg'
+ block_sound = 'sound/items/weapons/sonic_jackhammer.ogg'
item_flags = SLOWS_WHILE_IN_HAND | IMMUTABLE_SLOW
slowdown = 3
attack_speed = 1.2 SECONDS
@@ -355,7 +355,7 @@
/obj/item/shovel/giant_wrench/proc/on_transform(obj/item/source, mob/user, active)
SIGNAL_HANDLER
- usesound = (active ? 'sound/items/ratchet.ogg' : initial(usesound))
+ usesound = (active ? 'sound/items/tools/ratchet.ogg' : initial(usesound))
block_chance = (active ? 0 : initial(block_chance))
recoil_factor = (active ? 2 : initial(recoil_factor))
do_launch = (active ? FALSE : initial(do_launch))
@@ -363,7 +363,7 @@
armour_penetration = (active ? 30 : initial(armour_penetration))
if(user)
balloon_alert(user, "folded Big Slappy [active ? "open" : "closed"]")
- playsound(src, 'sound/items/ratchet.ogg', 50, TRUE)
+ playsound(src, 'sound/items/tools/ratchet.ogg', 50, TRUE)
return COMPONENT_NO_DEFAULT_MESSAGE
/obj/item/shovel/giant_wrench/attack(mob/living/target_mob, mob/living/user)
diff --git a/code/modules/mining/equipment/monster_organs/monster_organ.dm b/code/modules/mining/equipment/monster_organs/monster_organ.dm
index 9b6330f3467c3..cf6131fa92258 100644
--- a/code/modules/mining/equipment/monster_organs/monster_organ.dm
+++ b/code/modules/mining/equipment/monster_organs/monster_organ.dm
@@ -132,6 +132,9 @@
return ..()
/obj/item/organ/internal/monster_core/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!isliving(interacting_with))
+ return NONE
+
try_apply(interacting_with, user)
return ITEM_INTERACT_SUCCESS
diff --git a/code/modules/mining/equipment/resonator.dm b/code/modules/mining/equipment/resonator.dm
index 99b27e15ca8ab..5019e023d3184 100644
--- a/code/modules/mining/equipment/resonator.dm
+++ b/code/modules/mining/equipment/resonator.dm
@@ -87,7 +87,7 @@
if(parent_resonator)
parent_resonator.fields += src
adding_failure = set_failure
- playsound(src,'sound/weapons/resonator_fire.ogg',50,TRUE)
+ playsound(src,'sound/items/weapons/resonator_fire.ogg',50,TRUE)
if(mode == RESONATOR_MODE_AUTO)
transform = matrix()*0.75
animate(src, transform = matrix()*1.5, time = duration)
@@ -123,7 +123,7 @@
var/turf/closed/mineral/mineral_turf = src_turf
mineral_turf.gets_drilled(creator)
check_pressure(src_turf)
- playsound(src_turf, 'sound/weapons/resonator_blast.ogg', 50, TRUE)
+ playsound(src_turf, 'sound/items/weapons/resonator_blast.ogg', 50, TRUE)
for(var/mob/living/attacked_living in src_turf)
if(creator)
log_combat(creator, attacked_living, "used a resonator field on", "resonator")
diff --git a/code/modules/mining/equipment/wormhole_jaunter.dm b/code/modules/mining/equipment/wormhole_jaunter.dm
index eb94f68a1f9a7..6ffcfa7bc6752 100644
--- a/code/modules/mining/equipment/wormhole_jaunter.dm
+++ b/code/modules/mining/equipment/wormhole_jaunter.dm
@@ -107,7 +107,7 @@
. = ..()
if(.)
// KERPLUNK
- playsound(M,'sound/weapons/resonator_blast.ogg',50,TRUE)
+ playsound(M,'sound/items/weapons/resonator_blast.ogg',50,TRUE)
if(iscarbon(M))
var/mob/living/carbon/L = M
L.Paralyze(60)
diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm
index 649acabfcae4a..1ef2f778bc54b 100644
--- a/code/modules/mining/fulton.dm
+++ b/code/modules/mining/fulton.dm
@@ -38,6 +38,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons)
var/obj/structure/extraction_point/extraction_point = point_ref.resolve()
if(isnull(extraction_point))
GLOB.total_extraction_beacons.Remove(point_ref)
+ continue
if(extraction_point.beacon_network in beacon_networks)
possible_beacons += extraction_point
if(!length(possible_beacons))
@@ -79,7 +80,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons)
balloon_alert(user, "too heavy!")
return .
balloon_alert_to_viewers("attaching...")
- playsound(thing, 'sound/items/zip.ogg', vol = 50, vary = TRUE)
+ playsound(thing, 'sound/items/zip/zip.ogg', vol = 50, vary = TRUE)
if(isliving(thing))
var/mob/living/creature = thing
if(creature.mind)
@@ -123,7 +124,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons)
balloon.appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM
holder_obj.cut_overlay(balloon2)
holder_obj.add_overlay(balloon)
- playsound(holder_obj.loc, 'sound/items/fultext_deploy.ogg', vol = 50, vary = TRUE, extrarange = -3)
+ playsound(holder_obj.loc, 'sound/items/fulton/fultext_deploy.ogg', vol = 50, vary = TRUE, extrarange = -3)
animate(holder_obj, pixel_z = 10, time = 2 SECONDS, flags = ANIMATION_RELATIVE)
animate(pixel_z = 5, time = 1 SECONDS, flags = ANIMATION_RELATIVE)
@@ -133,7 +134,7 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons)
sleep(6 SECONDS)
- playsound(holder_obj.loc, 'sound/items/fultext_launch.ogg', vol = 50, vary = TRUE, extrarange = -3)
+ playsound(holder_obj.loc, 'sound/items/fulton/fultext_launch.ogg', vol = 50, vary = TRUE, extrarange = -3)
animate(holder_obj, pixel_z = 1000, time = 3 SECONDS, flags = ANIMATION_RELATIVE)
if(ishuman(thing))
diff --git a/code/modules/mining/lavaland/megafauna_loot.dm b/code/modules/mining/lavaland/megafauna_loot.dm
index f0270b66d377b..d355014c6ad70 100644
--- a/code/modules/mining/lavaland/megafauna_loot.dm
+++ b/code/modules/mining/lavaland/megafauna_loot.dm
@@ -56,7 +56,7 @@
force = 15
attack_verb_continuous = list("clubs", "beats", "pummels")
attack_verb_simple = list("club", "beat", "pummel")
- hitsound = 'sound/weapons/sonic_jackhammer.ogg'
+ hitsound = 'sound/items/weapons/sonic_jackhammer.ogg'
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
actions_types = list(/datum/action/item_action/vortex_recall)
/// Linked teleport beacon for the group teleport functionality.
@@ -85,7 +85,7 @@
say("Xverwpsgexmrk...", forced = "hierophant club suicide")
user.visible_message(span_suicide("[user] holds [src] into the air! It looks like [user.p_theyre()] trying to commit suicide!"))
new/obj/effect/temp_visual/hierophant/telegraph(get_turf(user))
- playsound(user,'sound/machines/airlockopen.ogg', 75, TRUE)
+ playsound(user,'sound/machines/airlock/airlockopen.ogg', 75, TRUE)
user.visible_message(span_hierophant_warning("[user] fades out, leaving [user.p_their()] belongings behind!"))
for(var/obj/item/user_item in user)
if(user_item != src)
@@ -128,7 +128,7 @@
span_notice("You start detaching the hierophant beacon..."))
if(do_after(user, 5 SECONDS, target = user) && !beacon)
var/turf/user_turf = get_turf(user)
- playsound(user_turf,'sound/magic/blind.ogg', 200, TRUE, -4)
+ playsound(user_turf,'sound/effects/magic/blind.ogg', 200, TRUE, -4)
new /obj/effect/temp_visual/hierophant/telegraph/teleport(user_turf, user)
beacon = new/obj/effect/hierophant(user_turf)
user.update_mob_action_buttons()
@@ -166,8 +166,8 @@
return
new /obj/effect/temp_visual/hierophant/telegraph(destination, user)
new /obj/effect/temp_visual/hierophant/telegraph(source, user)
- playsound(destination,'sound/magic/wand_teleport.ogg', 200, TRUE)
- playsound(source,'sound/machines/airlockopen.ogg', 200, TRUE)
+ playsound(destination,'sound/effects/magic/wand_teleport.ogg', 200, TRUE)
+ playsound(source,'sound/machines/airlock/airlockopen.ogg', 200, TRUE)
if(!do_after(user, 0.3 SECONDS, target = user) || !user || !beacon || QDELETED(beacon)) //no walking away shitlord
teleporting = FALSE
if(user)
@@ -251,7 +251,7 @@
for(var/mob/living/carbon/human/target in range(7,user))
target.apply_status_effect(/datum/status_effect/mayhem)
to_chat(user, span_notice("You shatter the bottle!"))
- playsound(user.loc, 'sound/effects/glassbr1.ogg', 100, TRUE)
+ playsound(user.loc, 'sound/effects/glass/glassbr1.ogg', 100, TRUE)
message_admins(span_adminnotice("[ADMIN_LOOKUPFLW(user)] has activated a bottle of mayhem!"))
user.log_message("activated a bottle of mayhem", LOG_ATTACK)
qdel(src)
@@ -368,7 +368,7 @@
righthand_file = 'icons/mob/inhands/64x64_righthand.dmi'
attack_verb_continuous = list("chops", "slices", "cuts", "reaps")
attack_verb_simple = list("chop", "slice", "cut", "reap")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
inhand_x_dimension = 64
inhand_y_dimension = 64
force = 20
@@ -557,7 +557,7 @@
projectile.firer = src
projectile.fire(null, attacked_atom)
visible_message(span_danger("[src] fires at [attacked_atom]!"), span_notice("You fire at [attacked_atom]!"))
- playsound(src, 'sound/magic/fireball.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/magic/fireball.ogg', 50, TRUE)
/obj/item/soulscythe/proc/slash_target(atom/attacked_atom)
if(isliving(attacked_atom) && use_blood(10))
@@ -576,7 +576,7 @@
SpinAnimation(5)
addtimer(CALLBACK(src, PROC_REF(reset_spin)), 1 SECONDS)
visible_message(span_danger("[src] slashes [attacked_atom]!"), span_notice("You slash [attacked_atom]!"))
- playsound(src, 'sound/weapons/bladeslice.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/bladeslice.ogg', 50, TRUE)
do_attack_animation(attacked_atom, ATTACK_EFFECT_SLASH)
/obj/item/soulscythe/proc/charge_target(atom/attacked_atom)
@@ -592,7 +592,7 @@
return
visible_message(span_danger("[src] charges at [attacked_atom]!"), span_notice("You charge at [attacked_atom]!"))
new /obj/effect/temp_visual/mook_dust(get_turf(src))
- playsound(src, 'sound/weapons/thudswoosh.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE)
SpinAnimation(1)
throwforce *= 2
throw_at(attacked_atom, 10, 3, soul, FALSE)
@@ -657,7 +657,7 @@
force = 1
throwforce = 1
hitsound = 'sound/effects/ghost2.ogg'
- block_sound = 'sound/weapons/parry.ogg'
+ block_sound = 'sound/items/weapons/parry.ogg'
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "rends")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "rend")
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
@@ -794,7 +794,7 @@
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
attack_verb_continuous = list("sears", "clubs", "burn")
attack_verb_simple = list("sear", "club", "burn")
- hitsound = 'sound/weapons/sear.ogg'
+ hitsound = 'sound/items/weapons/sear.ogg'
var/turf_type = /turf/open/lava/smooth/weak
var/transform_string = "lava"
var/reset_turf_type = /turf/open/misc/asteroid/basalt
@@ -805,10 +805,12 @@
var/timer = 0
var/static/list/banned_turfs = typecacheof(list(/turf/open/space, /turf/closed))
-/obj/item/lava_staff/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
- return interact_with_atom(interacting_with, user, modifiers)
-
/obj/item/lava_staff/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(interacting_with.atom_storage || SHOULD_SKIP_INTERACTION(interacting_with, src, user))
+ return NONE
+ return ranged_interact_with_atom(interacting_with, user, modifiers)
+
+/obj/item/lava_staff/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
if(timer > world.time)
return NONE
if(is_type_in_typecache(interacting_with, banned_turfs))
@@ -831,7 +833,7 @@
message_admins("[ADMIN_LOOKUPFLW(user)] fired the lava staff at [ADMIN_VERBOSEJMP(T)]")
user.log_message("fired the lava staff at [AREACOORD(T)].", LOG_ATTACK)
timer = world.time + create_cooldown
- playsound(T,'sound/magic/fireball.ogg', 200, TRUE)
+ playsound(T,'sound/effects/magic/fireball.ogg', 200, TRUE)
else
timer = world.time
qdel(L)
@@ -840,7 +842,7 @@
if(T.TerraformTurf(reset_turf_type, flags = CHANGETURF_INHERIT_AIR))
user.visible_message(span_danger("[user] turns \the [old_name] into [reset_string]!"))
timer = world.time + reset_cooldown
- playsound(T,'sound/magic/fireball.ogg', 200, TRUE)
+ playsound(T,'sound/effects/magic/fireball.ogg', 200, TRUE)
return ITEM_INTERACT_SUCCESS
/obj/effect/temp_visual/lavastaff
@@ -871,7 +873,7 @@
inhand_x_dimension = 64
inhand_y_dimension = 64
slot_flags = ITEM_SLOT_BELT
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
w_class = WEIGHT_CLASS_BULKY
sharpness = SHARP_EDGED
/// List of factions we deal bonus damage to
@@ -973,7 +975,7 @@
user.changeNext_move(CLICK_CD_MELEE * 0.25)
if(user)
balloon_alert(user, "[active ? "opened" : "closed"] [src]")
- playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', 35, TRUE, frequency = 90000 - (active * 30000))
+ playsound(src, 'sound/effects/magic/clockwork/fellowship_armory.ogg', 35, TRUE, frequency = 90000 - (active * 30000))
return COMPONENT_NO_DEFAULT_MESSAGE
//Legion: Staff of Storms
@@ -990,7 +992,7 @@
w_class = WEIGHT_CLASS_BULKY
force = 20
damtype = BURN
- hitsound = 'sound/weapons/taserhit.ogg'
+ hitsound = 'sound/items/weapons/taserhit.ogg'
wound_bonus = -30
bare_wound_bonus = 20
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
@@ -1032,7 +1034,7 @@
return
user.visible_message(span_warning("[user] holds [src] skywards as an orange beam travels into the sky!"), \
span_notice("You hold [src] skyward, dispelling the storm!"))
- playsound(user, 'sound/magic/staff_change.ogg', 200, FALSE)
+ playsound(user, 'sound/effects/magic/staff_change.ogg', 200, FALSE)
var/old_color = user.color
user.color = list(340/255, 240/255, 0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1, 0,0,0,0)
var/old_transform = user.transform
@@ -1069,7 +1071,7 @@
if((target_turf.z in weather.impacted_z_levels) && ispath(target_area.type, weather.area_type))
power_boosted = TRUE
break
- playsound(src, 'sound/magic/lightningshock.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
+ playsound(src, 'sound/effects/magic/lightningshock.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
targeted_turfs += target_turf
balloon_alert(user, "you aim at [target_turf]...")
new /obj/effect/temp_visual/telegraphing/thunderbolt(target_turf)
@@ -1081,7 +1083,7 @@
/obj/item/storm_staff/proc/recharge(mob/user)
thunder_charges = min(thunder_charges + 1, max_thunder_charges)
- playsound(src, 'sound/magic/charge.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
+ playsound(src, 'sound/effects/magic/charge.ogg', 10, TRUE, extrarange = SILENCED_SOUND_EXTRARANGE, falloff_distance = 0)
/obj/item/storm_staff/proc/throw_thunderbolt(turf/target, boosted)
targeted_turfs -= target
@@ -1101,6 +1103,6 @@
for(var/obj/hit_thing in turf)
hit_thing.take_damage(20, BURN, ENERGY, FALSE)
- playsound(target, 'sound/magic/lightningbolt.ogg', 100, TRUE)
+ playsound(target, 'sound/effects/magic/lightningbolt.ogg', 100, TRUE)
target.visible_message(span_danger("A thunderbolt strikes [target]!"))
explosion(target, light_impact_range = (boosted ? 1 : 0), flame_range = (boosted ? 2 : 1), silent = TRUE)
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index f833e15d3f3de..3a1e88d5af13b 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -8,6 +8,7 @@
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
can_install_electronics = FALSE
paint_jobs = null
+ can_weld_shut = FALSE
/obj/structure/closet/crate/necropolis/tendril
desc = "It's watching you suspiciously. You need a skeleton key to open it."
diff --git a/code/modules/mining/lavaland/tendril_loot.dm b/code/modules/mining/lavaland/tendril_loot.dm
index 0403e17ad796a..af1b990a6fb6a 100644
--- a/code/modules/mining/lavaland/tendril_loot.dm
+++ b/code/modules/mining/lavaland/tendril_loot.dm
@@ -202,13 +202,13 @@
guardian.locked = TRUE
guardian.forceMove(src)
to_chat(guardian, span_userdanger("You have been locked away in your summoner's pendant!"))
- guardian.playsound_local(get_turf(guardian), 'sound/magic/summonitems_generic.ogg', 50, TRUE)
+ guardian.playsound_local(get_turf(guardian), 'sound/effects/magic/summonitems_generic.ogg', 50, TRUE)
/obj/item/clothing/neck/necklace/memento_mori/proc/regurgitate_guardian(mob/living/basic/guardian/guardian)
guardian.locked = FALSE
guardian.recall(forced = TRUE)
to_chat(guardian, span_notice("You have been returned back from your summoner's pendant!"))
- guardian.playsound_local(get_turf(guardian), 'sound/magic/repulse.ogg', 50, TRUE)
+ guardian.playsound_local(get_turf(guardian), 'sound/effects/magic/repulse.ogg', 50, TRUE)
/datum/action/item_action/hands_free/memento_mori
check_flags = NONE
@@ -425,8 +425,7 @@
if(!user)
return
- user.status_flags &= ~GODMODE
- REMOVE_TRAIT(user, TRAIT_NO_TRANSFORM, REF(src))
+ user.remove_traits(list(TRAIT_GODMODE, TRAIT_NO_TRANSFORM), REF(src))
user.forceMove(get_turf(src))
user.visible_message(span_danger("[user] pops back into reality!"))
@@ -437,8 +436,7 @@
setDir(user.dir)
user.forceMove(src)
- ADD_TRAIT(user, TRAIT_NO_TRANSFORM, REF(src))
- user.status_flags |= GODMODE
+ user.add_traits(list(TRAIT_GODMODE, TRAIT_NO_TRANSFORM), REF(src))
user_ref = WEAKREF(user)
@@ -557,7 +555,7 @@
var/obj/item/organ/external/wings/functional/wings = get_wing_choice(exposed_human, chest)
wings = new wings()
wings.Insert(exposed_human)
- playsound(exposed_human.loc, 'sound/items/poster_ripped.ogg', 50, TRUE, -1)
+ playsound(exposed_human.loc, 'sound/items/poster/poster_ripped.ogg', 50, TRUE, -1)
exposed_human.apply_damage(20, def_zone = BODY_ZONE_CHEST, forced = TRUE, wound_bonus = CANT_WOUND)
exposed_human.emote("scream")
@@ -771,7 +769,7 @@
/// Starts berserk, reducing incoming brute by 50%, doubled attacking speed, NOGUNS trait, adding a color and giving them the berserk movespeed modifier
/obj/item/clothing/head/hooded/berserker/proc/berserk_mode(mob/living/carbon/human/user)
to_chat(user, span_warning("You enter berserk mode."))
- playsound(user, 'sound/magic/staff_healing.ogg', 50)
+ playsound(user, 'sound/effects/magic/staff_healing.ogg', 50)
user.add_movespeed_modifier(/datum/movespeed_modifier/berserk)
user.physiology.brute_mod *= 0.5
user.next_move_modifier *= BERSERK_ATTACK_SPEED_MODIFIER
@@ -789,7 +787,7 @@
if(QDELETED(user))
return
to_chat(user, span_warning("You exit berserk mode."))
- playsound(user, 'sound/magic/summonitems_generic.ogg', 50)
+ playsound(user, 'sound/effects/magic/summonitems_generic.ogg', 50)
user.remove_movespeed_modifier(/datum/movespeed_modifier/berserk)
user.physiology.brute_mod *= 2
user.next_move_modifier /= BERSERK_ATTACK_SPEED_MODIFIER
@@ -905,7 +903,7 @@
healthscan(living_owner, living_scanned, 1, TRUE)
- owner.playsound_local(get_turf(owner), 'sound/magic/smoke.ogg', 50, TRUE)
+ owner.playsound_local(get_turf(owner), 'sound/effects/magic/smoke.ogg', 50, TRUE)
owner.balloon_alert(owner, "[living_scanned] scanned")
addtimer(CALLBACK(src, PROC_REF(send_cooldown_end_message), cooldown_time))
@@ -941,7 +939,7 @@
/obj/item/organ/internal/cyberimp/arm/shard/attack_self(mob/user, modifiers)
. = ..()
to_chat(user, span_userdanger("The mass goes up your arm and goes inside it!"))
- playsound(user, 'sound/magic/demon_consume.ogg', 50, TRUE)
+ playsound(user, 'sound/effects/magic/demon_consume.ogg', 50, TRUE)
var/index = user.get_held_index_of_item(src)
zone = (index == LEFT_HANDS ? BODY_ZONE_L_ARM : BODY_ZONE_R_ARM)
SetSlotFromZone()
@@ -962,7 +960,7 @@
return FALSE
if(!katana.drew_blood)
to_chat(owner, span_userdanger("[katana] lashes out at you in hunger!"))
- playsound(owner, 'sound/magic/demon_attack1.ogg', 50, TRUE)
+ playsound(owner, 'sound/effects/magic/demon_attack1.ogg', 50, TRUE)
var/obj/item/bodypart/part = owner.get_holding_bodypart_of_item(katana)
if(part)
part.receive_damage(brute = 25, wound_bonus = 10, sharpness = SHARP_EDGED)
@@ -988,12 +986,12 @@
force = 15
armour_penetration = 30
block_chance = 30
- block_sound = 'sound/weapons/parry.ogg'
+ block_sound = 'sound/items/weapons/parry.ogg'
sharpness = SHARP_EDGED
w_class = WEIGHT_CLASS_HUGE
attack_verb_continuous = list("attacks", "slashes", "stabs", "slices", "tears", "lacerates", "rips", "dices", "cuts")
attack_verb_simple = list("attack", "slash", "stab", "slice", "tear", "lacerate", "rip", "dice", "cut")
- hitsound = 'sound/weapons/bladeslice.ogg'
+ hitsound = 'sound/items/weapons/bladeslice.ogg'
resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | FREEZE_PROOF
var/shattered = FALSE
var/drew_blood = FALSE
@@ -1045,7 +1043,7 @@
user.visible_message(span_warning("[user] strikes [target] with [src]'s hilt!"),
span_notice("You hilt strike [target]!"))
to_chat(target, span_userdanger("You've been struck by [user]!"))
- playsound(src, 'sound/weapons/genhit3.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/genhit3.ogg', 50, TRUE)
RegisterSignal(target, COMSIG_MOVABLE_IMPACT, PROC_REF(strike_throw_impact))
var/atom/throw_target = get_edge_target_turf(target, user.dir)
target.throw_at(throw_target, 5, 3, user, FALSE, gentle = TRUE)
@@ -1070,7 +1068,7 @@
/obj/item/cursed_katana/proc/slice(mob/living/target, mob/user)
user.visible_message(span_warning("[user] does a wide slice!"),
span_notice("You do a wide slice!"))
- playsound(src, 'sound/weapons/bladeslice.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/bladeslice.ogg', 50, TRUE)
var/turf/user_turf = get_turf(user)
var/dir_to_target = get_dir(user_turf, get_turf(target))
var/static/list/cursed_katana_slice_angles = list(0, -45, 45, -90, 90) //so that the animation animates towards the target clicked and not towards a side target
@@ -1090,7 +1088,7 @@
user.visible_message(span_warning("[user] vanishes into thin air!"),
span_notice("You enter the dark cloak."))
new /obj/effect/temp_visual/mook_dust(get_turf(src))
- playsound(src, 'sound/magic/smoke.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/magic/smoke.ogg', 50, TRUE)
if(ishostile(target))
var/mob/living/simple_animal/hostile/hostile_target = target
if(hostile_target.target == user)
@@ -1103,7 +1101,7 @@
user.clear_sight(SEE_SELF)
user.visible_message(span_warning("[user] appears from thin air!"),
span_notice("You exit the dark cloak."))
- playsound(src, 'sound/magic/summonitems_generic.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/magic/summonitems_generic.ogg', 50, TRUE)
new /obj/effect/temp_visual/mook_dust(get_turf(src))
/obj/item/cursed_katana/proc/cut(mob/living/target, mob/user)
@@ -1112,7 +1110,7 @@
to_chat(target, span_userdanger("Your tendons have been cut by [user]!"))
target.apply_damage(damage = 15, sharpness = SHARP_EDGED, wound_bonus = 15)
user.do_attack_animation(target, ATTACK_EFFECT_DISARM)
- playsound(src, 'sound/weapons/rapierhit.ogg', 50, TRUE)
+ playsound(src, 'sound/items/weapons/rapierhit.ogg', 50, TRUE)
var/datum/status_effect/stacking/saw_bleed/bloodletting/status = target.has_status_effect(/datum/status_effect/stacking/saw_bleed/bloodletting)
if(!status)
target.apply_status_effect(/datum/status_effect/stacking/saw_bleed/bloodletting, 6)
@@ -1123,7 +1121,7 @@
user.visible_message(span_warning("[user] dashes through [target]!"),
span_notice("You dash through [target]!"))
to_chat(target, span_userdanger("[user] dashes through you!"))
- playsound(src, 'sound/magic/blink.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/magic/blink.ogg', 50, TRUE)
target.apply_damage(damage = 17, sharpness = SHARP_POINTY, bare_wound_bonus = 10)
var/turf/dash_target = get_turf(target)
for(var/distance in 0 to 8)
@@ -1143,7 +1141,7 @@
to_chat(target, span_userdanger("[user] shatters [src] over you!"))
target.apply_damage(damage = ishostile(target) ? 75 : 35, wound_bonus = 20)
user.do_attack_animation(target, ATTACK_EFFECT_SMASH)
- playsound(src, 'sound/effects/glassbr3.ogg', 100, TRUE)
+ playsound(src, 'sound/effects/glass/glassbr3.ogg', 100, TRUE)
shattered = TRUE
moveToNullspace()
balloon_alert(user, "katana shattered")
@@ -1152,7 +1150,7 @@
/obj/item/cursed_katana/proc/coagulate(mob/user)
balloon_alert(user, "katana coagulated")
shattered = FALSE
- playsound(src, 'sound/magic/demon_consume.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/magic/demon_consume.ogg', 50, TRUE)
#undef ATTACK_STRIKE
#undef ATTACK_SLICE
diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm
index 53fd78cf94d49..aaf127bf8b594 100644
--- a/code/modules/mining/mine_items.dm
+++ b/code/modules/mining/mine_items.dm
@@ -160,6 +160,7 @@
close_sound = 'sound/machines/trapdoor/trapdoor_shut.ogg'
set_dir_on_move = TRUE
can_buckle = TRUE
+ can_weld_shut = FALSE
/// Whether we're on a set of rails or just on the ground
var/on_rails = FALSE
@@ -320,7 +321,7 @@
return
update_rail_state(FALSE)
Move(new_destination)
- var/sound/thud_sound = sound('sound/weapons/thudswoosh.ogg')
+ var/sound/thud_sound = sound('sound/items/weapons/thudswoosh.ogg')
thud_sound.pitch = 0.5
playsound(src, thud_sound, 50, TRUE)
diff --git a/code/modules/mob/camera/camera.dm b/code/modules/mob/camera/camera.dm
index b4ddc8cd9c8b4..eb0d787f64b49 100644
--- a/code/modules/mob/camera/camera.dm
+++ b/code/modules/mob/camera/camera.dm
@@ -4,10 +4,10 @@
density = FALSE
move_force = INFINITY
move_resist = INFINITY
- status_flags = GODMODE // You can't damage it.
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
invisibility = INVISIBILITY_ABSTRACT // No one can see us
sight = SEE_SELF
+ status_flags = NONE
/// Toggles if the camera can move on shuttles
var/move_on_shuttle = FALSE
/// Toggles if the camera can use emotes
@@ -15,6 +15,7 @@
/mob/camera/Initialize(mapload)
. = ..()
+ ADD_TRAIT(src, TRAIT_GODMODE, INNATE_TRAIT)
SSpoints_of_interest.make_point_of_interest(src)
if(!move_on_shuttle)
ADD_TRAIT(src, TRAIT_BLOCK_SHUTTLE_MOVEMENT, INNATE_TRAIT)
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 4db5ae888496c..988dd6efdc14a 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -127,7 +127,7 @@
return GENERIC_JOB_UNAVAILABLE_ERROR
/mob/dead/new_player/proc/IsJobUnavailable(rank, latejoin = FALSE)
- var/datum/job/job = SSjob.GetJob(rank)
+ var/datum/job/job = SSjob.get_job(rank)
if(!(job.job_flags & JOB_NEW_PLAYER_JOINABLE))
return JOB_UNAVAILABLE_GENERIC
if((job.current_positions >= job.total_positions) && job.total_positions != -1)
@@ -172,9 +172,9 @@
SSticker.queued_players -= src
SSticker.queue_delay = 4
- var/datum/job/job = SSjob.GetJob(rank)
+ var/datum/job/job = SSjob.get_job(rank)
- if(!SSjob.AssignRole(src, job, TRUE))
+ if(!SSjob.assign_role(src, job, TRUE))
tgui_alert(usr, "There was an unexpected error putting you into your requested job. If you cannot join with any job, you should contact an admin.")
return FALSE
@@ -187,18 +187,18 @@
CRASH("Failed to create a character for latejoin.")
transfer_character()
- SSjob.EquipRank(character, job, character.client)
+ SSjob.equip_rank(character, job, character.client)
job.after_latejoin_spawn(character)
#define IS_NOT_CAPTAIN 0
#define IS_ACTING_CAPTAIN 1
#define IS_FULL_CAPTAIN 2
var/is_captain = IS_NOT_CAPTAIN
- var/captain_sound = 'sound/misc/notice2.ogg'
+ var/captain_sound = 'sound/announcer/notice/notice2.ogg'
// If we already have a captain, are they a "Captain" rank and are we allowing multiple of them to be assigned?
if(is_captain_job(job))
is_captain = IS_FULL_CAPTAIN
- captain_sound = 'sound/misc/announce.ogg'
+ captain_sound = 'sound/announcer/announcement/announce.ogg'
// If we don't have an assigned cap yet, check if this person qualifies for some from of captaincy.
else if(!SSjob.assigned_captain && ishuman(character) && SSjob.chain_of_command[rank] && !is_banned_from(character.ckey, list(JOB_CAPTAIN)))
is_captain = IS_ACTING_CAPTAIN
diff --git a/code/modules/mob/dead/new_player/preferences_setup.dm b/code/modules/mob/dead/new_player/preferences_setup.dm
index 49ac17d48ece9..fb3b97066e0bf 100644
--- a/code/modules/mob/dead/new_player/preferences_setup.dm
+++ b/code/modules/mob/dead/new_player/preferences_setup.dm
@@ -88,13 +88,13 @@
for(var/job in job_preferences)
if(job_preferences[job] > highest_pref)
- preview_job = SSjob.GetJob(job)
+ preview_job = SSjob.get_job(job)
highest_pref = job_preferences[job]
return preview_job
/datum/preferences/proc/render_new_preview_appearance(mob/living/carbon/human/dummy/mannequin, show_job_clothes = TRUE)
- var/datum/job/no_job = SSjob.GetJobType(/datum/job/unassigned)
+ var/datum/job/no_job = SSjob.get_job_type(/datum/job/unassigned)
var/datum/job/preview_job = get_highest_priority_job() || no_job
if(preview_job)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 80f7be5e3180e..8f5d07ee0fcc1 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -483,7 +483,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
var/list/icon_dimensions = get_icon_dimensions(target.icon)
var/orbitsize = (icon_dimensions["width"] + icon_dimensions["height"]) * 0.5
- orbitsize -= (orbitsize/world.icon_size)*(world.icon_size*0.25)
+ orbitsize -= (orbitsize/ICON_SIZE_ALL)*(ICON_SIZE_ALL*0.25)
var/rot_seg
diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm
index 7aba095a944a2..c812905d58fec 100644
--- a/code/modules/mob/emote.dm
+++ b/code/modules/mob/emote.dm
@@ -159,10 +159,10 @@
animate(transform = original_transform, time = 0.1 SECONDS)
/datum/emote/jump/get_sound(mob/user)
- return 'sound/weapons/thudswoosh.ogg'
+ return 'sound/items/weapons/thudswoosh.ogg'
// Avoids playing sounds if we're a ghost
/datum/emote/jump/should_play_sound(mob/user, intentional)
- if ishuman(user)
+ if(isliving(user))
return ..()
return FALSE
diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm
index 2d22c11ee71f8..708d8bbdd0702 100644
--- a/code/modules/mob/inventory.dm
+++ b/code/modules/mob/inventory.dm
@@ -586,19 +586,19 @@
hud_used.build_hand_slots()
//GetAllContents that is reasonable and not stupid
-/mob/living/proc/get_all_gear()
- var/list/processing_list = get_equipped_items(INCLUDE_POCKETS | INCLUDE_ACCESSORIES | INCLUDE_HELD)
+/mob/living/proc/get_all_gear(accessories = TRUE, recursive = TRUE)
+ var/list/processing_list = get_equipped_items(INCLUDE_POCKETS | INCLUDE_HELD | (accessories ? INCLUDE_ACCESSORIES : NONE))
list_clear_nulls(processing_list) // handles empty hands
var/i = 0
while(i < length(processing_list))
var/atom/A = processing_list[++i]
- if(A.atom_storage)
+ if(A.atom_storage && recursive)
processing_list += A.atom_storage.return_inv()
return processing_list
/// Returns a list of things that the provided mob has, including any storage-capable implants.
-/mob/living/proc/gather_belongings()
- var/list/belongings = get_all_gear()
+/mob/living/proc/gather_belongings(accessories = TRUE, recursive = TRUE)
+ var/list/belongings = get_all_gear(accessories, recursive)
for (var/obj/item/implant/storage/internal_bag in implants)
belongings += internal_bag.contents
return belongings
diff --git a/code/modules/mob/living/basic/alien/_alien.dm b/code/modules/mob/living/basic/alien/_alien.dm
index 907d28aaa4187..99b615fbf6089 100644
--- a/code/modules/mob/living/basic/alien/_alien.dm
+++ b/code/modules/mob/living/basic/alien/_alien.dm
@@ -35,10 +35,10 @@
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_CLAW
gold_core_spawnable = NO_SPAWN
- death_sound = 'sound/voice/hiss6.ogg'
+ death_sound = 'sound/mobs/non-humanoids/hiss/hiss6.ogg'
death_message = "lets out a waning guttural screech, green blood bubbling from its maw..."
habitable_atmos = null
diff --git a/code/modules/mob/living/basic/alien/queen.dm b/code/modules/mob/living/basic/alien/queen.dm
index f2d787743a258..8957d05d89b10 100644
--- a/code/modules/mob/living/basic/alien/queen.dm
+++ b/code/modules/mob/living/basic/alien/queen.dm
@@ -15,7 +15,7 @@
///The type of projectile that fires from attacks.
var/projectiletype = /obj/projectile/neurotoxin/damaging
///The sound that plays when the projectile is fired.
- var/projectilesound = 'sound/weapons/pierce.ogg'
+ var/projectilesound = 'sound/items/weapons/pierce.ogg'
/mob/living/basic/alien/queen/Initialize(mapload)
. = ..()
diff --git a/code/modules/mob/living/basic/alien/sentinel.dm b/code/modules/mob/living/basic/alien/sentinel.dm
index 8f5ae815c5ffd..7f39e1b12aab5 100644
--- a/code/modules/mob/living/basic/alien/sentinel.dm
+++ b/code/modules/mob/living/basic/alien/sentinel.dm
@@ -13,7 +13,7 @@
///The type of projectile that fires from attacks.
var/projectiletype = /obj/projectile/neurotoxin/damaging
///The sound that plays when the projectile is fired.
- var/projectilesound = 'sound/weapons/pierce.ogg'
+ var/projectilesound = 'sound/items/weapons/pierce.ogg'
/mob/living/basic/alien/sentinel/Initialize(mapload)
. = ..()
diff --git a/code/modules/mob/living/basic/basic_defense.dm b/code/modules/mob/living/basic/basic_defense.dm
index 646cabe339d42..b4100a73cc160 100644
--- a/code/modules/mob/living/basic/basic_defense.dm
+++ b/code/modules/mob/living/basic/basic_defense.dm
@@ -15,7 +15,7 @@
ignored_mobs = user,
)
to_chat(user, span_notice("You [response_help_simple] [src]."))
- playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ playsound(loc, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE, -1)
return TRUE
if(HAS_TRAIT(user, TRAIT_PACIFISM))
@@ -72,7 +72,7 @@
visible_message(span_notice("[user.name] [response_help_continuous] [src]."), \
span_notice("[user.name] [response_help_continuous] you."), null, COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_notice("You [response_help_simple] [src]."))
- playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ playsound(loc, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE, -1)
/mob/living/basic/attack_alien(mob/living/carbon/alien/adult/user, list/modifiers)
@@ -80,7 +80,7 @@
if(!.)
return
if(LAZYACCESS(modifiers, RIGHT_CLICK))
- playsound(loc, 'sound/weapons/pierce.ogg', 25, TRUE, -1)
+ playsound(loc, 'sound/items/weapons/pierce.ogg', 25, TRUE, -1)
visible_message(span_danger("[user] [response_disarm_continuous] [name]!"), \
span_userdanger("[user] [response_disarm_continuous] you!"), null, COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_danger("You [response_disarm_simple] [name]!"))
@@ -90,7 +90,7 @@
visible_message(span_danger("[user] slashes at [src]!"), \
span_userdanger("You're slashed at by [user]!"), null, COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_danger("You slash at [src]!"))
- playsound(loc, 'sound/weapons/slice.ogg', 25, TRUE, -1)
+ playsound(loc, 'sound/items/weapons/slice.ogg', 25, TRUE, -1)
apply_damage(damage)
log_combat(user, src, "attacked")
@@ -159,7 +159,7 @@
..()
/mob/living/basic/update_stat()
- if(status_flags & GODMODE)
+ if(HAS_TRAIT(src, TRAIT_GODMODE))
return
if(stat != DEAD)
if(health <= 0)
diff --git a/code/modules/mob/living/basic/blob_minions/blob_spore.dm b/code/modules/mob/living/basic/blob_minions/blob_spore.dm
index e8c3acc8b97f0..6946d30a631ec 100644
--- a/code/modules/mob/living/basic/blob_minions/blob_spore.dm
+++ b/code/modules/mob/living/basic/blob_minions/blob_spore.dm
@@ -19,7 +19,7 @@
obj_damage = 0
attack_verb_continuous = "batters"
attack_verb_simple = "batter"
- attack_sound = 'sound/weapons/genhit1.ogg'
+ attack_sound = 'sound/items/weapons/genhit1.ogg'
death_message = "explodes into a cloud of gas!"
gold_core_spawnable = HOSTILE_SPAWN
basic_mob_flags = DEL_ON_DEATH
diff --git a/code/modules/mob/living/basic/blob_minions/blob_zombie.dm b/code/modules/mob/living/basic/blob_minions/blob_zombie.dm
index 50299a38b3fee..b3ce7ea38cbc4 100644
--- a/code/modules/mob/living/basic/blob_minions/blob_zombie.dm
+++ b/code/modules/mob/living/basic/blob_minions/blob_zombie.dm
@@ -18,7 +18,7 @@
obj_damage = 20
attack_verb_continuous = "punches"
attack_verb_simple = "punch"
- attack_sound = 'sound/weapons/genhit1.ogg'
+ attack_sound = 'sound/items/weapons/genhit1.ogg'
death_message = "collapses to the ground!"
gold_core_spawnable = NO_SPAWN
basic_mob_flags = DEL_ON_DEATH
diff --git a/code/modules/mob/living/basic/blob_minions/blobbernaut.dm b/code/modules/mob/living/basic/blob_minions/blobbernaut.dm
index 8b94063ba7764..13146c3b5c51c 100644
--- a/code/modules/mob/living/basic/blob_minions/blobbernaut.dm
+++ b/code/modules/mob/living/basic/blob_minions/blobbernaut.dm
@@ -17,7 +17,7 @@
obj_damage = BLOBMOB_BLOBBERNAUT_DMG_OBJ
attack_verb_continuous = "slams"
attack_verb_simple = "slam"
- attack_sound = 'sound/effects/blobattack.ogg'
+ attack_sound = 'sound/effects/blob/blobattack.ogg'
verb_say = "gurgles"
verb_ask = "demands"
verb_exclaim = "roars"
@@ -84,8 +84,8 @@
key = ckey
flick("blobbernaut_produce", src)
health = maxHealth / 2 // Start out injured to encourage not beelining away from the blob
- SEND_SOUND(src, sound('sound/effects/blobattack.ogg'))
- SEND_SOUND(src, sound('sound/effects/attackblob.ogg'))
+ SEND_SOUND(src, sound('sound/effects/blob/blobattack.ogg'))
+ SEND_SOUND(src, sound('sound/effects/blob/attackblob.ogg'))
to_chat(src, span_infoplain("You are powerful, hard to kill, and slowly regenerate near nodes and cores, [span_cult_large("but will slowly die if not near the blob")] or if the factory that made you is killed."))
to_chat(src, span_infoplain("You can communicate with other blobbernauts and overminds telepathically by attempting to speak normally"))
to_chat(src, span_infoplain("Your overmind's blob reagent is: [blobstrain.name]!"))
diff --git a/code/modules/mob/living/basic/bots/_bots.dm b/code/modules/mob/living/basic/bots/_bots.dm
index cc6ac85c6cbc4..be7ce8bc5ddc5 100644
--- a/code/modules/mob/living/basic/bots/_bots.dm
+++ b/code/modules/mob/living/basic/bots/_bots.dm
@@ -538,6 +538,7 @@ GLOBAL_LIST_INIT(command_strings, list(
/mob/living/basic/bot/proc/bot_reset(bypass_ai_reset = FALSE)
SEND_SIGNAL(src, COMSIG_BOT_RESET)
access_card.set_access(initial_access)
+ update_bot_mode(new_mode = src::mode)
diag_hud_set_botstat()
diag_hud_set_botmode()
clear_path_hud()
@@ -558,8 +559,7 @@ GLOBAL_LIST_INIT(command_strings, list(
// process control input
switch(command)
if("patroloff")
- bot_reset() //HOLD IT!! //OBJECTION!!
- set_mode_flags(bot_mode_flags & ~BOT_MODE_AUTOPATROL)
+ set_patrol_off()
if("patrolon")
set_mode_flags(bot_mode_flags | BOT_MODE_AUTOPATROL)
if("summon")
@@ -567,6 +567,9 @@ GLOBAL_LIST_INIT(command_strings, list(
if("ejectpai")
eject_pai_remote(user)
+/mob/living/basic/bot/proc/set_patrol_off()
+ bot_reset()
+ set_mode_flags(bot_mode_flags & ~BOT_MODE_AUTOPATROL)
/mob/living/basic/bot/proc/bot_control_message(command, user)
if(command == "summon")
diff --git a/code/modules/mob/living/basic/bots/bot_ai.dm b/code/modules/mob/living/basic/bots/bot_ai.dm
index a0abbbfd48b40..fd89168ddf4f1 100644
--- a/code/modules/mob/living/basic/bots/bot_ai.dm
+++ b/code/modules/mob/living/basic/bots/bot_ai.dm
@@ -48,6 +48,12 @@
return
RegisterSignal(new_pawn, COMSIG_BOT_RESET, PROC_REF(reset_bot))
RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_BOT_SUMMON_TARGET), PROC_REF(clear_summon))
+ RegisterSignal(new_pawn, COMSIG_MOB_AI_MOVEMENT_STARTED, PROC_REF(on_movement_start))
+
+/datum/ai_controller/basic_controller/bot/proc/on_movement_start(mob/living/basic/bot/source, atom/target)
+ SIGNAL_HANDLER
+ if(current_movement_target == blackboard[BB_BEACON_TARGET])
+ source.update_bot_mode(new_mode = BOT_PATROL)
/datum/ai_controller/basic_controller/bot/proc/clear_summon()
SIGNAL_HANDLER
@@ -75,7 +81,7 @@
/datum/ai_controller/basic_controller/bot/proc/reset_bot()
SIGNAL_HANDLER
-
+ CancelActions()
if(!length(reset_keys))
return
for(var/key in reset_keys)
@@ -130,7 +136,6 @@
return
if(controller.blackboard_key_exists(BB_BEACON_TARGET))
- bot_pawn.update_bot_mode(new_mode = BOT_PATROL)
controller.queue_behavior(travel_behavior, BB_BEACON_TARGET)
return
@@ -195,8 +200,6 @@
/datum/ai_planning_subtree/respond_to_summon/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
if(!controller.blackboard_key_exists(BB_BOT_SUMMON_TARGET))
return
- controller.clear_blackboard_key(BB_PREVIOUS_BEACON_TARGET)
- controller.clear_blackboard_key(BB_BEACON_TARGET)
controller.queue_behavior(/datum/ai_behavior/travel_towards/bot_summon, BB_BOT_SUMMON_TARGET)
return SUBTREE_RETURN_FINISH_PLANNING
diff --git a/code/modules/mob/living/basic/bots/bot_hud.dm b/code/modules/mob/living/basic/bots/bot_hud.dm
index 61aee9f10b180..6cb3f6bcd01b0 100644
--- a/code/modules/mob/living/basic/bots/bot_hud.dm
+++ b/code/modules/mob/living/basic/bots/bot_hud.dm
@@ -1,13 +1,13 @@
/mob/living/basic/bot/proc/diag_hud_set_bothealth()
var/image/holder = hud_list[DIAG_HUD]
var/icon/icon_image = icon(icon, icon_state, dir)
- holder.pixel_y = icon_image.Height() - world.icon_size
+ holder.pixel_y = icon_image.Height() - ICON_SIZE_Y
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
/mob/living/basic/bot/proc/diag_hud_set_botstat() //On (With wireless on or off), Off, EMP'ed
var/image/holder = hud_list[DIAG_STAT_HUD]
var/icon/our_icon = icon(icon, icon_state, dir)
- holder.pixel_y = our_icon.Height() - world.icon_size
+ holder.pixel_y = our_icon.Height() - ICON_SIZE_Y
if(bot_mode_flags & BOT_MODE_ON)
holder.icon_state = "hudstat"
return
@@ -19,7 +19,7 @@
/mob/living/basic/bot/proc/diag_hud_set_botmode() //Shows a bot's current operation
var/image/holder = hud_list[DIAG_BOT_HUD]
var/icon/icon_image = icon(icon, icon_state, dir)
- holder.pixel_y = icon_image.Height() - world.icon_size
+ holder.pixel_y = icon_image.Height() - ICON_SIZE_Y
if(client) //If the bot is player controlled, it will not be following mode logic!
holder.icon_state = "hudsentient"
return
@@ -47,12 +47,14 @@
if(isnull(ai_controller))
return
+ //Removes path images and handles removing hud client images
clear_path_hud()
+ var/list/path_huds_watching_me = list(GLOB.huds[DATA_HUD_DIAGNOSTIC], GLOB.huds[DATA_HUD_BOT_PATH])
+
var/list/path_images = active_hud_list[DIAG_PATH_HUD]
LAZYCLEARLIST(path_images)
- var/list/path_huds_watching_me = list(GLOB.huds[DATA_HUD_DIAGNOSTIC], GLOB.huds[DATA_HUD_BOT_PATH])
var/atom/move_target = ai_controller.current_movement_target
if(move_target != ai_controller.blackboard[BB_BEACON_TARGET])
@@ -62,9 +64,6 @@
if(!length(our_path))
return
- for(var/datum/atom_hud/hud as anything in path_huds_watching_me)
- hud.remove_atom_from_hud(src)
-
for(var/index in 1 to our_path.len)
if(index == 1 || index == our_path.len)
continue
@@ -75,7 +74,9 @@
var/next_direction = get_dir(previous_turf, next_turf)
var/previous_direction = get_dir(current_turf, previous_turf)
- var/image/path_display = image(icon = path_image_icon, loc = current_turf, icon_state = path_image_icon_state, layer = GAME_PLANE, dir = next_direction)
+ var/image/path_display = image(icon = path_image_icon, loc = current_turf, icon_state = path_image_icon_state, layer = BOT_PATH_LAYER, dir = next_direction)
+
+ SET_PLANE(path_display, GAME_PLANE, current_turf)
if((ISDIAGONALDIR(next_direction) && (previous_direction & (NORTH|SOUTH))))
var/turn_value = (next_direction == SOUTHWEST || next_direction == NORTHEAST) ? 90 : -90
@@ -118,3 +119,8 @@
animate(our_image, alpha = 0, time = 0.3 SECONDS)
current_pathed_turfs -= index
+ // Call hud remove handlers to ensure viewing user client images are removed
+ var/list/path_huds_watching_me = list(GLOB.huds[DATA_HUD_DIAGNOSTIC], GLOB.huds[DATA_HUD_BOT_PATH])
+ for(var/datum/atom_hud/hud as anything in path_huds_watching_me)
+ hud.remove_atom_from_hud(src)
+
diff --git a/code/modules/mob/living/basic/bots/dedbot.dm b/code/modules/mob/living/basic/bots/dedbot.dm
index bf48ac93c3b57..0dd5bff9a7c66 100644
--- a/code/modules/mob/living/basic/bots/dedbot.dm
+++ b/code/modules/mob/living/basic/bots/dedbot.dm
@@ -21,7 +21,7 @@
sharpness = SHARP_EDGED
attack_verb_continuous = "eviscerates"
attack_verb_simple = "eviscerate"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
gold_core_spawnable = HOSTILE_SPAWN
limb_destroyer = TRUE
@@ -103,7 +103,7 @@
return FALSE
caster.Shake(1.4, 0.8, 0.3 SECONDS)
caster.visible_message(span_danger("[caster] shakes violently!"))
- playsound(caster, 'sound/weapons/drill.ogg', 120 , TRUE)
+ playsound(caster, 'sound/items/weapons/drill.ogg', 120 , TRUE)
slash_em(caster)
StartCooldown(cooldown_time)
diff --git a/code/modules/mob/living/basic/bots/firebot/firebot.dm b/code/modules/mob/living/basic/bots/firebot/firebot.dm
index 921909aa8a531..1db37c6340bff 100644
--- a/code/modules/mob/living/basic/bots/firebot/firebot.dm
+++ b/code/modules/mob/living/basic/bots/firebot/firebot.dm
@@ -29,20 +29,20 @@
/mob/living/basic/bot/firebot/generate_speak_list()
var/static/list/idle_lines = list(
- FIREBOT_VOICED_NO_FIRES = 'sound/voice/firebot/nofires.ogg',
- FIREBOT_VOICED_ONLY_YOU = 'sound/voice/firebot/onlyyou.ogg',
- FIREBOT_VOICED_TEMPERATURE_NOMINAL = 'sound/voice/firebot/tempnominal.ogg',
- FIREBOT_VOICED_KEEP_COOL = 'sound/voice/firebot/keepitcool.ogg',
+ FIREBOT_VOICED_NO_FIRES = 'sound/mobs/non-humanoids/firebot/nofires.ogg',
+ FIREBOT_VOICED_ONLY_YOU = 'sound/mobs/non-humanoids/firebot/onlyyou.ogg',
+ FIREBOT_VOICED_TEMPERATURE_NOMINAL = 'sound/mobs/non-humanoids/firebot/tempnominal.ogg',
+ FIREBOT_VOICED_KEEP_COOL = 'sound/mobs/non-humanoids/firebot/keepitcool.ogg',
)
var/static/list/fire_detected_lines = list(
- FIREBOT_VOICED_FIRE_DETECTED = 'sound/voice/firebot/detected.ogg',
- FIREBOT_VOICED_STOP_DROP = 'sound/voice/firebot/stopdropnroll.ogg',
- FIREBOT_VOICED_EXTINGUISHING = 'sound/voice/firebot/extinguishing.ogg',
+ FIREBOT_VOICED_FIRE_DETECTED = 'sound/mobs/non-humanoids/firebot/detected.ogg',
+ FIREBOT_VOICED_STOP_DROP = 'sound/mobs/non-humanoids/firebot/stopdropnroll.ogg',
+ FIREBOT_VOICED_EXTINGUISHING = 'sound/mobs/non-humanoids/firebot/extinguishing.ogg',
)
var/static/list/emagged_lines = list(
- FIREBOT_VOICED_CANDLE_TIP = 'sound/voice/firebot/candle_tip.ogg',
- FIREBOT_VOICED_ELECTRIC_FIRE = 'sound/voice/firebot/electric_fire_tip.ogg',
- FIREBOT_VOICED_FUEL_TIP = 'sound/voice/firebot/gasoline_tip.ogg'
+ FIREBOT_VOICED_CANDLE_TIP = 'sound/mobs/non-humanoids/firebot/candle_tip.ogg',
+ FIREBOT_VOICED_ELECTRIC_FIRE = 'sound/mobs/non-humanoids/firebot/electric_fire_tip.ogg',
+ FIREBOT_VOICED_FUEL_TIP = 'sound/mobs/non-humanoids/firebot/gasoline_tip.ogg'
)
ai_controller.set_blackboard_key(BB_FIREBOT_EMAGGED_LINES, emagged_lines)
ai_controller.set_blackboard_key(BB_FIREBOT_IDLE_LINES, idle_lines)
diff --git a/code/modules/mob/living/basic/bots/honkbots/honkbot.dm b/code/modules/mob/living/basic/bots/honkbots/honkbot.dm
index 38884bec503da..1fa30063dd581 100644
--- a/code/modules/mob/living/basic/bots/honkbots/honkbot.dm
+++ b/code/modules/mob/living/basic/bots/honkbots/honkbot.dm
@@ -48,7 +48,7 @@
can_slip_callback = CALLBACK(src, PROC_REF(pre_slip)),\
)
AddComponent(/datum/component/stun_n_cuff,\
- stun_sound = 'sound/items/AirHorn.ogg',\
+ stun_sound = 'sound/items/airhorn/AirHorn.ogg',\
post_stun_callback = CALLBACK(src, PROC_REF(post_stun)),\
post_arrest_callback = CALLBACK(src, PROC_REF(post_arrest)),\
handcuff_type = /obj/item/restraints/handcuffs/cable/zipties/fake,\
diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm
index 4dbd78dcac14a..6100a81279747 100644
--- a/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm
+++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot.dm
@@ -29,23 +29,23 @@
var/static/mutable_appearance/fire_overlay = mutable_appearance('icons/mob/silicon/aibots.dmi', "hygienebot-fire")
///announcements we say when we find a target
var/static/list/found_announcements = list(
- HYGIENEBOT_VOICED_UNHYGIENIC = 'sound/voice/hygienebot/unhygienicclient.ogg',
+ HYGIENEBOT_VOICED_UNHYGIENIC = 'sound/mobs/non-humanoids/hygienebot/unhygienicclient.ogg',
)
///announcements we say when the target keeps moving away
var/static/list/threat_announcements = list(
- HYGIENEBOT_VOICED_THREAT_AIRLOCK = 'sound/voice/hygienebot/dragyouout.ogg',
- HYGIENEBOT_VOICED_FOUL_SMELL = 'sound/voice/hygienebot/foulsmelling.ogg',
- HYGIENEBOT_VOICED_TROGLODYTE = 'sound/voice/hygienebot/troglodyte.ogg',
- HYGIENEBOT_VOICED_GREEN_CLOUD = 'sound/voice/hygienebot/greencloud.ogg',
- HYGIENEBOT_VOICED_ARSEHOLE = 'sound/voice/hygienebot/letmeclean.ogg',
- HYGIENEBOT_VOICED_THREAT_ARTERIES = 'sound/voice/hygienebot/cutarteries.ogg',
- HYGIENEBOT_VOICED_STOP_RUNNING = 'sound/voice/hygienebot/stoprunning.ogg',
+ HYGIENEBOT_VOICED_THREAT_AIRLOCK = 'sound/mobs/non-humanoids/hygienebot/dragyouout.ogg',
+ HYGIENEBOT_VOICED_FOUL_SMELL = 'sound/mobs/non-humanoids/hygienebot/foulsmelling.ogg',
+ HYGIENEBOT_VOICED_TROGLODYTE = 'sound/mobs/non-humanoids/hygienebot/troglodyte.ogg',
+ HYGIENEBOT_VOICED_GREEN_CLOUD = 'sound/mobs/non-humanoids/hygienebot/greencloud.ogg',
+ HYGIENEBOT_VOICED_ARSEHOLE = 'sound/mobs/non-humanoids/hygienebot/letmeclean.ogg',
+ HYGIENEBOT_VOICED_THREAT_ARTERIES = 'sound/mobs/non-humanoids/hygienebot/cutarteries.ogg',
+ HYGIENEBOT_VOICED_STOP_RUNNING = 'sound/mobs/non-humanoids/hygienebot/stoprunning.ogg',
)
///announcements we say after we have cleaned our target
var/static/list/cleaned_announcements = list(
- HYGIENEBOT_VOICED_FUCKING_FINALLY = 'sound/voice/hygienebot/finally.ogg',
- HYGIENEBOT_VOICED_THANK_GOD = 'sound/voice/hygienebot/thankgod.ogg',
- HYGIENEBOT_VOICED_DEGENERATE = 'sound/voice/hygienebot/degenerate.ogg',
+ HYGIENEBOT_VOICED_FUCKING_FINALLY = 'sound/mobs/non-humanoids/hygienebot/finally.ogg',
+ HYGIENEBOT_VOICED_THANK_GOD = 'sound/mobs/non-humanoids/hygienebot/thankgod.ogg',
+ HYGIENEBOT_VOICED_DEGENERATE = 'sound/mobs/non-humanoids/hygienebot/degenerate.ogg',
)
/mob/living/basic/bot/hygienebot/Initialize(mapload)
diff --git a/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm
index 2c614e003c8ab..f678843c7ccb9 100644
--- a/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm
+++ b/code/modules/mob/living/basic/bots/hygienebot/hygienebot_ai.dm
@@ -12,6 +12,7 @@
planning_subtrees = list(
/datum/ai_planning_subtree/manage_unreachable_list,
/datum/ai_planning_subtree/respond_to_summon,
+ /datum/ai_planning_subtree/handle_trash_talk,
/datum/ai_planning_subtree/wash_people,
/datum/ai_planning_subtree/salute_authority,
/datum/ai_planning_subtree/find_patrol_beacon,
@@ -23,16 +24,27 @@
BB_BOT_SUMMON_TARGET,
)
-/datum/ai_controller/basic_controller/bot/hygienebot/TryPossessPawn(atom/new_pawn)
- . = ..()
- if(. & AI_CONTROLLER_INCOMPATIBLE)
+/datum/ai_planning_subtree/handle_trash_talk
+
+/datum/ai_planning_subtree/handle_trash_talk/SelectBehaviors(datum/ai_controller/basic_controller/bot/controller, seconds_per_tick)
+ if(!controller.blackboard_key_exists(BB_WASH_TARGET))
return
- RegisterSignal(new_pawn, COMSIG_AI_BLACKBOARD_KEY_CLEARED(BB_WASH_TARGET), PROC_REF(reset_anger))
+ controller.queue_behavior(/datum/ai_behavior/commence_trashtalk, BB_WASH_TARGET)
-/datum/ai_controller/basic_controller/bot/hygienebot/proc/reset_anger()
- SIGNAL_HANDLER
+/datum/ai_behavior/commence_trashtalk
+ action_cooldown = 4 SECONDS
- set_blackboard_key(BB_WASH_FRUSTRATION, 0)
+/datum/ai_behavior/commence_trashtalk/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
+ if(!controller.blackboard_key_exists(target_key))
+ return AI_BEHAVIOR_FAILED | AI_BEHAVIOR_DELAY
+
+ var/frustration_count = controller.blackboard[BB_WASH_FRUSTRATION]
+ controller.set_blackboard_key(BB_WASH_FRUSTRATION, min(frustration_count + 1, BOT_FRUSTRATION_LIMIT))
+ if(controller.blackboard[BB_WASH_FRUSTRATION] < BOT_ANGER_THRESHOLD)
+ return AI_BEHAVIOR_FAILED | AI_BEHAVIOR_DELAY
+ var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY]
+ announcement?.announce(pick(controller.blackboard[BB_WASH_THREATS]))
+ return AI_BEHAVIOR_SUCCEEDED | AI_BEHAVIOR_DELAY
/datum/ai_planning_subtree/wash_people
@@ -85,8 +97,6 @@
controller.set_blackboard_key(target_key, found_target)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
-
-
/datum/ai_behavior/find_valid_wash_targets/finish_action(datum/ai_controller/controller, succeeded, target_key)
. = ..()
if(!succeeded)
@@ -95,9 +105,8 @@
announcement.announce(pick(controller.blackboard[BB_WASH_FOUND]))
/datum/ai_behavior/wash_target
- behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_MOVE_AND_PERFORM
+ behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
required_distance = 0
- action_cooldown = 1 SECONDS
/datum/ai_behavior/wash_target/setup(datum/ai_controller/controller, target_key)
. = ..()
@@ -117,25 +126,17 @@
living_pawn.melee_attack(unclean_target)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
- var/frustration_count = controller.blackboard[BB_WASH_FRUSTRATION]
- controller.set_blackboard_key(BB_WASH_FRUSTRATION, frustration_count + 1)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
/datum/ai_behavior/wash_target/finish_action(datum/ai_controller/controller, succeeded, target_key)
. = ..()
- var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY]
-
- if(succeeded)
- if(controller.blackboard[BB_WASH_FRUSTRATION] > BOT_ANGER_THRESHOLD)
- announcement.announce(pick(controller.blackboard[BB_WASH_DONE]))
- controller.clear_blackboard_key(target_key)
+ controller.clear_blackboard_key(target_key)
+ var/wash_frustration = controller.blackboard[BB_WASH_FRUSTRATION]
+ controller.clear_blackboard_key(BB_WASH_FRUSTRATION)
+ if(!succeeded || wash_frustration <= BOT_ANGER_THRESHOLD)
return
-
- if(controller.blackboard[BB_WASH_FRUSTRATION] < BOT_FRUSTRATION_LIMIT)
- return
-
- announcement.announce(pick(controller.blackboard[BB_WASH_THREATS]))
- controller.set_blackboard_key(BB_WASH_FRUSTRATION, 0)
+ var/datum/action/cooldown/bot_announcement/announcement = controller.blackboard[BB_ANNOUNCE_ABILITY]
+ announcement.announce(pick(controller.blackboard[BB_WASH_DONE]))
#undef BOT_ANGER_THRESHOLD
#undef BOT_FRUSTRATION_LIMIT
diff --git a/code/modules/mob/living/basic/bots/medbot/medbot.dm b/code/modules/mob/living/basic/bots/medbot/medbot.dm
index 7b100aa554582..2466ab931cd19 100644
--- a/code/modules/mob/living/basic/bots/medbot/medbot.dm
+++ b/code/modules/mob/living/basic/bots/medbot/medbot.dm
@@ -29,66 +29,66 @@
///anouncements when we find a target to heal
var/static/list/wait_announcements = list(
- MEDIBOT_VOICED_HOLD_ON = 'sound/voice/medbot/coming.ogg',
- MEDIBOT_VOICED_WANT_TO_HELP = 'sound/voice/medbot/help.ogg',
- MEDIBOT_VOICED_YOU_ARE_INJURED = 'sound/voice/medbot/injured.ogg',
+ MEDIBOT_VOICED_HOLD_ON = 'sound/mobs/non-humanoids/medbot/coming.ogg',
+ MEDIBOT_VOICED_WANT_TO_HELP = 'sound/mobs/non-humanoids/medbot/help.ogg',
+ MEDIBOT_VOICED_YOU_ARE_INJURED = 'sound/mobs/non-humanoids/medbot/injured.ogg',
)
///announcements after we heal someone
var/static/list/afterheal_announcements = list(
- MEDIBOT_VOICED_ALL_PATCHED_UP = 'sound/voice/medbot/patchedup.ogg',
- MEDIBOT_VOICED_APPLE_A_DAY = 'sound/voice/medbot/apple.ogg',
- MEDIBOT_VOICED_FEEL_BETTER = 'sound/voice/medbot/feelbetter.ogg',
+ MEDIBOT_VOICED_ALL_PATCHED_UP = 'sound/mobs/non-humanoids/medbot/patchedup.ogg',
+ MEDIBOT_VOICED_APPLE_A_DAY = 'sound/mobs/non-humanoids/medbot/apple.ogg',
+ MEDIBOT_VOICED_FEEL_BETTER = 'sound/mobs/non-humanoids/medbot/feelbetter.ogg',
)
///announcements when we are healing someone near death
var/static/list/near_death_announcements = list(
- MEDIBOT_VOICED_STAY_WITH_ME = 'sound/voice/medbot/no.ogg',
- MEDIBOT_VOICED_LIVE = 'sound/voice/medbot/live.ogg',
- MEDIBOT_VOICED_NEVER_LOST = 'sound/voice/medbot/lost.ogg',
+ MEDIBOT_VOICED_STAY_WITH_ME = 'sound/mobs/non-humanoids/medbot/no.ogg',
+ MEDIBOT_VOICED_LIVE = 'sound/mobs/non-humanoids/medbot/live.ogg',
+ MEDIBOT_VOICED_NEVER_LOST = 'sound/mobs/non-humanoids/medbot/lost.ogg',
)
///announcements when we are idle
var/static/list/idle_lines = list(
- MEDIBOT_VOICED_DELICIOUS = 'sound/voice/medbot/delicious.ogg',
- MEDIBOT_VOICED_PLASTIC_SURGEON = 'sound/voice/medbot/surgeon.ogg',
- MEDIBOT_VOICED_MASK_ON = 'sound/voice/medbot/radar.ogg',
- MEDIBOT_VOICED_ALWAYS_A_CATCH = 'sound/voice/medbot/catch.ogg',
- MEDIBOT_VOICED_LIKE_FLIES = 'sound/voice/medbot/flies.ogg',
- MEDIBOT_VOICED_SUFFER = 'sound/voice/medbot/why.ogg',
+ MEDIBOT_VOICED_DELICIOUS = 'sound/mobs/non-humanoids/medbot/delicious.ogg',
+ MEDIBOT_VOICED_PLASTIC_SURGEON = 'sound/mobs/non-humanoids/medbot/surgeon.ogg',
+ MEDIBOT_VOICED_MASK_ON = 'sound/mobs/non-humanoids/medbot/radar.ogg',
+ MEDIBOT_VOICED_ALWAYS_A_CATCH = 'sound/mobs/non-humanoids/medbot/catch.ogg',
+ MEDIBOT_VOICED_LIKE_FLIES = 'sound/mobs/non-humanoids/medbot/flies.ogg',
+ MEDIBOT_VOICED_SUFFER = 'sound/mobs/non-humanoids/medbot/why.ogg',
)
///announcements when we are emagged
var/static/list/emagged_announcements = list(
- MEDIBOT_VOICED_FUCK_YOU = 'sound/voice/medbot/fuck_you.ogg',
- MEDIBOT_VOICED_NOT_A_GAME = 'sound/voice/medbot/turn_off.ogg',
- MEDIBOT_VOICED_IM_DIFFERENT = 'sound/voice/medbot/im_different.ogg',
- MEDIBOT_VOICED_FOURTH_WALL = 'sound/voice/medbot/close.ogg',
- MEDIBOT_VOICED_SHINDEMASHOU = 'sound/voice/medbot/shindemashou.ogg',
+ MEDIBOT_VOICED_FUCK_YOU = 'sound/mobs/non-humanoids/medbot/fuck_you.ogg',
+ MEDIBOT_VOICED_NOT_A_GAME = 'sound/mobs/non-humanoids/medbot/turn_off.ogg',
+ MEDIBOT_VOICED_IM_DIFFERENT = 'sound/mobs/non-humanoids/medbot/im_different.ogg',
+ MEDIBOT_VOICED_FOURTH_WALL = 'sound/mobs/non-humanoids/medbot/close.ogg',
+ MEDIBOT_VOICED_SHINDEMASHOU = 'sound/mobs/non-humanoids/medbot/shindemashou.ogg',
)
///announcements when we are being tipped
var/static/list/tipped_announcements = list(
- MEDIBOT_VOICED_WAIT = 'sound/voice/medbot/hey_wait.ogg',
- MEDIBOT_VOICED_DONT = 'sound/voice/medbot/please_dont.ogg',
- MEDIBOT_VOICED_TRUSTED_YOU = 'sound/voice/medbot/i_trusted_you.ogg',
- MEDIBOT_VOICED_NO_SAD = 'sound/voice/medbot/nooo.ogg',
- MEDIBOT_VOICED_OH_FUCK = 'sound/voice/medbot/oh_fuck.ogg',
+ MEDIBOT_VOICED_WAIT = 'sound/mobs/non-humanoids/medbot/hey_wait.ogg',
+ MEDIBOT_VOICED_DONT = 'sound/mobs/non-humanoids/medbot/please_dont.ogg',
+ MEDIBOT_VOICED_TRUSTED_YOU = 'sound/mobs/non-humanoids/medbot/i_trusted_you.ogg',
+ MEDIBOT_VOICED_NO_SAD = 'sound/mobs/non-humanoids/medbot/nooo.ogg',
+ MEDIBOT_VOICED_OH_FUCK = 'sound/mobs/non-humanoids/medbot/oh_fuck.ogg',
)
///announcements when we are being untipped
var/static/list/untipped_announcements = list(
- MEDIBOT_VOICED_FORGIVE = 'sound/voice/medbot/forgive.ogg',
- MEDIBOT_VOICED_THANKS = 'sound/voice/medbot/thank_you.ogg',
- MEDIBOT_VOICED_GOOD_PERSON = 'sound/voice/medbot/youre_good.ogg',
+ MEDIBOT_VOICED_FORGIVE = 'sound/mobs/non-humanoids/medbot/forgive.ogg',
+ MEDIBOT_VOICED_THANKS = 'sound/mobs/non-humanoids/medbot/thank_you.ogg',
+ MEDIBOT_VOICED_GOOD_PERSON = 'sound/mobs/non-humanoids/medbot/youre_good.ogg',
)
///announcements when we are worried
var/static/list/worried_announcements = list(
- MEDIBOT_VOICED_PUT_BACK = 'sound/voice/medbot/please_put_me_back.ogg',
- MEDIBOT_VOICED_IM_SCARED = 'sound/voice/medbot/please_im_scared.ogg',
- MEDIBOT_VOICED_NEED_HELP = 'sound/voice/medbot/dont_like.ogg',
- MEDIBOT_VOICED_THIS_HURTS = 'sound/voice/medbot/pain_is_real.ogg',
- MEDIBOT_VOICED_THE_END = 'sound/voice/medbot/is_this_the_end.ogg',
- MEDIBOT_VOICED_NOOO = 'sound/voice/medbot/nooo.ogg',
+ MEDIBOT_VOICED_PUT_BACK = 'sound/mobs/non-humanoids/medbot/please_put_me_back.ogg',
+ MEDIBOT_VOICED_IM_SCARED = 'sound/mobs/non-humanoids/medbot/please_im_scared.ogg',
+ MEDIBOT_VOICED_NEED_HELP = 'sound/mobs/non-humanoids/medbot/dont_like.ogg',
+ MEDIBOT_VOICED_THIS_HURTS = 'sound/mobs/non-humanoids/medbot/pain_is_real.ogg',
+ MEDIBOT_VOICED_THE_END = 'sound/mobs/non-humanoids/medbot/is_this_the_end.ogg',
+ MEDIBOT_VOICED_NOOO = 'sound/mobs/non-humanoids/medbot/nooo.ogg',
)
var/static/list/misc_announcements= list(
- MEDIBOT_VOICED_CHICKEN = 'sound/voice/medbot/i_am_chicken.ogg',
+ MEDIBOT_VOICED_CHICKEN = 'sound/mobs/non-humanoids/medbot/i_am_chicken.ogg',
)
/// drop determining variable
var/health_analyzer = /obj/item/healthanalyzer
diff --git a/code/modules/mob/living/basic/clown/clown.dm b/code/modules/mob/living/basic/clown/clown.dm
index 9e8f6950525a7..3fd2328458293 100644
--- a/code/modules/mob/living/basic/clown/clown.dm
+++ b/code/modules/mob/living/basic/clown/clown.dm
@@ -247,7 +247,7 @@
armour_penetration = 20
attack_verb_continuous = "steals the girlfriend of"
attack_verb_simple = "steal the girlfriend of"
- attack_sound = 'sound/items/airhorn2.ogg'
+ attack_sound = 'sound/items/airhorn/airhorn2.ogg'
loot = list(
/obj/effect/gibspawner/human,
/obj/effect/spawner/foam_starter/small,
@@ -584,7 +584,7 @@
var/peels_to_spawn = min(peel_amount, reachable_turfs.len)
for(var/i in 1 to peels_to_spawn)
new banana_type(pick_n_take(reachable_turfs))
- playsound(owner, 'sound/creatures/clown/clownana_rustle.ogg', 60)
+ playsound(owner, 'sound/mobs/non-humanoids/clown/clownana_rustle.ogg', 60)
animate(owner, time = 1, pixel_x = 6, easing = CUBIC_EASING | EASE_OUT)
animate(time = 2, pixel_x = -8, easing = CUBIC_EASING)
animate(time = 1, pixel_x = 0, easing = CUBIC_EASING | EASE_IN)
@@ -615,7 +615,7 @@
if(!do_after(owner, 1 SECONDS))
activating = FALSE
return
- playsound(owner, 'sound/creatures/clown/hehe.ogg', 100)
+ playsound(owner, 'sound/mobs/non-humanoids/clown/hehe.ogg', 100)
if(!do_after(owner, 1 SECONDS))
activating = FALSE
return
@@ -626,5 +626,5 @@
. = ..()
new /obj/item/food/grown/banana/bunch(get_step(owner.loc, owner.dir))
playsound(owner, 'sound/items/bikehorn.ogg', 60)
- addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), owner, 'sound/creatures/clown/hohoho.ogg', 100, 1), 1 SECONDS)
+ addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(playsound), owner, 'sound/mobs/non-humanoids/clown/hohoho.ogg', 100, 1), 1 SECONDS)
StartCooldown()
diff --git a/code/modules/mob/living/basic/cult/constructs/_construct.dm b/code/modules/mob/living/basic/cult/constructs/_construct.dm
index 1c5e91b50c263..01004685607a0 100644
--- a/code/modules/mob/living/basic/cult/constructs/_construct.dm
+++ b/code/modules/mob/living/basic/cult/constructs/_construct.dm
@@ -106,7 +106,7 @@
return FALSE
to_chat(src, span_bold(playstyle_string))
-/mob/living/basic/construct/examine(mob/user)
+/mob/living/basic/construct/get_examine_name(mob/user)
var/text_span
switch(theme)
if(THEME_CULT)
@@ -115,13 +115,20 @@
text_span = "purple"
if(THEME_HOLY)
text_span = "blue"
- . = list("This is [icon2html(src, user)] \a [src]!\n[desc]")
+
+ if(!text_span)
+ return ..()
+
+ return "[..()]"
+
+/mob/living/basic/construct/examine(mob/user)
+ . = list()
if(health < maxHealth)
if(health >= maxHealth/2)
. += span_warning("[p_They()] look[p_s()] slightly dented.")
else
. += span_warning(span_bold("[p_They()] look[p_s()] severely dented!"))
- . += ""
+
return .
/mob/living/basic/construct/narsie_act()
diff --git a/code/modules/mob/living/basic/cult/constructs/artificer.dm b/code/modules/mob/living/basic/cult/constructs/artificer.dm
index 8856c0e66a2ad..1d66f193ebc29 100644
--- a/code/modules/mob/living/basic/cult/constructs/artificer.dm
+++ b/code/modules/mob/living/basic/cult/constructs/artificer.dm
@@ -13,7 +13,7 @@
melee_damage_upper = 5
attack_verb_continuous = "rams"
attack_verb_simple = "ram"
- attack_sound = 'sound/weapons/punch2.ogg'
+ attack_sound = 'sound/items/weapons/punch2.ogg'
construct_spells = list(
/datum/action/cooldown/spell/aoe/magic_missile/lesser,
/datum/action/cooldown/spell/conjure/construct/lesser,
diff --git a/code/modules/mob/living/basic/cult/constructs/harvester.dm b/code/modules/mob/living/basic/cult/constructs/harvester.dm
index b48a26b681937..95a5956825421 100644
--- a/code/modules/mob/living/basic/cult/constructs/harvester.dm
+++ b/code/modules/mob/living/basic/cult/constructs/harvester.dm
@@ -11,7 +11,7 @@
melee_damage_upper = 20
attack_verb_continuous = "butchers"
attack_verb_simple = "butcher"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
construct_spells = list(
/datum/action/cooldown/spell/aoe/area_conversion,
diff --git a/code/modules/mob/living/basic/cult/constructs/juggernaut.dm b/code/modules/mob/living/basic/cult/constructs/juggernaut.dm
index ef972d2b91693..5d5ae8c645499 100644
--- a/code/modules/mob/living/basic/cult/constructs/juggernaut.dm
+++ b/code/modules/mob/living/basic/cult/constructs/juggernaut.dm
@@ -14,7 +14,7 @@
attack_verb_continuous = "smashes their armored gauntlet into"
attack_verb_simple = "smash your armored gauntlet into"
speed = 2.5
- attack_sound = 'sound/weapons/punch3.ogg'
+ attack_sound = 'sound/items/weapons/punch3.ogg'
status_flags = NONE
mob_size = MOB_SIZE_LARGE
construct_spells = list(
diff --git a/code/modules/mob/living/basic/cult/constructs/proteon.dm b/code/modules/mob/living/basic/cult/constructs/proteon.dm
index 2ff58d2463c0b..c39af7831fe8b 100644
--- a/code/modules/mob/living/basic/cult/constructs/proteon.dm
+++ b/code/modules/mob/living/basic/cult/constructs/proteon.dm
@@ -12,7 +12,7 @@
attack_verb_continuous = "pinches"
attack_verb_simple = "pinch"
smashes_walls = TRUE
- attack_sound = 'sound/weapons/punch2.ogg'
+ attack_sound = 'sound/items/weapons/punch2.ogg'
playstyle_string = span_bold("You are a Proteon. Your abilities in combat are outmatched by most combat constructs, but you are still fast and nimble. Run metal and supplies, and cooperate with your fellow cultists.")
/// Hostile NPC version
diff --git a/code/modules/mob/living/basic/cult/constructs/wraith.dm b/code/modules/mob/living/basic/cult/constructs/wraith.dm
index 06a09b6446ed3..4a41bc1cc2aa7 100644
--- a/code/modules/mob/living/basic/cult/constructs/wraith.dm
+++ b/code/modules/mob/living/basic/cult/constructs/wraith.dm
@@ -10,7 +10,7 @@
melee_damage_upper = 20
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
construct_spells = list(
/datum/action/cooldown/spell/jaunt/ethereal_jaunt/shift,
diff --git a/code/modules/mob/living/basic/drone/_drone.dm b/code/modules/mob/living/basic/drone/_drone.dm
index 983ade8de0bcc..fe5dbb4d2d5a3 100644
--- a/code/modules/mob/living/basic/drone/_drone.dm
+++ b/code/modules/mob/living/basic/drone/_drone.dm
@@ -131,9 +131,9 @@
/obj/item/weldingtool/drone,
/obj/item/wirecutters/drone,
/obj/item/multitool/drone,
- /obj/item/pipe_dispenser,
- /obj/item/t_scanner,
- /obj/item/analyzer,
+ /obj/item/pipe_dispenser/drone,
+ /obj/item/t_scanner/drone,
+ /obj/item/analyzer/drone,
/obj/item/rack_parts,
)
/// whitelisted drone items, recursive/includes descendants
@@ -221,13 +221,13 @@
/mob/living/basic/drone/med_hud_set_health()
var/image/holder = hud_list[DIAG_HUD]
var/icon/hud_icon = icon(icon, icon_state, dir)
- holder.pixel_y = hud_icon.Height() - world.icon_size
+ holder.pixel_y = hud_icon.Height() - ICON_SIZE_Y
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
/mob/living/basic/drone/med_hud_set_status()
var/image/holder = hud_list[DIAG_STAT_HUD]
var/icon/hud_icon = icon(icon, icon_state, dir)
- holder.pixel_y = hud_icon.Height() - world.icon_size
+ holder.pixel_y = hud_icon.Height() - ICON_SIZE_Y
if(stat == DEAD)
holder.icon_state = "huddead2"
else if(incapacitated)
@@ -276,21 +276,21 @@
return icon('icons/mob/butts.dmi', BUTT_SPRITE_DRONE)
/mob/living/basic/drone/examine(mob/user)
- . = list("This is [icon2html(src, user)] \a [src]!")
+ . = list()
//Hands
for(var/obj/item/held_thing in held_items)
if(held_thing.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM))
continue
- . += "It has [held_thing.get_examine_string(user)] in its [get_held_index_name(get_held_index_of_item(held_thing))]."
+ . += "It has [held_thing.examine_title(user)] in its [get_held_index_name(get_held_index_of_item(held_thing))]."
//Internal storage
if(internal_storage && !(internal_storage.item_flags & ABSTRACT))
- . += "It is holding [internal_storage.get_examine_string(user)] in its internal storage."
+ . += "It is holding [internal_storage.examine_title(user)] in its internal storage."
//Cosmetic hat - provides no function other than looks
if(head && !(head.item_flags & ABSTRACT))
- . += "It is wearing [head.get_examine_string(user)] on its head."
+ . += "It is wearing [head.examine_title(user)] on its head."
//Braindead
if(!client && stat != DEAD)
@@ -313,8 +313,6 @@
. += span_deadsay("A message repeatedly flashes on its display: \"REBOOT -- REQUIRED\".")
else
. += span_deadsay("A message repeatedly flashes on its display: \"ERROR -- OFFLINE\".")
- . += ""
-
/mob/living/basic/drone/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null) //Secbots won't hunt maintenance drones.
return -10
diff --git a/code/modules/mob/living/basic/drone/drone_tools.dm b/code/modules/mob/living/basic/drone/drone_tools.dm
index 7effefcd7f906..2150ad0f7e29d 100644
--- a/code/modules/mob/living/basic/drone/drone_tools.dm
+++ b/code/modules/mob/living/basic/drone/drone_tools.dm
@@ -17,9 +17,9 @@
/obj/item/weldingtool/drone,
/obj/item/wirecutters/drone,
/obj/item/multitool/drone,
- /obj/item/pipe_dispenser,
- /obj/item/t_scanner,
- /obj/item/analyzer,
+ /obj/item/pipe_dispenser/drone,
+ /obj/item/t_scanner/drone,
+ /obj/item/analyzer/drone,
/obj/item/soap/drone,
)
atom_storage.max_total_storage = 40
@@ -37,9 +37,9 @@
builtintools += new /obj/item/weldingtool/drone(src)
builtintools += new /obj/item/wirecutters/drone(src)
builtintools += new /obj/item/multitool/drone(src)
- builtintools += new /obj/item/pipe_dispenser(src)
- builtintools += new /obj/item/t_scanner(src)
- builtintools += new /obj/item/analyzer(src)
+ builtintools += new /obj/item/pipe_dispenser/drone(src)
+ builtintools += new /obj/item/t_scanner/drone(src)
+ builtintools += new /obj/item/analyzer/drone(src)
builtintools += new /obj/item/soap/drone(src)
for(var/obj/item/tool as anything in builtintools)
tool.AddComponent(/datum/component/holderloving, src, TRUE)
@@ -103,3 +103,18 @@
icon_state = "toolkit_engiborg_multitool"
item_flags = NO_MAT_REDEMPTION
toolspeed = 0.5
+
+/obj/item/analyzer/drone
+ name = "digital gas analyzer"
+ desc = "A gas analyzer built into your chassis."
+ item_flags = NO_MAT_REDEMPTION
+
+/obj/item/t_scanner/drone
+ name = "digital T-ray scanner"
+ desc = "A T-ray scanner built into your chassis."
+ item_flags = NO_MAT_REDEMPTION
+
+/obj/item/pipe_dispenser/drone
+ name = "built-in rapid pipe dispenser"
+ desc = "A rapid pipe dispenser built into your chassis."
+ item_flags = NO_MAT_REDEMPTION
diff --git a/code/modules/mob/living/basic/drone/extra_drone_types.dm b/code/modules/mob/living/basic/drone/extra_drone_types.dm
index 08c9278b75331..402aca848de8c 100644
--- a/code/modules/mob/living/basic/drone/extra_drone_types.dm
+++ b/code/modules/mob/living/basic/drone/extra_drone_types.dm
@@ -126,7 +126,7 @@
" - Going to the main station in search of materials.\n"+\
" - Interacting with non-drone players outside KS13, dead or alive.\n"+\
"These rules are at admin discretion and will be heavily enforced.\n"+\
- "If you do not have the regular drone laws, follow your laws to the best of your ability."
+ span_warning("If you do not have the regular drone laws, follow your laws to the best of your ability.")
shy = FALSE
/mob/living/basic/drone/derelict/Initialize(mapload)
diff --git a/code/modules/mob/living/basic/drone/interaction.dm b/code/modules/mob/living/basic/drone/interaction.dm
index 0b0247c1c45f3..58b7cd88ef287 100644
--- a/code/modules/mob/living/basic/drone/interaction.dm
+++ b/code/modules/mob/living/basic/drone/interaction.dm
@@ -32,7 +32,7 @@
return ..()
/mob/living/basic/drone/mob_try_pickup(mob/living/user, instant=FALSE)
- if(stat == DEAD || status_flags & GODMODE)
+ if(stat == DEAD || HAS_TRAIT(src, TRAIT_GODMODE))
return
return ..()
diff --git a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm
index b4d73ad59273a..77fa9ce8ca088 100644
--- a/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm
+++ b/code/modules/mob/living/basic/farm_animals/bee/bee_ai_behavior.dm
@@ -26,14 +26,12 @@
/datum/ai_behavior/enter_exit_hive/perform(seconds_per_tick, datum/ai_controller/controller, target_key, attack_key)
var/obj/structure/beebox/current_home = controller.blackboard[target_key]
- var/mob/living/bee_pawn = controller.pawn
var/atom/attack_target = controller.blackboard[attack_key]
if(attack_target) // forget about who we attacking when we go home
controller.clear_blackboard_key(attack_key)
- var/datum/callback/callback = CALLBACK(bee_pawn, TYPE_PROC_REF(/mob/living/basic/bee, handle_habitation), current_home)
- callback.Invoke()
+ controller.ai_interact(target = current_home, combat_mode = FALSE)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
/datum/ai_behavior/inhabit_hive
@@ -53,8 +51,7 @@
if(!potential_home.habitable(bee_pawn)) //the house become full before we get to it
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
- var/datum/callback/callback = CALLBACK(bee_pawn, TYPE_PROC_REF(/mob/living/basic/bee, handle_habitation), potential_home)
- callback.Invoke()
+ controller.ai_interact(target = potential_home, combat_mode = FALSE)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
/datum/ai_behavior/inhabit_hive/finish_action(datum/ai_controller/controller, succeeded, target_key)
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 66191c92516f2..fadac576ea599 100644
--- a/code/modules/mob/living/basic/farm_animals/cow/_cow.dm
+++ b/code/modules/mob/living/basic/farm_animals/cow/_cow.dm
@@ -20,7 +20,7 @@
response_harm_simple = "kick"
attack_verb_continuous = "kicks"
attack_verb_simple = "kick"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
attack_vis_effect = ATTACK_EFFECT_KICK
health = 50
maxHealth = 50
diff --git a/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm b/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm
index dc3e09e38f211..47f11a02839e4 100644
--- a/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm
+++ b/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm
@@ -15,7 +15,7 @@
attack_verb_continuous = "telekinetically rams its moonihorn into"
attack_verb_simple = "telekinetically ram your moonihorn into"
gold_core_spawnable = NO_SPAWN
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
ai_controller = /datum/ai_controller/basic_controller/cow/moonicorn
food_types = list(/obj/item/food/grown/galaxythistle)
diff --git a/code/modules/mob/living/basic/farm_animals/deer.dm b/code/modules/mob/living/basic/farm_animals/deer/deer.dm
similarity index 72%
rename from code/modules/mob/living/basic/farm_animals/deer.dm
rename to code/modules/mob/living/basic/farm_animals/deer/deer.dm
index c51be81b77d04..dc27c82dd82f5 100644
--- a/code/modules/mob/living/basic/farm_animals/deer.dm
+++ b/code/modules/mob/living/basic/farm_animals/deer/deer.dm
@@ -16,7 +16,7 @@
response_harm_simple = "kick"
attack_verb_continuous = "bucks"
attack_verb_simple = "buck"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
health = 75
maxHealth = 75
blood_volume = BLOOD_VOLUME_NORMAL
@@ -24,28 +24,16 @@
/// Things that will scare us into being stationary. Vehicles are scary to deers because they might have headlights.
var/static/list/stationary_scary_things = list(/obj/vehicle)
+
/mob/living/basic/deer/Initialize(mapload)
. = ..()
+ AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_SHOE)
var/time_to_freeze_for = (rand(5, 10) SECONDS)
ai_controller.set_blackboard_key(BB_STATIONARY_SECONDS, time_to_freeze_for)
ai_controller.set_blackboard_key(BB_STATIONARY_COOLDOWN, (time_to_freeze_for * (rand(3, 5))))
ai_controller.set_blackboard_key(BB_STATIONARY_TARGETS, typecacheof(stationary_scary_things))
-/datum/ai_controller/basic_controller/deer
- blackboard = list(
- BB_STATIONARY_MOVE_TO_TARGET = TRUE,
- BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
- )
- ai_traits = STOP_MOVING_WHEN_PULLED
- ai_movement = /datum/ai_movement/basic_avoidance
- idle_behavior = /datum/idle_behavior/idle_random_walk
- planning_subtrees = list(
- /datum/ai_planning_subtree/random_speech/deer,
- /datum/ai_planning_subtree/stare_at_thing,
- /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee,
- /datum/ai_planning_subtree/flee_target,
- )
/// Cold resistent and doesn't need to breathe
/mob/living/basic/deer/ice
diff --git a/code/modules/mob/living/basic/farm_animals/deer/deer_ai.dm b/code/modules/mob/living/basic/farm_animals/deer/deer_ai.dm
new file mode 100644
index 0000000000000..f17e0c9014fba
--- /dev/null
+++ b/code/modules/mob/living/basic/farm_animals/deer/deer_ai.dm
@@ -0,0 +1,156 @@
+/datum/ai_controller/basic_controller/deer
+ blackboard = list(
+ BB_STATIONARY_MOVE_TO_TARGET = TRUE,
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ )
+ ai_traits = STOP_MOVING_WHEN_PULLED
+ ai_movement = /datum/ai_movement/basic_avoidance
+ idle_behavior = /datum/idle_behavior/idle_random_walk
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/random_speech/deer,
+ /datum/ai_planning_subtree/stare_at_thing,
+ /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee,
+ /datum/ai_planning_subtree/flee_target,
+ /datum/ai_planning_subtree/rest_at_home,
+ /datum/ai_planning_subtree/play_with_friends,
+ /datum/ai_planning_subtree/find_and_hunt_target/mark_territory,
+ /datum/ai_planning_subtree/find_and_hunt_target/graze,
+ /datum/ai_planning_subtree/find_and_hunt_target/drink_water,
+ )
+
+
+///subtree to go around drinking water
+/datum/ai_planning_subtree/find_and_hunt_target/drink_water
+ target_key = BB_DEER_WATER_TARGET
+ finding_behavior = /datum/ai_behavior/find_and_set/in_list/turf_types
+ hunting_behavior = /datum/ai_behavior/hunt_target/drink_water
+ hunt_targets = list(/turf/open/water)
+ hunt_range = 7
+ hunt_chance = 5
+
+
+/datum/ai_behavior/hunt_target/drink_water
+ always_reset_target = TRUE
+ hunt_cooldown = 20 SECONDS
+
+
+/datum/ai_behavior/hunt_target/drink_water/target_caught(mob/living/hunter, atom/hunted)
+ var/static/list/possible_emotes = list("drinks the water!", "dances in the water!", "splashes around happily!")
+ hunter.manual_emote(pick(possible_emotes))
+
+
+///subtree to go around grazing
+/datum/ai_planning_subtree/find_and_hunt_target/graze
+ target_key = BB_DEER_GRASS_TARGET
+ finding_behavior = /datum/ai_behavior/find_and_set/in_list/turf_types
+ hunting_behavior = /datum/ai_behavior/hunt_target/eat_grass
+ hunt_targets = list(/turf/open/floor/grass)
+ hunt_range = 7
+ hunt_chance = 45
+
+
+/datum/ai_behavior/hunt_target/eat_grass
+ always_reset_target = TRUE
+ hunt_cooldown = 15 SECONDS
+
+
+/datum/ai_behavior/hunt_target/eat_grass/target_caught(mob/living/hunter, atom/hunted)
+ var/static/list/possible_emotes = list("eats the grass!", "munches down the grass!", "chews on the grass!")
+ hunter.manual_emote(pick(possible_emotes))
+
+
+///subtree to go around playing with other deers
+/datum/ai_planning_subtree/play_with_friends
+
+
+/datum/ai_planning_subtree/play_with_friends/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ var/static/list/emote_list = list("plays with", "dances with", "celebrates with")
+ var/static/list/friend_types = typecacheof(list(/mob/living/basic/deer))
+ if(controller.blackboard_key_exists(BB_DEER_PLAYFRIEND))
+ controller.queue_behavior(/datum/ai_behavior/emote_on_target, BB_DEER_PLAYFRIEND, emote_list)
+ if(SPT_PROB(3, seconds_per_tick))
+ controller.queue_behavior(/datum/ai_behavior/find_hunt_target/valid_deer, BB_DEER_PLAYFRIEND, friend_types)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+
+/datum/ai_behavior/emote_on_target/deer_play
+
+
+/datum/ai_behavior/emote_on_target/deer_play/run_emote(mob/living/living_pawn, atom/target, list/emote_list)
+ . = ..()
+ living_pawn.spin(spintime = 4, speed = 1)
+
+
+/datum/ai_behavior/find_hunt_target/valid_deer/valid_dinner(mob/living/source, mob/living/deer, radius, datum/ai_controller/controller, seconds_per_tick)
+ if(deer.stat == DEAD)
+ return FALSE
+ if(!can_see(source, deer, radius))
+ return FALSE
+ deer.ai_controller?.set_blackboard_key(BB_DEER_PLAYFRIEND, source)
+ return can_see(source, deer, radius)
+
+
+///subtree to mark trees as territories
+/datum/ai_planning_subtree/find_and_hunt_target/mark_territory
+ target_key = BB_DEER_TREE_TARGET
+ finding_behavior = /datum/ai_behavior/find_hunt_target
+ hunting_behavior = /datum/ai_behavior/hunt_target/mark_territory
+ hunt_targets = list(/obj/structure/flora/tree)
+ hunt_range = 7
+ hunt_chance = 75
+
+
+/datum/ai_behavior/hunt_target/mark_territory
+ always_reset_target = TRUE
+ hunt_cooldown = 15 SECONDS
+
+
+/datum/ai_behavior/hunt_target/mark_territory/target_caught(mob/living/hunter, atom/hunted)
+ hunter.manual_emote("marks [hunted] with its hooves!")
+ hunter.ai_controller.set_blackboard_key(BB_DEER_TREEHOME, hunted)
+
+
+/datum/ai_planning_subtree/find_and_hunt_target/mark_territory/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ if(controller.blackboard_key_exists(BB_DEER_TREEHOME)) //already found our home, abort!
+ return
+ return ..()
+
+
+/datum/ai_planning_subtree/rest_at_home/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ if(controller.blackboard[BB_DEER_RESTING] > world.time) //we're resting for now, nothing more to do
+ return SUBTREE_RETURN_FINISH_PLANNING
+ if(!controller.blackboard_key_exists(BB_DEER_TREEHOME) || controller.blackboard[BB_DEER_NEXT_REST_TIMER] > world.time)
+ return
+ controller.queue_behavior(/datum/ai_behavior/return_home, BB_DEER_TREEHOME)
+
+
+/datum/ai_behavior/return_home
+ required_distance = 0
+ behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
+ ///minimum time till next rest
+ var/minimum_time = 2 MINUTES
+ ///maximum time till next rest
+ var/maximum_time = 4 MINUTES
+
+
+/datum/ai_behavior/return_home/setup(datum/ai_controller/controller, target_key)
+ . = ..()
+ var/atom/target = controller.blackboard[target_key]
+ if(QDELETED(target))
+ return FALSE
+ var/list/possible_turfs = get_adjacent_open_turfs(target)
+ shuffle_inplace(possible_turfs)
+ for(var/turf/possible_turf as anything in possible_turfs)
+ if(!possible_turf.is_blocked_turf())
+ set_movement_target(controller, possible_turf)
+ return TRUE
+ return FALSE
+
+
+/datum/ai_behavior/return_home/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
+ var/mob/living/living_pawn = controller.pawn
+ var/static/list/possible_emotes = list("rests its legs...", "yawns and naps...", "curls up and rests...")
+ living_pawn.manual_emote(pick(possible_emotes))
+ controller.set_blackboard_key(BB_DEER_RESTING, world.time + 15 SECONDS)
+ controller.set_blackboard_key(BB_DEER_NEXT_REST_TIMER, world.time + rand(minimum_time, maximum_time))
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
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 f2354cc5f149a..7b177c5c17d24 100644
--- a/code/modules/mob/living/basic/farm_animals/goat/_goat.dm
+++ b/code/modules/mob/living/basic/farm_animals/goat/_goat.dm
@@ -15,7 +15,7 @@
response_harm_simple = "kick"
attack_verb_continuous = "kicks"
attack_verb_simple = "kick"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
attack_vis_effect = ATTACK_EFFECT_KICK
butcher_results = list(/obj/item/food/meat/slab/grassfed = 4)
diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
index d1b1aebf9eb00..b536cb3aebfe6 100644
--- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
+++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla.dm
@@ -31,7 +31,7 @@
obj_damage = 40
attack_verb_continuous = "pummels"
attack_verb_simple = "pummel"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
unique_name = TRUE
ai_controller = /datum/ai_controller/basic_controller/gorilla
faction = list(FACTION_MONKEY, FACTION_JUNGLE)
@@ -146,10 +146,7 @@
obj_damage = 15
ai_controller = /datum/ai_controller/basic_controller/gorilla/lesser
butcher_results = list(/obj/item/food/meat/slab/gorilla = 2)
-
-/mob/living/basic/gorilla/lesser/Initialize(mapload)
- . = ..()
- transform *= 0.75
+ current_size = 0.75
/// Cargo's wonderful mascot, the tranquil box-carrying ape
/mob/living/basic/gorilla/cargorilla
@@ -167,4 +164,21 @@
ADD_TRAIT(src, TRAIT_PACIFISM, INNATE_TRAIT)
AddComponent(/datum/component/crate_carrier)
+/// A version of the gorilla achieved by reaching enough genetic damage as a monkey
+/mob/living/basic/gorilla/genetics
+ name = "Lab Gorilla"
+ maxHealth = 180
+ health = 180
+ desc = "A gorilla created via \"advanced genetic science\". While not quite as strong as their wildborne brethren, this simian still packs a punch."
+ melee_damage_lower = 15
+ melee_damage_upper = 18
+ obj_damage = 25
+ speed = 0.1
+ paralyze_chance = 0
+ current_size = 0.9
+
+/mob/living/basic/gorilla/genetics/Initialize(mapload)
+ . = ..()
+ qdel(GetComponent(/datum/component/amputating_limbs))
+
#undef GORILLA_HANDS_LAYER
diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_emotes.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_emotes.dm
index 94133336c4d49..063e6bfb6599a 100644
--- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_emotes.dm
+++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_emotes.dm
@@ -8,4 +8,4 @@
message = "oogas."
message_param = "oogas at %t."
emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
- sound = 'sound/creatures/gorilla.ogg'
+ sound = 'sound/mobs/non-humanoids/gorilla/gorilla.ogg'
diff --git a/code/modules/mob/living/basic/farm_animals/pig.dm b/code/modules/mob/living/basic/farm_animals/pig.dm
index 6f732f1684479..d0fbb5a82473a 100644
--- a/code/modules/mob/living/basic/farm_animals/pig.dm
+++ b/code/modules/mob/living/basic/farm_animals/pig.dm
@@ -18,7 +18,7 @@
response_harm_simple = "kick"
attack_verb_continuous = "kicks"
attack_verb_simple = "kick"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
attack_vis_effect = ATTACK_EFFECT_KICK
melee_damage_lower = 1
melee_damage_upper = 2
diff --git a/code/modules/mob/living/basic/farm_animals/pony.dm b/code/modules/mob/living/basic/farm_animals/pony.dm
index 9f008d85fcc7a..29672e032c84b 100644
--- a/code/modules/mob/living/basic/farm_animals/pony.dm
+++ b/code/modules/mob/living/basic/farm_animals/pony.dm
@@ -15,7 +15,7 @@
response_harm_simple = "kick"
attack_verb_continuous = "kicks"
attack_verb_simple = "kick"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
attack_vis_effect = ATTACK_EFFECT_KICK
melee_damage_lower = 5
melee_damage_upper = 10
@@ -46,7 +46,7 @@
/mob/living/basic/pony/tamed(mob/living/tamer, atom/food)
can_buckle = TRUE
buckle_lying = 0
- playsound(src, 'sound/creatures/pony/snort.ogg', 50)
+ 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."))
new /obj/effect/temp_visual/heart(loc)
@@ -85,9 +85,9 @@
manual_emote("whinnies ANGRILY!")
playsound(src, pick(list(
- 'sound/creatures/pony/whinny01.ogg',
- 'sound/creatures/pony/whinny02.ogg',
- 'sound/creatures/pony/whinny03.ogg'
+ 'sound/mobs/non-humanoids/pony/whinny01.ogg',
+ 'sound/mobs/non-humanoids/pony/whinny02.ogg',
+ 'sound/mobs/non-humanoids/pony/whinny03.ogg'
)), 50)
/mob/living/basic/pony/take_damage(damage_amount, damage_type, damage_flag, sound_effect, attack_dir, armour_penetration)
diff --git a/code/modules/mob/living/basic/farm_animals/rabbit.dm b/code/modules/mob/living/basic/farm_animals/rabbit.dm
index f77772ab17c7b..dec48ea8be4af 100644
--- a/code/modules/mob/living/basic/farm_animals/rabbit.dm
+++ b/code/modules/mob/living/basic/farm_animals/rabbit.dm
@@ -26,7 +26,7 @@
response_help_simple = "pet"
response_disarm_continuous = "gently pushes aside"
response_disarm_simple = "gently push aside"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
attack_vis_effect = ATTACK_EFFECT_KICK
response_harm_continuous = "kicks"
response_harm_simple = "kick"
diff --git a/code/modules/mob/living/basic/farm_animals/sheep.dm b/code/modules/mob/living/basic/farm_animals/sheep.dm
index 5617da83a53a5..6fd4c485db79e 100644
--- a/code/modules/mob/living/basic/farm_animals/sheep.dm
+++ b/code/modules/mob/living/basic/farm_animals/sheep.dm
@@ -18,7 +18,7 @@
response_harm_simple = "kick"
attack_verb_continuous = "kicks"
attack_verb_simple = "kick"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
attack_vis_effect = ATTACK_EFFECT_KICK
health = 50
maxHealth = 50
@@ -40,7 +40,7 @@
item_generation_wait = 3 MINUTES, \
item_reduction_time = 30 SECONDS, \
item_harvest_time = 5 SECONDS, \
- item_harvest_sound = 'sound/surgery/scalpel1.ogg', \
+ item_harvest_sound = 'sound/items/handling/surgery/scalpel1.ogg', \
)
AddElement(/datum/element/ai_retaliate)
RegisterSignal(src, COMSIG_LIVING_CULT_SACRIFICED, PROC_REF(on_sacrificed))
diff --git a/code/modules/mob/living/basic/festivus_pole.dm b/code/modules/mob/living/basic/festivus_pole.dm
index 1c1a88dd31fe5..7ff4ab6c53262 100644
--- a/code/modules/mob/living/basic/festivus_pole.dm
+++ b/code/modules/mob/living/basic/festivus_pole.dm
@@ -30,7 +30,7 @@
melee_damage_upper = 12
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
faction = list(FACTION_HOSTILE)
diff --git a/code/modules/mob/living/basic/guardian/guardian.dm b/code/modules/mob/living/basic/guardian/guardian.dm
index b338a5a4435c3..9ddf2c1fc319f 100644
--- a/code/modules/mob/living/basic/guardian/guardian.dm
+++ b/code/modules/mob/living/basic/guardian/guardian.dm
@@ -31,7 +31,7 @@
response_disarm_simple = "flail at"
response_harm_continuous = "punches"
response_harm_simple = "punch"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
attack_verb_continuous = "punches"
attack_verb_simple = "punch"
combat_mode = TRUE
diff --git a/code/modules/mob/living/basic/guardian/guardian_fluff.dm b/code/modules/mob/living/basic/guardian/guardian_fluff.dm
index 4ede238921ed3..2b8a8ff8cec31 100644
--- a/code/modules/mob/living/basic/guardian/guardian_fluff.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_fluff.dm
@@ -22,7 +22,7 @@
/// Verb shown to attacker when attacking
var/attack_verb_simple = "punch"
/// Sound played when we attack
- var/attack_sound = 'sound/weapons/punch1.ogg'
+ var/attack_sound = 'sound/items/weapons/punch1.ogg'
/// Visible effect when we attack
var/attack_vis_effect = ATTACK_EFFECT_PUNCH
/// An associative list of type of guardian to some kind of descriptive text to show on appearance.
diff --git a/code/modules/mob/living/basic/guardian/guardian_types/assassin.dm b/code/modules/mob/living/basic/guardian/guardian_types/assassin.dm
index 2e94aaf039498..19f98a70f409b 100644
--- a/code/modules/mob/living/basic/guardian/guardian_types/assassin.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_types/assassin.dm
@@ -9,7 +9,7 @@
melee_damage_upper = 15
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
sharpness = SHARP_POINTY
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 0, OXY = 1)
diff --git a/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm b/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm
index ae9cbfbeecbc8..2b9d50f085493 100644
--- a/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_types/dextrous.dm
@@ -28,7 +28,7 @@
. = ..()
if(isnull(internal_storage) || (internal_storage.item_flags & ABSTRACT))
return
- . += span_info("It is holding [internal_storage.get_examine_string(user)] in its internal storage.")
+ . += span_info("It is holding [internal_storage.examine_title(user)] in its internal storage.")
/mob/living/basic/guardian/dextrous/recall_effects()
. = ..()
diff --git a/code/modules/mob/living/basic/guardian/guardian_types/lightning.dm b/code/modules/mob/living/basic/guardian/guardian_types/lightning.dm
index b2ac9d66e8c23..3f8f492f1d0f0 100644
--- a/code/modules/mob/living/basic/guardian/guardian_types/lightning.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_types/lightning.dm
@@ -6,7 +6,7 @@
attack_verb_continuous = "shocks"
attack_verb_simple = "shock"
melee_damage_type = BURN
- attack_sound = 'sound/machines/defib_zap.ogg'
+ attack_sound = 'sound/machines/defib/defib_zap.ogg'
damage_coeff = list(BRUTE = 0.7, BURN = 0.7, TOX = 0.7, STAMINA = 0, OXY = 0.7)
range = 7
playstyle_string = span_holoparasite("As a lightning type, you will apply lightning chains to targets on attack and have a lightning chain to your summoner. Lightning chains will shock anyone near them.")
diff --git a/code/modules/mob/living/basic/guardian/guardian_types/support.dm b/code/modules/mob/living/basic/guardian/guardian_types/support.dm
index 8ab24b7e9b7d1..46b69652cf5d9 100644
--- a/code/modules/mob/living/basic/guardian/guardian_types/support.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_types/support.dm
@@ -134,7 +134,7 @@
/// Start teleporting
/datum/action/cooldown/mob_cooldown/guardian_bluespace_beacon/proc/perform_teleport(mob/living/source, atom/target)
source.do_attack_animation(target)
- playsound(target, 'sound/weapons/punch1.ogg', 50, TRUE, TRUE, frequency = -1)
+ playsound(target, 'sound/items/weapons/punch1.ogg', 50, TRUE, TRUE, frequency = -1)
source.balloon_alert(source, "teleporting...")
target.visible_message(
span_danger("[target] starts to glow faintly!"), \
diff --git a/code/modules/mob/living/basic/guardian/guardian_verbs.dm b/code/modules/mob/living/basic/guardian/guardian_verbs.dm
index 80a2af7db7a27..b69fac878209a 100644
--- a/code/modules/mob/living/basic/guardian/guardian_verbs.dm
+++ b/code/modules/mob/living/basic/guardian/guardian_verbs.dm
@@ -57,7 +57,7 @@
if (isnull(summoner))
return
var/sender_key = key
- var/input = tgui_input_text(src, "Enter a message to tell your summoner", "Guardian")
+ var/input = tgui_input_text(src, "Enter a message to tell your summoner", "Guardian", max_length = MAX_MESSAGE_LEN)
if (sender_key != key || !input) //guardian got reset, or did not enter anything
return
@@ -91,7 +91,7 @@
/datum/action/cooldown/mob_cooldown/guardian_comms/Activate(atom/target)
StartCooldown(360 SECONDS)
- var/input = tgui_input_text(owner, "Enter a message to tell your guardian", "Message")
+ var/input = tgui_input_text(owner, "Enter a message to tell your guardian", "Message", max_length = MAX_MESSAGE_LEN)
StartCooldown()
if (!input)
return FALSE
diff --git a/code/modules/mob/living/basic/health_adjustment.dm b/code/modules/mob/living/basic/health_adjustment.dm
index bae9d7b9e57b5..ca98f98eb8e91 100644
--- a/code/modules/mob/living/basic/health_adjustment.dm
+++ b/code/modules/mob/living/basic/health_adjustment.dm
@@ -4,12 +4,12 @@
* Arguments:
* * amount The amount that will be used to adjust the mob's health
* * updating_health If the mob's health should be immediately updated to the new value
- * * forced If we should force update the adjustment of the mob's health no matter the restrictions, like GODMODE
+ * * forced If we should force update the adjustment of the mob's health no matter the restrictions, like TRAIT_GODMODE
* returns the net change in bruteloss after applying the damage amount
*/
/mob/living/basic/proc/adjust_health(amount, updating_health = TRUE, forced = FALSE)
. = FALSE
- if(!forced && (status_flags & GODMODE))
+ if(!forced && HAS_TRAIT(src, TRAIT_GODMODE))
return 0
. = bruteloss // bruteloss value before applying damage
bruteloss = round(clamp(bruteloss + amount, 0, maxHealth * 2), DAMAGE_PRECISION)
diff --git a/code/modules/mob/living/basic/heretic/_heretic_summon.dm b/code/modules/mob/living/basic/heretic/_heretic_summon.dm
index b6336182a2224..b482ee2d211a2 100644
--- a/code/modules/mob/living/basic/heretic/_heretic_summon.dm
+++ b/code/modules/mob/living/basic/heretic/_heretic_summon.dm
@@ -13,7 +13,7 @@
speed = 0
melee_attack_cooldown = CLICK_CD_MELEE
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
response_help_continuous = "thinks better of touching"
response_help_simple = "think better of touching"
response_disarm_continuous = "flails at"
diff --git a/code/modules/mob/living/basic/heretic/fire_shark.dm b/code/modules/mob/living/basic/heretic/fire_shark.dm
index c4106050bc26e..1ac4ccb7b237a 100644
--- a/code/modules/mob/living/basic/heretic/fire_shark.dm
+++ b/code/modules/mob/living/basic/heretic/fire_shark.dm
@@ -11,7 +11,7 @@
maxHealth = 16
melee_damage_lower = 8
melee_damage_upper = 8
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
obj_damage = 0
attack_verb_continuous = "bites"
@@ -30,4 +30,5 @@
AddComponent(/datum/component/swarming)
AddComponent(/datum/component/regenerator, outline_colour = COLOR_DARK_RED)
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
+ ADD_TRAIT(src, TRAIT_FREE_HYPERSPACE_MOVEMENT, INNATE_TRAIT)
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
diff --git a/code/modules/mob/living/basic/heretic/flesh_worm.dm b/code/modules/mob/living/basic/heretic/flesh_worm.dm
index 92b910c717fae..cddd34ba44184 100644
--- a/code/modules/mob/living/basic/heretic/flesh_worm.dm
+++ b/code/modules/mob/living/basic/heretic/flesh_worm.dm
@@ -98,7 +98,7 @@
if(!istype(target, /obj/item/bodypart/arm))
return ..()
visible_message(span_warning("[src] devours [target]!"))
- playsound(src, 'sound/magic/demon_consume.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/magic/demon_consume.ogg', 50, TRUE)
qdel(target)
on_arm_eaten()
diff --git a/code/modules/mob/living/basic/heretic/star_gazer.dm b/code/modules/mob/living/basic/heretic/star_gazer.dm
index e503cd65aea16..57a0ddfe322d8 100644
--- a/code/modules/mob/living/basic/heretic/star_gazer.dm
+++ b/code/modules/mob/living/basic/heretic/star_gazer.dm
@@ -21,11 +21,11 @@
attack_verb_continuous = "ravages"
attack_verb_simple = "ravage"
attack_vis_effect = ATTACK_EFFECT_SLASH
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
melee_attack_cooldown = 0.6 SECONDS
speak_emote = list("growls")
damage_coeff = list(BRUTE = 1, BURN = 0.5, TOX = 0, STAMINA = 0, OXY = 0)
- death_sound = 'sound/magic/cosmic_expansion.ogg'
+ death_sound = 'sound/effects/magic/cosmic_expansion.ogg'
slowed_by_drag = FALSE
move_force = MOVE_FORCE_OVERPOWERING
diff --git a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.dm b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.dm
index a83953ae1c94d..0b27ac6958e0e 100644
--- a/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.dm
+++ b/code/modules/mob/living/basic/icemoon/ice_demon/ice_demon.dm
@@ -16,7 +16,7 @@
melee_damage_upper = 15
attack_verb_continuous = "slices"
attack_verb_simple = "slice"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
move_force = MOVE_FORCE_VERY_STRONG
move_resist = MOVE_FORCE_VERY_STRONG
@@ -24,7 +24,7 @@
crusher_loot = /obj/item/crusher_trophy/ice_demon_cube
ai_controller = /datum/ai_controller/basic_controller/ice_demon
death_message = "fades as the energies that tied it to this world dissipate."
- death_sound = 'sound/magic/demon_dies.ogg'
+ death_sound = 'sound/effects/magic/demon_dies.ogg'
/mob/living/basic/mining/ice_demon/Initialize(mapload)
. = ..()
@@ -38,7 +38,7 @@
AddComponent(\
/datum/component/ranged_attacks,\
projectile_type = /obj/projectile/temp/ice_demon,\
- projectile_sound = 'sound/weapons/pierce.ogg',\
+ projectile_sound = 'sound/items/weapons/pierce.ogg',\
)
var/static/list/death_loot = list(/obj/item/stack/ore/bluespace_crystal = 3)
AddElement(/datum/element/death_drops, death_loot)
@@ -66,7 +66,7 @@
melee_damage_upper = 5
attack_verb_continuous = "slices"
attack_verb_simple = "slice"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
alpha = 80
ai_controller = /datum/ai_controller/basic_controller/ice_demon/afterimage
///how long do we exist for
diff --git a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.dm b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.dm
index 768375cfce8a6..43f8c61d0c880 100644
--- a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.dm
+++ b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp.dm
@@ -26,9 +26,9 @@
attack_verb_continuous = "chomps"
attack_verb_simple = "chomp"
death_message = "collapses on its side."
- death_sound = 'sound/magic/demon_dies.ogg'
+ death_sound = 'sound/effects/magic/demon_dies.ogg'
- attack_sound = 'sound/magic/demon_attack1.ogg'
+ attack_sound = 'sound/effects/magic/demon_attack1.ogg'
move_force = MOVE_FORCE_VERY_STRONG
move_resist = MOVE_FORCE_VERY_STRONG
pull_force = MOVE_FORCE_VERY_STRONG
diff --git a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm
index 53d7e7191ef05..33d091db4e4b1 100644
--- a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm
+++ b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm
@@ -21,7 +21,7 @@
/datum/ai_planning_subtree/find_and_hunt_target/corpses/ice_whelp
target_key = BB_TARGET_CANNIBAL
finding_behavior = /datum/ai_behavior/find_hunt_target/corpses/dragon_corpse
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/dragon_cannibalise
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/dragon_cannibalise
hunt_targets = list(/mob/living/basic/mining/ice_whelp)
hunt_range = 10
@@ -32,10 +32,10 @@
return FALSE
return ..()
-/datum/ai_behavior/hunt_target/unarmed_attack_target/dragon_cannibalise
+/datum/ai_behavior/hunt_target/interact_with_target/dragon_cannibalise
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
-/datum/ai_behavior/hunt_target/unarmed_attack_target/dragon_cannibalise/perform(seconds_per_tick, datum/ai_controller/controller, target_key, attack_key)
+/datum/ai_behavior/hunt_target/interact_with_target/dragon_cannibalise/perform(seconds_per_tick, datum/ai_controller/controller, target_key, attack_key)
var/mob/living/target = controller.blackboard[target_key]
if(QDELETED(target) || target.stat != DEAD || target.pulledby) //we were too slow
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
@@ -66,20 +66,14 @@
set_movement_target(controller, target)
/datum/ai_behavior/sculpt_statue/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
- var/atom/target = controller.blackboard[target_key]
- var/mob/living/basic/living_pawn = controller.pawn
-
- if(QDELETED(target))
+ if(!controller.ai_interact(target = target_key, combat_mode = FALSE))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
-
- living_pawn.melee_attack(target)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
/datum/ai_behavior/sculpt_statue/finish_action(datum/ai_controller/controller, succeeded, target_key)
. = ..()
controller.clear_blackboard_key(target_key)
-
//subtree to use our attacks on the victim
/datum/ai_planning_subtree/targeted_mob_ability/ice_whelp
ability_key = BB_WHELP_STRAIGHTLINE_FIRE
diff --git a/code/modules/mob/living/basic/icemoon/wolf/wolf.dm b/code/modules/mob/living/basic/icemoon/wolf/wolf.dm
index b7a947f00e309..3708d754ab4b0 100644
--- a/code/modules/mob/living/basic/icemoon/wolf/wolf.dm
+++ b/code/modules/mob/living/basic/icemoon/wolf/wolf.dm
@@ -30,7 +30,7 @@
attack_verb_simple = "bite"
death_message = "snarls its last and perishes."
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
move_force = MOVE_FORCE_WEAK
move_resist = MOVE_FORCE_WEAK
pull_force = MOVE_FORCE_WEAK
diff --git a/code/modules/mob/living/basic/jungle/leaper/leaper.dm b/code/modules/mob/living/basic/jungle/leaper/leaper.dm
index f3213897f9bdb..94babd0218e5b 100644
--- a/code/modules/mob/living/basic/jungle/leaper/leaper.dm
+++ b/code/modules/mob/living/basic/jungle/leaper/leaper.dm
@@ -23,7 +23,7 @@
minimum_survivable_temperature = 0
maximum_survivable_temperature = INFINITY
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
status_flags = NONE
lighting_cutoff_red = 5
diff --git a/code/modules/mob/living/basic/jungle/leaper/leaper_abilities.dm b/code/modules/mob/living/basic/jungle/leaper/leaper_abilities.dm
index efc09410db488..d753c42c51ec8 100644
--- a/code/modules/mob/living/basic/jungle/leaper/leaper_abilities.dm
+++ b/code/modules/mob/living/basic/jungle/leaper/leaper_abilities.dm
@@ -137,7 +137,7 @@
if(!length(possible_turfs))
return FALSE
- playsound(owner, 'sound/magic/fireball.ogg', 70, TRUE)
+ playsound(owner, 'sound/effects/magic/fireball.ogg', 70, TRUE)
new /obj/effect/temp_visual/blood_drop_rising(get_turf(owner))
addtimer(CALLBACK(src, PROC_REF(fire_droplets), possible_turfs), 1.5 SECONDS)
StartCooldown()
diff --git a/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid.dm b/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid.dm
index 62b36a1bbaf94..059375a7ed6a6 100644
--- a/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid.dm
+++ b/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid.dm
@@ -29,7 +29,7 @@
mob_size = MOB_SIZE_LARGE
speak_emote = list("chitters")
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
ai_controller = /datum/ai_controller/basic_controller/mega_arachnid
alpha = 40
diff --git a/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_ai.dm b/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_ai.dm
index fa2a86787d861..7d1b40fc5ec23 100644
--- a/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_ai.dm
+++ b/code/modules/mob/living/basic/jungle/mega_arachnid/mega_arachnid_ai.dm
@@ -19,7 +19,7 @@
/datum/ai_planning_subtree/find_and_hunt_target/destroy_surveillance
target_key = BB_SURVEILLANCE_TARGET
finding_behavior = /datum/ai_behavior/find_hunt_target/find_active_surveillance
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target
hunt_targets = list(/obj/machinery/camera, /obj/machinery/light)
hunt_range = 7
diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling.dm b/code/modules/mob/living/basic/jungle/seedling/seedling.dm
index 7a853b8a9d086..00db708bc3299 100644
--- a/code/modules/mob/living/basic/jungle/seedling/seedling.dm
+++ b/code/modules/mob/living/basic/jungle/seedling/seedling.dm
@@ -31,7 +31,7 @@
lighting_cutoff_blue = 25
mob_size = MOB_SIZE_LARGE
faction = list(FACTION_PLANTS)
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
ai_controller = /datum/ai_controller/basic_controller/seedling
///the state of combat we are in
@@ -338,7 +338,7 @@
living_target.ignite_mob()
living_target.adjustFireLoss(30)
- playsound(target_turf, 'sound/magic/lightningbolt.ogg', 50, TRUE)
+ playsound(target_turf, 'sound/effects/magic/lightningbolt.ogg', 50, TRUE)
if(!is_seedling)
return
var/mob/living/basic/seedling/seed_firer = firer
diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm b/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm
index 2ed4811e46f25..440cfc2861b69 100644
--- a/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm
+++ b/code/modules/mob/living/basic/jungle/seedling/seedling_ai.dm
@@ -19,7 +19,7 @@
/datum/ai_planning_subtree/find_and_hunt_target/watering_can
target_key = BB_WATERCAN_TARGET
finding_behavior = /datum/ai_behavior/find_hunt_target
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target
hunt_targets = list(/obj/item/reagent_containers/cup/watering_can)
hunt_range = 7
@@ -32,7 +32,7 @@
/datum/ai_planning_subtree/find_and_hunt_target/treat_hydroplants
target_key = BB_HYDROPLANT_TARGET
finding_behavior = /datum/ai_behavior/find_and_set/treatable_hydro
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/treat_hydroplant
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/treat_hydroplant
hunt_targets = list(/obj/machinery/hydroponics)
hunt_range = 7
@@ -58,11 +58,11 @@
if(possible_trays.len)
return pick(possible_trays)
-/datum/ai_behavior/hunt_target/unarmed_attack_target/treat_hydroplant
+/datum/ai_behavior/hunt_target/interact_with_target/treat_hydroplant
hunt_cooldown = 2 SECONDS
always_reset_target = TRUE
-/datum/ai_behavior/hunt_target/unarmed_attack_target/treat_hydroplant/target_caught(mob/living/living_pawn, obj/machinery/hydroponics/hydro_target)
+/datum/ai_behavior/hunt_target/interact_with_target/treat_hydroplant/target_caught(mob/living/living_pawn, obj/machinery/hydroponics/hydro_target)
if(QDELETED(hydro_target) || QDELETED(hydro_target.myseed))
return
@@ -112,7 +112,7 @@
/datum/ai_planning_subtree/find_and_hunt_target/fill_watercan
target_key = BB_LOW_PRIORITY_HUNTING_TARGET
finding_behavior = /datum/ai_behavior/find_hunt_target/suitable_dispenser
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/water_source
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/water_source
hunt_targets = list(/obj/structure/sink, /obj/structure/reagent_dispensers)
hunt_range = 7
@@ -135,7 +135,7 @@
return can_see(source, water_source, radius)
-/datum/ai_behavior/hunt_target/unarmed_attack_target/water_source
+/datum/ai_behavior/hunt_target/interact_with_target/water_source
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
hunt_cooldown = 5 SECONDS
diff --git a/code/modules/mob/living/basic/jungle/seedling/seedling_projectiles.dm b/code/modules/mob/living/basic/jungle/seedling/seedling_projectiles.dm
index 37a3ec1c0aca9..303776384d1f6 100644
--- a/code/modules/mob/living/basic/jungle/seedling/seedling_projectiles.dm
+++ b/code/modules/mob/living/basic/jungle/seedling/seedling_projectiles.dm
@@ -7,8 +7,8 @@
armor_flag = ENERGY
light_color = LIGHT_COLOR_DIM_YELLOW
speed = 1.6
- hitsound = 'sound/weapons/sear.ogg'
- hitsound_wall = 'sound/weapons/effects/searwall.ogg'
+ hitsound = 'sound/items/weapons/sear.ogg'
+ hitsound_wall = 'sound/items/weapons/effects/searwall.ogg'
nondirectional_sprite = TRUE
/obj/projectile/seedling/on_hit(atom/target, blocked = 0, pierce_hit)
diff --git a/code/modules/mob/living/basic/jungle/venus_human_trap.dm b/code/modules/mob/living/basic/jungle/venus_human_trap.dm
index ec375283fcea0..0a4a557eef15b 100644
--- a/code/modules/mob/living/basic/jungle/venus_human_trap.dm
+++ b/code/modules/mob/living/basic/jungle/venus_human_trap.dm
@@ -143,9 +143,9 @@
combat_mode = TRUE
basic_mob_flags = DEL_ON_DEATH
death_message = "collapses into bits of plant matter."
- attacked_sound = 'sound/creatures/venus_trap_hurt.ogg'
- death_sound = 'sound/creatures/venus_trap_death.ogg'
- attack_sound = 'sound/creatures/venus_trap_hit.ogg'
+ attacked_sound = 'sound/mobs/non-humanoids/venus_trap/venus_trap_hurt.ogg'
+ death_sound = 'sound/mobs/non-humanoids/venus_trap/venus_trap_death.ogg'
+ attack_sound = 'sound/mobs/non-humanoids/venus_trap/venus_trap_hit.ogg'
unsuitable_heat_damage = 5 // heat damage is different from cold damage since coldmos is significantly more common than plasmafires
unsuitable_cold_damage = 2 // they now do take cold damage, but this should be sufficiently small that it does not cause major issues
habitable_atmos = null
diff --git a/code/modules/mob/living/basic/lavaland/basilisk/basilisk.dm b/code/modules/mob/living/basic/lavaland/basilisk/basilisk.dm
index 6c1f5ba0846cd..5f13d53160a83 100644
--- a/code/modules/mob/living/basic/lavaland/basilisk/basilisk.dm
+++ b/code/modules/mob/living/basic/lavaland/basilisk/basilisk.dm
@@ -16,7 +16,7 @@
attack_verb_continuous = "bites into"
attack_verb_simple = "bite into"
throw_blocked_message = "bounces off the shell of"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
ai_controller = /datum/ai_controller/basic_controller/basilisk
butcher_results = list(
@@ -30,7 +30,7 @@
/mob/living/basic/mining/basilisk/Initialize(mapload)
. = ..()
AddComponent(/datum/component/basic_mob_attack_telegraph)
- ranged_attacks = AddComponent(/datum/component/ranged_attacks, projectile_type = /obj/projectile/temp/watcher, projectile_sound = 'sound/weapons/pierce.ogg')
+ ranged_attacks = AddComponent(/datum/component/ranged_attacks, projectile_type = /obj/projectile/temp/watcher, projectile_sound = 'sound/items/weapons/pierce.ogg')
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(check_lava))
/mob/living/basic/mining/basilisk/Destroy()
diff --git a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_actions.dm b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_actions.dm
index f023215d7dd5c..8e43e43e72c31 100644
--- a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_actions.dm
+++ b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_actions.dm
@@ -19,14 +19,14 @@
return
playsound(burrower, 'sound/effects/break_stone.ogg', 50, TRUE)
new /obj/effect/temp_visual/mook_dust(get_turf(burrower))
- burrower.status_flags |= GODMODE
+ ADD_TRAIT(burrower, TRAIT_GODMODE, REF(src))
burrower.SetInvisibility(INVISIBILITY_MAXIMUM, id=type)
burrower.forceMove(unburrow_turf)
//not that it's gonna die with godmode but still
SLEEP_CHECK_DEATH(rand(0.7 SECONDS, 1.2 SECONDS), burrower)
playsound(burrower, 'sound/effects/break_stone.ogg', 50, TRUE)
new /obj/effect/temp_visual/mook_dust(unburrow_turf)
- burrower.status_flags &= ~GODMODE
+ REMOVE_TRAIT(burrower, TRAIT_GODMODE, REF(src))
burrower.RemoveInvisibility(type)
/datum/action/cooldown/mob_cooldown/resurface/proc/get_unburrow_turf(mob/living/burrower, atom/target)
@@ -53,7 +53,7 @@
name = "Spew Bile"
desc = "Spews bile everywhere. Must resurface after use to refresh."
projectile_type = /obj/projectile/bileworm_acid
- projectile_sound = 'sound/creatures/bileworm/bileworm_spit.ogg'
+ projectile_sound = 'sound/mobs/non-humanoids/bileworm/bileworm_spit.ogg'
shared_cooldown = MOB_SHARED_COOLDOWN_1 | MOB_SHARED_COOLDOWN_2
/datum/action/cooldown/mob_cooldown/projectile_attack/dir_shots/bileworm/Activate(atom/target_atom)
@@ -70,7 +70,7 @@
/obj/projectile/bileworm_acid
name = "acidic bile"
icon_state = "neurotoxin"
- hitsound = 'sound/weapons/sear.ogg'
+ hitsound = 'sound/items/weapons/sear.ogg'
damage = 20
speed = 2
range = 20
@@ -108,14 +108,14 @@
return //this will give up on devouring the target which is fine by me
playsound(devourer, 'sound/effects/break_stone.ogg', 50, TRUE)
new /obj/effect/temp_visual/mook_dust(get_turf(devourer))
- devourer.status_flags |= GODMODE
+ ADD_TRAIT(devourer, TRAIT_GODMODE, REF(src))
devourer.SetInvisibility(INVISIBILITY_MAXIMUM, id=type)
devourer.forceMove(devour_turf)
//not that it's gonna die with godmode but still
SLEEP_CHECK_DEATH(rand(0.7 SECONDS, 1.2 SECONDS), devourer)
playsound(devourer, 'sound/effects/break_stone.ogg', 50, TRUE)
new /obj/effect/temp_visual/mook_dust(devour_turf)
- devourer.status_flags &= ~GODMODE
+ REMOVE_TRAIT(devourer, TRAIT_GODMODE, REF(src))
devourer.RemoveInvisibility(type)
if(!(target in devour_turf))
to_chat(devourer, span_warning("Someone stole your dinner!"))
diff --git a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_loot.dm b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_loot.dm
index 7c04ed65a61c7..a5740a51ebd9a 100644
--- a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_loot.dm
+++ b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_loot.dm
@@ -51,7 +51,7 @@
owner_has_control = FALSE
cooldown_time = 10 SECONDS
projectile_type = /obj/projectile/bileworm_acid
- projectile_sound = 'sound/creatures/bileworm/bileworm_spit.ogg'
+ projectile_sound = 'sound/mobs/non-humanoids/bileworm/bileworm_spit.ogg'
/datum/action/cooldown/mob_cooldown/projectile_attack/dir_shots/spewlet/New(Target)
firing_directions = GLOB.cardinals.Copy()
diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimbeam.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimbeam.dm
index 61f31f7044dbc..5900289cae569 100644
--- a/code/modules/mob/living/basic/lavaland/brimdemon/brimbeam.dm
+++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimbeam.dm
@@ -52,7 +52,7 @@
/// Create a laser in the direction we are facing
/datum/action/cooldown/mob_cooldown/brimbeam/proc/fire_laser()
owner.visible_message(span_danger("[owner] fires a brimbeam!"))
- playsound(owner, 'sound/creatures/brimdemon.ogg', 150, FALSE, 0, 3)
+ playsound(owner, 'sound/mobs/non-humanoids/brimdemon/brimdemon.ogg', 150, FALSE, 0, 3)
var/turf/target_turf = get_ranged_target_turf(owner, owner.dir, beam_range)
var/turf/origin_turf = get_turf(owner)
var/list/affected_turfs = get_line(origin_turf, target_turf) - origin_turf
diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.dm
index 9a88c636cf511..81dc34002eedb 100644
--- a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.dm
+++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon.dm
@@ -14,13 +14,13 @@
speak_emote = list("cackles")
melee_damage_lower = 7.5
melee_damage_upper = 7.5
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
melee_attack_cooldown = 0.6 SECONDS
attack_vis_effect = ATTACK_EFFECT_BITE
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
death_message = "wails as infernal energy escapes from its wounds, leaving it an empty husk."
- death_sound = 'sound/magic/demon_dies.ogg'
+ death_sound = 'sound/effects/magic/demon_dies.ogg'
light_color = LIGHT_COLOR_BLOOD_MAGIC
light_power = 5
light_range = 1.4
diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm
index 11fd7b6aa260f..014cfb626be0a 100644
--- a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm
+++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_loot.dm
@@ -8,11 +8,11 @@
var/static/list/comic_phrases = list("BOOM", "BANG", "KABLOW", "KAPOW", "OUCH", "BAM", "KAPOW", "WHAM", "POW", "KABOOM")
/obj/item/crusher_trophy/brimdemon_fang/effect_desc()
- return "mark detonation creates visual and audiosensory effects on the target"
+ return "mark detonation to create visual and audiosensory effects at the target"
/obj/item/crusher_trophy/brimdemon_fang/on_mark_detonation(mob/living/target, mob/living/user)
target.balloon_alert_to_viewers("[pick(comic_phrases)]!")
- playsound(target, 'sound/lavaland/brimdemon_crush.ogg', 100)
+ playsound(target, 'sound/mobs/non-humanoids/brimdemon/brimdemon_crush.ogg', 100)
/// Reagent pool left by dying brimdemon
/obj/effect/decal/cleanable/brimdust
@@ -20,7 +20,8 @@
desc = "Dust from a brimdemon. It is considered valuable for its' botanical abilities."
icon_state = "brimdust"
icon = 'icons/obj/mining.dmi'
- layer = FLOOR_CLEAN_LAYER
+ plane = GAME_PLANE
+ layer = GAME_CLEAN_LAYER
mergeable_decal = FALSE
/obj/effect/decal/cleanable/brimdust/Initialize(mapload)
diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm
index 59f7ffb249d07..26c0d79540a73 100644
--- a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm
+++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm
@@ -19,7 +19,7 @@
melee_damage_upper = 0
attack_verb_continuous = "barrels into"
attack_verb_simple = "barrel into"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
combat_mode = FALSE
speak_emote = list("screeches")
death_message = "stops moving as green liquid oozes from the carcass!"
diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm
index a31bf1f3e1d2e..8ea2467a2a813 100644
--- a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm
+++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm
@@ -1,3 +1,4 @@
+#define BURROW_RANGE 5
/datum/ai_controller/basic_controller/goldgrub
blackboard = list(
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
@@ -11,6 +12,7 @@
planning_subtrees = list(
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/pet_planning,
+ /datum/ai_planning_subtree/burrow_through_ground,
/datum/ai_planning_subtree/dig_away_from_danger,
/datum/ai_planning_subtree/flee_target,
/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores,
@@ -39,10 +41,50 @@
/datum/ai_planning_subtree/look_for_adult,
)
+/datum/ai_planning_subtree/burrow_through_ground
+
+/datum/ai_planning_subtree/burrow_through_ground/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ if(is_jaunting(controller.pawn) && controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET))
+ controller.queue_behavior(/datum/ai_behavior/burrow_through_ground, BB_BASIC_MOB_CURRENT_TARGET)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+/datum/ai_behavior/burrow_through_ground
+ action_cooldown = 10 SECONDS
+
+/datum/ai_behavior/burrow_through_ground/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
+ var/atom/target = controller.blackboard[target_key]
+ if(!is_jaunting(controller.pawn) || QDELETED(target))
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
+
+ var/mob/living/living_pawn = controller.pawn
+ var/atom/movable/phased = living_pawn.loc
+
+ var/list/turfs_list = RANGE_TURFS(BURROW_RANGE, phased)
+ var/current_max_distance = 0
+ var/turf/selected_turf
+
+ for(var/turf/possible_turf as anything in turfs_list)
+ if(!ismineralturf(possible_turf) && !isasteroidturf(possible_turf))
+ continue
+
+ var/distance_to_target = get_dist(possible_turf, target)
+ if(distance_to_target > current_max_distance)
+ current_max_distance = distance_to_target
+ selected_turf = possible_turf
+
+ if(distance_to_target == BURROW_RANGE)
+ break
+
+ if(isnull(selected_turf))
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
+
+ phased.forceMove(selected_turf)
+ return AI_BEHAVIOR_SUCCEEDED | AI_BEHAVIOR_DELAY
+
///consume food!
/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores
target_key = BB_ORE_TARGET
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/hunt_ores
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/hunt_ores
finding_behavior = /datum/ai_behavior/find_hunt_target/hunt_ores
hunt_targets = list(/obj/item/stack/ore)
hunt_chance = 90
@@ -65,13 +107,13 @@
return can_see(source, target, radius)
-/datum/ai_behavior/hunt_target/unarmed_attack_target/hunt_ores
+/datum/ai_behavior/hunt_target/interact_with_target/hunt_ores
always_reset_target = TRUE
///break boulders so that we can find more food!
/datum/ai_planning_subtree/find_and_hunt_target/harvest_vents
target_key = BB_VENT_TARGET
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target //We call the ore vent's produce_boulder() proc here to produce a single boulder.
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target //We call the ore vent's produce_boulder() proc here to produce a single boulder.
finding_behavior = /datum/ai_behavior/find_hunt_target/harvest_vents
hunt_targets = list(/obj/structure/ore_vent)
hunt_chance = 25
@@ -95,7 +137,7 @@
///break boulders so that we can find more food!
/datum/ai_planning_subtree/find_and_hunt_target/break_boulders
target_key = BB_BOULDER_TARGET
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target //We process boulders once every tap, so we dont need to do anything special here
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target //We process boulders once every tap, so we dont need to do anything special here
finding_behavior = /datum/ai_behavior/find_hunt_target/break_boulders
hunt_targets = list(/obj/item/boulder)
hunt_chance = 100 //If we can, we should always break boulders.
@@ -179,3 +221,5 @@
controller.queue_behavior(/datum/ai_behavior/use_mob_ability, BB_SPIT_ABILITY)
controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND)
return SUBTREE_RETURN_FINISH_PLANNING
+
+#undef BURROW_RANGE
diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath.dm b/code/modules/mob/living/basic/lavaland/goliath/goliath.dm
index 4a28fba66309e..7d6b1dc6e404a 100644
--- a/code/modules/mob/living/basic/lavaland/goliath/goliath.dm
+++ b/code/modules/mob/living/basic/lavaland/goliath/goliath.dm
@@ -19,7 +19,7 @@
obj_damage = 100
melee_damage_lower = 25
melee_damage_upper = 25
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
attack_verb_continuous = "pulverizes"
attack_verb_simple = "pulverize"
throw_blocked_message = "does nothing to the tough hide of"
@@ -136,7 +136,7 @@
if (!COOLDOWN_FINISHED(src, ability_animation_cooldown))
return
COOLDOWN_START(src, ability_animation_cooldown, 2 SECONDS)
- playsound(src, 'sound/magic/demon_attack1.ogg', vol = 50, vary = TRUE)
+ playsound(src, 'sound/effects/magic/demon_attack1.ogg', vol = 50, vary = TRUE)
Shake(1, 0, 1.5 SECONDS)
/// Called slightly before tentacles ability comes off cooldown, as a warning
diff --git a/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers.dm b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers.dm
index f9e1d458ef2ed..6b822d490de78 100644
--- a/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers.dm
+++ b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers.dm
@@ -139,12 +139,12 @@
can_breed = FALSE
gender = NEUTER
ai_controller = /datum/ai_controller/basic_controller/gutlunch/gutlunch_baby
+ current_size = 0.6
///list of stats we inherited
var/datum/gutlunch_inherited_stats/inherited_stats
/mob/living/basic/mining/gutlunch/grub/Initialize(mapload)
. = ..()
- transform = transform.Scale(0.6, 0.6)
AddComponent(\
/datum/component/growth_and_differentiation,\
growth_time = 3 MINUTES,\
diff --git a/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers_ai.dm b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers_ai.dm
index c7f7e86c86680..261f6d22a021b 100644
--- a/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers_ai.dm
+++ b/code/modules/mob/living/basic/lavaland/gutlunchers/gutlunchers_ai.dm
@@ -73,7 +73,7 @@
///consume food!
/datum/ai_planning_subtree/find_and_hunt_target/food_trough
target_key = BB_TROUGH_TARGET
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/food_trough
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/food_trough
finding_behavior = /datum/ai_behavior/find_hunt_target/food_trough
hunt_targets = list(/obj/structure/ore_container/food_trough/gutlunch_trough)
hunt_chance = 75
@@ -96,9 +96,9 @@
return can_see(source, target, radius)
-/datum/ai_behavior/hunt_target/unarmed_attack_target/food_trough
+/datum/ai_behavior/hunt_target/interact_with_target/food_trough
always_reset_target = TRUE
- switch_combat_mode = TRUE
+ behavior_combat_mode = FALSE
/datum/pet_command/mine_walls
command_name = "Mine"
diff --git a/code/modules/mob/living/basic/lavaland/hivelord/hivelord.dm b/code/modules/mob/living/basic/lavaland/hivelord/hivelord.dm
index f0de6c3272e55..931b568e5ee3f 100644
--- a/code/modules/mob/living/basic/lavaland/hivelord/hivelord.dm
+++ b/code/modules/mob/living/basic/lavaland/hivelord/hivelord.dm
@@ -17,7 +17,7 @@
attack_verb_continuous = "weakly tackles"
attack_verb_simple = "weakly tackles"
speak_emote = list("telepathically cries")
- attack_sound = 'sound/weapons/pierce.ogg'
+ attack_sound = 'sound/items/weapons/pierce.ogg'
throw_blocked_message = "passes between the bodies of the"
obj_damage = 0
pass_flags = PASSTABLE
@@ -94,7 +94,7 @@
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
speak_emote = list("telepathically cries")
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
obj_damage = 0
density = FALSE
diff --git a/code/modules/mob/living/basic/lavaland/hivelord/spawn_hivelord_brood.dm b/code/modules/mob/living/basic/lavaland/hivelord/spawn_hivelord_brood.dm
index 7d50806e63a0c..6dbbe72a2459b 100644
--- a/code/modules/mob/living/basic/lavaland/hivelord/spawn_hivelord_brood.dm
+++ b/code/modules/mob/living/basic/lavaland/hivelord/spawn_hivelord_brood.dm
@@ -98,8 +98,8 @@
var/turf/my_turf = get_turf(src)
dir = get_dir(spawn_from, my_turf)
- var/move_x = (my_turf.x - spawn_from.x) * world.icon_size
- var/move_y = (my_turf.y - spawn_from.y) * world.icon_size
+ var/move_x = (my_turf.x - spawn_from.x) * ICON_SIZE_X
+ var/move_y = (my_turf.y - spawn_from.y) * ICON_SIZE_Y
pixel_x = -move_x
pixel_y = -move_y
diff --git a/code/modules/mob/living/basic/lavaland/legion/legion.dm b/code/modules/mob/living/basic/lavaland/legion/legion.dm
index 12bf6555d97d4..76d3f51947e63 100644
--- a/code/modules/mob/living/basic/lavaland/legion/legion.dm
+++ b/code/modules/mob/living/basic/lavaland/legion/legion.dm
@@ -21,7 +21,7 @@
attack_verb_continuous = "lashes out at"
attack_verb_simple = "lash out at"
speak_emote = list("gurgles")
- attack_sound = 'sound/weapons/pierce.ogg'
+ attack_sound = 'sound/items/weapons/pierce.ogg'
throw_blocked_message = "bounces harmlessly off of"
crusher_loot = /obj/item/crusher_trophy/legion_skull
death_message = "wails in chorus and dissolves into quivering flesh."
diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm b/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm
index 1bae1b3035379..856ff315d0c1e 100644
--- a/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm
+++ b/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm
@@ -66,6 +66,9 @@
if (QDELETED(victim) || prob(30))
return ..()
+ if(HAS_MIND_TRAIT(victim, TRAIT_MIMING)) // mimes cant talk
+ return
+
var/list/remembered_speech = controller.blackboard[BB_LEGION_RECENT_LINES] || list()
if (length(remembered_speech) && prob(50)) // Don't spam the radio
diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm b/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm
index e578067a44576..df493825ec4e9 100644
--- a/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm
+++ b/code/modules/mob/living/basic/lavaland/legion/legion_brood.dm
@@ -26,7 +26,7 @@
attack_verb_simple = "bite"
attack_vis_effect = ATTACK_EFFECT_BITE
speak_emote = list("echoes") // who the fuck speaking as this mob it dies 10 seconds after it spawns
- attack_sound = 'sound/weapons/pierce.ogg'
+ attack_sound = 'sound/items/weapons/pierce.ogg'
density = FALSE
ai_controller = /datum/ai_controller/basic_controller/legion_brood
/// Reference to a guy who made us
diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_monkey.dm b/code/modules/mob/living/basic/lavaland/legion/legion_monkey.dm
index 5345adc88da3c..9526dcaef52c6 100644
--- a/code/modules/mob/living/basic/lavaland/legion/legion_monkey.dm
+++ b/code/modules/mob/living/basic/lavaland/legion/legion_monkey.dm
@@ -12,7 +12,7 @@
attack_verb_continuous = "mauls"
attack_verb_simple = "maul"
attack_vis_effect = ATTACK_EFFECT_BITE
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
speak_emote = list("chimpers")
corpse_type = /obj/effect/mob_spawn/corpse/human/monkey
ai_controller = /datum/ai_controller/basic_controller/legion_monkey
diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm b/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm
index 3f678da6910bc..d4503230e482f 100644
--- a/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm
+++ b/code/modules/mob/living/basic/lavaland/legion/legion_tumour.dm
@@ -7,7 +7,7 @@
icon_state = "legion_remains"
zone = BODY_ZONE_CHEST
slot = ORGAN_SLOT_PARASITE_EGG
- organ_flags = ORGAN_ORGANIC | ORGAN_EDIBLE | ORGAN_VIRGIN | ORGAN_PROMINENT
+ organ_flags = parent_type::organ_flags | ORGAN_HAZARDOUS
decay_factor = STANDARD_ORGAN_DECAY * 3 // About 5 minutes outside of a host
/// What stage of growth the corruption has reached.
var/stage = 0
@@ -21,10 +21,10 @@
var/spawn_type = /mob/living/basic/mining/legion
/// Spooky sounds to play as you start to turn
var/static/list/spooky_sounds = list(
- 'sound/voice/lowHiss1.ogg',
- 'sound/voice/lowHiss2.ogg',
- 'sound/voice/lowHiss3.ogg',
- 'sound/voice/lowHiss4.ogg',
+ 'sound/mobs/non-humanoids/hiss/lowHiss1.ogg',
+ 'sound/mobs/non-humanoids/hiss/lowHiss2.ogg',
+ 'sound/mobs/non-humanoids/hiss/lowHiss3.ogg',
+ 'sound/mobs/non-humanoids/hiss/lowHiss4.ogg',
)
/obj/item/organ/internal/legion_tumour/Initialize(mapload)
@@ -117,7 +117,7 @@
to_chat(owner, span_danger("Something flexes under your skin."))
if(SPT_PROB(2, seconds_per_tick))
if (prob(40))
- SEND_SOUND(owner, sound('sound/voice/ghost_whisper.ogg'))
+ SEND_SOUND(owner, sound('sound/music/antag/bloodcult/ghost_whisper.ogg'))
else
SEND_SOUND(owner, sound(pick(spooky_sounds)))
if(SPT_PROB(3, seconds_per_tick))
diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm
index 8c879d3ab48c8..b4de3d2321fe6 100644
--- a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm
+++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity.dm
@@ -16,7 +16,7 @@
melee_damage_upper = 19
attack_verb_continuous = "snips"
attack_verb_simple = "snip"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE // Closer than a scratch to a crustacean pinching effect
melee_attack_cooldown = 1 SECONDS
butcher_results = list(
diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm
index 32ec66a62cc37..de62b43e4a054 100644
--- a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm
+++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm
@@ -104,7 +104,7 @@
/datum/ai_planning_subtree/find_and_hunt_target/lobster_fishing
target_key = BB_FISHING_TARGET
hunt_targets = list(/turf/open/lava)
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/reset_target_combat_mode
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off
/datum/ai_planning_subtree/basic_melee_attack_subtree/lobster
melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/lobster
@@ -323,7 +323,7 @@
var/atom/fingers = controller.blackboard[target_key]
if (QDELETED(fingers) || living_pawn.pulling != fingers)
return AI_BEHAVIOR_FAILED
- living_pawn.melee_attack(fingers)
+ controller.ai_interact(target = fingers)
return AI_BEHAVIOR_SUCCEEDED
/datum/ai_behavior/hoard_fingers/finish_action(datum/ai_controller/controller, succeeded, target_key)
diff --git a/code/modules/mob/living/basic/lavaland/mook/mook.dm b/code/modules/mob/living/basic/lavaland/mook/mook.dm
index 6ecf54bc26482..888023897397e 100644
--- a/code/modules/mob/living/basic/lavaland/mook/mook.dm
+++ b/code/modules/mob/living/basic/lavaland/mook/mook.dm
@@ -15,9 +15,9 @@
move_resist = MOVE_FORCE_VERY_STRONG
melee_damage_lower = 8
melee_damage_upper = 8
- attack_sound = 'sound/weapons/rapierhit.ogg'
+ attack_sound = 'sound/items/weapons/rapierhit.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
- death_sound = 'sound/voice/mook_death.ogg'
+ death_sound = 'sound/mobs/non-humanoids/mook/mook_death.ogg'
ai_controller = /datum/ai_controller/basic_controller/mook/support
speed = 5
pixel_x = -16
@@ -243,8 +243,8 @@
melee_damage_lower = 10
melee_damage_upper = 10
gender = MALE
- attack_sound = 'sound/weapons/stringsmash.ogg'
- death_sound = 'sound/voice/mook_death.ogg'
+ attack_sound = 'sound/items/weapons/stringsmash.ogg'
+ death_sound = 'sound/mobs/non-humanoids/mook/mook_death.ogg'
ai_controller = /datum/ai_controller/basic_controller/mook/bard
///our guitar
var/obj/item/instrument/guitar/held_guitar
diff --git a/code/modules/mob/living/basic/lavaland/mook/mook_abilities.dm b/code/modules/mob/living/basic/lavaland/mook/mook_abilities.dm
index f80f1f3dae29e..90d7cb0d5b42d 100644
--- a/code/modules/mob/living/basic/lavaland/mook/mook_abilities.dm
+++ b/code/modules/mob/living/basic/lavaland/mook/mook_abilities.dm
@@ -53,8 +53,8 @@
/datum/action/cooldown/mob_cooldown/mook_ability/mook_leap/proc/launch_towards_target(atom/target)
new /obj/effect/temp_visual/mook_dust(get_turf(owner))
- playsound(get_turf(owner), 'sound/weapons/thudswoosh.ogg', 25, TRUE)
- playsound(owner, 'sound/voice/mook_leap_yell.ogg', 100, TRUE)
+ playsound(get_turf(owner), 'sound/items/weapons/thudswoosh.ogg', 25, TRUE)
+ playsound(owner, 'sound/mobs/non-humanoids/mook/mook_leap_yell.ogg', 100, TRUE)
var/turf/target_turf = get_turf(target)
if(!target_turf.is_blocked_turf())
@@ -117,7 +117,7 @@
var/mob/living/basic/mining/mook/mook_owner = owner
mook_owner.change_combatant_state(state = MOOK_ATTACK_ACTIVE)
new /obj/effect/temp_visual/mook_dust(get_turf(owner))
- playsound(get_turf(owner), 'sound/weapons/thudswoosh.ogg', 50, TRUE)
+ playsound(get_turf(owner), 'sound/items/weapons/thudswoosh.ogg', 50, TRUE)
animate(owner, pixel_y = owner.base_pixel_y + 146, time = 0.5 SECONDS)
addtimer(CALLBACK(src, PROC_REF(land_on_turf), target), 0.5 SECONDS)
diff --git a/code/modules/mob/living/basic/lavaland/mook/mook_ai.dm b/code/modules/mob/living/basic/lavaland/mook/mook_ai.dm
index eeefc7a8b5c72..15da812a0b237 100644
--- a/code/modules/mob/living/basic/lavaland/mook/mook_ai.dm
+++ b/code/modules/mob/living/basic/lavaland/mook/mook_ai.dm
@@ -70,7 +70,7 @@ GLOBAL_LIST_INIT(mook_commands, list(
///deposit ores into the stand!
/datum/ai_planning_subtree/find_and_hunt_target/material_stand
target_key = BB_MATERIAL_STAND_TARGET
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/material_stand
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/material_stand
finding_behavior = /datum/ai_behavior/find_hunt_target
hunt_targets = list(/obj/structure/ore_container/material_stand)
hunt_range = 9
@@ -81,14 +81,14 @@ GLOBAL_LIST_INIT(mook_commands, list(
return
return ..()
-/datum/ai_behavior/hunt_target/unarmed_attack_target/material_stand
+/datum/ai_behavior/hunt_target/interact_with_target/material_stand
required_distance = 0
always_reset_target = TRUE
- switch_combat_mode = TRUE
+ behavior_combat_mode = FALSE
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT
///try to face the counter when depositing ores
-/datum/ai_behavior/hunt_target/unarmed_attack_target/material_stand/setup(datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key)
+/datum/ai_behavior/hunt_target/interact_with_target/material_stand/setup(datum/ai_controller/controller, hunting_target_key, hunting_cooldown_key)
. = ..()
var/atom/hunt_target = controller.blackboard[hunting_target_key]
if (QDELETED(hunt_target))
@@ -297,7 +297,7 @@ GLOBAL_LIST_INIT(mook_commands, list(
///find injured miner mooks after they come home from a long day of work
/datum/ai_planning_subtree/find_and_hunt_target/injured_mooks
target_key = BB_INJURED_MOOK
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/injured_mooks
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/injured_mooks
finding_behavior = /datum/ai_behavior/find_hunt_target/injured_mooks
hunt_targets = list(/mob/living/basic/mining/mook/worker)
hunt_range = 9
@@ -313,9 +313,7 @@ GLOBAL_LIST_INIT(mook_commands, list(
/datum/ai_behavior/find_hunt_target/injured_mooks/valid_dinner(mob/living/source, mob/living/injured_mook)
return (injured_mook.health < injured_mook.maxHealth)
-/datum/ai_behavior/hunt_target/unarmed_attack_target/injured_mooks
-
-/datum/ai_behavior/hunt_target/unarmed_attack_target/injured_mooks
+/datum/ai_behavior/hunt_target/interact_with_target/injured_mooks
always_reset_target = TRUE
hunt_cooldown = 10 SECONDS
@@ -405,7 +403,7 @@ GLOBAL_LIST_INIT(mook_commands, list(
/datum/ai_planning_subtree/find_and_hunt_target/bonfire
target_key = BB_MOOK_BONFIRE_TARGET
finding_behavior = /datum/ai_behavior/find_hunt_target/bonfire
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/bonfire
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/bonfire
hunt_targets = list(/obj/structure/bonfire)
hunt_range = 9
@@ -418,5 +416,5 @@ GLOBAL_LIST_INIT(mook_commands, list(
return can_see(source, fire, radius)
-/datum/ai_behavior/hunt_target/unarmed_attack_target/bonfire
+/datum/ai_behavior/hunt_target/interact_with_target/bonfire
always_reset_target = TRUE
diff --git a/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm b/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm
index 87687c5699963..00ca34757c8d6 100644
--- a/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm
+++ b/code/modules/mob/living/basic/lavaland/node_drone/node_drone.dm
@@ -126,7 +126,7 @@
animate(src, pixel_z = 400, time = 2 SECONDS, easing = QUAD_EASING|EASE_IN, flags = ANIMATION_PARALLEL)
sleep(2 SECONDS)
if(funny_ending)
- playsound(src, 'sound/effects/explosion3.ogg', 50, FALSE) //node drone died on the way back to his home planet.
+ playsound(src, 'sound/effects/explosion/explosion3.ogg', 50, FALSE) //node drone died on the way back to his home planet.
visible_message(span_notice("...or maybe not."))
qdel(src)
diff --git a/code/modules/mob/living/basic/lavaland/raptor/_raptor.dm b/code/modules/mob/living/basic/lavaland/raptor/_raptor.dm
index 784f5dd369907..b7f7ffa9cf693 100644
--- a/code/modules/mob/living/basic/lavaland/raptor/_raptor.dm
+++ b/code/modules/mob/living/basic/lavaland/raptor/_raptor.dm
@@ -36,7 +36,7 @@ GLOBAL_LIST_EMPTY(raptor_population)
maximum_survivable_temperature = INFINITY
attack_verb_continuous = "pecks"
attack_verb_simple = "chomps"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
faction = list(FACTION_RAPTOR, FACTION_NEUTRAL)
speak_emote = list("screeches")
ai_controller = /datum/ai_controller/basic_controller/raptor
@@ -85,11 +85,11 @@ GLOBAL_LIST_EMPTY(raptor_population)
BB_EMOTE_SAY = list("Chirp chirp chirp!", "Kweh!", "Bwark!"),
BB_EMOTE_SEE = list("shakes its feathers!", "stretches!", "flaps its wings!", "pecks at the ground!"),
BB_EMOTE_SOUND = list(
- 'sound/creatures/raptor_1.ogg',
- 'sound/creatures/raptor_2.ogg',
- 'sound/creatures/raptor_3.ogg',
- 'sound/creatures/raptor_4.ogg',
- 'sound/creatures/raptor_5.ogg',
+ 'sound/mobs/non-humanoids/raptor/raptor_1.ogg',
+ 'sound/mobs/non-humanoids/raptor/raptor_2.ogg',
+ 'sound/mobs/non-humanoids/raptor/raptor_3.ogg',
+ 'sound/mobs/non-humanoids/raptor/raptor_4.ogg',
+ 'sound/mobs/non-humanoids/raptor/raptor_5.ogg',
),
BB_SPEAK_CHANCE = 2,
)
diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_behavior.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_behavior.dm
index 33a655869072a..7e3022f95716d 100644
--- a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_behavior.dm
+++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_behavior.dm
@@ -1,4 +1,4 @@
-/datum/ai_behavior/hunt_target/unarmed_attack_target/heal_raptor
+/datum/ai_behavior/hunt_target/interact_with_target/heal_raptor
always_reset_target = TRUE
/datum/ai_behavior/find_hunt_target/injured_raptor
@@ -11,12 +11,11 @@
/datum/ai_behavior/find_hunt_target/raptor_victim/valid_dinner(mob/living/source, mob/living/target, radius)
if(target.ai_controller?.blackboard[BB_RAPTOR_TROUBLE_MAKER])
return FALSE
- return target.stat != DEAD && can_see(source, target, radius)
+ return target.stat != DEAD && can_see(source, target, radius)
-/datum/ai_behavior/hunt_target/unarmed_attack_target/bully_raptors
- always_reset_target = TRUE
+/datum/ai_behavior/hunt_target/interact_with_target/reset_target/bully_raptors
-/datum/ai_behavior/hunt_target/unarmed_attack_target/bully_raptors/finish_action(datum/ai_controller/controller, succeeded, hunting_target_key, hunting_cooldown_key)
+/datum/ai_behavior/hunt_target/interact_with_target/bully_raptors/finish_action(datum/ai_controller/controller, succeeded, hunting_target_key, hunting_cooldown_key)
if(succeeded)
controller.set_blackboard_key(BB_RAPTOR_TROUBLE_COOLDOWN, world.time + 2 MINUTES)
return ..()
@@ -24,31 +23,13 @@
/datum/ai_behavior/find_hunt_target/raptor_baby/valid_dinner(mob/living/source, mob/living/target, radius)
return can_see(source, target, radius) && target.stat != DEAD
-/datum/ai_behavior/hunt_target/care_for_young
- always_reset_target = TRUE
+/datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off/care_for_young
-/datum/ai_behavior/hunt_target/care_for_young/target_caught(mob/living/hunter, atom/hunted)
+/datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off/care_for_young/target_caught(mob/living/hunter, atom/hunted)
hunter.manual_emote("grooms [hunted]!")
- hunter.set_combat_mode(FALSE)
- hunter.ClickOn(hunted)
-
-/datum/ai_behavior/hunt_target/care_for_young/finish_action(datum/ai_controller/controller, succeeded, hunting_target_key, hunting_cooldown_key)
- var/mob/living/living_pawn = controller.pawn
- living_pawn.set_combat_mode(initial(living_pawn.combat_mode))
return ..()
/datum/ai_behavior/find_hunt_target/raptor_trough
/datum/ai_behavior/find_hunt_target/raptor_trough/valid_dinner(mob/living/source, atom/movable/trough, radius)
return !!(locate(/obj/item/stack/ore) in trough.contents)
-
-/datum/ai_behavior/hunt_target/unarmed_attack_target/raptor_trough
- always_reset_target = TRUE
-
-/datum/ai_behavior/hunt_target/unarmed_attack_target/raptor_trough/target_caught(mob/living/hunter, atom/hunted)
- hunter.set_combat_mode(FALSE)
-
-/datum/ai_behavior/hunt_target/unarmed_attack_target/raptor_trough/finish_action(datum/ai_controller/controller, succeeded, hunting_target_key, hunting_cooldown_key)
- var/mob/living/living_pawn = controller.pawn
- living_pawn.set_combat_mode(initial(living_pawn.combat_mode))
- return ..()
diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_controller.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_controller.dm
index a23bcf7801b2e..8178df7b78c24 100644
--- a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_controller.dm
+++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_controller.dm
@@ -8,8 +8,8 @@
"wags their tail against",
"playfully leans against"
),
- BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/raptor,
- BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic/raptor,
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ BB_PET_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/raptor),
BB_BABIES_CHILD_TYPES = list(/mob/living/basic/raptor/baby_raptor),
BB_MAX_CHILDREN = 5,
@@ -23,7 +23,7 @@
/datum/ai_planning_subtree/find_and_hunt_target/heal_raptors,
/datum/ai_planning_subtree/random_speech/blackboard,
/datum/ai_planning_subtree/pet_planning,
- /datum/ai_planning_subtree/target_retaliate,
+ /datum/ai_planning_subtree/target_retaliate/check_faction,
/datum/ai_planning_subtree/simple_find_target,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
/datum/ai_planning_subtree/find_and_hunt_target/raptor_trough,
@@ -41,19 +41,13 @@
RegisterSignal(new_pawn, COMSIG_MOB_ATE, PROC_REF(post_eat))
/datum/ai_controller/basic_controller/raptor/proc/post_eat()
+ SIGNAL_HANDLER
clear_blackboard_key(BB_RAPTOR_TROUGH_TARGET)
set_blackboard_key(BB_RAPTOR_EAT_COOLDOWN, world.time + NEXT_EAT_COOLDOWN)
-/datum/targeting_strategy/basic/raptor
-
-//dont attack anyone that shares our factions.
-/datum/targeting_strategy/basic/raptor/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target)
- . = ..()
- return .
-
/datum/ai_controller/basic_controller/baby_raptor
blackboard = list(
- BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic/raptor,
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
BB_FIND_MOM_TYPES = list(/mob/living/basic/raptor),
BB_IGNORE_MOM_TYPES = list(/mob/living/basic/raptor/baby_raptor),
)
diff --git a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_subtrees.dm b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_subtrees.dm
index 7ba0dad5561f6..a8d91963ebfb7 100644
--- a/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_subtrees.dm
+++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_ai_subtrees.dm
@@ -1,6 +1,6 @@
/datum/ai_planning_subtree/find_and_hunt_target/heal_raptors
target_key = BB_INJURED_RAPTOR
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/heal_raptor
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/heal_raptor
finding_behavior = /datum/ai_behavior/find_hunt_target/injured_raptor
hunt_targets = list(/mob/living/basic/raptor)
hunt_chance = 70
@@ -13,7 +13,7 @@
/datum/ai_planning_subtree/find_and_hunt_target/raptor_start_trouble
target_key = BB_RAPTOR_VICTIM
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/bully_raptors
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target/bully_raptors
finding_behavior = /datum/ai_behavior/find_hunt_target/raptor_victim
hunt_targets = list(/mob/living/basic/raptor)
hunt_chance = 30
@@ -36,7 +36,7 @@
/datum/ai_planning_subtree/find_and_hunt_target/care_for_young
target_key = BB_RAPTOR_BABY
- hunting_behavior = /datum/ai_behavior/hunt_target/care_for_young
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off/care_for_young
finding_behavior = /datum/ai_behavior/find_hunt_target/raptor_baby
hunt_targets = list(/mob/living/basic/raptor/baby_raptor)
hunt_chance = 75
@@ -49,7 +49,7 @@
/datum/ai_planning_subtree/find_and_hunt_target/raptor_trough
target_key = BB_RAPTOR_TROUGH_TARGET
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target_combat_mode_off
finding_behavior = /datum/ai_behavior/find_hunt_target/raptor_trough
hunt_targets = list(/obj/structure/ore_container/food_trough/raptor_trough)
hunt_chance = 80
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 ef7e6fa3167c6..cd1439b3c5ffc 100644
--- a/code/modules/mob/living/basic/lavaland/raptor/raptor_dex.dm
+++ b/code/modules/mob/living/basic/lavaland/raptor/raptor_dex.dm
@@ -32,7 +32,7 @@
var/happiness_percentage = my_raptor.ai_controller?.blackboard[BB_BASIC_HAPPINESS]
var/obj/effect/overlay/happiness_overlay/display = new
display.set_hearts(happiness_percentage)
- display.pixel_y = world.icon_size * 0.5
+ display.pixel_y = ICON_SIZE_Y * 0.5
data["raptor_happiness"] = icon2base64(getFlatIcon(display))
qdel(display)
@@ -56,7 +56,7 @@
return NONE
raptor = WEAKREF(attacked_atom)
- playsound(src, 'sound/items/orbie_send_out.ogg', 20)
+ playsound(src, 'sound/mobs/non-humanoids/orbie/orbie_send_out.ogg', 20)
balloon_alert(user, "scanned")
ui_interact(user)
return ITEM_INTERACT_SUCCESS
diff --git a/code/modules/mob/living/basic/lavaland/watcher/watcher.dm b/code/modules/mob/living/basic/lavaland/watcher/watcher.dm
index c3c30526dff74..c7648aa38b259 100644
--- a/code/modules/mob/living/basic/lavaland/watcher/watcher.dm
+++ b/code/modules/mob/living/basic/lavaland/watcher/watcher.dm
@@ -29,7 +29,7 @@
/// Icon state for our eye overlay
var/eye_glow = "ice_glow"
/// Sound to play when we shoot
- var/shoot_sound = 'sound/weapons/pierce.ogg'
+ var/shoot_sound = 'sound/items/weapons/pierce.ogg'
/// Typepath of our gaze ability
var/gaze_attack = /datum/action/cooldown/mob_cooldown/watcher_gaze
// We attract and eat these things for some reason
diff --git a/code/modules/mob/living/basic/lavaland/watcher/watcher_overwatch.dm b/code/modules/mob/living/basic/lavaland/watcher/watcher_overwatch.dm
index a61ea0743f9a4..f88624ce125be 100644
--- a/code/modules/mob/living/basic/lavaland/watcher/watcher_overwatch.dm
+++ b/code/modules/mob/living/basic/lavaland/watcher/watcher_overwatch.dm
@@ -17,7 +17,7 @@
/// Type of projectile to fire
var/projectile_type = /obj/projectile/temp/watcher
/// Sound the projectile we fire makes
- var/projectile_sound = 'sound/weapons/pierce.ogg'
+ var/projectile_sound = 'sound/items/weapons/pierce.ogg'
/// Time to watch for
var/overwatch_duration = 3 SECONDS
diff --git a/code/modules/mob/living/basic/minebots/minebot.dm b/code/modules/mob/living/basic/minebots/minebot.dm
index 54fe9a07367cf..bcf8071d7c289 100644
--- a/code/modules/mob/living/basic/minebots/minebot.dm
+++ b/code/modules/mob/living/basic/minebots/minebot.dm
@@ -18,7 +18,7 @@
obj_damage = 10
attack_verb_continuous = "drills"
attack_verb_simple = "drill"
- attack_sound = 'sound/weapons/circsawhit.ogg'
+ attack_sound = 'sound/items/weapons/circsawhit.ogg'
sentience_type = SENTIENCE_MINEBOT
speak_emote = list("states")
mob_biotypes = MOB_ROBOTIC
diff --git a/code/modules/mob/living/basic/minebots/minebot_abilities.dm b/code/modules/mob/living/basic/minebots/minebot_abilities.dm
index 8d1a93bd42916..a421c5d81f48c 100644
--- a/code/modules/mob/living/basic/minebots/minebot_abilities.dm
+++ b/code/modules/mob/living/basic/minebots/minebot_abilities.dm
@@ -119,7 +119,7 @@
return FALSE
var/obj/effect/mine/minebot/my_mine = new(my_turf)
my_mine.ignore_list = owner.faction.Copy()
- playsound(my_turf, 'sound/weapons/armbomb.ogg', 20)
+ playsound(my_turf, 'sound/items/weapons/armbomb.ogg', 20)
StartCooldown()
return TRUE
@@ -132,7 +132,7 @@
/obj/effect/temp_visual/rising_rocket/Initialize(mapload)
. = ..()
- playsound(src, 'sound/weapons/minebot_rocket.ogg', 100, FALSE)
+ playsound(src, 'sound/items/weapons/minebot_rocket.ogg', 100, FALSE)
animate(src, pixel_y = base_pixel_y + 500, time = duration, easing = EASE_IN)
/obj/effect/temp_visual/falling_rocket
@@ -154,7 +154,7 @@
animate(src, pixel_y = 0, time = duration)
/obj/effect/temp_visual/falling_rocket/proc/create_explosion()
- playsound(src, 'sound/weapons/minebot_rocket.ogg', 100, FALSE)
+ playsound(src, 'sound/items/weapons/minebot_rocket.ogg', 100, FALSE)
var/datum/effect_system/fluid_spread/smoke/smoke = new
smoke.set_up(1, holder = src)
smoke.start()
@@ -176,7 +176,7 @@
var/datum/effect_system/fluid_spread/smoke/smoke = new
smoke.set_up(0, holder = src)
smoke.start()
- playsound(src, 'sound/effects/explosion3.ogg', 100)
+ playsound(src, 'sound/effects/explosion/explosion3.ogg', 100)
victim.apply_damage(damage_to_apply)
/obj/effect/mine/minebot/can_trigger(atom/movable/on_who)
diff --git a/code/modules/mob/living/basic/minebots/minebot_ai.dm b/code/modules/mob/living/basic/minebots/minebot_ai.dm
index 31fed0ec1f32c..8043fda65d0a6 100644
--- a/code/modules/mob/living/basic/minebots/minebot_ai.dm
+++ b/code/modules/mob/living/basic/minebots/minebot_ai.dm
@@ -98,7 +98,7 @@
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
var/mob/living/living_pawn = controller.pawn
living_pawn.say("REPAIRING [target]!")
- living_pawn.UnarmedAttack(target)
+ controller.ai_interact(target = target)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
/datum/ai_behavior/repair_drone/finish_action(datum/ai_controller/controller, success, target_key)
@@ -251,7 +251,7 @@
///store ores in our body
/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/minebot
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/consume_ores/minebot
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/consume_ores/minebot
hunt_chance = 100
/datum/ai_planning_subtree/find_and_hunt_target/hunt_ores/minebot/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
@@ -263,10 +263,10 @@
return ..()
-/datum/ai_behavior/hunt_target/unarmed_attack_target/consume_ores/minebot
+/datum/ai_behavior/hunt_target/interact_with_target/consume_ores/minebot
hunt_cooldown = 2 SECONDS
-/datum/ai_behavior/hunt_target/unarmed_attack_target/consume_ores/minebot/target_caught(mob/living/hunter, obj/item/stack/ore/hunted)
+/datum/ai_behavior/hunt_target/interact_with_target/consume_ores/minebot/target_caught(mob/living/hunter, obj/item/stack/ore/hunted)
if(hunter.combat_mode)
hunter.set_combat_mode(FALSE)
return ..()
diff --git a/code/modules/mob/living/basic/minebots/minebot_remote_control.dm b/code/modules/mob/living/basic/minebots/minebot_remote_control.dm
index 3601940912389..742785769e90d 100644
--- a/code/modules/mob/living/basic/minebots/minebot_remote_control.dm
+++ b/code/modules/mob/living/basic/minebots/minebot_remote_control.dm
@@ -44,6 +44,8 @@
user.update_mouse_pointer()
/obj/item/minebot_remote_control/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(HAS_TRAIT(interacting_with, TRAIT_COMBAT_MODE_SKIP_INTERACTION))
+ return NONE
return ranged_interact_with_atom(interacting_with, user, modifiers)
/obj/item/minebot_remote_control/ranged_interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
@@ -54,7 +56,7 @@
if(isnull(target_turf) || isclosedturf(target_turf) || isgroundlessturf(target_turf))
user.balloon_alert(user, "invalid target!")
return ITEM_INTERACT_BLOCKING
- playsound(src, 'sound/machines/beep.ogg', 30)
+ playsound(src, 'sound/machines/beep/beep.ogg', 30)
clear_priming()
new /obj/effect/temp_visual/minebot_target(target_turf)
COOLDOWN_START(src, bomb_timer, BOMB_COOLDOWN)
diff --git a/code/modules/mob/living/basic/pets/cat/bread_cat_ai.dm b/code/modules/mob/living/basic/pets/cat/bread_cat_ai.dm
index 35a5d9e12afcf..655a6431fc2a7 100644
--- a/code/modules/mob/living/basic/pets/cat/bread_cat_ai.dm
+++ b/code/modules/mob/living/basic/pets/cat/bread_cat_ai.dm
@@ -9,7 +9,7 @@
/datum/ai_planning_subtree/find_and_hunt_target/turn_off_stove
target_key = BB_STOVE_TARGET
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/stove_target
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/stove_target
finding_behavior = /datum/ai_behavior/find_hunt_target/stove
hunt_targets = list(/obj/machinery/oven/range)
hunt_range = 9
@@ -25,10 +25,10 @@
return FALSE
return TRUE
-/datum/ai_behavior/hunt_target/unarmed_attack_target/stove_target
+/datum/ai_behavior/hunt_target/interact_with_target/stove_target
always_reset_target = TRUE
-/datum/ai_behavior/hunt_target/unarmed_attack_target/stove_target/target_caught(mob/living/hunter, obj/machinery/oven/range/stove)
+/datum/ai_behavior/hunt_target/interact_with_target/stove_target/target_caught(mob/living/hunter, obj/machinery/oven/range/stove)
if(stove.open)
return
return ..()
diff --git a/code/modules/mob/living/basic/pets/cat/cat.dm b/code/modules/mob/living/basic/pets/cat/cat.dm
index 2bcd715d7f5c8..e923d559a36ce 100644
--- a/code/modules/mob/living/basic/pets/cat/cat.dm
+++ b/code/modules/mob/living/basic/pets/cat/cat.dm
@@ -29,7 +29,7 @@
held_state = "cat2"
attack_verb_continuous = "claws"
attack_verb_simple = "claw"
- attack_sound = 'sound/weapons/slash.ogg'
+ attack_sound = 'sound/items/weapons/slash.ogg'
attack_vis_effect = ATTACK_EFFECT_CLAW
///icon of the collar we can wear
var/collar_icon_state = "cat"
@@ -52,12 +52,32 @@
///icon state of our cult icon
var/cult_icon_state = "cat_cult"
+/datum/emote/cat
+ mob_type_allowed_typecache = /mob/living/basic/pet/cat
+ mob_type_blacklist_typecache = list()
+
+/datum/emote/cat/meow
+ key = "meow"
+ key_third_person = "meows"
+ message = "meows!"
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
+ vary = TRUE
+ sound = SFX_CAT_MEOW
+
+/datum/emote/cat/purr
+ key = "purr"
+ key_third_person = "purrs"
+ message = "purrs."
+ emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE
+ vary = TRUE
+ sound = SFX_CAT_PURR
+
/mob/living/basic/pet/cat/Initialize(mapload)
. = ..()
AddElement(/datum/element/cultist_pet, pet_cult_icon_state = cult_icon_state)
AddElement(/datum/element/wears_collar, collar_icon_state = collar_icon_state, collar_resting_icon_state = TRUE)
AddElement(/datum/element/ai_retaliate)
- AddElement(/datum/element/pet_bonus, "purrs!")
+ AddElement(/datum/element/pet_bonus, null, /datum/mood_event/pet_animal, "purr")
AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_CLAW)
add_cell_sample()
add_verb(src, /mob/living/proc/toggle_resting)
diff --git a/code/modules/mob/living/basic/pets/cat/cat_ai.dm b/code/modules/mob/living/basic/pets/cat/cat_ai.dm
index d6589d29b407e..8dde10495681d 100644
--- a/code/modules/mob/living/basic/pets/cat/cat_ai.dm
+++ b/code/modules/mob/living/basic/pets/cat/cat_ai.dm
@@ -63,7 +63,7 @@
var/obj/structure/cat_house/home = controller.blackboard[target_key]
var/mob/living/basic/living_pawn = controller.pawn
if(living_pawn == home.resident_cat || isnull(home.resident_cat))
- living_pawn.melee_attack(home)
+ controller.ai_interact(target = home)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
@@ -213,13 +213,13 @@
/datum/ai_planning_subtree/find_and_hunt_target/find_cat_food
target_key = BB_CAT_FOOD_TARGET
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/find_cat_food
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/find_cat_food
finding_behavior = /datum/ai_behavior/find_hunt_target/find_cat_food
hunt_targets = list(/obj/item/fish, /obj/item/food/deadmouse, /obj/item/food/fishmeat)
hunt_chance = 75
hunt_range = 9
-/datum/ai_behavior/hunt_target/unarmed_attack_target/find_cat_food
+/datum/ai_behavior/hunt_target/interact_with_target/find_cat_food
always_reset_target = TRUE
/datum/ai_behavior/find_hunt_target/find_cat_food/valid_dinner(mob/living/source, atom/dinner, radius)
@@ -253,7 +253,6 @@
return kitten
return null
-/datum/ai_behavior/deliver_food_to_kitten
/datum/ai_behavior/deliver_food_to_kitten
behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION | AI_BEHAVIOR_REQUIRE_REACH
action_cooldown = 5 SECONDS
diff --git a/code/modules/mob/living/basic/pets/dog/_dog.dm b/code/modules/mob/living/basic/pets/dog/_dog.dm
index b1c2c5e486fc1..1ff991941d327 100644
--- a/code/modules/mob/living/basic/pets/dog/_dog.dm
+++ b/code/modules/mob/living/basic/pets/dog/_dog.dm
@@ -31,7 +31,7 @@
// The dog attack pet command can raise melee attack above 0
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
melee_attack_cooldown = 0.8 SECONDS
/// Instructions you can give to dogs
diff --git a/code/modules/mob/living/basic/pets/dog/corgi.dm b/code/modules/mob/living/basic/pets/dog/corgi.dm
index e1f05b33d81fb..ac213fbf30960 100644
--- a/code/modules/mob/living/basic/pets/dog/corgi.dm
+++ b/code/modules/mob/living/basic/pets/dog/corgi.dm
@@ -116,7 +116,7 @@
user.visible_message(span_notice("[user] starts to shave [src] using \the [attacking_item]."), span_notice("You start to shave [src] using \the [attacking_item]..."))
if(do_after(user, 5 SECONDS, target = src))
user.visible_message(span_notice("[user] shaves [src]'s hair using \the [attacking_item]."))
- playsound(get_turf(src), 'sound/items/welder2.ogg', 20, TRUE)
+ playsound(get_turf(src), 'sound/items/tools/welder2.ogg', 20, TRUE)
shaved = TRUE
icon_living = "[icon_living]_shaved"
icon_dead = "[icon_living]_shaved_dead"
@@ -399,7 +399,7 @@
place_on_head(new /obj/item/clothing/glasses/eyepatch/medical)
/mob/living/basic/pet/dog/corgi/ian/narsie_act()
- playsound(src, 'sound/magic/demon_dies.ogg', 75, TRUE)
+ playsound(src, 'sound/effects/magic/demon_dies.ogg', 75, TRUE)
var/mob/living/basic/pet/dog/corgi/narsie/narsIan = new(loc)
narsIan.setDir(dir)
investigate_log("has been gibbed and replaced with Nars-Ian by Nar'Sie.", INVESTIGATE_DEATHS)
@@ -486,7 +486,7 @@
return
visible_message(span_warning("Dark magic resonating from [src] devours [prey]!"), \
"DELICIOUS SOULS")
- playsound(src, 'sound/magic/demon_attack1.ogg', 75, TRUE)
+ playsound(src, 'sound/effects/magic/demon_attack1.ogg', 75, TRUE)
new /obj/effect/temp_visual/cult/sac(get_turf(prey))
narsie_act()
prey.investigate_log("has been sacrificed by [src].", INVESTIGATE_DEATHS)
diff --git a/code/modules/mob/living/basic/pets/fox.dm b/code/modules/mob/living/basic/pets/fox.dm
index e4e978568a0e6..45ea29024ad66 100644
--- a/code/modules/mob/living/basic/pets/fox.dm
+++ b/code/modules/mob/living/basic/pets/fox.dm
@@ -25,7 +25,7 @@
melee_damage_upper = 5
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
ai_controller = /datum/ai_controller/basic_controller/fox
diff --git a/code/modules/mob/living/basic/pets/orbie/orbie.dm b/code/modules/mob/living/basic/pets/orbie/orbie.dm
index c0c6dd7b023fc..a0fbba899e354 100644
--- a/code/modules/mob/living/basic/pets/orbie/orbie.dm
+++ b/code/modules/mob/living/basic/pets/orbie/orbie.dm
@@ -51,6 +51,7 @@
AddComponent(/datum/component/obeys_commands, pet_commands)
AddElement(/datum/element/basic_eating, food_types = food_types)
ADD_TRAIT(src, TRAIT_SILICON_EMOTES_ALLOWED, INNATE_TRAIT)
+
RegisterSignal(src, COMSIG_ATOM_CAN_BE_PULLED, PROC_REF(on_pulled))
RegisterSignal(src, COMSIG_VIRTUAL_PET_LEVEL_UP, PROC_REF(on_level_up))
RegisterSignal(src, COMSIG_MOB_CLICKON, PROC_REF(on_click))
diff --git a/code/modules/mob/living/basic/pets/parrot/_parrot.dm b/code/modules/mob/living/basic/pets/parrot/_parrot.dm
index c23481d85a7ec..e61b052217909 100644
--- a/code/modules/mob/living/basic/pets/parrot/_parrot.dm
+++ b/code/modules/mob/living/basic/pets/parrot/_parrot.dm
@@ -152,11 +152,6 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
. = ..()
. += "Held Item: [held_item]"
-/mob/living/basic/parrot/Process_Spacemove(movement_dir = 0, continuous_move = FALSE)
- if(stat != DEAD) // parrots have evolved to let them fly in space because fucking uhhhhhhhhhh
- return TRUE
- return ..()
-
/mob/living/basic/parrot/radio(message, list/message_mods = list(), list/spans, language) //literally copied from human/radio(), but there's no other way to do this. at least it's better than it used to be.
. = ..()
if(. != NONE)
diff --git a/code/modules/mob/living/basic/pets/parrot/poly.dm b/code/modules/mob/living/basic/pets/parrot/poly.dm
index cba3dd6e588e3..f825788decd98 100644
--- a/code/modules/mob/living/basic/pets/parrot/poly.dm
+++ b/code/modules/mob/living/basic/pets/parrot/poly.dm
@@ -191,9 +191,9 @@
name = "The Ghost of Poly"
desc = "Doomed to squawk the Earth."
color = "#FFFFFF77"
- status_flags = GODMODE
sentience_type = SENTIENCE_BOSS //This is so players can't mindswap into ghost poly to become a literal god
incorporeal_move = INCORPOREAL_MOVE_BASIC
+ status_flags = NONE
butcher_results = list(/obj/item/ectoplasm = 1)
ai_controller = /datum/ai_controller/basic_controller/parrot/ghost
speech_probability_rate = 1
@@ -202,7 +202,7 @@
/mob/living/basic/parrot/poly/ghost/Initialize(mapload)
// block anything and everything that could possibly happen with writing memory for ghosts
memory_saved = TRUE
- ADD_TRAIT(src, TRAIT_DONT_WRITE_MEMORY, INNATE_TRAIT)
+ add_traits(list(TRAIT_GODMODE, TRAIT_DONT_WRITE_MEMORY), INNATE_TRAIT)
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
return ..()
diff --git a/code/modules/mob/living/basic/pets/pet_cult/pet_cult_abilities.dm b/code/modules/mob/living/basic/pets/pet_cult/pet_cult_abilities.dm
index 83d70336f2c0c..79631c96dca4f 100644
--- a/code/modules/mob/living/basic/pets/pet_cult/pet_cult_abilities.dm
+++ b/code/modules/mob/living/basic/pets/pet_cult/pet_cult_abilities.dm
@@ -11,4 +11,4 @@
)
summon_radius = 0
create_summon_timer = 5 SECONDS
- sound = 'sound/magic/exit_blood.ogg'
+ sound = 'sound/effects/magic/exit_blood.ogg'
diff --git a/code/modules/mob/living/basic/pets/pet_cult/pet_cult_ai.dm b/code/modules/mob/living/basic/pets/pet_cult/pet_cult_ai.dm
index fb10680991895..77263b1748963 100644
--- a/code/modules/mob/living/basic/pets/pet_cult/pet_cult_ai.dm
+++ b/code/modules/mob/living/basic/pets/pet_cult/pet_cult_ai.dm
@@ -111,9 +111,7 @@
if(isnull(revive_mob) || revive_mob.stat != DEAD || !(revive_mob.mind in cult_team.members))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
- var/mob/living/basic/living_pawn = controller.pawn
- living_pawn.melee_attack(target)
-
+ controller.ai_interact(target = target)
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
/datum/ai_behavior/activate_rune/finish_action(datum/ai_controller/controller, success, target_key)
diff --git a/code/modules/mob/living/basic/pets/sloth.dm b/code/modules/mob/living/basic/pets/sloth.dm
index 125a3a7b97d6a..9cd7711aa0672 100644
--- a/code/modules/mob/living/basic/pets/sloth.dm
+++ b/code/modules/mob/living/basic/pets/sloth.dm
@@ -22,7 +22,7 @@ GLOBAL_DATUM(cargo_sloth, /mob/living/basic/sloth)
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
mob_biotypes = MOB_ORGANIC|MOB_BEAST
diff --git a/code/modules/mob/living/basic/revolutionary.dm b/code/modules/mob/living/basic/revolutionary.dm
new file mode 100644
index 0000000000000..8aa41b3c7230d
--- /dev/null
+++ b/code/modules/mob/living/basic/revolutionary.dm
@@ -0,0 +1,146 @@
+/mob/living/basic/revolutionary
+ name = "Revolutionary"
+ desc = "They stand for a cause..."
+ mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
+ faction = list(FACTION_HOSTILE)
+ icon = 'icons/mob/simple/simple_human.dmi'
+ gender = MALE
+ basic_mob_flags = DEL_ON_DEATH
+ attack_verb_continuous = "robusts"
+ attack_verb_simple = "robust"
+ maxHealth = 50
+ health = 50
+ melee_damage_lower = 15
+ melee_damage_upper = 20
+ obj_damage = 20
+ attack_sound = 'sound/items/weapons/smash.ogg'
+ ai_controller = /datum/ai_controller/basic_controller/revolutionary
+ /// list of weapons we can have
+ var/static/list/possible_weapons = list(
+ /obj/item/storage/toolbox/mechanical = "robust",
+ /obj/item/spear = "pierce",
+ /obj/item/fireaxe = "slice",
+ /obj/item/melee/baseball_bat = "bat",
+ /obj/item/melee/baton = "discipline",
+ )
+ /// List of things to shout
+ var/static/list/phrases = list(
+ "The revolution will not be televized!",
+ "VIVA!",
+ "Dirty pig!",
+ "Gondola meat is murder!",
+ "Free Cargonia!",
+ "Mime rights are human rights!",
+ "猫娘 Free Terry!",
+ )
+ /// List of causes to #support
+ var/static/list/causes = list(
+ "Worker's rights",
+ "Icemoon climate change",
+ "Fair clown treatment",
+ "Lizards",
+ "Moths",
+ "Stop Lavaland drilling",
+ "The Captain has been replaced by a robot",
+ "Free Cargonia",
+ "Befriend all space dragons",
+ "The Grey Tide",
+ "Rising cost of medbay",
+ )
+ /// Monkey screeches
+ var/static/list/monkey_screeches = list(
+ 'sound/mobs/non-humanoids/monkey/monkey_screech_1.ogg',
+ 'sound/mobs/non-humanoids/monkey/monkey_screech_2.ogg',
+ 'sound/mobs/non-humanoids/monkey/monkey_screech_3.ogg',
+ 'sound/mobs/non-humanoids/monkey/monkey_screech_4.ogg',
+ )
+ /// Male screams
+ var/static/list/male_screams = list(
+ 'sound/mobs/humanoids/human/scream/malescream_1.ogg',
+ 'sound/mobs/humanoids/human/scream/malescream_2.ogg',
+ 'sound/mobs/humanoids/human/scream/malescream_3.ogg',
+ 'sound/mobs/humanoids/human/scream/malescream_4.ogg',
+ 'sound/mobs/humanoids/human/scream/malescream_5.ogg',
+ )
+ /// Female screams
+ var/static/list/female_screams = list(
+ 'sound/mobs/humanoids/human/scream/femalescream_1.ogg',
+ 'sound/mobs/humanoids/human/scream/femalescream_2.ogg',
+ 'sound/mobs/humanoids/human/scream/femalescream_3.ogg',
+ 'sound/mobs/humanoids/human/scream/femalescream_4.ogg',
+ 'sound/mobs/humanoids/human/scream/femalescream_5.ogg',
+ )
+
+
+/mob/living/basic/revolutionary/Initialize(mapload)
+ . = ..()
+ shuffle_inplace(phrases)
+ var/static/list/display_emote = list(
+ BB_EMOTE_SAY = phrases,
+ BB_EMOTE_SOUND = monkey_screeches,
+ BB_SPEAK_CHANCE = 5,
+ )
+ ai_controller.set_blackboard_key(BB_BASIC_MOB_SPEAK_LINES, display_emote)
+ var/obj/item/weapon_of_choice = pick(possible_weapons)
+ attack_sound = weapon_of_choice::hitsound
+ attack_verb_simple = possible_weapons[weapon_of_choice]
+ attack_verb_continuous = "[attack_verb_simple]s"
+
+ var/static/list/death_loot = list(/obj/effect/mob_spawn/corpse/human/revolutionary)
+ AddElement(/datum/element/death_drops, death_loot)
+ apply_dynamic_human_appearance(src, mob_spawn_path = /obj/effect/mob_spawn/corpse/human/revolutionary, l_hand = weapon_of_choice)
+
+ gender = pick(MALE, FEMALE, PLURAL)
+ var/first_name
+ switch(gender)
+ if(MALE)
+ first_name = pick(GLOB.first_names_male)
+ death_sound = pick(male_screams + monkey_screeches)
+ if(FEMALE)
+ first_name = pick(GLOB.first_names_female)
+ death_sound = pick(female_screams + monkey_screeches)
+ if(PLURAL)
+ first_name = pick(GLOB.first_names)
+ death_sound = pick(male_screams + female_screams + monkey_screeches)
+
+ fully_replace_character_name(name, "[first_name] [pick(GLOB.last_names)]")
+ desc += span_infoplain("\nToday, that cause is: ")
+ shuffle_inplace(causes)
+ desc += span_notice("#[pick(causes)].")
+
+
+/obj/effect/mob_spawn/corpse/human/revolutionary
+ name = "Revolutionary"
+ outfit = /datum/outfit/revolution
+
+
+/datum/outfit/revolution
+ name = "Revolution"
+ uniform = /obj/item/clothing/under/color/grey
+ head = /obj/item/clothing/head/costume/ushanka
+ mask = /obj/item/clothing/mask/gas
+ gloves = /obj/item/clothing/gloves/color/black
+ shoes = /obj/item/clothing/shoes/jackboots
+
+
+/datum/ai_controller/basic_controller/revolutionary
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ )
+ ai_movement = /datum/ai_movement/basic_avoidance
+ idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/random_speech/blackboard/revolutionary,
+ /datum/ai_planning_subtree/simple_find_target,
+ /datum/ai_planning_subtree/basic_melee_attack_subtree,
+ )
+
+
+/datum/ai_planning_subtree/random_speech/blackboard/revolutionary
+
+
+/datum/ai_planning_subtree/random_speech/blackboard/revolutionary/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick)
+ if(!controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET))
+ return
+
+ return ..()
diff --git a/code/modules/mob/living/basic/ruin_defender/cybersun_aicore.dm b/code/modules/mob/living/basic/ruin_defender/cybersun_aicore.dm
index e9b62f9deaf2e..6287c1f25e19e 100644
--- a/code/modules/mob/living/basic/ruin_defender/cybersun_aicore.dm
+++ b/code/modules/mob/living/basic/ruin_defender/cybersun_aicore.dm
@@ -31,8 +31,8 @@
var/datum/action/cooldown/mob_cooldown/targeted_mob_ability/donk_laser
//is this being used as part of the haunted trading post ruin? if true, stuff there will self destruct when this mob dies
var/donk_ai_master = FALSE
-// list of stuff tagged to self destruct when this mob dies
-GLOBAL_LIST_EMPTY(selfdestructs_when_boss_dies)
+ /// the queue id for the stuff that selfdestructs when we die
+ var/selfdestruct_queue_id = "hauntedtradingpost_sd"
/mob/living/basic/cybersun_ai_core/Initialize(mapload)
. = ..()
@@ -45,21 +45,20 @@ GLOBAL_LIST_EMPTY(selfdestructs_when_boss_dies)
BARRAGE_ABILITY_TYPEPATH = BB_CYBERSUN_CORE_BARRAGE,
)
grant_actions_by_list(innate_actions)
+ if(mapload && donk_ai_master)
+ return INITIALIZE_HINT_LATELOAD
+
+/mob/living/basic/cybersun_ai_core/LateInitialize()
+ SSqueuelinks.add_to_queue(src, selfdestruct_queue_id)
+ SSqueuelinks.pop_link(selfdestruct_queue_id)
+
+/mob/living/basic/cybersun_ai_core/MatchedLinks(id, list/partners) // id == queue id (for multiple queue objects)
+ if(id != selfdestruct_queue_id)
+ return
+ for(var/datum/partner as anything in partners) // all our partners in the selfdestruct queue are now registered to qdel if I die
+ partner.RegisterSignal(src, COMSIG_QDELETING, TYPE_PROC_REF(/datum, selfdelete))
/mob/living/basic/cybersun_ai_core/death(gibbed)
- if(donk_ai_master == TRUE)
- //disable all the tripwire traps
- for (var/obj/item/pressure_plate/puzzle/invisible_tripwire as anything in GLOB.selfdestructs_when_boss_dies)
- qdel(invisible_tripwire)
- //and the electric overload traps
- for (var/obj/effect/overloader_trap as anything in GLOB.selfdestructs_when_boss_dies)
- qdel(overloader_trap)
- //then disable the AI defence holograms
- for (var/obj/structure/holosign/barrier/cyborg/cybersun_ai_shield as anything in GLOB.selfdestructs_when_boss_dies)
- qdel(cybersun_ai_shield)
- //then the power generator
- for (var/obj/machinery/power/smes/magical/cybersun as anything in GLOB.selfdestructs_when_boss_dies)
- qdel(cybersun)
do_sparks(number = 5, source = src)
return ..()
@@ -78,7 +77,7 @@ GLOBAL_LIST_EMPTY(selfdestructs_when_boss_dies)
///dramatic death animations
var/turf/my_turf = get_turf(src)
new /obj/effect/gibspawner/robot(my_turf)
- playsound(loc, 'sound/effects/explosion2.ogg', vol = 75, vary = TRUE, pressure_affected = FALSE)
+ playsound(loc, 'sound/effects/explosion/explosion2.ogg', vol = 75, vary = TRUE, pressure_affected = FALSE)
for (var/mob/witness in range(10, src))
if (!witness.client || !isliving(witness))
continue
@@ -120,7 +119,7 @@ GLOBAL_LIST_EMPTY(selfdestructs_when_boss_dies)
. = ..()
//this is where the spell will hit. it will not move even if the target does, allowing the spell to be dodged.
new/obj/effect/temp_visual/lightning_strike(get_turf(target))
- playsound(owner, 'sound/effects/sparks1.ogg', vol = 120, vary = TRUE)
+ playsound(owner, 'sound/effects/sparks/sparks1.ogg', vol = 120, vary = TRUE)
/obj/effect/temp_visual/lightning_strike
name = "lightning strike"
@@ -143,7 +142,7 @@ GLOBAL_LIST_EMPTY(selfdestructs_when_boss_dies)
/obj/effect/temp_visual/lightning_strike/proc/zap()
new/obj/effect/temp_visual/lightning_strike_zap(loc)
- playsound(src, 'sound/magic/lightningbolt.ogg', vol = 70, vary = TRUE)
+ playsound(src, 'sound/effects/magic/lightningbolt.ogg', vol = 70, vary = TRUE)
if (!isturf(loc))
return
for(var/mob/living/victim in loc)
@@ -189,7 +188,7 @@ GLOBAL_LIST_EMPTY(selfdestructs_when_boss_dies)
if(lockon_zone == my_turf)
return
my_turf.Beam(lockon_zone, icon_state = "1-full", beam_color = COLOR_MEDIUM_DARK_RED, time = barrage_delay)
- playsound(lockon_zone, 'sound/machines/terminal_prompt_deny.ogg', vol = 60, vary = TRUE)
+ playsound(lockon_zone, 'sound/machines/terminal/terminal_prompt_deny.ogg', vol = 60, vary = TRUE)
StartCooldown(cooldown_time)
return ..()
diff --git a/code/modules/mob/living/basic/ruin_defender/flesh.dm b/code/modules/mob/living/basic/ruin_defender/flesh.dm
index 359705ecff2bd..c5ff2fb90e740 100644
--- a/code/modules/mob/living/basic/ruin_defender/flesh.dm
+++ b/code/modules/mob/living/basic/ruin_defender/flesh.dm
@@ -25,7 +25,7 @@
melee_damage_upper = 10
health = 20
maxHealth = 20
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
attack_verb_continuous = "tries desperately to attach to"
attack_verb_simple = "try to attach to"
@@ -83,7 +83,7 @@
victim.visible_message(span_warning("[victim]'s [current_bodypart.name] instinctively starts feeling [candidate]!"))
if (!victim.anchored && !prob(victim.combat_mode ? LIVING_FLESH_COMBAT_TOUCH_CHANCE : LIVING_FLESH_TOUCH_CHANCE))
- victim.start_pulling(candidate, supress_message = TRUE)
+ INVOKE_ASYNC(victim, TYPE_PROC_REF(/atom/movable, start_pulling), candidate, supress_message = TRUE)
return
var/active_hand = victim.active_hand_index
diff --git a/code/modules/mob/living/basic/ruin_defender/living_floor.dm b/code/modules/mob/living/basic/ruin_defender/living_floor.dm
index 105838f0c55dd..7a9b6ae59f18a 100644
--- a/code/modules/mob/living/basic/ruin_defender/living_floor.dm
+++ b/code/modules/mob/living/basic/ruin_defender/living_floor.dm
@@ -26,7 +26,7 @@
icon_living = "floor"
mob_size = MOB_SIZE_HUGE
mob_biotypes = MOB_SPECIAL
- status_flags = GODMODE //nothing but crowbars may kill us
+ status_flags = NONE
death_message = ""
unsuitable_atmos_damage = 0
minimum_survivable_temperature = 0
@@ -40,7 +40,7 @@
faction = list(FACTION_HOSTILE)
melee_damage_lower = 20
melee_damage_upper = 40 //pranked.....
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
@@ -52,7 +52,7 @@
/mob/living/basic/living_floor/Initialize(mapload)
. = ..()
- ADD_TRAIT(src, TRAIT_IMMOBILIZED, INNATE_TRAIT)
+ add_traits(list(TRAIT_GODMODE, TRAIT_IMMOBILIZED), INNATE_TRAIT) //nothing but crowbars may kill us
var/static/list/connections = list(COMSIG_ATOM_ENTERED = PROC_REF(look_aggro), COMSIG_ATOM_EXITED = PROC_REF(look_deaggro))
AddComponent(/datum/component/connect_range, tracked = src, connections = connections, range = 1, works_in_containers = FALSE)
@@ -84,7 +84,7 @@
if(weapon.tool_behaviour != TOOL_CROWBAR)
return ..()
balloon_alert(user, "prying...")
- playsound(src, 'sound/items/crowbar.ogg', 45, TRUE)
+ playsound(src, 'sound/items/tools/crowbar.ogg', 45, TRUE)
if(!do_after(user, 5 SECONDS, src))
return
new /obj/effect/gibspawner/generic(loc)
diff --git a/code/modules/mob/living/basic/ruin_defender/skeleton.dm b/code/modules/mob/living/basic/ruin_defender/skeleton.dm
index fbe3b74c43f27..e6754a80a22da 100644
--- a/code/modules/mob/living/basic/ruin_defender/skeleton.dm
+++ b/code/modules/mob/living/basic/ruin_defender/skeleton.dm
@@ -15,7 +15,7 @@
unsuitable_heat_damage = 0
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
- attack_sound = 'sound/weapons/slash.ogg'
+ attack_sound = 'sound/items/weapons/slash.ogg'
attack_vis_effect = ATTACK_EFFECT_CLAW
faction = list(FACTION_SKELETON)
// Going for a sort of pale bluegreen here, shooting for boneish
@@ -63,7 +63,7 @@
melee_damage_upper = 20
attack_verb_continuous = "jabs"
attack_verb_simple = "jab"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
death_message = "collapses into a pile of bones, its gear falling to the floor!"
loot = list(
@@ -92,7 +92,7 @@
melee_damage_upper = 30
attack_verb_continuous = "slices"
attack_verb_simple = "slice"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
death_message = "collapses into a pile of bones, its gear clanging as it hits the ground!"
loot = list(
@@ -146,7 +146,7 @@
melee_damage_upper = 25
attack_verb_continuous = "blasts"
attack_verb_simple = "blast"
- attack_sound = 'sound/weapons/sonic_jackhammer.ogg'
+ attack_sound = 'sound/items/weapons/sonic_jackhammer.ogg'
attack_vis_effect = null
loot = list(/obj/effect/decal/remains/plasma, /obj/item/pickaxe/drill/jackhammer)
held_item = /obj/item/pickaxe/drill/jackhammer
diff --git a/code/modules/mob/living/basic/ruin_defender/stickman.dm b/code/modules/mob/living/basic/ruin_defender/stickman.dm
index 588a75c634643..3435f873ea35f 100644
--- a/code/modules/mob/living/basic/ruin_defender/stickman.dm
+++ b/code/modules/mob/living/basic/ruin_defender/stickman.dm
@@ -14,7 +14,7 @@
melee_damage_lower = 10
melee_damage_upper = 10
melee_attack_cooldown = 1.5 SECONDS
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
combat_mode = TRUE
faction = list(FACTION_STICKMAN)
unsuitable_atmos_damage = 7.5
@@ -54,7 +54,7 @@
attack_vis_effect = ATTACK_EFFECT_BITE
sharpness = SHARP_POINTY
mob_biotypes = MOB_BEAST
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
/mob/living/basic/stickman/ranged
name = "Angry Stick Gunman"
@@ -65,7 +65,7 @@
attack_verb_simple = "whack"
melee_damage_lower = 5
melee_damage_upper = 5
- attack_sound = 'sound/weapons/genhit1.ogg'
+ attack_sound = 'sound/items/weapons/genhit1.ogg'
ai_controller = /datum/ai_controller/basic_controller/stickman/ranged
diff --git a/code/modules/mob/living/basic/ruin_defender/wizard/wizard.dm b/code/modules/mob/living/basic/ruin_defender/wizard/wizard.dm
index 7c35184af3717..48dca2eea60f0 100644
--- a/code/modules/mob/living/basic/ruin_defender/wizard/wizard.dm
+++ b/code/modules/mob/living/basic/ruin_defender/wizard/wizard.dm
@@ -14,7 +14,7 @@
melee_damage_upper = 5
attack_verb_continuous = "punches"
attack_verb_simple = "punch"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
combat_mode = TRUE
habitable_atmos = list("min_oxy" = 5, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
unsuitable_atmos_damage = 7.5
diff --git a/code/modules/mob/living/basic/slime/ai/behaviours.dm b/code/modules/mob/living/basic/slime/ai/behaviours.dm
index 35fe1a60c91a1..fe8102eee112f 100644
--- a/code/modules/mob/living/basic/slime/ai/behaviours.dm
+++ b/code/modules/mob/living/basic/slime/ai/behaviours.dm
@@ -54,9 +54,9 @@
//We are not THAT hungry
return FALSE
-/datum/ai_behavior/hunt_target/unarmed_attack_target/slime
+/datum/ai_behavior/hunt_target/interact_with_target/slime
-/datum/ai_behavior/hunt_target/unarmed_attack_target/slime/target_caught(mob/living/basic/slime/hunter, mob/living/hunted)
+/datum/ai_behavior/hunt_target/interact_with_target/slime/target_caught(mob/living/basic/slime/hunter, mob/living/hunted)
if (!hunter.can_feed_on(hunted)) // Target is no longer edible
hunter.UnarmedAttack(hunted, TRUE)
return
@@ -71,7 +71,7 @@
hunter.start_feeding(hunted)
-/datum/ai_behavior/hunt_target/unarmed_attack_target/slime/finish_action(datum/ai_controller/controller, succeeded, hunting_target_key, hunting_cooldown_key)
+/datum/ai_behavior/hunt_target/interact_with_target/slime/finish_action(datum/ai_controller/controller, succeeded, hunting_target_key, hunting_cooldown_key)
. = ..()
var/mob/living/basic/slime/slime_pawn = controller.pawn
var/atom/target = controller.blackboard[hunting_target_key]
diff --git a/code/modules/mob/living/basic/slime/ai/pet_command.dm b/code/modules/mob/living/basic/slime/ai/pet_command.dm
index 42d6b28993728..33484e360fbed 100644
--- a/code/modules/mob/living/basic/slime/ai/pet_command.dm
+++ b/code/modules/mob/living/basic/slime/ai/pet_command.dm
@@ -4,7 +4,7 @@
pointed_reaction = "and blorbles"
refuse_reaction = "jiggles sadly"
- var/hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/slime
+ var/hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/slime
/datum/pet_command/point_targeting/attack/slime/execute_action(datum/ai_controller/controller)
diff --git a/code/modules/mob/living/basic/slime/ai/subtrees.dm b/code/modules/mob/living/basic/slime/ai/subtrees.dm
index 056befece5d4b..08886f98365a6 100644
--- a/code/modules/mob/living/basic/slime/ai/subtrees.dm
+++ b/code/modules/mob/living/basic/slime/ai/subtrees.dm
@@ -24,7 +24,7 @@
// Slime subtree for hunting down people to drain
/datum/ai_planning_subtree/find_and_hunt_target/find_slime_food
finding_behavior = /datum/ai_behavior/find_hunt_target/find_slime_food
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/slime
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/slime
hunt_targets = list(/mob/living)
hunt_range = 7
diff --git a/code/modules/mob/living/basic/slime/defense.dm b/code/modules/mob/living/basic/slime/defense.dm
index a3242525170c7..19507b4cd029d 100644
--- a/code/modules/mob/living/basic/slime/defense.dm
+++ b/code/modules/mob/living/basic/slime/defense.dm
@@ -15,11 +15,11 @@
if(buckled == attacker ? prob(60) : prob(30)) //its easier to remove the slime from yourself
attacker.visible_message(span_warning("[attacker] attempts to wrestle \the [defender_slime.name] off [buckled == attacker ? "" : buckled] !"), \
span_danger("[buckled == attacker ? "You attempt" : "[attacker] attempts" ] to wrestle \the [defender_slime.name] off [buckled == attacker ? "" : buckled]!"))
- playsound(loc, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
+ playsound(loc, 'sound/items/weapons/punchmiss.ogg', 25, TRUE, -1)
return
attacker.visible_message(span_warning("[attacker] manages to wrestle \the [defender_slime.name] off!"), span_notice("You manage to wrestle \the [defender_slime.name] off!"))
- playsound(loc, 'sound/weapons/shove.ogg', 50, TRUE, -1)
+ playsound(loc, 'sound/items/weapons/shove.ogg', 50, TRUE, -1)
defender_slime.discipline_slime()
@@ -87,7 +87,7 @@
has_found = TRUE
if(applied_crossbreed_amount >= SLIME_EXTRACT_CROSSING_REQUIRED)
to_chat(user, span_notice("You feed the slime as many of the extracts from the bag as you can, and it mutates!"))
- playsound(src, 'sound/effects/attackblob.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/blob/attackblob.ogg', 50, TRUE)
spawn_corecross()
has_output = TRUE
break
@@ -99,7 +99,7 @@
to_chat(user, span_warning("There are no extracts in the bag that this slime will accept!"))
else
to_chat(user, span_notice("You feed the slime some extracts from the bag."))
- playsound(src, 'sound/effects/attackblob.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/blob/attackblob.ogg', 50, TRUE)
///Handles the adverse effects of water on slimes
/mob/living/basic/slime/proc/apply_water()
diff --git a/code/modules/mob/living/basic/slime/slime.dm b/code/modules/mob/living/basic/slime/slime.dm
index 6adf4e35f3582..010913f44258b 100644
--- a/code/modules/mob/living/basic/slime/slime.dm
+++ b/code/modules/mob/living/basic/slime/slime.dm
@@ -14,7 +14,7 @@
icon_living = "grey-baby"
icon_dead = "grey-baby-dead"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
//Base physiology
@@ -353,7 +353,7 @@
/mob/living/basic/slime/proc/spawn_corecross()
var/static/list/crossbreeds = subtypesof(/obj/item/slimecross)
visible_message(span_danger("[src] shudders, its mutated core consuming the rest of its body!"))
- playsound(src, 'sound/magic/smoke.ogg', 50, TRUE)
+ playsound(src, 'sound/effects/magic/smoke.ogg', 50, TRUE)
var/selected_crossbreed_path
for(var/crossbreed_path in crossbreeds)
var/obj/item/slimecross/cross_item = crossbreed_path
diff --git a/code/modules/mob/living/basic/space_fauna/ant.dm b/code/modules/mob/living/basic/space_fauna/ant.dm
index 26a904340b7c5..7fbbe03e631cd 100644
--- a/code/modules/mob/living/basic/space_fauna/ant.dm
+++ b/code/modules/mob/living/basic/space_fauna/ant.dm
@@ -15,7 +15,7 @@
melee_damage_upper = 10
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
butcher_results = list(/obj/effect/decal/cleanable/ants = 3) //It's just a bunch of ants glued together into a larger ant
response_help_continuous = "pets"
diff --git a/code/modules/mob/living/basic/space_fauna/bear/_bear.dm b/code/modules/mob/living/basic/space_fauna/bear/_bear.dm
index f4a1267e9db70..9b60f7f815998 100644
--- a/code/modules/mob/living/basic/space_fauna/bear/_bear.dm
+++ b/code/modules/mob/living/basic/space_fauna/bear/_bear.dm
@@ -26,7 +26,7 @@
sharpness = SHARP_EDGED
attack_verb_continuous = "claws"
attack_verb_simple = "claw"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_CLAW
friendly_verb_continuous = "bear hugs"
friendly_verb_simple = "bear hug"
@@ -42,7 +42,7 @@
/mob/living/basic/bear/Initialize(mapload)
. = ..()
- add_traits(list(TRAIT_SPACEWALK, TRAIT_FENCE_CLIMBER), INNATE_TRAIT)
+ add_traits(list(TRAIT_SPACEWALK, TRAIT_FENCE_CLIMBER, TRAIT_SNOWSTORM_IMMUNE), INNATE_TRAIT)
AddElement(/datum/element/ai_retaliate)
AddComponent(/datum/component/tree_climber, climbing_distance = 15)
AddElement(/datum/element/swabable, CELL_LINE_TABLE_BEAR, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
@@ -90,16 +90,16 @@
maxHealth = 250
health = 250
faction = list(FACTION_NEUTRAL)
+ status_flags = CANPUSH
/mob/living/basic/bear/snow/ancient
name = "ancient polar bear"
desc = "A grizzled old polar bear, its hide thick enough to make it impervious to almost all weapons."
- status_flags = CANPUSH | GODMODE
gold_core_spawnable = NO_SPAWN
-/mob/living/basic/bear/snow/Initialize(mapload)
+/mob/living/basic/bear/snow/ancient/Initialize(mapload)
. = ..()
- ADD_TRAIT(src, TRAIT_SNOWSTORM_IMMUNE, INNATE_TRAIT)
+ ADD_TRAIT(src, TRAIT_GODMODE, INNATE_TRAIT)
/mob/living/basic/bear/russian
name = "combat bear"
@@ -135,7 +135,7 @@
attacked_sound = 'sound/items/eatfood.ogg'
death_message = "loses its false life and collapses!"
butcher_results = list(/obj/item/food/butter = 6, /obj/item/food/meat/slab = 3, /obj/item/organ/internal/brain = 1, /obj/item/organ/internal/heart = 1)
- attack_sound = 'sound/weapons/slap.ogg'
+ attack_sound = 'sound/items/weapons/slap.ogg'
attack_vis_effect = ATTACK_EFFECT_DISARM
attack_verb_simple = "slap"
attack_verb_continuous = "slaps"
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 1d32b7809a89e..3461f5b104e98 100644
--- a/code/modules/mob/living/basic/space_fauna/carp/carp.dm
+++ b/code/modules/mob/living/basic/space_fauna/carp/carp.dm
@@ -27,7 +27,7 @@
obj_damage = 50
melee_damage_lower = 20
melee_damage_upper = 20
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_abilities.dm b/code/modules/mob/living/basic/space_fauna/carp/carp_abilities.dm
index 322d4db193c21..24c5fb4ece2ec 100644
--- a/code/modules/mob/living/basic/space_fauna/carp/carp_abilities.dm
+++ b/code/modules/mob/living/basic/space_fauna/carp/carp_abilities.dm
@@ -9,7 +9,7 @@
button_icon = 'icons/obj/weapons/guns/projectiles.dmi'
button_icon_state = "arcane_barrage"
cooldown_time = 5 SECONDS
- projectile_sound = 'sound/weapons/emitter.ogg'
+ projectile_sound = 'sound/items/weapons/emitter.ogg'
melee_cooldown_time = 0 SECONDS // Without this they become extremely hesitant to bite anyone ever
shared_cooldown = MOB_SHARED_COOLDOWN_2
@@ -140,8 +140,8 @@
var/turf/destination = pick(exit_locs)
do_teleport(entered_atom, destination, channel = TELEPORT_CHANNEL_MAGIC)
- playsound(src, 'sound/magic/wand_teleport.ogg', 50)
- playsound(destination, 'sound/magic/wand_teleport.ogg', 50)
+ playsound(src, 'sound/effects/magic/wand_teleport.ogg', 50)
+ playsound(destination, 'sound/effects/magic/wand_teleport.ogg', 50)
/// Doesn't actually do anything, just a visual marker
/obj/effect/temp_visual/lesser_carp_rift/exit
diff --git a/code/modules/mob/living/basic/space_fauna/cat_surgeon.dm b/code/modules/mob/living/basic/space_fauna/cat_surgeon.dm
index 02faf7c2cc9f2..246b025d559dc 100644
--- a/code/modules/mob/living/basic/space_fauna/cat_surgeon.dm
+++ b/code/modules/mob/living/basic/space_fauna/cat_surgeon.dm
@@ -19,7 +19,7 @@
melee_damage_upper = 15
attack_verb_continuous = "slashes at"
attack_verb_simple = "slash at"
- attack_sound = 'sound/weapons/circsawhit.ogg'
+ attack_sound = 'sound/items/weapons/circsawhit.ogg'
combat_mode = TRUE
mob_biotypes = MOB_ORGANIC|MOB_HUMANOID
sentience_type = SENTIENCE_HUMANOID
diff --git a/code/modules/mob/living/basic/space_fauna/changeling/flesh_spider.dm b/code/modules/mob/living/basic/space_fauna/changeling/flesh_spider.dm
index 80e1768c90eba..844535c01e9ba 100644
--- a/code/modules/mob/living/basic/space_fauna/changeling/flesh_spider.dm
+++ b/code/modules/mob/living/basic/space_fauna/changeling/flesh_spider.dm
@@ -28,7 +28,7 @@
melee_attack_cooldown = CLICK_CD_MELEE
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
unsuitable_cold_damage = 4
unsuitable_heat_damage = 4
diff --git a/code/modules/mob/living/basic/space_fauna/changeling/headslug.dm b/code/modules/mob/living/basic/space_fauna/changeling/headslug.dm
index 27a9f7d07ae1e..d7aa8903398a6 100644
--- a/code/modules/mob/living/basic/space_fauna/changeling/headslug.dm
+++ b/code/modules/mob/living/basic/space_fauna/changeling/headslug.dm
@@ -16,7 +16,7 @@
melee_damage_upper = 5
attack_verb_continuous = "chomps"
attack_verb_simple = "chomp"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
mob_biotypes = MOB_ORGANIC|MOB_SPECIAL
faction = list(FACTION_CREATURE)
diff --git a/code/modules/mob/living/basic/space_fauna/demon/demon.dm b/code/modules/mob/living/basic/space_fauna/demon/demon.dm
index 21eaf66a3e869..f61591a10c7e7 100644
--- a/code/modules/mob/living/basic/space_fauna/demon/demon.dm
+++ b/code/modules/mob/living/basic/space_fauna/demon/demon.dm
@@ -23,7 +23,7 @@
status_flags = CANPUSH
combat_mode = TRUE
- attack_sound = 'sound/magic/demon_attack1.ogg'
+ attack_sound = 'sound/effects/magic/demon_attack1.ogg'
attack_vis_effect = ATTACK_EFFECT_CLAW
faction = list(FACTION_HELL)
@@ -34,7 +34,7 @@
melee_damage_upper = 15
melee_attack_cooldown = CLICK_CD_MELEE
death_message = "screams in agony as it sublimates into a sulfurous smoke."
- death_sound = 'sound/magic/demon_dies.ogg'
+ death_sound = 'sound/effects/magic/demon_dies.ogg'
habitable_atmos = null
minimum_survivable_temperature = T0C - 25 //Weak to cold
@@ -66,9 +66,9 @@
if(isnull(antag_type) || mind.has_antag_datum(antag_type))
return // we weren't built for this proc to run
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/slaughter_demon))
+ mind.set_assigned_role(SSjob.get_job_type(/datum/job/slaughter_demon))
mind.special_role = ROLE_SLAUGHTER_DEMON
mind.add_antag_datum(antag_type)
- SEND_SOUND(src, 'sound/magic/demon_dies.ogg')
+ SEND_SOUND(src, 'sound/effects/magic/demon_dies.ogg')
to_chat(src, span_bold("You are currently not currently in the same plane of existence as the station. Use your Blood Crawl ability near a pool of blood to manifest and wreak havoc."))
diff --git a/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm b/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm
index 1cc7549f0ea3b..811186d43ad9a 100644
--- a/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm
+++ b/code/modules/mob/living/basic/space_fauna/demon/demon_items.dm
@@ -18,7 +18,7 @@
span_warning("[user] raises [src] to [user.p_their()] mouth and tears into it with [user.p_their()] teeth!"),
span_danger("An unnatural hunger consumes you. You raise [src] your mouth and devour it!"),
)
- playsound(user, 'sound/magic/demon_consume.ogg', 50, TRUE)
+ playsound(user, 'sound/effects/magic/demon_consume.ogg', 50, TRUE)
if(locate(/datum/action/cooldown/spell/jaunt/bloodcrawl) in user.actions)
to_chat(user, span_warning("...and you don't feel any different."))
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 6ad7ff77f1dd4..c036fe461690e 100644
--- a/code/modules/mob/living/basic/space_fauna/eyeball/_eyeball.dm
+++ b/code/modules/mob/living/basic/space_fauna/eyeball/_eyeball.dm
@@ -24,7 +24,7 @@
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
faction = list(FACTION_SPOOKY)
diff --git a/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_behavior.dm b/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_behavior.dm
index f50fca8c559da..5051f8153714d 100644
--- a/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_behavior.dm
+++ b/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_behavior.dm
@@ -83,6 +83,6 @@
else
return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED
-/datum/ai_behavior/hunt_target/unarmed_attack_target/carrot
+/datum/ai_behavior/hunt_target/interact_with_target/carrot
hunt_cooldown = 2 SECONDS
always_reset_target = TRUE
diff --git a/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_subtree.dm b/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_subtree.dm
index 29ea1dfc352ea..17b260d03ed72 100644
--- a/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_subtree.dm
+++ b/code/modules/mob/living/basic/space_fauna/eyeball/eyeball_ai_subtree.dm
@@ -47,6 +47,6 @@
/datum/ai_planning_subtree/find_and_hunt_target/carrot
target_key = BB_LOW_PRIORITY_HUNTING_TARGET
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/carrot
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/carrot
hunt_targets = list(/obj/item/food/grown/carrot)
hunt_range = 6
diff --git a/code/modules/mob/living/basic/space_fauna/faithless.dm b/code/modules/mob/living/basic/space_fauna/faithless.dm
index 39f5652d1a0a4..1cf8218a0fced 100644
--- a/code/modules/mob/living/basic/space_fauna/faithless.dm
+++ b/code/modules/mob/living/basic/space_fauna/faithless.dm
@@ -17,7 +17,7 @@
melee_damage_upper = 15
attack_verb_continuous = "grips"
attack_verb_simple = "grip"
- attack_sound = 'sound/hallucinations/growl1.ogg'
+ attack_sound = 'sound/effects/hallucinations/growl1.ogg'
melee_attack_cooldown = 1 SECONDS
speak_emote = list("growls")
diff --git a/code/modules/mob/living/basic/space_fauna/garden_gnome.dm b/code/modules/mob/living/basic/space_fauna/garden_gnome.dm
index 6608867f2a391..08e4cc7d2c0ff 100644
--- a/code/modules/mob/living/basic/space_fauna/garden_gnome.dm
+++ b/code/modules/mob/living/basic/space_fauna/garden_gnome.dm
@@ -16,7 +16,7 @@
melee_damage_upper = 10
attack_verb_continuous = "punches"
attack_verb_simple = "punch"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
melee_attack_cooldown = 1.2 SECONDS
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 0, OXY = 1)
speak_emote = list("announces")
diff --git a/code/modules/mob/living/basic/space_fauna/ghost.dm b/code/modules/mob/living/basic/space_fauna/ghost.dm
index 728c5ead9f4a8..00bcc45445e29 100644
--- a/code/modules/mob/living/basic/space_fauna/ghost.dm
+++ b/code/modules/mob/living/basic/space_fauna/ghost.dm
@@ -19,7 +19,7 @@
unsuitable_atmos_damage = 0
unsuitable_cold_damage = 0
unsuitable_heat_damage = 0
- attack_sound = 'sound/hallucinations/growl1.ogg'
+ attack_sound = 'sound/effects/hallucinations/growl1.ogg'
death_message = "wails, disintegrating into a pile of ectoplasm!"
gold_core_spawnable = NO_SPAWN //too spooky for science
light_system = OVERLAY_LIGHT
diff --git a/code/modules/mob/living/basic/space_fauna/hivebot/_hivebot.dm b/code/modules/mob/living/basic/space_fauna/hivebot/_hivebot.dm
index c9e155d2bdaed..6b72f1f09757e 100644
--- a/code/modules/mob/living/basic/space_fauna/hivebot/_hivebot.dm
+++ b/code/modules/mob/living/basic/space_fauna/hivebot/_hivebot.dm
@@ -16,7 +16,7 @@
attack_verb_continuous = "claws"
attack_verb_simple = "claw"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_CLAW
verb_say = "states"
verb_ask = "queries"
diff --git a/code/modules/mob/living/basic/space_fauna/killer_tomato.dm b/code/modules/mob/living/basic/space_fauna/killer_tomato.dm
index c859289b56d7d..a784f57763247 100644
--- a/code/modules/mob/living/basic/space_fauna/killer_tomato.dm
+++ b/code/modules/mob/living/basic/space_fauna/killer_tomato.dm
@@ -26,7 +26,7 @@
melee_damage_upper = 12
attack_verb_continuous = "slams"
attack_verb_simple = "slam"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
faction = list(FACTION_PLANTS)
habitable_atmos = list("min_oxy" = 5, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
diff --git a/code/modules/mob/living/basic/space_fauna/meteor_heart/chasing_spikes.dm b/code/modules/mob/living/basic/space_fauna/meteor_heart/chasing_spikes.dm
index a0f9d2fb51be7..b50b089b0ffa4 100644
--- a/code/modules/mob/living/basic/space_fauna/meteor_heart/chasing_spikes.dm
+++ b/code/modules/mob/living/basic/space_fauna/meteor_heart/chasing_spikes.dm
@@ -12,7 +12,7 @@
/datum/action/cooldown/mob_cooldown/chasing_spikes/Activate(atom/target)
. = ..()
- playsound(owner, 'sound/magic/demon_attack1.ogg', vol = 100, vary = TRUE, pressure_affected = FALSE)
+ playsound(owner, 'sound/effects/magic/demon_attack1.ogg', vol = 100, vary = TRUE, pressure_affected = FALSE)
var/obj/effect/temp_visual/effect_trail/spike_chaser/chaser = new(get_turf(owner), target)
LAZYADD(active_chasers, WEAKREF(chaser))
RegisterSignal(chaser, COMSIG_QDELETING, PROC_REF(on_chaser_destroyed))
@@ -75,6 +75,6 @@
var/target_zone = victim.resting ? BODY_ZONE_CHEST : pick_weight(standing_damage_zones)
victim.apply_damage(impale_damage, damagetype = BRUTE, def_zone = target_zone, sharpness = SHARP_POINTY)
if (hit_someone)
- playsound(src, 'sound/weapons/slice.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
+ playsound(src, 'sound/items/weapons/slice.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
else
playsound(src, 'sound/misc/splort.ogg', vol = 25, vary = TRUE, pressure_affected = FALSE)
diff --git a/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.dm b/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.dm
index 7a46e84435555..ae66369dd9328 100644
--- a/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.dm
+++ b/code/modules/mob/living/basic/space_fauna/meteor_heart/meteor_heart.dm
@@ -102,7 +102,7 @@
/obj/effect/temp_visual/meteor_heart_death/Initialize(mapload)
. = ..()
- playsound(src, 'sound/magic/demon_dies.ogg', vol = 100, vary = TRUE, pressure_affected = FALSE)
+ playsound(src, 'sound/effects/magic/demon_dies.ogg', vol = 100, vary = TRUE, pressure_affected = FALSE)
Shake(2, 0, 3 SECONDS)
addtimer(CALLBACK(src, PROC_REF(gib)), duration - 1, TIMER_DELETE_ME)
soundloop = new(src, start_immediately = FALSE)
@@ -116,7 +116,7 @@
/// Make this place a mess
/obj/effect/temp_visual/meteor_heart_death/proc/gib()
- playsound(loc, 'sound/effects/attackblob.ogg', vol = 100, vary = TRUE, pressure_affected = FALSE)
+ playsound(loc, 'sound/effects/blob/attackblob.ogg', vol = 100, vary = TRUE, pressure_affected = FALSE)
var/turf/my_turf = get_turf(src)
new /obj/effect/gibspawner/human(my_turf)
for (var/obj/structure/eyeball as anything in GLOB.meteor_eyeballs)
diff --git a/code/modules/mob/living/basic/space_fauna/meteor_heart/spine_traps.dm b/code/modules/mob/living/basic/space_fauna/meteor_heart/spine_traps.dm
index 8ddcc1ed9e401..e41fa80a15b1c 100644
--- a/code/modules/mob/living/basic/space_fauna/meteor_heart/spine_traps.dm
+++ b/code/modules/mob/living/basic/space_fauna/meteor_heart/spine_traps.dm
@@ -17,7 +17,7 @@
/datum/action/cooldown/mob_cooldown/spine_traps/Activate(atom/target)
. = ..()
- playsound(owner, 'sound/magic/demon_consume.ogg', vol = 100, falloff_exponent = 2, vary = TRUE, pressure_affected = FALSE)
+ playsound(owner, 'sound/effects/magic/demon_consume.ogg', vol = 100, falloff_exponent = 2, vary = TRUE, pressure_affected = FALSE)
var/list/valid_turfs = list()
var/turf/our_turf = get_turf(owner)
for (var/turf/zone_turf in orange(range, our_turf))
@@ -92,7 +92,7 @@
return
COOLDOWN_START(src, thrust_delay, 0.7 SECONDS)
- playsound(src, 'sound/weapons/pierce.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
+ playsound(src, 'sound/items/weapons/pierce.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
var/mob/living/victim = arrived
flick("spikes_stabbing", src)
var/target_zone = victim.resting ? BODY_ZONE_CHEST : pick_weight(standing_damage_zones)
diff --git a/code/modules/mob/living/basic/space_fauna/morph.dm b/code/modules/mob/living/basic/space_fauna/morph.dm
index 8b4fe3802d73e..f1f568a261b01 100644
--- a/code/modules/mob/living/basic/space_fauna/morph.dm
+++ b/code/modules/mob/living/basic/space_fauna/morph.dm
@@ -30,7 +30,7 @@
attack_verb_continuous = "glomps"
attack_verb_simple = "glomp"
- attack_sound = 'sound/effects/blobattack.ogg'
+ attack_sound = 'sound/effects/blob/blobattack.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE //nom nom nom
butcher_results = list(/obj/item/food/meat/slab = 2)
diff --git a/code/modules/mob/living/basic/space_fauna/mushroom.dm b/code/modules/mob/living/basic/space_fauna/mushroom.dm
index b45c2714d4ab4..de501eaea2ee1 100644
--- a/code/modules/mob/living/basic/space_fauna/mushroom.dm
+++ b/code/modules/mob/living/basic/space_fauna/mushroom.dm
@@ -19,7 +19,7 @@
maxHealth = 60
attack_verb_continuous = "chomps"
attack_verb_simple = "chomp"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
faction = list(FACTION_MUSHROOM)
@@ -78,15 +78,10 @@
/datum/ai_planning_subtree/find_and_hunt_target/mushroom_food
target_key = BB_LOW_PRIORITY_HUNTING_TARGET
- hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/mushroom_food
+ hunting_behavior = /datum/ai_behavior/hunt_target/interact_with_target/reset_target
hunt_targets = list(/obj/item/food/grown/mushroom)
hunt_range = 6
-
-/datum/ai_behavior/hunt_target/unarmed_attack_target/mushroom_food
- hunt_cooldown = 15 SECONDS
- always_reset_target = TRUE
-
/mob/living/basic/mushroom/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers)
. = ..()
if(!.)
diff --git a/code/modules/mob/living/basic/space_fauna/netherworld/blankbody.dm b/code/modules/mob/living/basic/space_fauna/netherworld/blankbody.dm
index d49932fb70465..474c2cf77d0e5 100644
--- a/code/modules/mob/living/basic/space_fauna/netherworld/blankbody.dm
+++ b/code/modules/mob/living/basic/space_fauna/netherworld/blankbody.dm
@@ -12,7 +12,7 @@
speed = 1
attack_verb_continuous = "punches"
attack_verb_simple = "punch"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
melee_attack_cooldown = 1 SECONDS
faction = list(FACTION_NETHER)
diff --git a/code/modules/mob/living/basic/space_fauna/netherworld/creature.dm b/code/modules/mob/living/basic/space_fauna/netherworld/creature.dm
index 15dfcdc29c045..e7f5ad852cc31 100644
--- a/code/modules/mob/living/basic/space_fauna/netherworld/creature.dm
+++ b/code/modules/mob/living/basic/space_fauna/netherworld/creature.dm
@@ -13,7 +13,7 @@
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
gold_core_spawnable = HOSTILE_SPAWN
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
melee_attack_cooldown = 1 SECONDS
faction = list(FACTION_NETHER)
@@ -28,16 +28,18 @@
lighting_cutoff_blue = 15
ai_controller = /datum/ai_controller/basic_controller/simple_hostile_obstacles
+ var/health_scaling = TRUE
/mob/living/basic/creature/Initialize(mapload)
. = ..()
AddElement(/datum/element/swabable, CELL_LINE_TABLE_NETHER, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 0)
- AddComponent(
- /datum/component/health_scaling_effects,\
- min_health_attack_modifier_lower = 15,\
- min_health_attack_modifier_upper = 30,\
- min_health_slowdown = -1.5,\
- )
+ if(health_scaling)
+ AddComponent(
+ /datum/component/health_scaling_effects,\
+ min_health_attack_modifier_lower = 15,\
+ min_health_attack_modifier_upper = 30,\
+ min_health_slowdown = -1.5,\
+ )
GRANT_ACTION(/datum/action/cooldown/spell/jaunt/creature_teleport)
@@ -100,3 +102,13 @@
exit_jaunt(cast_on)
return
enter_jaunt(cast_on)
+
+/mob/living/basic/creature/tiggles
+ name = "Miss Tiggles"
+
+/mob/living/basic/creature/hatchling
+ name = "hatchling"
+ health = 25
+ maxHealth = 25
+ health_scaling = FALSE
+ current_size = 0.85
diff --git a/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm b/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm
index 11e00be288b74..3f7adc2272252 100644
--- a/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm
+++ b/code/modules/mob/living/basic/space_fauna/netherworld/migo.dm
@@ -14,12 +14,12 @@
attack_verb_simple = "lacerate"
melee_attack_cooldown = 1 SECONDS
gold_core_spawnable = HOSTILE_SPAWN
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
faction = list(FACTION_NETHER)
speak_emote = list("screams", "clicks", "chitters", "barks", "moans", "growls", "meows", "reverberates", "roars", "squeaks", "rattles", "exclaims", "yells", "remarks", "mumbles", "jabbers", "stutters", "seethes")
death_message = "wails as its form turns into a pulpy mush."
- death_sound = 'sound/voice/hiss6.ogg'
+ death_sound = 'sound/mobs/non-humanoids/hiss/hiss6.ogg'
unsuitable_atmos_damage = 0
unsuitable_cold_damage = 0
unsuitable_heat_damage = 0
@@ -35,7 +35,7 @@
/mob/living/basic/migo/Initialize(mapload)
. = ..()
- migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/change_jaws.ogg', 'sound/items/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/carhorn.ogg', 'sound/items/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/megaphone.ogg', 'sound/items/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/wirecutter.ogg', 'sound/items/welder.ogg', 'sound/items/zip.ogg', 'sound/items/rped.ogg', 'sound/items/ratchet.ogg', 'sound/items/polaroid1.ogg', 'sound/items/pshoom.ogg', 'sound/items/airhorn.ogg', 'sound/items/geiger/high1.ogg', 'sound/items/geiger/high2.ogg', 'sound/voice/beepsky/creep.ogg', 'sound/voice/beepsky/iamthelaw.ogg', 'sound/voice/ed209_20sec.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss6.ogg', 'sound/voice/medbot/patchedup.ogg', 'sound/voice/medbot/feelbetter.ogg', 'sound/voice/human/manlaugh1.ogg', 'sound/voice/human/womanlaugh.ogg', 'sound/weapons/sear.ogg', 'sound/ambience/antag/clockcultalr.ogg', 'sound/ambience/antag/ling_alert.ogg', 'sound/ambience/antag/tatoralert.ogg', 'sound/ambience/antag/monkey.ogg', 'sound/mecha/nominal.ogg', 'sound/mecha/weapdestr.ogg', 'sound/mecha/critdestr.ogg', 'sound/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles.ogg', 'sound/effects/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clockcult_gateway_disrupted.ogg', 'sound/effects/footstep/clownstep2.ogg', 'sound/effects/curse1.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosion_distant.ogg', 'sound/effects/explosionfar.ogg', 'sound/effects/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heart_beat.ogg', 'sound/runtime/hyperspace/hyperspace_begin.ogg', 'sound/runtime/hyperspace/hyperspace_end.ogg', 'sound/effects/his_grace_awaken.ogg', 'sound/effects/pai_boot.ogg', 'sound/effects/phasein.ogg', 'sound/effects/picaxe1.ogg', 'sound/effects/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/misc/desecration-01.ogg', 'sound/misc/desecration-02.ogg', 'sound/misc/desecration-03.ogg', 'sound/misc/bloblarm.ogg', 'sound/misc/airraid.ogg', 'sound/misc/bang.ogg','sound/misc/highlander.ogg', 'sound/misc/interference.ogg', 'sound/misc/notice1.ogg', 'sound/misc/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/misc/splort.ogg', 'sound/weapons/armbomb.ogg', 'sound/weapons/beam_sniper.ogg', 'sound/weapons/chainsawhit.ogg', 'sound/weapons/emitter.ogg', 'sound/weapons/emitter2.ogg', 'sound/weapons/blade1.ogg', 'sound/weapons/bladeslice.ogg', 'sound/weapons/blastcannon.ogg', 'sound/weapons/blaster.ogg', 'sound/weapons/bulletflyby3.ogg', 'sound/weapons/circsawhit.ogg', 'sound/weapons/cqchit2.ogg', 'sound/weapons/drill.ogg', 'sound/weapons/genhit1.ogg', 'sound/weapons/gun/pistol/shot_suppressed.ogg', 'sound/weapons/gun/pistol/shot.ogg', 'sound/weapons/handcuffs.ogg', 'sound/weapons/homerun.ogg', 'sound/weapons/kinetic_accel.ogg', 'sound/machines/clockcult/steam_whoosh.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock.ogg', 'sound/machines/airlock_alien_prying.ogg', 'sound/machines/airlockclose.ogg', 'sound/machines/airlockforced.ogg', 'sound/machines/airlockopen.ogg', 'sound/machines/alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/boltsdown.ogg', 'sound/machines/boltsup.ogg', 'sound/machines/buzz-sigh.ogg', 'sound/machines/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/cryo_warning.ogg', 'sound/machines/defib_charge.ogg', 'sound/machines/defib_failed.ogg', 'sound/machines/defib_ready.ogg', 'sound/machines/defib_zap.ogg', 'sound/machines/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door_close.ogg', 'sound/machines/door_open.ogg', 'sound/machines/engine_alert1.ogg', 'sound/machines/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/machines/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/ambience/signal.ogg', 'sound/machines/synth_no.ogg', 'sound/machines/synth_yes.ogg', 'sound/machines/terminal_alert.ogg', 'sound/machines/triple_beep.ogg', 'sound/machines/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/ai/default/outbreak5.ogg', 'sound/ai/default/outbreak7.ogg', 'sound/ai/default/poweroff.ogg', 'sound/ai/default/radiation.ogg', 'sound/ai/default/shuttlecalled.ogg', 'sound/ai/default/shuttledock.ogg', 'sound/ai/default/shuttlerecalled.ogg', 'sound/ai/default/aimalf.ogg') //hahahaha fuck you code divers
+ migo_sounds = list('sound/items/bubblewrap.ogg', 'sound/items/tools/change_jaws.ogg', 'sound/items/tools/crowbar.ogg', 'sound/items/drink.ogg', 'sound/items/deconstruct.ogg', 'sound/items/carhorn.ogg', 'sound/items/tools/change_drill.ogg', 'sound/items/dodgeball.ogg', 'sound/items/eatfood.ogg', 'sound/items/megaphone.ogg', 'sound/items/tools/screwdriver.ogg', 'sound/items/weeoo1.ogg', 'sound/items/tools/wirecutter.ogg', 'sound/items/tools/welder.ogg', 'sound/items/zip/zip.ogg', 'sound/items/tools/rped.ogg', 'sound/items/tools/ratchet.ogg', 'sound/items/polaroid/polaroid1.ogg', 'sound/items/pshoom/pshoom.ogg', 'sound/items/airhorn/airhorn.ogg', 'sound/items/geiger/high1.ogg', 'sound/items/geiger/high2.ogg', 'sound/mobs/non-humanoids/beepsky/creep.ogg', 'sound/mobs/non-humanoids/beepsky/iamthelaw.ogg', 'sound/mobs/non-humanoids/ed209/ed209_20sec.ogg', 'sound/mobs/non-humanoids/hiss/hiss3.ogg', 'sound/mobs/non-humanoids/hiss/hiss6.ogg', 'sound/mobs/non-humanoids/medbot/patchedup.ogg', 'sound/mobs/non-humanoids/medbot/feelbetter.ogg', 'sound/mobs/humanoids/human/laugh/manlaugh1.ogg', 'sound/mobs/humanoids/human/laugh/womanlaugh.ogg', 'sound/items/weapons/sear.ogg', 'sound/music/antag/clockcultalr.ogg', 'sound/music/antag/ling_alert.ogg', 'sound/music/antag/traitor/tatoralert.ogg', 'sound/music/antag/monkey.ogg', 'sound/vehicles/mecha/nominal.ogg', 'sound/vehicles/mecha/weapdestr.ogg', 'sound/vehicles/mecha/critdestr.ogg', 'sound/vehicles/mecha/imag_enh.ogg', 'sound/effects/adminhelp.ogg', 'sound/effects/alert.ogg', 'sound/effects/blob/attackblob.ogg', 'sound/effects/bamf.ogg', 'sound/effects/blob/blobattack.ogg', 'sound/effects/break_stone.ogg', 'sound/effects/bubbles/bubbles.ogg', 'sound/effects/bubbles/bubbles2.ogg', 'sound/effects/clang.ogg', 'sound/effects/clockcult_gateway_disrupted.ogg', 'sound/effects/footstep/clownstep2.ogg', 'sound/effects/curse/curse1.ogg', 'sound/effects/dimensional_rend.ogg', 'sound/effects/doorcreaky.ogg', 'sound/effects/empulse.ogg', 'sound/effects/explosion/explosion_distant.ogg', 'sound/effects/explosion/explosionfar.ogg', 'sound/effects/explosion/explosion1.ogg', 'sound/effects/grillehit.ogg', 'sound/effects/genetics.ogg', 'sound/effects/heart_beat.ogg', 'sound/runtime/hyperspace/hyperspace_begin.ogg', 'sound/runtime/hyperspace/hyperspace_end.ogg', 'sound/effects/his_grace/his_grace_awaken.ogg', 'sound/effects/pai_boot.ogg', 'sound/effects/phasein.ogg', 'sound/effects/pickaxe/picaxe1.ogg', 'sound/effects/sparks/sparks1.ogg', 'sound/effects/smoke.ogg', 'sound/effects/splat.ogg', 'sound/effects/snap.ogg', 'sound/effects/tendril_destroyed.ogg', 'sound/effects/supermatter.ogg', 'sound/effects/desecration/desecration-01.ogg', 'sound/effects/desecration/desecration-02.ogg', 'sound/effects/desecration/desecration-03.ogg', 'sound/announcer/alarm/bloblarm.ogg', 'sound/announcer/alarm/airraid.ogg', 'sound/misc/bang.ogg','sound/misc/highlander.ogg', 'sound/misc/interference.ogg', 'sound/announcer/notice/notice1.ogg', 'sound/announcer/notice/notice2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/misc/slip.ogg', 'sound/misc/splort.ogg', 'sound/items/weapons/armbomb.ogg', 'sound/items/weapons/beam_sniper.ogg', 'sound/items/weapons/chainsawhit.ogg', 'sound/items/weapons/emitter.ogg', 'sound/items/weapons/emitter2.ogg', 'sound/items/weapons/blade1.ogg', 'sound/items/weapons/bladeslice.ogg', 'sound/items/weapons/blastcannon.ogg', 'sound/items/weapons/blaster.ogg', 'sound/items/weapons/bulletflyby3.ogg', 'sound/items/weapons/circsawhit.ogg', 'sound/items/weapons/cqchit2.ogg', 'sound/items/weapons/drill.ogg', 'sound/items/weapons/genhit1.ogg', 'sound/items/weapons/gun/pistol/shot_suppressed.ogg', 'sound/items/weapons/gun/pistol/shot.ogg', 'sound/items/weapons/handcuffs.ogg', 'sound/items/weapons/homerun.ogg', 'sound/items/weapons/kinetic_accel.ogg', 'sound/machines/clockcult/steam_whoosh.ogg', 'sound/machines/fryer/deep_fryer_emerge.ogg', 'sound/machines/airlock/airlock.ogg', 'sound/machines/airlock/airlock_alien_prying.ogg', 'sound/machines/airlock/airlockclose.ogg', 'sound/machines/airlock/airlockforced.ogg', 'sound/machines/airlock/airlockopen.ogg', 'sound/announcer/alarm/nuke_alarm.ogg', 'sound/machines/blender.ogg', 'sound/machines/airlock/boltsdown.ogg', 'sound/machines/airlock/boltsup.ogg', 'sound/machines/buzz/buzz-sigh.ogg', 'sound/machines/buzz/buzz-two.ogg', 'sound/machines/chime.ogg', 'sound/machines/cryo_warning.ogg', 'sound/machines/defib/defib_charge.ogg', 'sound/machines/defib/defib_failed.ogg', 'sound/machines/defib/defib_ready.ogg', 'sound/machines/defib/defib_zap.ogg', 'sound/machines/beep/deniedbeep.ogg', 'sound/machines/ding.ogg', 'sound/machines/disposalflush.ogg', 'sound/machines/door/door_close.ogg', 'sound/machines/door/door_open.ogg', 'sound/machines/engine_alert/engine_alert1.ogg', 'sound/machines/engine_alert/engine_alert2.ogg', 'sound/machines/hiss.ogg', 'sound/mobs/non-humanoids/honkbot/honkbot_evil_laugh.ogg', 'sound/machines/juicer.ogg', 'sound/machines/ping.ogg', 'sound/ambience/misc/signal.ogg', 'sound/machines/synth/synth_no.ogg', 'sound/machines/synth/synth_yes.ogg', 'sound/machines/terminal/terminal_alert.ogg', 'sound/machines/beep/triple_beep.ogg', 'sound/machines/beep/twobeep.ogg', 'sound/machines/ventcrawl.ogg', 'sound/machines/warning-buzzer.ogg', 'sound/announcer/default/outbreak5.ogg', 'sound/announcer/default/outbreak7.ogg', 'sound/announcer/default/poweroff.ogg', 'sound/announcer/default/radiation.ogg', 'sound/announcer/default/shuttlecalled.ogg', 'sound/announcer/default/shuttledock.ogg', 'sound/announcer/default/shuttlerecalled.ogg', 'sound/announcer/default/aimalf.ogg') //hahahaha fuck you code divers
if(!istype(src, /mob/living/basic/migo/hatsune) && prob(0.1)) // chance on-load mi-gos will spawn with a miku wig on (shiny variant)
new /mob/living/basic/migo/hatsune(get_turf(loc), mapload)
@@ -90,7 +90,7 @@
faction = list(FACTION_NEUTRAL)
/mob/living/basic/migo/hatsune/make_migo_sound()
- playsound(src, 'sound/creatures/tourist/tourist_talk_japanese1.ogg', 50, TRUE)
+ playsound(src, 'sound/mobs/non-humanoids/tourist/tourist_talk_japanese1.ogg', 50, TRUE)
/mob/living/basic/migo/hatsune/Initialize(mapload)
. = ..()
diff --git a/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm b/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm
index 720e0c031c0a3..cf76f347be3f3 100644
--- a/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm
+++ b/code/modules/mob/living/basic/space_fauna/paper_wizard/paper_wizard.dm
@@ -18,7 +18,7 @@
melee_damage_lower = 10
melee_damage_upper = 20
obj_damage = 50
- attack_sound = 'sound/hallucinations/growl1.ogg'
+ attack_sound = 'sound/effects/hallucinations/growl1.ogg'
ai_controller = /datum/ai_controller/basic_controller/paper_wizard
///spell to summon minions
var/datum/action/cooldown/spell/conjure/wizard_summon_minions/summon
@@ -161,8 +161,8 @@
/obj/effect/temp_visual/paperwiz_dying/Initialize(mapload)
. = ..()
visible_message(span_boldannounce("The wizard cries out in pain as a gate appears behind him, sucking him in!"))
- playsound(get_turf(src), 'sound/magic/mandswap.ogg', 50, vary = TRUE, pressure_affected = TRUE)
- playsound(get_turf(src), 'sound/hallucinations/wail.ogg', 50, vary = TRUE, pressure_affected = TRUE)
+ playsound(get_turf(src), 'sound/effects/magic/mandswap.ogg', 50, vary = TRUE, pressure_affected = TRUE)
+ playsound(get_turf(src), 'sound/effects/hallucinations/wail.ogg', 50, vary = TRUE, pressure_affected = TRUE)
RegisterSignal(src, COMSIG_PREQDELETED, PROC_REF(on_delete))
/obj/effect/temp_visual/paperwiz_dying/proc/on_delete()
@@ -171,7 +171,7 @@
for(var/mob/nearby in range(7, src))
shake_camera(nearby, duration = 7 SECONDS, strength = 1)
var/turf/current_turf = get_turf(src)
- playsound(current_turf,'sound/magic/summon_magic.ogg', 50, vary = TRUE, vary = TRUE)
+ playsound(current_turf,'sound/effects/magic/summon_magic.ogg', 50, vary = TRUE, vary = TRUE)
new /obj/effect/temp_visual/paper_scatter(current_turf)
new /obj/item/clothing/suit/wizrobe/paper(current_turf)
new /obj/item/clothing/head/collectable/paper(current_turf)
diff --git a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm
index c0fb9b67e7f73..9f9598b11ae20 100644
--- a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm
+++ b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm
@@ -27,7 +27,7 @@
melee_attack_cooldown = CLICK_CD_MELEE
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
// Slightly brown red, for the eyes
lighting_cutoff_red = 22
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 a154ba9da0c65..615f314bf2e4b 100644
--- a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
+++ b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm
@@ -125,7 +125,7 @@
return TRUE
generated_objectives_and_spells = TRUE
- mind.set_assigned_role(SSjob.GetJobType(/datum/job/revenant))
+ mind.set_assigned_role(SSjob.get_job_type(/datum/job/revenant))
mind.special_role = ROLE_REVENANT
SEND_SOUND(src, sound('sound/effects/ghost.ogg'))
mind.add_antag_datum(/datum/antagonist/revenant)
@@ -235,7 +235,7 @@
var/list/icon_dimensions = get_icon_dimensions(target.icon)
var/orbitsize = (icon_dimensions["width"] + icon_dimensions["height"]) * 0.5
- orbitsize -= (orbitsize / world.icon_size) * (world.icon_size * 0.25)
+ orbitsize -= (orbitsize / ICON_SIZE_ALL) * (ICON_SIZE_ALL * 0.25)
orbit(target, orbitsize)
/mob/living/basic/revenant/adjust_health(amount, updating_health = TRUE, forced = FALSE)
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 9f565dab11253..fa21d157e9061 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
@@ -135,7 +135,7 @@
human_mob.electrocute_act(shock_damage, to_shock, flags = SHOCK_NOGLOVES)
do_sparks(4, FALSE, human_mob)
- playsound(human_mob, 'sound/machines/defib_zap.ogg', 50, TRUE, -1)
+ playsound(human_mob, 'sound/machines/defib/defib_zap.ogg', 50, TRUE, -1)
//Defile: Corrupts nearby stuff, unblesses floor tiles.
/datum/action/cooldown/spell/aoe/revenant/defile
diff --git a/code/modules/mob/living/basic/space_fauna/snake/snake.dm b/code/modules/mob/living/basic/space_fauna/snake/snake.dm
index d526189c5626b..78f7d86e0db3f 100644
--- a/code/modules/mob/living/basic/space_fauna/snake/snake.dm
+++ b/code/modules/mob/living/basic/space_fauna/snake/snake.dm
@@ -18,7 +18,7 @@
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
response_help_continuous = "pets"
diff --git a/code/modules/mob/living/basic/space_fauna/snake/snake_ai.dm b/code/modules/mob/living/basic/space_fauna/snake/snake_ai.dm
index 3eb404761c522..189a7fb2eb097 100644
--- a/code/modules/mob/living/basic/space_fauna/snake/snake_ai.dm
+++ b/code/modules/mob/living/basic/space_fauna/snake/snake_ai.dm
@@ -1,6 +1,6 @@
/datum/ai_planning_subtree/random_speech/snake
speech_chance = 5
speak = list("hsssss","sssSSsssss...","hiisssss")
- sound = list('sound/creatures/snake_hissing1.ogg', 'sound/creatures/snake_hissing2.ogg')
+ sound = list('sound/mobs/non-humanoids/snake/snake_hissing1.ogg', 'sound/mobs/non-humanoids/snake/snake_hissing2.ogg')
emote_hear = list("hisses.")
emote_see = list("slithers around.", "glances.", "stares.")
diff --git a/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm b/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
index 1776e69358139..f672e60ee2940 100644
--- a/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
+++ b/code/modules/mob/living/basic/space_fauna/space_dragon/space_dragon.dm
@@ -30,7 +30,7 @@
speed = 0
attack_verb_continuous = "chomps"
attack_verb_simple = "chomp"
- attack_sound = 'sound/magic/demon_attack1.ogg'
+ attack_sound = 'sound/effects/magic/demon_attack1.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
obj_damage = 50
melee_damage_upper = 35
@@ -43,7 +43,7 @@
maptext_height = 64
maptext_width = 64
mouse_opacity = MOUSE_OPACITY_ICON
- death_sound = 'sound/creatures/space_dragon_roar.ogg'
+ death_sound = 'sound/mobs/non-humanoids/space_dragon/space_dragon_roar.ogg'
death_message = "screeches in agony as it collapses to the floor, its life extinguished."
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30)
can_buckle_to = FALSE
@@ -176,7 +176,7 @@
adjust_health(-food.maxHealth * 0.25)
if (QDELETED(food) || food.loc == src)
return FALSE
- playsound(src, 'sound/magic/demon_attack1.ogg', 60, TRUE)
+ playsound(src, 'sound/effects/magic/demon_attack1.ogg', 60, TRUE)
visible_message(span_boldwarning("[src] swallows [food] whole!"))
food.extinguish_mob() // It's wet in there, and our food is likely to be on fire. Let's be decent and not husk them.
food.forceMove(src)
diff --git a/code/modules/mob/living/basic/space_fauna/spaceman.dm b/code/modules/mob/living/basic/space_fauna/spaceman.dm
index 8a9ba36287ae3..42f28a74960bf 100644
--- a/code/modules/mob/living/basic/space_fauna/spaceman.dm
+++ b/code/modules/mob/living/basic/space_fauna/spaceman.dm
@@ -21,7 +21,7 @@
melee_damage_upper = 10
attack_verb_continuous = "hits"
attack_verb_simple = "hit"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
obj_damage = 0
environment_smash = ENVIRONMENT_SMASH_NONE
ai_controller = /datum/ai_controller/basic_controller/spaceman
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 b9938631ec5d5..118487f038392 100644
--- a/code/modules/mob/living/basic/space_fauna/spider/spider.dm
+++ b/code/modules/mob/living/basic/space_fauna/spider/spider.dm
@@ -24,7 +24,7 @@
pass_flags = PASSTABLE
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
unique_name = TRUE
lighting_cutoff_red = 22
diff --git a/code/modules/mob/living/basic/space_fauna/spider/spider_abilities/hivemind.dm b/code/modules/mob/living/basic/space_fauna/spider/spider_abilities/hivemind.dm
index 790879b0de2c1..8fc79080cac84 100644
--- a/code/modules/mob/living/basic/space_fauna/spider/spider_abilities/hivemind.dm
+++ b/code/modules/mob/living/basic/space_fauna/spider/spider_abilities/hivemind.dm
@@ -18,7 +18,7 @@
var/current_directive = ""
/datum/action/cooldown/mob_cooldown/set_spider_directive/Activate(atom/target)
- var/new_directive = tgui_input_text(owner, "Enter the new directive", "Create directive", "[current_directive]")
+ var/new_directive = tgui_input_text(owner, "Enter the new directive", "Create directive", "[current_directive]", max_length = MAX_MESSAGE_LEN)
if(isnull(new_directive) || QDELETED(src) || QDELETED(owner) || !IsAvailable(feedback = TRUE))
return
@@ -44,7 +44,7 @@
click_to_activate = FALSE
/datum/action/cooldown/mob_cooldown/command_spiders/Activate(trigger_flags)
- var/input = tgui_input_text(owner, "Input a command for your legions to follow.", "Command")
+ var/input = tgui_input_text(owner, "Input a command for your legions to follow.", "Command", max_length = MAX_MESSAGE_LEN)
if(!input || QDELETED(src) || QDELETED(owner) || !IsAvailable(feedback = TRUE))
return
spider_command(owner, input)
diff --git a/code/modules/mob/living/basic/space_fauna/statue/statue.dm b/code/modules/mob/living/basic/space_fauna/statue/statue.dm
index 3ddbc2364e81c..3bd308f34a609 100644
--- a/code/modules/mob/living/basic/space_fauna/statue/statue.dm
+++ b/code/modules/mob/living/basic/space_fauna/statue/statue.dm
@@ -25,7 +25,7 @@
melee_damage_upper = 83
attack_verb_continuous = "claws"
attack_verb_simple = "claw"
- attack_sound = 'sound/hallucinations/growl1.ogg'
+ attack_sound = 'sound/effects/hallucinations/growl1.ogg'
attack_vis_effect = ATTACK_EFFECT_CLAW
melee_attack_cooldown = 1 SECONDS
diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm
index bba6e0eb460c6..a25b3c52ad8fc 100644
--- a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm
+++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm
@@ -58,13 +58,12 @@
return FALSE
RegisterSignal(fugu, COMSIG_MOB_STATCHANGE, PROC_REF(check_death))
fugu.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/inflated)
- ADD_TRAIT(fugu, TRAIT_FUGU_GLANDED, TRAIT_STATUS_EFFECT(id))
+ fugu.add_traits(list(TRAIT_FUGU_GLANDED, TRAIT_GODMODE), TRAIT_STATUS_EFFECT(id))
fugu.AddElement(/datum/element/wall_tearer, allow_reinforced = FALSE)
fugu.mob_size = MOB_SIZE_LARGE
fugu.icon_state = "Fugu1"
fugu.melee_damage_lower = 15
fugu.melee_damage_upper = 20
- fugu.status_flags |= GODMODE
fugu.obj_damage = 60
fugu.ai_controller.set_blackboard_key(BB_BASIC_MOB_STOP_FLEEING, TRUE)
fugu.ai_controller.CancelActions()
@@ -76,12 +75,11 @@
return // Check again in case you changed mob after application but somehow kept the status effect
UnregisterSignal(fugu, COMSIG_MOB_STATCHANGE)
fugu.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/inflated)
- REMOVE_TRAIT(fugu, TRAIT_FUGU_GLANDED, TRAIT_STATUS_EFFECT(id))
+ fugu.remove_traits(list(TRAIT_FUGU_GLANDED, TRAIT_GODMODE), TRAIT_STATUS_EFFECT(id))
fugu.RemoveElement(/datum/element/wall_tearer, allow_reinforced = FALSE)
fugu.mob_size = MOB_SIZE_SMALL
fugu.melee_damage_lower = 0
fugu.melee_damage_upper = 0
- fugu.status_flags &= ~GODMODE
if (fugu.stat != DEAD)
fugu.icon_state = "Fugu0"
fugu.obj_damage = 0
diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.dm
index 675b32c84b55d..a3670c6e5ac56 100644
--- a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.dm
+++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.dm
@@ -27,7 +27,7 @@
obj_damage = 0
melee_damage_lower = 0
melee_damage_upper = 0
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
melee_attack_cooldown = 2.5 SECONDS
attack_verb_continuous = "chomps"
diff --git a/code/modules/mob/living/basic/trader/trader.dm b/code/modules/mob/living/basic/trader/trader.dm
index 29a2bda419930..9b01261fa51a4 100644
--- a/code/modules/mob/living/basic/trader/trader.dm
+++ b/code/modules/mob/living/basic/trader/trader.dm
@@ -9,7 +9,7 @@
melee_damage_upper = 10
attack_verb_continuous = "punches"
attack_verb_simple = "punch"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
basic_mob_flags = DEL_ON_DEATH
unsuitable_atmos_damage = 2.5
combat_mode = FALSE
@@ -33,7 +33,7 @@
///Casing used to shoot during retaliation
var/ranged_attack_casing = /obj/item/ammo_casing/shotgun/buckshot
///Sound to make while doing a retalitory attack
- var/ranged_attack_sound = 'sound/weapons/gun/pistol/shot.ogg'
+ var/ranged_attack_sound = 'sound/items/weapons/gun/pistol/shot.ogg'
///Weapon path, for visuals
var/held_weapon_visual = /obj/item/gun/ballistic/shotgun
@@ -69,7 +69,7 @@
ai_controller = /datum/ai_controller/basic_controller/trader/jumpscare
- sell_sound = 'sound/voice/hiss2.ogg'
+ sell_sound = 'sound/mobs/non-humanoids/hiss/hiss2.ogg'
species_path = /datum/species/skeleton
spawner_path = /obj/effect/mob_spawn/corpse/human/skeleton/mrbones
loot = list(/obj/effect/decal/remains/human)
diff --git a/code/modules/mob/living/basic/trader/trader_data.dm b/code/modules/mob/living/basic/trader/trader_data.dm
index 9762dc02be500..c47e200154f28 100644
--- a/code/modules/mob/living/basic/trader/trader_data.dm
+++ b/code/modules/mob/living/basic/trader/trader_data.dm
@@ -85,7 +85,7 @@
/datum/trader_data/mr_bones
shop_spot_type = /obj/structure/chair/wood/wings
sign_type = /obj/structure/trader_sign/mrbones
- sell_sound = 'sound/voice/hiss2.ogg'
+ sell_sound = 'sound/mobs/non-humanoids/hiss/hiss2.ogg'
initial_products = list(
/obj/item/clothing/head/helmet/skull = list(PAYCHECK_CREW * 3, INFINITY),
diff --git a/code/modules/mob/living/basic/tree.dm b/code/modules/mob/living/basic/tree.dm
index 3f3894f190b5e..b6f7e5ca4eb41 100644
--- a/code/modules/mob/living/basic/tree.dm
+++ b/code/modules/mob/living/basic/tree.dm
@@ -28,7 +28,7 @@
melee_damage_upper = 12
attack_verb_continuous = "bites"
attack_verb_simple = "bite"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
faction = list(FACTION_HOSTILE)
diff --git a/code/modules/mob/living/basic/trooper/abductor.dm b/code/modules/mob/living/basic/trooper/abductor.dm
index fdb8b41cc5ec5..8163fb72c5af6 100644
--- a/code/modules/mob/living/basic/trooper/abductor.dm
+++ b/code/modules/mob/living/basic/trooper/abductor.dm
@@ -12,7 +12,7 @@
loot = list(/obj/effect/gibspawner/human)
attack_verb_continuous = "beats"
attack_verb_simple = "beat"
- attack_sound = 'sound/weapons/egloves.ogg'
+ attack_sound = 'sound/items/weapons/egloves.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
r_hand = /obj/item/melee/baton/abductor
var/projectile_deflect_chance = 0
@@ -24,7 +24,7 @@
/// Type of bullet we use
var/casingtype = /obj/item/ammo_casing/energy/lasergun
/// Sound to play when firing weapon
- var/projectilesound = 'sound/weapons/laser2.ogg'
+ var/projectilesound = 'sound/items/weapons/laser2.ogg'
/// number of burst shots
var/burst_shots = 1
/// Time between taking shots
diff --git a/code/modules/mob/living/basic/trooper/nanotrasen.dm b/code/modules/mob/living/basic/trooper/nanotrasen.dm
index af32edde7e3f7..6d285b871d83e 100644
--- a/code/modules/mob/living/basic/trooper/nanotrasen.dm
+++ b/code/modules/mob/living/basic/trooper/nanotrasen.dm
@@ -21,7 +21,7 @@
/// Type of bullet we use
var/casingtype = /obj/item/ammo_casing/c45
/// Sound to play when firing weapon
- var/projectilesound = 'sound/weapons/gun/pistol/shot_alt.ogg'
+ var/projectilesound = 'sound/items/weapons/gun/pistol/shot_alt.ogg'
/// number of burst shots
var/burst_shots
/// Time between taking shots
@@ -42,7 +42,7 @@
/mob/living/basic/trooper/nanotrasen/ranged/smg
ai_controller = /datum/ai_controller/basic_controller/trooper/ranged/burst
casingtype = /obj/item/ammo_casing/c46x30mm
- projectilesound = 'sound/weapons/gun/smg/shot.ogg'
+ projectilesound = 'sound/items/weapons/gun/smg/shot.ogg'
r_hand = /obj/item/gun/ballistic/automatic/wt550
burst_shots = 3
ranged_cooldown = 3 SECONDS
@@ -54,7 +54,7 @@
casingtype = /obj/item/ammo_casing/a223/weak
burst_shots = 4
ranged_cooldown = 3 SECONDS
- projectilesound = 'sound/weapons/gun/smg/shot.ogg'
+ projectilesound = 'sound/items/weapons/gun/smg/shot.ogg'
r_hand = /obj/item/gun/ballistic/automatic/ar
loot = list(/obj/effect/mob_spawn/corpse/human/nanotrasenassaultsoldier)
mob_spawner = /obj/effect/mob_spawn/corpse/human/nanotrasenassaultsoldier
@@ -68,7 +68,7 @@
unsuitable_cold_damage = 0
casingtype = /obj/item/ammo_casing/energy/laser
burst_shots = 3
- projectilesound = 'sound/weapons/laser.ogg'
+ projectilesound = 'sound/items/weapons/laser.ogg'
ranged_cooldown = 5 SECONDS
faction = list(ROLE_DEATHSQUAD)
loot = list(/obj/effect/gibspawner/human)
diff --git a/code/modules/mob/living/basic/trooper/pirate.dm b/code/modules/mob/living/basic/trooper/pirate.dm
index 6a51b901ebc4d..0af2f2aa6973a 100644
--- a/code/modules/mob/living/basic/trooper/pirate.dm
+++ b/code/modules/mob/living/basic/trooper/pirate.dm
@@ -23,7 +23,7 @@
armour_penetration = 35
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
- attack_sound = 'sound/weapons/blade1.ogg'
+ attack_sound = 'sound/items/weapons/blade1.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
loot = list(/obj/effect/mob_spawn/corpse/human/pirate/melee)
light_range = 2
@@ -57,7 +57,7 @@
/// Type of bullet we use
var/projectiletype = /obj/projectile/beam/laser
/// Sound to play when firing weapon
- var/projectilesound = 'sound/weapons/laser.ogg'
+ var/projectilesound = 'sound/items/weapons/laser.ogg'
/// number of burst shots
var/burst_shots = 2
/// Time between taking shots
diff --git a/code/modules/mob/living/basic/trooper/russian.dm b/code/modules/mob/living/basic/trooper/russian.dm
index 6c8ff52c019a9..e3334ddddbd39 100644
--- a/code/modules/mob/living/basic/trooper/russian.dm
+++ b/code/modules/mob/living/basic/trooper/russian.dm
@@ -10,7 +10,7 @@
faction = list(FACTION_RUSSIAN)
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
mob_spawner = /obj/effect/mob_spawn/corpse/human/russian
@@ -29,7 +29,7 @@
/obj/item/gun/ballistic/revolver/nagant,
)
var/casingtype = /obj/item/ammo_casing/n762
- var/projectilesound = 'sound/weapons/gun/revolver/shot.ogg'
+ var/projectilesound = 'sound/items/weapons/gun/revolver/shot.ogg'
/mob/living/basic/trooper/russian/ranged/Initialize(mapload)
. = ..()
diff --git a/code/modules/mob/living/basic/trooper/syndicate.dm b/code/modules/mob/living/basic/trooper/syndicate.dm
index 8f8d564693b66..6d4766db8cd07 100644
--- a/code/modules/mob/living/basic/trooper/syndicate.dm
+++ b/code/modules/mob/living/basic/trooper/syndicate.dm
@@ -32,7 +32,7 @@
loot = list(/obj/effect/gibspawner/human)
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
r_hand = /obj/item/knife/combat/survival
var/projectile_deflect_chance = 0
@@ -67,7 +67,7 @@
melee_damage_upper = 30
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
- attack_sound = 'sound/weapons/blade1.ogg'
+ attack_sound = 'sound/items/weapons/blade1.ogg'
armour_penetration = 35
projectile_deflect_chance = 50
light_range = 2
@@ -105,7 +105,7 @@
/// Type of bullet we use
var/casingtype = /obj/item/ammo_casing/c9mm
/// Sound to play when firing weapon
- var/projectilesound = 'sound/weapons/gun/pistol/shot.ogg'
+ var/projectilesound = 'sound/items/weapons/gun/pistol/shot.ogg'
/// number of burst shots
var/burst_shots
/// Time between taking shots
@@ -124,7 +124,7 @@
AddComponent(/datum/component/ranged_mob_full_auto)
/mob/living/basic/trooper/syndicate/ranged/infiltrator //shuttle loan event
- projectilesound = 'sound/weapons/gun/smg/shot_suppressed.ogg'
+ projectilesound = 'sound/items/weapons/gun/smg/shot_suppressed.ogg'
loot = list(/obj/effect/mob_spawn/corpse/human/syndicatesoldier)
/mob/living/basic/trooper/syndicate/ranged/space
@@ -148,7 +148,7 @@
/mob/living/basic/trooper/syndicate/ranged/smg
casingtype = /obj/item/ammo_casing/c45
- projectilesound = 'sound/weapons/gun/smg/shot.ogg'
+ projectilesound = 'sound/items/weapons/gun/smg/shot.ogg'
ai_controller = /datum/ai_controller/basic_controller/trooper/ranged/burst
burst_shots = 3
ranged_cooldown = 3 SECONDS
@@ -236,7 +236,7 @@
obj_damage = 0
attack_verb_continuous = "cuts"
attack_verb_simple = "cut"
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
faction = list(ROLE_SYNDICATE)
mob_size = MOB_SIZE_TINY
diff --git a/code/modules/mob/living/basic/trooper/trooper.dm b/code/modules/mob/living/basic/trooper/trooper.dm
index 1886c8fc2ff5e..7c9fd698d0895 100644
--- a/code/modules/mob/living/basic/trooper/trooper.dm
+++ b/code/modules/mob/living/basic/trooper/trooper.dm
@@ -10,7 +10,7 @@
melee_damage_upper = 10
attack_verb_continuous = "punches"
attack_verb_simple = "punch"
- attack_sound = 'sound/weapons/punch1.ogg'
+ attack_sound = 'sound/items/weapons/punch1.ogg'
melee_attack_cooldown = 1.2 SECONDS
combat_mode = TRUE
unsuitable_atmos_damage = 7.5
diff --git a/code/modules/mob/living/basic/vermin/cockroach.dm b/code/modules/mob/living/basic/vermin/cockroach.dm
index 0680de631cbae..c6eead9a16661 100644
--- a/code/modules/mob/living/basic/vermin/cockroach.dm
+++ b/code/modules/mob/living/basic/vermin/cockroach.dm
@@ -145,7 +145,7 @@
obj_damage = 10
melee_attack_cooldown = 1 SECONDS
gold_core_spawnable = HOSTILE_SPAWN
- attack_sound = 'sound/weapons/bladeslice.ogg'
+ attack_sound = 'sound/items/weapons/bladeslice.ogg'
attack_vis_effect = ATTACK_EFFECT_SLASH
faction = list(FACTION_HOSTILE, FACTION_MAINT_CREATURES)
sharpness = SHARP_POINTY
diff --git a/code/modules/mob/living/basic/vermin/crab.dm b/code/modules/mob/living/basic/vermin/crab.dm
index 3c1c9146a064d..26eca65b97209 100644
--- a/code/modules/mob/living/basic/vermin/crab.dm
+++ b/code/modules/mob/living/basic/vermin/crab.dm
@@ -23,7 +23,7 @@
///In the case 'melee_damage_upper' is somehow raised above 0
attack_verb_continuous = "snips"
attack_verb_simple = "snip"
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
ai_controller = /datum/ai_controller/basic_controller/crab
diff --git a/code/modules/mob/living/basic/vermin/frog.dm b/code/modules/mob/living/basic/vermin/frog.dm
index 3a288918dbdc1..d2a634b7e9edd 100644
--- a/code/modules/mob/living/basic/vermin/frog.dm
+++ b/code/modules/mob/living/basic/vermin/frog.dm
@@ -26,7 +26,7 @@
response_harm_simple = "splat"
density = FALSE
faction = list(FACTION_HOSTILE, FACTION_MAINT_CREATURES)
- attack_sound = 'sound/effects/reee.ogg'
+ attack_sound = 'sound/mobs/non-humanoids/frog/reee.ogg'
butcher_results = list(/obj/item/food/nugget = 1)
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
mob_size = MOB_SIZE_TINY
@@ -40,7 +40,7 @@
ai_controller = /datum/ai_controller/basic_controller/frog
- var/stepped_sound = 'sound/effects/huuu.ogg'
+ var/stepped_sound = 'sound/mobs/non-humanoids/frog/huuu.ogg'
///How much of a reagent the mob injects on attack
var/poison_per_bite = 3
///What reagent the mob injects targets with
diff --git a/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm b/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm
index a0079065de437..9659408b8b1aa 100644
--- a/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm
+++ b/code/modules/mob/living/basic/vermin/mothroach/mothroach.dm
@@ -59,14 +59,14 @@
if(src.stat == DEAD)
return
else
- playsound(loc, 'sound/voice/moth/scream_moth.ogg', 50, TRUE)
+ playsound(loc, 'sound/mobs/humanoids/moth/scream_moth.ogg', 50, TRUE)
/mob/living/basic/mothroach/attackby(obj/item/attacking_item, mob/living/user, params)
. = ..()
if(src.stat == DEAD)
return
else
- playsound(loc, 'sound/voice/moth/scream_moth.ogg', 50, TRUE)
+ playsound(loc, 'sound/mobs/humanoids/moth/scream_moth.ogg', 50, TRUE)
/mob/living/basic/mothroach/bar
name = "mothroach bartender"
diff --git a/code/modules/mob/living/basic/vermin/mouse.dm b/code/modules/mob/living/basic/vermin/mouse.dm
index 7361e4be7b310..cb62ad956f95c 100644
--- a/code/modules/mob/living/basic/vermin/mouse.dm
+++ b/code/modules/mob/living/basic/vermin/mouse.dm
@@ -1,6 +1,6 @@
/mob/living/basic/mouse
name = "mouse"
- desc = "This cute little guy just loves the taste of uninsulated electrical cables. Isn't he adorable?"
+ desc = "This cute little guy just loves the taste of insulated electrical cables. Isn't he adorable?"
icon_state = "mouse_gray"
icon_living = "mouse_gray"
icon_dead = "mouse_gray_dead"
@@ -51,7 +51,7 @@
held_state = "mouse_[body_color]" // not handled by variety element
AddElement(/datum/element/animal_variety, "mouse", body_color, FALSE)
AddElement(/datum/element/swabable, CELL_LINE_TABLE_MOUSE, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 10)
- AddComponent(/datum/component/squeak, list('sound/creatures/mousesqueek.ogg' = 1), 100, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) //as quiet as a mouse or whatever
+ AddComponent(/datum/component/squeak, list('sound/mobs/non-humanoids/mouse/mousesqueek.ogg' = 1), 100, extrarange = SHORT_RANGE_SOUND_EXTRARANGE) //as quiet as a mouse or whatever
var/static/list/loc_connections = list(
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
)
@@ -232,7 +232,7 @@
span_notice("You chew through \the [cable]."),
)
- playsound(cable, 'sound/effects/sparks2.ogg', 100, TRUE)
+ playsound(cable, 'sound/effects/sparks/sparks2.ogg', 100, TRUE)
cable.deconstruct()
/mob/living/basic/mouse/white
diff --git a/code/modules/mob/living/basic/vermin/space_bat.dm b/code/modules/mob/living/basic/vermin/space_bat.dm
index 53f367f448727..07bce71f2f587 100644
--- a/code/modules/mob/living/basic/vermin/space_bat.dm
+++ b/code/modules/mob/living/basic/vermin/space_bat.dm
@@ -24,7 +24,7 @@
butcher_results = list(/obj/item/food/meat/slab = 1)
pass_flags = PASSTABLE
- attack_sound = 'sound/weapons/bite.ogg'
+ attack_sound = 'sound/items/weapons/bite.ogg'
attack_vis_effect = ATTACK_EFFECT_BITE
environment_smash = ENVIRONMENT_SMASH_NONE
mob_size = MOB_SIZE_TINY
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index 7d5d6bec936a7..5d4cf831f9075 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -122,7 +122,7 @@
//Makes a blood drop, leaking amt units of blood from the mob
/mob/living/carbon/proc/bleed(amt)
- if(!blood_volume || (status_flags & GODMODE))
+ if(!blood_volume || HAS_TRAIT(src, TRAIT_GODMODE))
return
blood_volume = max(blood_volume - amt, 0)
diff --git a/code/modules/mob/living/brain/MMI.dm b/code/modules/mob/living/brain/MMI.dm
index 1963e13dbf552..7ae89e8d0ae1d 100644
--- a/code/modules/mob/living/brain/MMI.dm
+++ b/code/modules/mob/living/brain/MMI.dm
@@ -88,10 +88,10 @@
brainmob.set_stat(CONSCIOUS) //we manually revive the brain mob
else if(!fubar_brain && newbrain.organ_flags & ORGAN_FAILING) // the brain is damaged, but not from a suicider
to_chat(user, span_warning("[src]'s indicator light turns yellow and its brain integrity alarm beeps softly. Perhaps you should check [newbrain] for damage."))
- playsound(src, 'sound/machines/synth_no.ogg', 5, TRUE)
+ playsound(src, 'sound/machines/synth/synth_no.ogg', 5, TRUE)
else
to_chat(user, span_warning("[src]'s indicator light turns red and its brainwave activity alarm beeps softly. Perhaps you should check [newbrain] again."))
- playsound(src, 'sound/machines/triple_beep.ogg', 5, TRUE)
+ playsound(src, 'sound/machines/beep/triple_beep.ogg', 5, TRUE)
brainmob.reset_perspective()
brain = newbrain
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index 46419d70e6dee..ced02095e410b 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -8,7 +8,7 @@
layer = ABOVE_MOB_LAYER
zone = BODY_ZONE_HEAD
slot = ORGAN_SLOT_BRAIN
- organ_flags = ORGAN_ORGANIC | ORGAN_VITAL
+ organ_flags = ORGAN_ORGANIC | ORGAN_VITAL | ORGAN_PROMINENT
attack_verb_continuous = list("attacks", "slaps", "whacks")
attack_verb_simple = list("attack", "slap", "whack")
@@ -258,6 +258,26 @@
else
return span_info("This one is completely devoid of life.")
+/obj/item/organ/internal/brain/get_status_appendix(advanced, add_tooltips)
+ var/list/trauma_text
+ for(var/datum/brain_trauma/trauma as anything in traumas)
+ var/trauma_desc = ""
+ switch(trauma.resilience)
+ if(TRAUMA_RESILIENCE_BASIC)
+ trauma_desc = conditional_tooltip("Mild ", "Repair via brain surgery or medication such as [/datum/reagent/medicine/neurine::name].", add_tooltips)
+ if(TRAUMA_RESILIENCE_SURGERY)
+ trauma_desc = conditional_tooltip("Severe ", "Repair via brain surgery.", add_tooltips)
+ if(TRAUMA_RESILIENCE_LOBOTOMY)
+ trauma_desc = conditional_tooltip("Deep-rooted ", "Repair via Lobotomy.", add_tooltips)
+ if(TRAUMA_RESILIENCE_WOUND)
+ trauma_desc = conditional_tooltip("Fracture-derived ", "Repair via treatment of wounds afflicting the head.", add_tooltips)
+ if(TRAUMA_RESILIENCE_MAGIC, TRAUMA_RESILIENCE_ABSOLUTE)
+ trauma_desc = conditional_tooltip("Permanent ", "Irreparable under normal circumstances.", add_tooltips)
+ trauma_desc += capitalize(trauma.scan_desc)
+ LAZYADD(trauma_text, trauma_desc)
+ if(LAZYLEN(trauma_text))
+ return "Mental trauma: [english_list(trauma_text, and_text = ", and ")]."
+
/obj/item/organ/internal/brain/attack(mob/living/carbon/C, mob/user)
if(!istype(C))
return ..()
diff --git a/code/modules/mob/living/brain/life.dm b/code/modules/mob/living/brain/life.dm
index 9bebeac70ec83..7ffe49951ec26 100644
--- a/code/modules/mob/living/brain/life.dm
+++ b/code/modules/mob/living/brain/life.dm
@@ -14,7 +14,7 @@
handle_emp_damage(seconds_per_tick, times_fired)
/mob/living/brain/update_stat()
- if(status_flags & GODMODE)
+ if(HAS_TRAIT(src, TRAIT_GODMODE))
return
if(health > HEALTH_THRESHOLD_DEAD)
return
diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm
index 7d4255d54d50b..6557d0d1da889 100644
--- a/code/modules/mob/living/brain/posibrain.dm
+++ b/code/modules/mob/living/brain/posibrain.dm
@@ -11,17 +11,17 @@ GLOBAL_VAR(posibrain_notify_cooldown)
braintype = "Android"
///Message sent to the user when polling ghosts
- var/begin_activation_message = "You carefully locate the manual activation switch and start the positronic brain's boot process."
+ var/begin_activation_message = span_notice("You carefully locate the manual activation switch and start the positronic brain's boot process.")
///Message sent as a visible message on success
- var/success_message = "The positronic brain pings, and its lights start flashing. Success!"
+ var/success_message = span_notice("The positronic brain pings, and its lights start flashing. Success!")
///Message sent as a visible message on failure
- var/fail_message = "The positronic brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?"
+ var/fail_message = span_notice("The positronic brain buzzes quietly, and the golden lights fade away. Perhaps you could try again?")
///Visible message sent when a player possesses the brain
- var/new_mob_message = "The positronic brain chimes quietly."
+ var/new_mob_message = span_notice("The positronic brain chimes quietly.")
///Examine message when the posibrain has no mob
- var/dead_message = "It appears to be completely inactive. The reset light is blinking."
+ var/dead_message = span_deadsay("It appears to be completely inactive. The reset light is blinking.")
///Examine message when the posibrain cannot poll ghosts due to cooldown
- var/recharge_message = "The positronic brain isn't ready to activate again yet! Give it some time to recharge."
+ var/recharge_message = span_warning("The positronic brain isn't ready to activate again yet! Give it some time to recharge.")
///Can be set to tell ghosts what the brain will be used for
var/ask_role = ""
@@ -76,7 +76,7 @@ GLOBAL_VAR(posibrain_notify_cooldown)
addtimer(CALLBACK(src, PROC_REF(check_success)), ask_delay)
/obj/item/mmi/posibrain/click_alt(mob/living/user)
- var/input_seed = tgui_input_text(user, "Enter a personality seed", "Enter seed", ask_role, MAX_NAME_LEN)
+ var/input_seed = tgui_input_text(user, "Enter a personality seed", "Enter seed", ask_role, max_length = MAX_NAME_LEN)
if(isnull(input_seed))
return CLICK_ACTION_BLOCKING
if(!user.can_perform_action(src))
@@ -137,7 +137,7 @@ GLOBAL_VAR(posibrain_notify_cooldown)
brainmob.timeofdeath = transferred_user.timeofdeath
brainmob.set_stat(CONSCIOUS)
if(brainmob.mind)
- brainmob.mind.set_assigned_role(SSjob.GetJobType(posibrain_job_path))
+ brainmob.mind.set_assigned_role(SSjob.get_job_type(posibrain_job_path))
if(transferred_user.mind)
transferred_user.mind.transfer_to(brainmob)
@@ -160,7 +160,7 @@ GLOBAL_VAR(posibrain_notify_cooldown)
var/policy = get_policy(ROLE_POSIBRAIN)
if(policy)
to_chat(brainmob, policy)
- brainmob.mind.set_assigned_role(SSjob.GetJobType(posibrain_job_path))
+ brainmob.mind.set_assigned_role(SSjob.get_job_type(posibrain_job_path))
brainmob.set_stat(CONSCIOUS)
visible_message(new_mob_message)
diff --git a/code/modules/mob/living/carbon/alien/adult/adult.dm b/code/modules/mob/living/carbon/alien/adult/adult.dm
index 663419ce22cff..ce95c0e6ce5a7 100644
--- a/code/modules/mob/living/carbon/alien/adult/adult.dm
+++ b/code/modules/mob/living/carbon/alien/adult/adult.dm
@@ -14,7 +14,7 @@
var/leap_on_click = 0
var/pounce_cooldown = 0
var/pounce_cooldown_time = 30
- death_sound = 'sound/voice/hiss6.ogg'
+ death_sound = 'sound/mobs/non-humanoids/hiss/hiss6.ogg'
bodyparts = list(
/obj/item/bodypart/chest/alien,
/obj/item/bodypart/head/alien,
@@ -41,7 +41,7 @@ GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list(
return ..()
/mob/living/carbon/alien/adult/cuff_resist(obj/item/I)
- playsound(src, 'sound/voice/hiss5.ogg', 40, TRUE, TRUE) //Alien roars when starting to break free
+ playsound(src, 'sound/mobs/non-humanoids/hiss/hiss5.ogg', 40, TRUE, TRUE) //Alien roars when starting to break free
..(I, cuff_break = INSTANT_CUFFBREAK)
/mob/living/carbon/alien/adult/resist_grab(moving_resist)
@@ -67,7 +67,7 @@ GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list(
/mob/living/carbon/alien/adult/check_breath(datum/gas_mixture/breath)
if(breath?.total_moles() > 0 && !HAS_TRAIT(src, TRAIT_SNEAK))
- playsound(get_turf(src), pick('sound/voice/lowHiss2.ogg', 'sound/voice/lowHiss3.ogg', 'sound/voice/lowHiss4.ogg'), 50, FALSE, -5)
+ playsound(get_turf(src), pick('sound/mobs/non-humanoids/hiss/lowHiss2.ogg', 'sound/mobs/non-humanoids/hiss/lowHiss3.ogg', 'sound/mobs/non-humanoids/hiss/lowHiss4.ogg'), 50, FALSE, -5)
return ..()
/mob/living/carbon/alien/adult/setGrabState(newstate)
@@ -121,7 +121,7 @@ GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list(
lucky_winner.visible_message(span_danger("[src] is attempting to devour [lucky_winner]!"), \
span_userdanger("[src] is attempting to devour you!"))
- playsound(lucky_winner, 'sound/creatures/alien_eat.ogg', 100)
+ playsound(lucky_winner, 'sound/mobs/non-humanoids/alien/alien_eat.ogg', 100)
if(!do_after(src, devour_time, lucky_winner, extra_checks = CALLBACK(src, PROC_REF(can_consume), lucky_winner)))
return TRUE
if(!can_consume(lucky_winner))
diff --git a/code/modules/mob/living/carbon/alien/adult/adult_defense.dm b/code/modules/mob/living/carbon/alien/adult/adult_defense.dm
index d89f6de30d6e7..3b65549d6d9c0 100644
--- a/code/modules/mob/living/carbon/alien/adult/adult_defense.dm
+++ b/code/modules/mob/living/carbon/alien/adult/adult_defense.dm
@@ -33,7 +33,7 @@
apply_damage(damage, BRUTE, affecting)
log_combat(user, src, "attacked")
else
- playsound(loc, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
+ playsound(loc, 'sound/items/weapons/punchmiss.ogg', 25, TRUE, -1)
visible_message(span_danger("[user]'s punch misses [src]!"), \
span_danger("You avoid [user]'s punch!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_warning("Your punch misses [src]!"))
diff --git a/code/modules/mob/living/carbon/alien/adult/alien_powers.dm b/code/modules/mob/living/carbon/alien/adult/alien_powers.dm
index 999ec07f41a65..0b760c0f2e7b9 100644
--- a/code/modules/mob/living/carbon/alien/adult/alien_powers.dm
+++ b/code/modules/mob/living/carbon/alien/adult/alien_powers.dm
@@ -135,7 +135,7 @@ Doesn't work on other aliens/AI.*/
if(!chosen_recipient)
return FALSE
- var/to_whisper = tgui_input_text(owner, title = "Alien Whisper")
+ var/to_whisper = tgui_input_text(owner, title = "Alien Whisper", max_length = MAX_MESSAGE_LEN)
if(QDELETED(chosen_recipient) || QDELETED(src) || QDELETED(owner) || !IsAvailable(feedback = TRUE) || !to_whisper)
return FALSE
if(chosen_recipient.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0))
@@ -299,7 +299,7 @@ Doesn't work on other aliens/AI.*/
neurotoxin.preparePixelProjectile(target, caller, modifiers)
neurotoxin.firer = caller
neurotoxin.fire()
- caller.newtonian_move(get_dir(target, caller))
+ caller.newtonian_move(get_angle(target, caller))
return TRUE
// Has to return TRUE, otherwise is skipped.
@@ -379,7 +379,7 @@ Doesn't work on other aliens/AI.*/
owner.visible_message(span_danger("[owner] hurls out the contents of their stomach!"))
var/dir_angle = dir2angle(owner.dir)
- playsound(owner, 'sound/creatures/alien_york.ogg', 100)
+ playsound(owner, 'sound/mobs/non-humanoids/alien/alien_york.ogg', 100)
melting_pot.eject_stomach(slice_off_turfs(owner, border_diamond_range_turfs(owner, 9), dir_angle - angle_delta, dir_angle + angle_delta), 4, mob_speed, spit_speed)
/// Gets the plasma level of this carbon's plasma vessel, or -1 if they don't have one
diff --git a/code/modules/mob/living/carbon/alien/adult/queen.dm b/code/modules/mob/living/carbon/alien/adult/queen.dm
index 4387f7db3eca0..a925b4c5460ea 100644
--- a/code/modules/mob/living/carbon/alien/adult/queen.dm
+++ b/code/modules/mob/living/carbon/alien/adult/queen.dm
@@ -168,10 +168,9 @@
span_noticealien("The queen has granted you a promotion to Praetorian!"),
)
- var/mob/living/carbon/alien/adult/royal/praetorian/new_prae = new(to_promote.loc)
- to_promote.mind.transfer_to(new_prae)
-
- qdel(to_promote)
+ var/mob/living/carbon/alien/lucky_winner = to_promote
+ var/mob/living/carbon/alien/adult/royal/praetorian/new_prae = new(lucky_winner.loc)
+ lucky_winner.alien_evolve(new_prae)
qdel(src)
return TRUE
diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm
index f587b9d00160c..9744bcbada7e5 100644
--- a/code/modules/mob/living/carbon/alien/alien.dm
+++ b/code/modules/mob/living/carbon/alien/alien.dm
@@ -23,6 +23,13 @@
unique_name = TRUE
var/static/regex/alien_name_regex = new("alien (larva|sentinel|drone|hunter|praetorian|queen)( \\(\\d+\\))?")
+ var/static/list/xeno_allowed_items = typecacheof(list(
+ /obj/item/clothing/mask/facehugger,
+ /obj/item/toy/basketball, // playing ball against a xeno is rigged since they cannot be disarmed, their game is out of this world
+ /obj/item/toy/toy_xeno,
+ /obj/item/sticker, //funny ~Jimmyl
+ /obj/item/toy/plush/rouny,
+ ))
/mob/living/carbon/alien/Initialize(mapload)
add_verb(src, /mob/living/proc/mob_sleep)
@@ -37,6 +44,11 @@
. = ..()
if(alien_speed)
update_alien_speed()
+ LoadComponent( \
+ /datum/component/itempicky, \
+ xeno_allowed_items, \
+ span_alien("Your claws lack the dexterity to hold %TARGET."), \
+ CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_has_trait), src, TRAIT_ADVANCEDTOOLUSER))
/mob/living/carbon/alien/create_internal_organs()
organs += new /obj/item/organ/internal/brain/alien
@@ -131,6 +143,11 @@ Des: Removes all infected images from the alien.
mind.name = new_xeno.real_name
mind.transfer_to(new_xeno)
+ var/obj/item/organ/internal/stomach/alien/melting_pot = get_organ_slot(ORGAN_SLOT_STOMACH)
+ var/obj/item/organ/internal/stomach/alien/frying_pan = new_xeno.get_organ_slot(ORGAN_SLOT_STOMACH)
+ if(istype(melting_pot) && istype(frying_pan))
+ for (var/atom/movable/poor_sod as anything in melting_pot.stomach_contents)
+ frying_pan.consume_thing(poor_sod)
qdel(src)
/// Changes the name of the xeno we are evolving into in order to keep the same numerical identifier the old xeno had.
@@ -149,9 +166,6 @@ Des: Removes all infected images from the alien.
set_name()
-/mob/living/carbon/alien/can_hold_items(obj/item/I)
- return (I && (I.item_flags & XENOMORPH_HOLDABLE || ISADVANCEDTOOLUSER(src)) && ..())
-
/mob/living/carbon/alien/on_lying_down(new_lying_angle)
. = ..()
update_icons()
diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm
index 630171ba2c15d..0217a99fa04be 100644
--- a/code/modules/mob/living/carbon/alien/alien_defense.dm
+++ b/code/modules/mob/living/carbon/alien/alien_defense.dm
@@ -27,7 +27,7 @@ In all, this is a lot like the monkey code. /N
visible_message(span_notice("[user.name] nuzzles [src] trying to wake [p_them()] up!"))
else if(health > 0)
user.do_attack_animation(src, ATTACK_EFFECT_BITE)
- playsound(loc, 'sound/weapons/bite.ogg', 50, TRUE, -1)
+ playsound(loc, 'sound/items/weapons/bite.ogg', 50, TRUE, -1)
visible_message(span_danger("[user.name] bites [src]!"), \
span_userdanger("[user.name] bites you!"), span_hear("You hear a chomp!"), COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_danger("You bite [src]!"))
diff --git a/code/modules/mob/living/carbon/alien/emote.dm b/code/modules/mob/living/carbon/alien/emote.dm
index 10d7550bb78fc..717e18c9b3166 100644
--- a/code/modules/mob/living/carbon/alien/emote.dm
+++ b/code/modules/mob/living/carbon/alien/emote.dm
@@ -27,4 +27,4 @@
/datum/emote/living/alien/roar/get_sound(mob/living/user)
if(isalienadult(user))
- return 'sound/voice/hiss5.ogg'
+ return 'sound/mobs/non-humanoids/hiss/hiss5.ogg'
diff --git a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
index 62dd4f88b2177..8f2446d000fde 100644
--- a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
+++ b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
@@ -17,7 +17,7 @@
var/obj/item/bodypart/affecting = get_bodypart(get_random_valid_zone(user.zone_selected))
apply_damage(damage, BRUTE, affecting)
else
- playsound(loc, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1)
+ playsound(loc, 'sound/items/weapons/punchmiss.ogg', 25, TRUE, -1)
visible_message(span_danger("[user]'s kick misses [src]!"), \
span_danger("You avoid [user]'s kick!"), span_hear("You hear a swoosh!"), COMBAT_MESSAGE_RANGE, user)
to_chat(user, span_warning("Your kick misses [src]!"))
diff --git a/code/modules/mob/living/carbon/alien/larva/life.dm b/code/modules/mob/living/carbon/alien/larva/life.dm
index 147079ae720c3..52f7b8dfc4789 100644
--- a/code/modules/mob/living/carbon/alien/larva/life.dm
+++ b/code/modules/mob/living/carbon/alien/larva/life.dm
@@ -11,7 +11,7 @@
/mob/living/carbon/alien/larva/update_stat()
- if(status_flags & GODMODE)
+ if(HAS_TRAIT(src, TRAIT_GODMODE))
return
if(stat != DEAD)
if(health <= -maxHealth || !get_organ_by_type(/obj/item/organ/internal/brain))
diff --git a/code/modules/mob/living/carbon/alien/life.dm b/code/modules/mob/living/carbon/alien/life.dm
index f7ecd3075171e..7bd7d7aec49af 100644
--- a/code/modules/mob/living/carbon/alien/life.dm
+++ b/code/modules/mob/living/carbon/alien/life.dm
@@ -3,7 +3,7 @@
return..()
/mob/living/carbon/alien/check_breath(datum/gas_mixture/breath)
- if(status_flags & GODMODE)
+ if(HAS_TRAIT(src, TRAIT_GODMODE))
return
if(!breath || (breath.total_moles() == 0))
diff --git a/code/modules/mob/living/carbon/alien/organs.dm b/code/modules/mob/living/carbon/alien/organs.dm
index 3c063aec7e752..ac9fd81cf976b 100644
--- a/code/modules/mob/living/carbon/alien/organs.dm
+++ b/code/modules/mob/living/carbon/alien/organs.dm
@@ -290,7 +290,7 @@
// At 100% damage, the stomach burts
// Otherwise, we give them a -50% -> 50% chance scaling with damage dealt
if(!prob((damage_ratio * 100) - 50) && damage_ratio != 1)
- playsound(play_from, 'sound/creatures/alien_organ_cut.ogg', 100, 1)
+ playsound(play_from, 'sound/mobs/non-humanoids/alien/alien_organ_cut.ogg', 100, 1)
// We try and line up the "jump" here with the sound of the hit
var/oldx = play_from.pixel_x
var/oldy = play_from.pixel_y
@@ -318,7 +318,7 @@
play_from.visible_message(span_danger("[user] blows a hole in [stomach_text] and escapes!"), \
span_userdanger("[user] escapes from your [stomach_text]. Hell, that hurts."))
- playsound(get_turf(play_from), 'sound/creatures/alien_explode.ogg', 100, extrarange = 4)
+ playsound(get_turf(play_from), 'sound/mobs/non-humanoids/alien/alien_explode.ogg', 100, extrarange = 4)
eject_stomach(border_diamond_range_turfs(play_from, 6), 5, 1.5, 1, 8)
shake_camera(user, 1 SECONDS, 3)
if(owner)
diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
index 6d1ad16c8b1c8..f4b0fffb7a085 100644
--- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
+++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
@@ -120,7 +120,7 @@
var/atom/xeno_loc = get_turf(owner)
var/mob/living/carbon/alien/larva/new_xeno = new(xeno_loc)
new_xeno.key = ghost.key
- SEND_SOUND(new_xeno, sound('sound/voice/hiss5.ogg',0,0,0,100)) //To get the player's attention
+ SEND_SOUND(new_xeno, sound('sound/mobs/non-humanoids/hiss/hiss5.ogg',0,0,0,100)) //To get the player's attention
new_xeno.add_traits(list(TRAIT_HANDS_BLOCKED, TRAIT_IMMOBILIZED, TRAIT_NO_TRANSFORM), type) //so we don't move during the bursting animation
new_xeno.SetInvisibility(INVISIBILITY_MAXIMUM, id=type)
diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm
index d1ec6d7e88038..597769f11f271 100644
--- a/code/modules/mob/living/carbon/alien/special/facehugger.dm
+++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm
@@ -23,7 +23,6 @@
flags_cover = MASKCOVERSEYES | MASKCOVERSMOUTH
layer = MOB_LAYER
max_integrity = 100
- item_flags = XENOMORPH_HOLDABLE
slowdown = 2
var/stat = CONSCIOUS //UNCONSCIOUS is the idle state in this case
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index a4becbcd0eb60..a18bb74bb34b3 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -99,7 +99,7 @@
log_combat(src, victim, "crashed into")
if(oof_noise)
- playsound(src,'sound/weapons/punch1.ogg',50,TRUE)
+ playsound(src,'sound/items/weapons/punch1.ogg',50,TRUE)
//Throwing stuff
/mob/living/carbon/proc/toggle_throw_mode()
@@ -182,13 +182,13 @@
if(thrown_item.throw_verb)
verb_text = thrown_item.throw_verb
do_attack_animation(target, no_effect = 1)
- var/sound/throwsound = 'sound/weapons/throw.ogg'
+ var/sound/throwsound = 'sound/items/weapons/throw.ogg'
var/power_throw_text = "."
if(power_throw > 0) //If we have anything that boosts our throw power like hulk, we use the rougher heavier variant.
- throwsound = 'sound/weapons/throwhard.ogg'
+ throwsound = 'sound/items/weapons/throwhard.ogg'
power_throw_text = " really hard!"
if(power_throw < 0) //if we have anything that weakens our throw power like dward, we use a slower variant.
- throwsound = 'sound/weapons/throwsoft.ogg'
+ throwsound = 'sound/items/weapons/throwsoft.ogg'
power_throw_text = " flimsily."
frequency_number = frequency_number + (rand(-5,5)/100); //Adds a bit of randomness in the frequency to not sound exactly the same.
//The volume of the sound takes the minimum between the distance thrown or the max range an item, but no more than 50. Short throws are quieter. A fast throwing speed also makes the noise sharper.
@@ -202,7 +202,12 @@
if(istype(potential_spine))
extra_throw_range += potential_spine.added_throw_range
- newtonian_move(get_dir(target, src))
+ var/drift_force = max(0.5 NEWTONS, 1 NEWTONS + power_throw)
+ if (isitem(thrown_thing))
+ var/obj/item/thrown_item = thrown_thing
+ drift_force *= WEIGHT_TO_NEWTONS(thrown_item.w_class)
+
+ newtonian_move(get_angle(target, src), drift_force = drift_force)
thrown_thing.safe_throw_at(target, thrown_thing.throw_range + extra_throw_range, max(1,thrown_thing.throw_speed + power_throw), src, null, null, null, move_force)
/mob/living/carbon/proc/canBeHandcuffed()
@@ -550,7 +555,7 @@
//Updates the mob's health from bodyparts and mob damage variables
/mob/living/carbon/updatehealth()
- if(status_flags & GODMODE)
+ if(HAS_TRAIT(src, TRAIT_GODMODE))
return
var/total_burn = 0
var/total_brute = 0
@@ -835,7 +840,7 @@
/mob/living/carbon/update_stat()
- if(status_flags & GODMODE)
+ if(HAS_TRAIT(src, TRAIT_GODMODE))
return
if(stat != DEAD)
if(health <= HEALTH_THRESHOLD_DEAD && !HAS_TRAIT(src, TRAIT_NODEATH))
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 978714e9bf7a3..f8552fdfa243f 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -392,7 +392,7 @@
if(body_position != STANDING_UP && !resting && !buckled && !HAS_TRAIT(src, TRAIT_FLOORED))
get_up(TRUE)
- playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ playsound(loc, 'sound/items/weapons/thudswoosh.ogg', 50, TRUE, -1)
// Shake animation
if (incapacitated)
@@ -499,7 +499,7 @@
ears.set_organ_damage(ears.maxHealth)
else if(ears.damage >= 5)
to_chat(src, span_warning("Your ears start to ring!"))
- SEND_SOUND(src, sound('sound/weapons/flash_ring.ogg',0,1,0,250))
+ SEND_SOUND(src, sound('sound/items/weapons/flash_ring.ogg',0,1,0,250))
return effect_amount //how soundbanged we are
@@ -645,7 +645,7 @@
var/bleed_rate = grasped_part.get_modified_bleed_rate()
var/bleeding_text = (bleed_rate ? ", trying to stop the bleeding" : "")
user.visible_message(span_danger("[user] grasps at [user.p_their()] [grasped_part.name][bleeding_text]."), span_notice("You grab hold of your [grasped_part.name] tightly."), vision_distance=COMBAT_MESSAGE_RANGE)
- playsound(get_turf(src), 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
+ playsound(get_turf(src), 'sound/items/weapons/thudswoosh.ogg', 50, TRUE, -1)
return TRUE
/// Randomise a body part and organ of this mob
diff --git a/code/modules/mob/living/carbon/damage_procs.dm b/code/modules/mob/living/carbon/damage_procs.dm
index b781b296bc882..ccbf735bd85aa 100644
--- a/code/modules/mob/living/carbon/damage_procs.dm
+++ b/code/modules/mob/living/carbon/damage_procs.dm
@@ -10,6 +10,7 @@
sharpness = NONE,
attack_direction = null,
attacking_item,
+ wound_clothing = TRUE,
)
// Spread damage should always have def zone be null
if(spread_damage)
@@ -46,6 +47,7 @@
sharpness = NONE,
attack_direction = null,
attacking_item,
+ wound_clothing = TRUE,
)
// Add relevant DR modifiers into blocked value to pass to parent
@@ -103,7 +105,7 @@
. = heal_overall_damage(brute = abs(amount), required_bodytype = required_bodytype, updating_health = updating_health, forced = forced)
/mob/living/carbon/setBruteLoss(amount, updating_health = TRUE, forced = FALSE, required_bodytype)
- if(!forced && (status_flags & GODMODE))
+ if(!forced && HAS_TRAIT(src, TRAIT_GODMODE))
return FALSE
var/current = getBruteLoss()
var/diff = amount - current
@@ -120,7 +122,7 @@
. = heal_overall_damage(burn = abs(amount), required_bodytype = required_bodytype, updating_health = updating_health, forced = forced)
/mob/living/carbon/setFireLoss(amount, updating_health = TRUE, forced = FALSE, required_bodytype)
- if(!forced && (status_flags & GODMODE))
+ if(!forced && HAS_TRAIT(src, TRAIT_GODMODE))
return FALSE
var/current = getFireLoss()
var/diff = amount - current
@@ -150,7 +152,7 @@
apply_status_effect(/datum/status_effect/incapacitating/stamcrit)
/**
- * If an organ exists in the slot requested, and we are capable of taking damage (we don't have [GODMODE] on), call the damage proc on that organ.
+ * If an organ exists in the slot requested, and we are capable of taking damage (we don't have TRAIT_GODMODE), call the damage proc on that organ.
*
* Arguments:
* * slot - organ slot, like [ORGAN_SLOT_HEART]
@@ -162,14 +164,14 @@
*/
/mob/living/carbon/adjustOrganLoss(slot, amount, maximum, required_organ_flag = NONE)
var/obj/item/organ/affected_organ = get_organ_slot(slot)
- if(!affected_organ || (status_flags & GODMODE))
+ if(!affected_organ || HAS_TRAIT(src, TRAIT_GODMODE))
return FALSE
if(required_organ_flag && !(affected_organ.organ_flags & required_organ_flag))
return FALSE
return affected_organ.apply_organ_damage(amount, maximum)
/**
- * If an organ exists in the slot requested, and we are capable of taking damage (we don't have [GODMODE] on), call the set damage proc on that organ, which can
+ * If an organ exists in the slot requested, and we are capable of taking damage (we don't have TRAIT_GODMODE), call the set damage proc on that organ, which can
* set or clear the failing variable on that organ, making it either cease or start functions again, unlike adjustOrganLoss.
*
* Arguments:
@@ -181,7 +183,7 @@
*/
/mob/living/carbon/setOrganLoss(slot, amount, required_organ_flag = NONE)
var/obj/item/organ/affected_organ = get_organ_slot(slot)
- if(!affected_organ || (status_flags & GODMODE))
+ if(!affected_organ || HAS_TRAIT(src, TRAIT_GODMODE))
return FALSE
if(required_organ_flag && !(affected_organ.organ_flags & required_organ_flag))
return FALSE
@@ -267,7 +269,7 @@
*/
/mob/living/carbon/take_bodypart_damage(brute = 0, burn = 0, updating_health = TRUE, required_bodytype, check_armor = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = NONE)
. = FALSE
- if(status_flags & GODMODE)
+ if(HAS_TRAIT(src, TRAIT_GODMODE))
return
var/list/obj/item/bodypart/parts = get_damageable_bodyparts(required_bodytype)
if(!parts.len)
@@ -314,7 +316,7 @@
/mob/living/carbon/take_overall_damage(brute = 0, burn = 0, stamina = 0, updating_health = TRUE, forced = FALSE, required_bodytype)
. = FALSE
- if(!forced && (status_flags & GODMODE))
+ if(!forced && HAS_TRAIT(src, TRAIT_GODMODE))
return
// treat negative args as positive
brute = abs(brute)
diff --git a/code/modules/mob/living/carbon/emote.dm b/code/modules/mob/living/carbon/emote.dm
index 6995defb6dafe..74a5091565c74 100644
--- a/code/modules/mob/living/carbon/emote.dm
+++ b/code/modules/mob/living/carbon/emote.dm
@@ -29,17 +29,17 @@
if(!user.get_bodypart(BODY_ZONE_L_ARM) || !user.get_bodypart(BODY_ZONE_R_ARM))
return
return pick(
- 'sound/misc/clap1.ogg',
- 'sound/misc/clap2.ogg',
- 'sound/misc/clap3.ogg',
- 'sound/misc/clap4.ogg',
+ 'sound/mobs/humanoids/human/clap/clap1.ogg',
+ 'sound/mobs/humanoids/human/clap/clap2.ogg',
+ 'sound/mobs/humanoids/human/clap/clap3.ogg',
+ 'sound/mobs/humanoids/human/clap/clap4.ogg',
)
/datum/emote/living/carbon/crack
key = "crack"
key_third_person = "cracks"
message = "cracks their knuckles."
- sound = 'sound/misc/knuckles.ogg'
+ sound = 'sound/mobs/humanoids/human/knuckle_crack/knuckles.ogg'
hands_use_check = TRUE
cooldown = 6 SECONDS
@@ -180,7 +180,7 @@
/datum/emote/living/carbon/snap/get_sound(mob/living/user)
if(ishuman(user))
- return pick('sound/misc/fingersnap1.ogg', 'sound/misc/fingersnap2.ogg')
+ return pick('sound/mobs/humanoids/human/snap/fingersnap1.ogg', 'sound/mobs/humanoids/human/snap/fingersnap2.ogg')
return null
/datum/emote/living/carbon/shoesteal
diff --git a/code/modules/mob/living/carbon/examine.dm b/code/modules/mob/living/carbon/examine.dm
index 6d6d7ada73ceb..43e0e5f856fa6 100644
--- a/code/modules/mob/living/carbon/examine.dm
+++ b/code/modules/mob/living/carbon/examine.dm
@@ -1,4 +1,19 @@
+/// Adds a newline to the examine list if the above entry is not empty and it is not the first element in the list
+#define ADD_NEWLINE_IF_NECESSARY(list) if(length(list) > 0 && list[length(list)]) { list += "" }
+
+/mob/living/carbon/human/get_examine_name(mob/user)
+ if(!HAS_TRAIT(user, TRAIT_PROSOPAGNOSIA))
+ return ..()
+
+ return "Unknown"
+
+/mob/living/carbon/human/get_examine_icon(mob/user)
+ return null
+
/mob/living/carbon/examine(mob/user)
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN))
+ return list(span_warning("You're struggling to make out any details..."))
+
var/t_He = p_They()
var/t_His = p_Their()
var/t_his = p_their()
@@ -6,157 +21,551 @@
var/t_has = p_have()
var/t_is = p_are()
- . = list("This is [icon2html(src, user)] \a [src]!")
- var/obscured = check_obscured_slots()
-
- if (handcuffed)
- . += span_warning("[t_He] [t_is] [icon2html(handcuffed, user)] handcuffed!")
- if (head)
- . += "[t_He] [t_is] wearing [head.get_examine_string(user)] on [t_his] head. "
- if(wear_mask && !(obscured & ITEM_SLOT_MASK))
- . += "[t_He] [t_is] wearing [wear_mask.get_examine_string(user)] on [t_his] face."
- if(wear_neck && !(obscured & ITEM_SLOT_NECK))
- . += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck."
+ . = list()
+ . += get_clothing_examine_info(user)
+ // give us some space between clothing examine and the rest
+ ADD_NEWLINE_IF_NECESSARY(.)
- for(var/obj/item/held_thing in held_items)
- if(held_thing.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM))
- continue
- . += "[t_He] [t_is] holding [held_thing.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(held_thing))]."
-
- if (back)
- . += "[t_He] [t_has] [back.get_examine_string(user)] on [t_his] back."
var/appears_dead = FALSE
- if (stat == DEAD)
+ var/just_sleeping = FALSE
+
+ if(!appears_alive())
appears_dead = TRUE
- if(get_organ_by_type(/obj/item/organ/internal/brain))
- . += span_deadsay("[t_He] [t_is] limp and unresponsive, with no signs of life.")
- else if(get_bodypart(BODY_ZONE_HEAD))
- . += span_deadsay("It appears that [t_his] brain is missing...")
-
- var/list/msg = list("")
- for(var/obj/item/bodypart/bodypart as anything in bodyparts)
- for(var/obj/item/embedded_item as anything in bodypart.embedded_objects)
- if(embedded_item.is_embed_harmless())
- msg += "[t_He] [t_has] [icon2html(embedded_item, user)] \a [embedded_item] stuck to [t_his] [bodypart.name]!\n"
- else
- msg += "[t_He] [t_has] [icon2html(embedded_item, user)] \a [embedded_item] embedded in [t_his] [bodypart.name]!\n"
- for(var/datum/wound/bodypart_wound as anything in bodypart.wounds)
- msg += "[bodypart_wound.get_examine_description(user)]\n"
- for(var/obj/item/bodypart/disabled_limb as anything in get_disabled_limbs())
+ var/obj/item/clothing/glasses/shades = get_item_by_slot(ITEM_SLOT_EYES)
+ var/are_we_in_weekend_at_bernies = shades?.tint && buckled && istype(buckled, /obj/vehicle/ridden/wheelchair)
+
+ if(isliving(user) && (HAS_MIND_TRAIT(user, TRAIT_NAIVE) || are_we_in_weekend_at_bernies))
+ just_sleeping = TRUE
+
+ if(!just_sleeping)
+ // since this is relatively important and giving it space makes it easier to read
+ ADD_NEWLINE_IF_NECESSARY(.)
+ if(HAS_TRAIT(src, TRAIT_SUICIDED))
+ . += span_warning("[t_He] appear[p_s()] to have committed suicide... there is no hope of recovery.")
+
+ . += generate_death_examine_text()
+
+ //Status effects
+ var/list/status_examines = get_status_effect_examinations()
+ if (length(status_examines))
+ . += status_examines
+
+ if(get_bodypart(BODY_ZONE_HEAD) && !get_organ_by_type(/obj/item/organ/internal/brain))
+ . += span_deadsay("It appears that [t_his] brain is missing...")
+
+ var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
+ var/list/disabled = list()
+ for(var/obj/item/bodypart/body_part as anything in bodyparts)
+ if(body_part.bodypart_disabled)
+ disabled += body_part
+ missing -= body_part.body_zone
+ for(var/obj/item/embedded as anything in body_part.embedded_objects)
+ var/stuck_wordage = embedded.is_embed_harmless() ? "stuck to" : "embedded in"
+ . += span_boldwarning("[t_He] [t_has] [icon2html(embedded, user)] \a [embedded] [stuck_wordage] [t_his] [body_part.plaintext_zone]!")
+
+ for(var/datum/wound/iter_wound as anything in body_part.wounds)
+ . += span_danger(iter_wound.get_examine_description(user))
+
+ for(var/obj/item/bodypart/body_part as anything in disabled)
var/damage_text
- damage_text = (disabled_limb.brute_dam >= disabled_limb.burn_dam) ? disabled_limb.heavy_brute_msg : disabled_limb.heavy_burn_msg
- msg += "[t_His] [disabled_limb.name] is [damage_text]!\n"
+ if(HAS_TRAIT(body_part, TRAIT_DISABLED_BY_WOUND))
+ continue // skip if it's disabled by a wound (cuz we'll be able to see the bone sticking out!)
+ if(body_part.get_damage() < body_part.max_damage) //we don't care if it's stamcritted
+ damage_text = "limp and lifeless"
+ else
+ damage_text = (body_part.brute_dam >= body_part.burn_dam) ? body_part.heavy_brute_msg : body_part.heavy_burn_msg
+ . += span_boldwarning("[capitalize(t_his)] [body_part.plaintext_zone] is [damage_text]!")
- for(var/obj/item/bodypart/missing_limb as anything in get_missing_limbs())
- if(missing_limb == BODY_ZONE_HEAD)
- msg += "[span_deadsay("[t_His] [parse_zone(missing_limb)] is missing!")]\n"
+ //stores missing limbs
+ var/l_limbs_missing = 0
+ var/r_limbs_missing = 0
+ for(var/gone in missing)
+ if(gone == BODY_ZONE_HEAD)
+ . += span_deadsay("[t_His] [parse_zone(gone)] is missing!")
continue
- msg += "[span_warning("[t_His] [parse_zone(missing_limb)] is missing!")]\n"
+ if(gone == BODY_ZONE_L_ARM || gone == BODY_ZONE_L_LEG)
+ l_limbs_missing++
+ else if(gone == BODY_ZONE_R_ARM || gone == BODY_ZONE_R_LEG)
+ r_limbs_missing++
+
+ . += span_boldwarning("[capitalize(t_his)] [parse_zone(gone)] is missing!")
+ if(l_limbs_missing >= 2 && r_limbs_missing == 0)
+ . += span_tinydanger("[t_He] look[p_s()] all right now...")
+ else if(l_limbs_missing == 0 && r_limbs_missing >= 2)
+ . += span_tinydanger("[t_He] really keep[p_s()] to the left...")
+ else if(l_limbs_missing >= 2 && r_limbs_missing >= 2)
+ . += span_tinydanger("[t_He] [p_do()]n't seem all there...")
- var/temp = getBruteLoss()
if(!(user == src && has_status_effect(/datum/status_effect/grouped/screwy_hud/fake_healthy))) //fake healthy
+ var/temp
+ if(user == src && has_status_effect(/datum/status_effect/grouped/screwy_hud/fake_crit))//fake damage
+ temp = 50
+ else
+ temp = getBruteLoss()
+ var/list/damage_desc = get_majority_bodypart_damage_desc()
if(temp)
- if (temp < 25)
- msg += "[t_He] [t_has] minor bruising.\n"
- else if (temp < 50)
- msg += "[t_He] [t_has] moderate bruising!\n"
+ if(temp < 25)
+ . += span_danger("[t_He] [t_has] minor [damage_desc[BRUTE]].")
+ else if(temp < 50)
+ . += span_danger("[t_He] [t_has] moderate [damage_desc[BRUTE]]!")
else
- msg += "[t_He] [t_has] severe bruising!\n"
+ . += span_bolddanger("[t_He] [t_has] severe [damage_desc[BRUTE]]!")
temp = getFireLoss()
if(temp)
- if (temp < 25)
- msg += "[t_He] [t_has] minor burns.\n"
+ if(temp < 25)
+ . += span_danger("[t_He] [t_has] minor [damage_desc[BURN]].")
else if (temp < 50)
- msg += "[t_He] [t_has] moderate burns!\n"
+ . += span_danger("[t_He] [t_has] moderate [damage_desc[BURN]]!")
else
- msg += "[t_He] [t_has] severe burns!\n"
+ . += span_bolddanger("[t_He] [t_has] severe [damage_desc[BURN]]!")
- if(HAS_TRAIT(src, TRAIT_DUMB))
- msg += "[t_He] seem[p_s()] to be clumsy and unable to think.\n"
+ if(pulledby?.grab_state)
+ . += span_warning("[t_He] [t_is] restrained by [pulledby]'s grip.")
- if(has_status_effect(/datum/status_effect/fire_handler/fire_stacks))
- msg += "[t_He] [t_is] covered in something flammable.\n"
- if(has_status_effect(/datum/status_effect/fire_handler/wet_stacks))
- msg += "[t_He] look[p_s()] a little soaked.\n"
+ if(nutrition < NUTRITION_LEVEL_STARVING - 50)
+ . += span_warning("[t_He] [t_is] severely malnourished.")
+ else if(nutrition >= NUTRITION_LEVEL_FAT)
+ if(user.nutrition < NUTRITION_LEVEL_STARVING - 50)
+ . += span_hypnophrase("[t_He] [t_is] plump and delicious looking - Like a fat little piggy. A tasty piggy.")
+ else
+ . += "[t_He] [t_is] quite chubby."
+ switch(disgust)
+ if(DISGUST_LEVEL_GROSS to DISGUST_LEVEL_VERYGROSS)
+ . += "[t_He] look[p_s()] a bit grossed out."
+ if(DISGUST_LEVEL_VERYGROSS to DISGUST_LEVEL_DISGUSTED)
+ . += "[t_He] look[p_s()] really grossed out."
+ if(DISGUST_LEVEL_DISGUSTED to INFINITY)
+ . += "[t_He] look[p_s()] extremely disgusted."
- if(pulledby?.grab_state)
- msg += "[t_He] [t_is] restrained by [pulledby]'s grip.\n"
+ var/apparent_blood_volume = blood_volume
+ if(HAS_TRAIT(src, TRAIT_USES_SKINTONES) && ishuman(src))
+ var/mob/living/carbon/human/husrc = src // gross istypesrc but easier than refactoring even further for now
+ if(husrc.skin_tone == "albino")
+ apparent_blood_volume -= (BLOOD_VOLUME_NORMAL * 0.25) // knocks you down a few pegs
+ switch(apparent_blood_volume)
+ if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
+ . += span_warning("[t_He] [t_has] pale skin.")
+ if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
+ . += span_boldwarning("[t_He] look[p_s()] like pale death.")
+ if(-INFINITY to BLOOD_VOLUME_BAD)
+ . += span_deadsay("[t_He] resemble[p_s()] a crushed, empty juice pouch.")
+
+ if(is_bleeding())
+ 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)
+ if(body_part.get_modified_bleed_rate())
+ bleeding_limbs += body_part.plaintext_zone
+ if(body_part.grasped_by)
+ grasped_limbs += body_part.plaintext_zone
+
+ if(LAZYLEN(bleeding_limbs))
+ var/bleed_text = ""
+ if(appears_dead)
+ bleed_text += ""
+ bleed_text += "Blood is visible in [t_his] open"
+ else
+ bleed_text += ""
+ bleed_text += "[t_He] [t_is] bleeding from [t_his] "
+
+ bleed_text += english_list(bleeding_limbs, and_text = " and ")
+
+ if(appears_dead)
+ bleed_text += ", but it has pooled and is not flowing."
+ else
+ if(HAS_TRAIT(src, TRAIT_BLOODY_MESS))
+ bleed_text += " incredibly quickly"
+ bleed_text += "!"
+
+ if(appears_dead)
+ bleed_text += ""
+ else
+ bleed_text += ""
+ bleed_text += ""
+
+ . += bleed_text
+ if(LAZYLEN(grasped_limbs))
+ for(var/grasped_part in grasped_limbs)
+ . += "[t_He] [t_is] holding [t_his] [grasped_part] to slow the bleeding!"
+
+ if(reagents.has_reagent(/datum/reagent/teslium, needs_metabolizing = TRUE))
+ . += span_smallnoticeital("[t_He] [t_is] emitting a gentle blue glow!") // this should be signalized
+
+ if(just_sleeping)
+ . += span_notice("[t_He] [t_is]n't responding to anything around [t_him] and seem[p_s()] to be asleep.")
+
+ else if(!appears_dead)
+ var/mob/living/living_user = user
+ if(src != user)
+ if(HAS_TRAIT(user, TRAIT_EMPATH))
+ if (combat_mode)
+ . += "[t_He] seem[p_s()] to be on guard."
+ if (getOxyLoss() >= 10)
+ . += "[t_He] seem[p_s()] winded."
+ if (getToxLoss() >= 10)
+ . += "[t_He] seem[p_s()] sickly."
+ if(mob_mood.sanity <= SANITY_DISTURBED)
+ . += "[t_He] seem[p_s()] distressed."
+ living_user.add_mood_event("empath", /datum/mood_event/sad_empath, src)
+ if(is_blind())
+ . += "[t_He] appear[p_s()] to be staring off into space."
+ if (HAS_TRAIT(src, TRAIT_DEAF))
+ . += "[t_He] appear[p_s()] to not be responding to noises."
+ if (bodytemperature > dna.species.bodytemp_heat_damage_limit)
+ . += "[t_He] [t_is] flushed and wheezing."
+ if (bodytemperature < dna.species.bodytemp_cold_damage_limit)
+ . += "[t_He] [t_is] shivering."
+
+ if(HAS_TRAIT(user, TRAIT_SPIRITUAL) && mind?.holy_role)
+ . += "[t_He] [t_has] a holy aura about [t_him]."
+ living_user.add_mood_event("religious_comfort", /datum/mood_event/religiously_comforted)
- var/scar_severity = 0
- for(var/i in all_scars)
- var/datum/scar/S = i
- if(S.is_visible(user))
- scar_severity += S.severity
-
- switch(scar_severity)
- if(1 to 4)
- msg += "[span_tinynoticeital("[t_He] [t_has] visible scarring, you can look again to take a closer look...")]\n"
- if(5 to 8)
- msg += "[span_smallnoticeital("[t_He] [t_has] several bad scars, you can look again to take a closer look...")]\n"
- if(9 to 11)
- msg += "[span_notice("[t_He] [t_has] significantly disfiguring scarring, you can look again to take a closer look...")]\n"
- if(12 to INFINITY)
- msg += "[span_notice("[t_He] [t_is] just absolutely fucked up, you can look again to take a closer look...")]\n"
-
- msg += ""
-
- . += msg.Join("")
-
- if(!appears_dead)
switch(stat)
- if(SOFT_CRIT)
- . += "[t_His] breathing is shallow and labored."
if(UNCONSCIOUS, HARD_CRIT)
- . += "[t_He] [t_is]n't responding to anything around [t_him] and seems to be asleep."
-
- var/trait_exam = common_trait_examine()
- if (!isnull(trait_exam))
- . += trait_exam
-
- if(mob_mood)
- switch(mob_mood.shown_mood)
- if(-INFINITY to MOOD_SAD4)
- . += "[t_He] look[p_s()] depressed."
- if(MOOD_SAD4 to MOOD_SAD3)
- . += "[t_He] look[p_s()] very sad."
- if(MOOD_SAD3 to MOOD_SAD2)
- . += "[t_He] look[p_s()] a bit down."
- if(MOOD_HAPPY2 to MOOD_HAPPY3)
- . += "[t_He] look[p_s()] quite happy."
- if(MOOD_HAPPY3 to MOOD_HAPPY4)
- . += "[t_He] look[p_s()] very happy."
- if(MOOD_HAPPY4 to INFINITY)
- . += "[t_He] look[p_s()] ecstatic."
- . += ""
+ . += span_notice("[t_He] [t_is]n't responding to anything around [t_him] and seem[p_s()] to be asleep.")
+ if(SOFT_CRIT)
+ . += span_notice("[t_He] [t_is] barely conscious.")
+ if(CONSCIOUS)
+ if(HAS_TRAIT(src, TRAIT_DUMB))
+ . += "[t_He] [t_has] a stupid expression on [t_his] face."
+ if(get_organ_by_type(/obj/item/organ/internal/brain) && isnull(ai_controller))
+ var/npc_message = ""
+ if(!key)
+ npc_message = "[t_He] [t_is] totally catatonic. The stresses of life in deep-space must have been too much for [t_him]. Any recovery is unlikely."
+ else if(!client)
+ npc_message ="[t_He] [t_has] a blank, absent-minded stare and appears completely unresponsive to anything. [t_He] may snap out of it soon."
+ if(npc_message)
+ // give some space since this is usually near the end
+ ADD_NEWLINE_IF_NECESSARY(.)
+ . += span_deadsay(npc_message)
+
+ var/scar_severity = 0
+ for(var/datum/scar/scar as anything in all_scars)
+ if(scar.is_visible(user))
+ scar_severity += scar.severity
+
+ if(scar_severity >= 1)
+ // give some space since this is even more usually near the end
+ ADD_NEWLINE_IF_NECESSARY(.)
+ switch(scar_severity)
+ if(1 to 4)
+ . += span_tinynoticeital("[t_He] [t_has] visible scarring, you can look again to take a closer look...")
+ if(5 to 8)
+ . += span_smallnoticeital("[t_He] [t_has] several bad scars, you can look again to take a closer look...")
+ if(9 to 11)
+ . += span_notice("[t_He] [t_has] significantly disfiguring scarring, you can look again to take a closer look...")
+ if(12 to INFINITY)
+ . += span_notice("[t_He] [t_is] just absolutely fucked up, you can look again to take a closer look...")
+
+ if(HAS_TRAIT(src, TRAIT_HUSK))
+ . += span_warning("This body has been reduced to a grotesque husk.")
+ if(HAS_MIND_TRAIT(user, TRAIT_MORBID))
+ if(HAS_TRAIT(src, TRAIT_DISSECTED))
+ . += span_notice("[user.p_They()] appear[user.p_s()] to have been dissected. Useless for examination... for now.")
+ if(HAS_TRAIT(src, TRAIT_SURGICALLY_ANALYZED))
+ . += span_notice("A skilled hand has mapped this one's internal intricacies. It will be far easier to perform future experimentations upon [user.p_them()]. Exquisite.")
+ if(HAS_MIND_TRAIT(user, TRAIT_EXAMINE_FITNESS))
+ . += compare_fitness(user)
+
+ var/hud_info = get_hud_examine_info(user)
+ if(length(hud_info))
+ . += hud_info
+
+ if(isobserver(user))
+ ADD_NEWLINE_IF_NECESSARY(.)
+ . += "Quirks: [get_quirk_string(FALSE, CAT_QUIRK_ALL)]"
SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE, user, .)
+ if(length(.))
+ .[1] = "" + .[1]
+ .[length(.)] += ""
+ return .
-/mob/living/carbon/examine_more(mob/user)
- . = ..()
- . += span_notice("You examine [src] closer, and note the following...")
-
- if(dna) //not all carbons have it. eg - xenos
- //On closer inspection, this man isnt a man at all!
- var/list/covered_zones = get_covered_body_zones()
- for(var/obj/item/bodypart/part as anything in bodyparts)
- if(part.body_zone in covered_zones)
- continue
- if(part.limb_id != dna.species.examine_limb_id)
- . += "[span_info("[p_They()] [p_have()] \an [part.name].")]"
-
- var/list/visible_scars
- for(var/i in all_scars)
- var/datum/scar/S = i
- if(S.is_visible(user))
- LAZYADD(visible_scars, S)
-
- for(var/i in visible_scars)
- var/datum/scar/S = i
- var/scar_text = S.get_examine_description(user)
- if(scar_text)
- . += "[scar_text]"
+/**
+ * Shows any and all examine text related to any status effects the user has.
+ */
+/mob/living/proc/get_status_effect_examinations()
+ var/list/examine_list = list()
+ for(var/datum/status_effect/effect as anything in status_effects)
+ var/effect_text = effect.get_examine_text()
+ if(!effect_text)
+ continue
+
+ examine_list += effect_text
+
+ if(!length(examine_list))
+ return
+
+ return examine_list.Join(" ")
+
+/// Returns death message for mob examine text
+/mob/living/carbon/proc/generate_death_examine_text()
+ var/mob/dead/observer/ghost = get_ghost(TRUE, TRUE)
+ var/t_He = p_They()
+ var/t_his = p_their()
+ var/t_is = p_are()
+ //This checks to see if the body is revivable
+ if(get_organ_by_type(/obj/item/organ/internal/brain) && (client || HAS_TRAIT(src, TRAIT_MIND_TEMPORARILY_GONE) || (ghost?.can_reenter_corpse && ghost?.client)))
+ return span_deadsay("[t_He] [t_is] limp and unresponsive; there are no signs of life...")
+ else
+ return span_deadsay("[t_He] [t_is] limp and unresponsive; there are no signs of life and [t_his] soul has departed...")
+
+/// Returns a list of "damtype" => damage description based off of which bodypart description is most common
+/mob/living/carbon/proc/get_majority_bodypart_damage_desc()
+ 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/damage_type in part.damage_examines)
+ var/damage_desc = part.damage_examines[damage_type]
+ if(!seen_damage[damage_type])
+ seen_damage[damage_type] = list()
+
+ if(!seen_damage[damage_type][damage_desc])
+ seen_damage[damage_type][damage_desc] = 1
+ else
+ seen_damage[damage_type][damage_desc] += 1
+
+ if(seen_damage[damage_type][damage_desc] > most_seen_damage[damage_type])
+ most_seen_damage[damage_type] = seen_damage[damage_type][damage_desc]
+ final_descriptions[damage_type] = damage_desc
+ return final_descriptions
+
+/// Coolects examine information about the mob's clothing and equipment
+/mob/living/carbon/proc/get_clothing_examine_info(mob/living/user)
+ . = list()
+ var/obscured = check_obscured_slots()
+ var/t_He = p_They()
+ var/t_His = p_Their()
+ var/t_his = p_their()
+ var/t_has = p_have()
+ var/t_is = p_are()
+ //head
+ if(head && !(obscured & ITEM_SLOT_HEAD) && !(head.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_is] wearing [head.examine_title(user)] on [t_his] head."
+ //back
+ if(back && !(back.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_has] [back.examine_title(user)] on [t_his] back."
+ //Hands
+ for(var/obj/item/held_thing in held_items)
+ if(held_thing.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM))
+ 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))]."
+ //gloves
+ if(gloves && !(obscured & ITEM_SLOT_GLOVES) && !(gloves.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_has] [gloves.examine_title(user)] on [t_his] hands."
+ else if(GET_ATOM_BLOOD_DNA_LENGTH(src))
+ if(num_hands)
+ . += span_warning("[t_He] [t_has] [num_hands > 1 ? "" : "a "]blood-stained hand[num_hands > 1 ? "s" : ""]!")
+ //handcuffed?
+ if(handcuffed)
+ var/cables_or_cuffs = istype(handcuffed, /obj/item/restraints/handcuffs/cable) ? "restrained with cable" : "handcuffed"
+ . += span_warning("[t_He] [t_is] [icon2html(handcuffed, user)] [cables_or_cuffs]!")
+ //shoes
+ if(shoes && !(obscured & ITEM_SLOT_FEET) && !(shoes.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_is] wearing [shoes.examine_title(user)] on [t_his] feet."
+ //mask
+ if(wear_mask && !(obscured & ITEM_SLOT_MASK) && !(wear_mask.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_has] [wear_mask.examine_title(user)] on [t_his] face."
+ if(wear_neck && !(obscured & ITEM_SLOT_NECK) && !(wear_neck.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_is] wearing [wear_neck.examine_title(user)] around [t_his] neck."
+ //eyes
+ if(!(obscured & ITEM_SLOT_EYES) )
+ if(glasses && !(glasses.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_has] [glasses.examine_title(user)] covering [t_his] eyes."
+ else if(HAS_TRAIT(src, TRAIT_UNNATURAL_RED_GLOWY_EYES))
+ . += span_warning("[t_His] eyes are glowing with an unnatural red aura!")
+ else if(HAS_TRAIT(src, TRAIT_BLOODSHOT_EYES))
+ . += span_warning("[t_His] eyes are bloodshot!")
+ //ears
+ if(ears && !(obscured & ITEM_SLOT_EARS) && !(ears.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_has] [ears.examine_title(user)] on [t_his] ears."
+
+// Yes there's a lot of copypasta here, we can improve this later when carbons are less dumb in general
+/mob/living/carbon/human/get_clothing_examine_info(mob/living/user)
+ . = list()
+ var/obscured = check_obscured_slots()
+ var/t_He = p_They()
+ var/t_His = p_Their()
+ var/t_his = p_their()
+ var/t_has = p_have()
+ var/t_is = p_are()
+
+ //uniform
+ if(w_uniform && !(obscured & ITEM_SLOT_ICLOTHING) && !(w_uniform.item_flags & EXAMINE_SKIP))
+ //accessory
+ var/accessory_message = ""
+ if(istype(w_uniform, /obj/item/clothing/under))
+ var/obj/item/clothing/under/undershirt = w_uniform
+ var/list/accessories = undershirt.list_accessories_with_icon(user)
+ if(length(accessories))
+ accessory_message = " with [english_list(accessories)] attached"
+
+ . += "[t_He] [t_is] wearing [w_uniform.examine_title(user)][accessory_message]."
+ //head
+ if(head && !(obscured & ITEM_SLOT_HEAD) && !(head.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_is] wearing [head.examine_title(user)] on [t_his] head."
+ //mask
+ if(wear_mask && !(obscured & ITEM_SLOT_MASK) && !(wear_mask.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_has] [wear_mask.examine_title(user)] on [t_his] face."
+ //neck
+ if(wear_neck && !(obscured & ITEM_SLOT_NECK) && !(wear_neck.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_is] wearing [wear_neck.examine_title(user)] around [t_his] neck."
+ //eyes
+ if(!(obscured & ITEM_SLOT_EYES) )
+ if(glasses && !(glasses.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_has] [glasses.examine_title(user)] covering [t_his] eyes."
+ else if(HAS_TRAIT(src, TRAIT_UNNATURAL_RED_GLOWY_EYES))
+ . += span_warning("[t_His] eyes are glowing with an unnatural red aura!")
+ else if(HAS_TRAIT(src, TRAIT_BLOODSHOT_EYES))
+ . += span_warning("[t_His] eyes are bloodshot!")
+ //ears
+ if(ears && !(obscured & ITEM_SLOT_EARS) && !(ears.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_has] [ears.examine_title(user)] on [t_his] ears."
+ //suit/armor
+ if(wear_suit && !(wear_suit.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_is] wearing [wear_suit.examine_title(user)]."
+ //suit/armor storage
+ if(s_store && !(obscured & ITEM_SLOT_SUITSTORE) && !(s_store.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_is] carrying [s_store.examine_title(user)] on [t_his] [wear_suit.name]."
+ //back
+ if(back && !(back.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_has] [back.examine_title(user)] on [t_his] back."
+ //ID
+ if(wear_id && !(wear_id.item_flags & EXAMINE_SKIP))
+ var/obj/item/card/id/id = wear_id.GetID()
+ if(id && get_dist(user, src) <= ID_EXAMINE_DISTANCE)
+ var/id_href = "[wear_id.examine_title(user)]"
+ . += "[t_He] [t_is] wearing [id_href]."
+
+ else
+ . += "[t_He] [t_is] wearing [wear_id.examine_title(user)]."
+ //Hands
+ for(var/obj/item/held_thing in held_items)
+ if(held_thing.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM))
+ 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))]."
+ //gloves
+ if(gloves && !(obscured & ITEM_SLOT_GLOVES) && !(gloves.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_has] [gloves.examine_title(user)] on [t_his] hands."
+ else if(GET_ATOM_BLOOD_DNA_LENGTH(src) || blood_in_hands)
+ if(num_hands)
+ . += span_warning("[t_He] [t_has] [num_hands > 1 ? "" : "a "]blood-stained hand[num_hands > 1 ? "s" : ""]!")
+ //handcuffed?
+ if(handcuffed)
+ var/cables_or_cuffs = istype(handcuffed, /obj/item/restraints/handcuffs/cable) ? "restrained with cable" : "handcuffed"
+ . += span_warning("[t_He] [t_is] [icon2html(handcuffed, user)] [cables_or_cuffs]!")
+ //belt
+ if(belt && !(obscured & ITEM_SLOT_BELT) && !(belt.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_has] [belt.examine_title(user)] about [t_his] waist."
+ //shoes
+ if(shoes && !(obscured & ITEM_SLOT_FEET) && !(shoes.item_flags & EXAMINE_SKIP))
+ . += "[t_He] [t_is] wearing [shoes.examine_title(user)] on [t_his] feet."
+
+/// Collects info displayed about any HUDs the user has when examining src
+/mob/living/carbon/proc/get_hud_examine_info(mob/living/user)
+ return
+
+/mob/living/carbon/human/get_hud_examine_info(mob/living/user)
+ . = list()
+
+ var/perpname = get_face_name(get_id_name(""))
+ var/title = ""
+ if(perpname && (HAS_TRAIT(user, TRAIT_SECURITY_HUD) || HAS_TRAIT(user, TRAIT_MEDICAL_HUD)) && (user.stat == CONSCIOUS || isobserver(user)) && user != src)
+ var/datum/record/crew/target_record = find_record(perpname)
+ if(target_record)
+ . += "Rank: [target_record.rank]"
+ . += "\[Front photo\]\[Side photo\]"
+ if(HAS_TRAIT(user, TRAIT_MEDICAL_HUD) && HAS_TRAIT(user, TRAIT_SECURITY_HUD))
+ title = separator_hr("Medical & Security Analysis")
+ . += get_medhud_examine_info(user, target_record)
+ . += get_sechud_examine_info(user, target_record)
+
+ else if(HAS_TRAIT(user, TRAIT_MEDICAL_HUD))
+ title = separator_hr("Medical Analysis")
+ . += get_medhud_examine_info(user, target_record)
+
+ else if(HAS_TRAIT(user, TRAIT_SECURITY_HUD))
+ title = separator_hr("Security Analysis")
+ . += get_sechud_examine_info(user, target_record)
+
+ // applies the separator correctly without an extra line break
+ if(title && length(.))
+ .[1] = title + .[1]
return .
+
+/// Collects information displayed about src when examined by a user with a medical HUD.
+/mob/living/carbon/proc/get_medhud_examine_info(mob/living/user, datum/record/crew/target_record)
+ . = list()
+
+ var/list/cybers = list()
+ for(var/obj/item/organ/internal/cyberimp/cyberimp in organs)
+ if(IS_ROBOTIC_ORGAN(cyberimp) && !(cyberimp.organ_flags & ORGAN_HIDDEN))
+ cybers += cyberimp.examine_title(user)
+ if(length(cybers))
+ . += "Detected cybernetic modifications:"
+ . += "[english_list(cybers, and_text = ", and")]"
+ if(target_record)
+ . += "\[[target_record.physical_status]\]"
+ . += "\[[target_record.mental_status]\]"
+ else
+ . += "\[Record Missing\]"
+ . += "\[Record Missing\]"
+ . += "\[Medical evaluation\]"
+ . += "\[See quirks\]"
+
+/// Collects information displayed about src when examined by a user with a security HUD.
+/mob/living/carbon/proc/get_sechud_examine_info(mob/living/user, datum/record/crew/target_record)
+ . = list()
+
+ var/wanted_status = WANTED_NONE
+ var/security_note = "None."
+
+ if(target_record)
+ wanted_status = target_record.wanted_status
+ if(target_record.security_note)
+ security_note = target_record.security_note
+ if(ishuman(user))
+ . += "Criminal status: \[[wanted_status]\]"
+ else
+ . += "Criminal status: [wanted_status]"
+ . += "Important Notes: [security_note]"
+ . += "Security record: \[View\]"
+ if(ishuman(user))
+ . += "\[Add citation\]\
+ \[Add crime\]\
+ \[Add note\]"
+
+/mob/living/carbon/human/examine_more(mob/user)
+ . = ..()
+ if((wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE)))
+ return
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN) || HAS_TRAIT(src, TRAIT_INVISIBLE_MAN))
+ return
+ var/age_text
+ switch(age)
+ if(-INFINITY to 25)
+ age_text = "very young"
+ if(26 to 35)
+ age_text = "of adult age"
+ if(36 to 55)
+ age_text = "middle-aged"
+ if(56 to 75)
+ age_text = "rather old"
+ if(76 to 100)
+ age_text = "very old"
+ if(101 to INFINITY)
+ age_text = "withering away"
+ . += list(span_notice("[p_They()] appear[p_s()] to be [age_text]."))
+
+ if(istype(w_uniform, /obj/item/clothing/under))
+ var/obj/item/clothing/under/undershirt = w_uniform
+ if(undershirt.has_sensor == BROKEN_SENSORS)
+ . += list(span_notice("The [undershirt]'s medical sensors are sparking."))
+
+#undef ADD_NEWLINE_IF_NECESSARY
diff --git a/code/modules/mob/living/carbon/human/_species.dm b/code/modules/mob/living/carbon/human/_species.dm
index c2ed9b27866ec..a045c1df04207 100644
--- a/code/modules/mob/living/carbon/human/_species.dm
+++ b/code/modules/mob/living/carbon/human/_species.dm
@@ -1110,7 +1110,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
humi.adjust_coretemperature(skin_core_change)
// get the enviroment details of where the mob is standing
- var/datum/gas_mixture/environment = humi.loc.return_air()
+ var/datum/gas_mixture/environment = humi.loc?.return_air()
if(!environment) // if there is no environment (nullspace) drop out here.
return
@@ -1240,10 +1240,10 @@ GLOBAL_LIST_EMPTY(features_by_species)
// 40% for level 3 damage on humans to scream in pain
if (humi.stat < UNCONSCIOUS && (prob(burn_damage) * 10) / 4)
- humi.emote("scream")
+ INVOKE_ASYNC(humi, TYPE_PROC_REF(/mob, emote), "scream")
// Apply the damage to all body parts
- humi.apply_damage(burn_damage, BURN, spread_damage = TRUE)
+ humi.apply_damage(burn_damage, BURN, spread_damage = TRUE, wound_clothing = FALSE)
// For cold damage, we cap at the threshold if you're dead
if(humi.getFireLoss() >= abs(HEALTH_THRESHOLD_DEAD) && humi.stat == DEAD)
@@ -1259,11 +1259,11 @@ GLOBAL_LIST_EMPTY(features_by_species)
var/damage_mod = coldmod * humi.physiology.cold_mod * (is_hulk ? HULK_COLD_DAMAGE_MOD : 1)
// Can't be a switch due to http://www.byond.com/forum/post/2750423
if(humi.coretemperature in 201 to cold_damage_limit)
- humi.apply_damage(COLD_DAMAGE_LEVEL_1 * damage_mod * seconds_per_tick, damage_type)
+ humi.apply_damage(COLD_DAMAGE_LEVEL_1 * damage_mod * seconds_per_tick, damage_type, wound_clothing = FALSE)
else if(humi.coretemperature in 120 to 200)
- humi.apply_damage(COLD_DAMAGE_LEVEL_2 * damage_mod * seconds_per_tick, damage_type)
+ humi.apply_damage(COLD_DAMAGE_LEVEL_2 * damage_mod * seconds_per_tick, damage_type, wound_clothing = FALSE)
else
- humi.apply_damage(COLD_DAMAGE_LEVEL_3 * damage_mod * seconds_per_tick, damage_type)
+ humi.apply_damage(COLD_DAMAGE_LEVEL_3 * damage_mod * seconds_per_tick, damage_type, wound_clothing = FALSE)
/**
* Used to apply burn wounds on random limbs
@@ -1312,7 +1312,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
if(humi.bodytemperature > BODYTEMP_HEAT_WOUND_LIMIT + 2800)
burn_damage = HEAT_DAMAGE_LEVEL_3
- humi.apply_damage(burn_damage * seconds_per_tick, BURN, bodypart)
+ humi.apply_damage(burn_damage * seconds_per_tick, BURN, bodypart, wound_clothing = FALSE)
/// Handle the air pressure of the environment
/datum/species/proc/handle_environment_pressure(mob/living/carbon/human/H, datum/gas_mixture/environment, seconds_per_tick, times_fired)
@@ -1369,7 +1369,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
////////////
/datum/species/proc/spec_stun(mob/living/carbon/human/H,amount)
- if(H.movement_type & FLYING)
+ if((H.movement_type & FLYING) && !H.buckled)
var/obj/item/organ/external/wings/functional/wings = H.get_organ_slot(ORGAN_SLOT_EXTERNAL_WINGS)
if(wings)
wings.toggle_flight(H)
@@ -1406,10 +1406,9 @@ GLOBAL_LIST_EMPTY(features_by_species)
for (var/preference_type in GLOB.preference_entries)
var/datum/preference/preference = GLOB.preference_entries[preference_type]
-
if ( \
(preference.relevant_inherent_trait in inherent_traits) \
- || (preference.relevant_external_organ in mutant_organs) \
+ || (preference.relevant_external_organ in get_mut_organs()) \
|| (preference.relevant_head_flag && check_head_flags(preference.relevant_head_flag)) \
|| (preference.relevant_body_markings in body_markings) \
)
@@ -1457,23 +1456,24 @@ GLOBAL_LIST_EMPTY(features_by_species)
/datum/species/proc/get_sneeze_sound(mob/living/carbon/human/human)
return
+/datum/species/proc/get_mut_organs(include_brain = TRUE)
+ var/list/mut_organs = list()
+ mut_organs += mutant_organs
+ if (include_brain)
+ mut_organs += mutantbrain
+ mut_organs += mutantheart
+ mut_organs += mutantlungs
+ mut_organs += mutanteyes
+ mut_organs += mutantears
+ mut_organs += mutanttongue
+ mut_organs += mutantliver
+ mut_organs += mutantstomach
+ mut_organs += mutantappendix
+ list_clear_nulls(mut_organs)
+ return mut_organs
+
/datum/species/proc/get_types_to_preload()
- var/list/to_store = list()
- to_store += mutant_organs
- for(var/obj/item/organ/horny as anything in mutant_organs)
- to_store += horny //Haha get it?
-
- //Don't preload brains, cause reuse becomes a horrible headache
- to_store += mutantheart
- to_store += mutantlungs
- to_store += mutanteyes
- to_store += mutantears
- to_store += mutanttongue
- to_store += mutantliver
- to_store += mutantstomach
- to_store += mutantappendix
- //We don't cache mutant hands because it's not constrained enough, too high a potential for failure
- return to_store
+ return get_mut_organs(FALSE)
/**
diff --git a/code/modules/mob/living/carbon/human/dummy.dm b/code/modules/mob/living/carbon/human/dummy.dm
index f13e90719c1dc..23b1ad5126694 100644
--- a/code/modules/mob/living/carbon/human/dummy.dm
+++ b/code/modules/mob/living/carbon/human/dummy.dm
@@ -1,13 +1,16 @@
/mob/living/carbon/human/dummy
real_name = "Test Dummy"
- status_flags = GODMODE|CANPUSH
mouse_drag_pointer = MOUSE_INACTIVE_POINTER
visual_only_organs = TRUE
var/in_use = FALSE
INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy)
+/mob/living/carbon/human/dummy/Initialize(mapload)
+ . = ..()
+ ADD_TRAIT(src, TRAIT_GODMODE, INNATE_TRAIT)
+
/mob/living/carbon/human/dummy/Destroy()
in_use = FALSE
return ..()
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index 501f3c782f29b..325abea10d66d 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -63,7 +63,7 @@
message = "screams!"
message_mime = "acts out a scream!"
emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE
- only_forced_audio = TRUE
+ audio_cooldown = 5 SECONDS
vary = TRUE
/datum/emote/living/carbon/human/scream/can_run_emote(mob/user, status_check = TRUE , intentional, params)
@@ -105,7 +105,7 @@
message = "salutes."
message_param = "salutes to %t."
hands_use_check = TRUE
- sound = 'sound/misc/salute.ogg'
+ sound = 'sound/mobs/humanoids/human/salute/salute.ogg'
/datum/emote/living/carbon/human/shrug
key = "shrug"
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 0c50e881635f6..04a056e0011de 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -1,450 +1,9 @@
-/mob/living/carbon/human/examine(mob/user)
-//this is very slightly better than it was because you can use it more places. still can't do \his[src] though.
- var/t_He = p_They()
- var/t_His = p_Their()
- var/t_his = p_their()
- var/t_him = p_them()
- var/t_has = p_have()
- var/t_is = p_are()
- var/obscure_name
- var/obscure_examine
-
- if(isliving(user))
- var/mob/living/L = user
- if(HAS_TRAIT(L, TRAIT_PROSOPAGNOSIA) || HAS_TRAIT(L, TRAIT_INVISIBLE_MAN))
- obscure_name = TRUE
- if(HAS_TRAIT(src, TRAIT_UNKNOWN))
- obscure_name = TRUE
- obscure_examine = TRUE
-
- . = list("This is [!obscure_name ? name : "Unknown"]!")
-
- if(obscure_examine)
- return list("You're struggling to make out any details...")
-
- var/obscured = check_obscured_slots()
-
- //uniform
- if(w_uniform && !(obscured & ITEM_SLOT_ICLOTHING) && !(w_uniform.item_flags & EXAMINE_SKIP))
- //accessory
- var/accessory_message = ""
- if(istype(w_uniform, /obj/item/clothing/under))
- var/obj/item/clothing/under/undershirt = w_uniform
- var/list/accessories = undershirt.list_accessories_with_icon(user)
- if(length(accessories))
- accessory_message = " with [english_list(accessories)] attached"
-
- . += "[t_He] [t_is] wearing [w_uniform.get_examine_string(user)][accessory_message]."
- //head
- if(head && !(obscured & ITEM_SLOT_HEAD) && !(head.item_flags & EXAMINE_SKIP))
- . += "[t_He] [t_is] wearing [head.get_examine_string(user)] on [t_his] head."
- //suit/armor
- if(wear_suit && !(wear_suit.item_flags & EXAMINE_SKIP))
- . += "[t_He] [t_is] wearing [wear_suit.get_examine_string(user)]."
- //suit/armor storage
- if(s_store && !(obscured & ITEM_SLOT_SUITSTORE) && !(s_store.item_flags & EXAMINE_SKIP))
- . += "[t_He] [t_is] carrying [s_store.get_examine_string(user)] on [t_his] [wear_suit.name]."
- //back
- if(back && !(back.item_flags & EXAMINE_SKIP))
- . += "[t_He] [t_has] [back.get_examine_string(user)] on [t_his] back."
-
- //Hands
- for(var/obj/item/held_thing in held_items)
- if(held_thing.item_flags & (ABSTRACT|EXAMINE_SKIP|HAND_ITEM))
- continue
- . += "[t_He] [t_is] holding [held_thing.get_examine_string(user)] in [t_his] [get_held_index_name(get_held_index_of_item(held_thing))]."
-
- //gloves
- if(gloves && !(obscured & ITEM_SLOT_GLOVES) && !(gloves.item_flags & EXAMINE_SKIP))
- . += "[t_He] [t_has] [gloves.get_examine_string(user)] on [t_his] hands."
- else if(GET_ATOM_BLOOD_DNA_LENGTH(src))
- if(num_hands)
- . += span_warning("[t_He] [t_has] [num_hands > 1 ? "" : "a "]blood-stained hand[num_hands > 1 ? "s" : ""]!")
-
- //handcuffed?
- if(handcuffed)
- if(istype(handcuffed, /obj/item/restraints/handcuffs/cable))
- . += span_warning("[t_He] [t_is] [icon2html(handcuffed, user)] restrained with cable!")
- else
- . += span_warning("[t_He] [t_is] [icon2html(handcuffed, user)] handcuffed!")
-
- //belt
- if(belt && !(belt.item_flags & EXAMINE_SKIP))
- . += "[t_He] [t_has] [belt.get_examine_string(user)] about [t_his] waist."
-
- //shoes
- if(shoes && !(obscured & ITEM_SLOT_FEET) && !(shoes.item_flags & EXAMINE_SKIP))
- . += "[t_He] [t_is] wearing [shoes.get_examine_string(user)] on [t_his] feet."
-
- //mask
- if(wear_mask && !(obscured & ITEM_SLOT_MASK) && !(wear_mask.item_flags & EXAMINE_SKIP))
- . += "[t_He] [t_has] [wear_mask.get_examine_string(user)] on [t_his] face."
-
- if(wear_neck && !(obscured & ITEM_SLOT_NECK) && !(wear_neck.item_flags & EXAMINE_SKIP))
- . += "[t_He] [t_is] wearing [wear_neck.get_examine_string(user)] around [t_his] neck."
-
- //eyes
- if(!(obscured & ITEM_SLOT_EYES) )
- if(glasses && !(glasses.item_flags & EXAMINE_SKIP))
- . += "[t_He] [t_has] [glasses.get_examine_string(user)] covering [t_his] eyes."
- else if(HAS_TRAIT(src, TRAIT_UNNATURAL_RED_GLOWY_EYES))
- . += "[t_His] eyes are glowing with an unnatural red aura!"
- else if(HAS_TRAIT(src, TRAIT_BLOODSHOT_EYES))
- . += "[t_His] eyes are bloodshot!"
-
- //ears
- if(ears && !(obscured & ITEM_SLOT_EARS) && !(ears.item_flags & EXAMINE_SKIP))
- . += "[t_He] [t_has] [ears.get_examine_string(user)] on [t_his] ears."
-
- //ID
- if(wear_id && !(wear_id.item_flags & EXAMINE_SKIP))
- . += "[t_He] [t_is] wearing [wear_id.get_examine_string(user)]."
-
- . += wear_id.get_id_examine_strings(user)
-
- //Status effects
- var/list/status_examines = get_status_effect_examinations()
- if (length(status_examines))
- . += status_examines
-
- var/appears_dead = FALSE
- var/just_sleeping = FALSE
-
- if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH)))
- appears_dead = TRUE
-
- var/obj/item/clothing/glasses/G = get_item_by_slot(ITEM_SLOT_EYES)
- var/are_we_in_weekend_at_bernies = G?.tint && buckled && istype(buckled, /obj/vehicle/ridden/wheelchair)
-
- if(isliving(user) && (HAS_MIND_TRAIT(user, TRAIT_NAIVE) || are_we_in_weekend_at_bernies))
- just_sleeping = TRUE
-
- if(!just_sleeping)
- if(HAS_TRAIT(src, TRAIT_SUICIDED))
- . += span_warning("[t_He] appear[p_s()] to have committed suicide... there is no hope of recovery.")
-
- . += generate_death_examine_text()
-
- if(get_bodypart(BODY_ZONE_HEAD) && !get_organ_by_type(/obj/item/organ/internal/brain))
- . += span_deadsay("It appears that [t_his] brain is missing...")
-
- var/list/msg = list()
-
- var/list/missing = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
- var/list/disabled = list()
- for(var/X in bodyparts)
- var/obj/item/bodypart/body_part = X
- if(body_part.bodypart_disabled)
- disabled += body_part
- missing -= body_part.body_zone
- for(var/obj/item/I in body_part.embedded_objects)
- if(I.is_embed_harmless())
- msg += "[t_He] [t_has] [icon2html(I, user)] \a [I] stuck to [t_his] [body_part.name]!\n"
- else
- msg += "[t_He] [t_has] [icon2html(I, user)] \a [I] embedded in [t_his] [body_part.name]!\n"
-
- for(var/i in body_part.wounds)
- var/datum/wound/iter_wound = i
- msg += "[iter_wound.get_examine_description(user)]\n"
-
- for(var/X in disabled)
- var/obj/item/bodypart/body_part = X
- var/damage_text
- if(HAS_TRAIT(body_part, TRAIT_DISABLED_BY_WOUND))
- continue // skip if it's disabled by a wound (cuz we'll be able to see the bone sticking out!)
- if(!(body_part.get_damage() >= body_part.max_damage)) //we don't care if it's stamcritted
- damage_text = "limp and lifeless"
- else
- damage_text = (body_part.brute_dam >= body_part.burn_dam) ? body_part.heavy_brute_msg : body_part.heavy_burn_msg
- msg += "[capitalize(t_his)] [body_part.name] is [damage_text]!\n"
-
- //stores missing limbs
- var/l_limbs_missing = 0
- var/r_limbs_missing = 0
- for(var/t in missing)
- if(t == BODY_ZONE_HEAD)
- msg += "[t_His] [parse_zone(t)] is missing!\n"
- continue
- if(t == BODY_ZONE_L_ARM || t == BODY_ZONE_L_LEG)
- l_limbs_missing++
- else if(t == BODY_ZONE_R_ARM || t == BODY_ZONE_R_LEG)
- r_limbs_missing++
-
- msg += "[capitalize(t_his)] [parse_zone(t)] is missing!\n"
-
- if(l_limbs_missing >= 2 && r_limbs_missing == 0)
- msg += "[t_He] look[p_s()] all right now.\n"
- else if(l_limbs_missing == 0 && r_limbs_missing >= 2)
- msg += "[t_He] really keep[p_s()] to the left.\n"
- else if(l_limbs_missing >= 2 && r_limbs_missing >= 2)
- msg += "[t_He] [p_do()]n't seem all there.\n"
-
- if(!(user == src && has_status_effect(/datum/status_effect/grouped/screwy_hud/fake_healthy))) //fake healthy
- var/temp
- if(user == src && has_status_effect(/datum/status_effect/grouped/screwy_hud/fake_crit))//fake damage
- temp = 50
- else
- temp = getBruteLoss()
- var/list/damage_desc = get_majority_bodypart_damage_desc()
- if(temp)
- if(temp < 25)
- msg += "[t_He] [t_has] minor [damage_desc[BRUTE]].\n"
- else if(temp < 50)
- msg += "[t_He] [t_has] moderate [damage_desc[BRUTE]]!\n"
- else
- msg += "[t_He] [t_has] severe [damage_desc[BRUTE]]!\n"
-
- temp = getFireLoss()
- if(temp)
- if(temp < 25)
- msg += "[t_He] [t_has] minor [damage_desc[BURN]].\n"
- else if (temp < 50)
- msg += "[t_He] [t_has] moderate [damage_desc[BURN]]!\n"
- else
- msg += "[t_He] [t_has] severe [damage_desc[BURN]]!\n"
-
- if(has_status_effect(/datum/status_effect/fire_handler/fire_stacks))
- msg += "[t_He] [t_is] covered in something flammable.\n"
- if(has_status_effect(/datum/status_effect/fire_handler/wet_stacks))
- msg += "[t_He] look[p_s()] a little soaked.\n"
-
-
- if(pulledby?.grab_state)
- msg += "[t_He] [t_is] restrained by [pulledby]'s grip.\n"
-
- if(nutrition < NUTRITION_LEVEL_STARVING - 50)
- msg += "[t_He] [t_is] severely malnourished.\n"
- else if(nutrition >= NUTRITION_LEVEL_FAT)
- if(user.nutrition < NUTRITION_LEVEL_STARVING - 50)
- msg += "[t_He] [t_is] plump and delicious looking - Like a fat little piggy. A tasty piggy.\n"
- else
- msg += "[t_He] [t_is] quite chubby.\n"
- switch(disgust)
- if(DISGUST_LEVEL_GROSS to DISGUST_LEVEL_VERYGROSS)
- msg += "[t_He] look[p_s()] a bit grossed out.\n"
- if(DISGUST_LEVEL_VERYGROSS to DISGUST_LEVEL_DISGUSTED)
- msg += "[t_He] look[p_s()] really grossed out.\n"
- if(DISGUST_LEVEL_DISGUSTED to INFINITY)
- msg += "[t_He] look[p_s()] extremely disgusted.\n"
-
- var/apparent_blood_volume = blood_volume
- if(HAS_TRAIT(src, TRAIT_USES_SKINTONES) && (skin_tone == "albino"))
- apparent_blood_volume -= 150 // enough to knock you down one tier
- switch(apparent_blood_volume)
- if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
- msg += "[t_He] [t_has] pale skin.\n"
- if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
- msg += "[t_He] look[p_s()] like pale death.\n"
- if(-INFINITY to BLOOD_VOLUME_BAD)
- msg += "[span_deadsay("[t_He] resemble[p_s()] a crushed, empty juice pouch.")]\n"
-
- if(is_bleeding())
- 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)
- if(body_part.get_modified_bleed_rate())
- bleeding_limbs += body_part
- if(body_part.grasped_by)
- grasped_limbs += body_part
-
- var/num_bleeds = LAZYLEN(bleeding_limbs)
-
- var/list/bleed_text
- if(appears_dead)
- bleed_text = list("Blood is visible in [t_his] open")
- else
- bleed_text = list("[t_He] [t_is] bleeding from [t_his]")
-
- switch(num_bleeds)
- if(1 to 2)
- bleed_text += " [bleeding_limbs[1].name][num_bleeds == 2 ? " and [bleeding_limbs[2].name]" : ""]"
- if(3 to INFINITY)
- for(var/i in 1 to (num_bleeds - 1))
- var/obj/item/bodypart/body_part = bleeding_limbs[i]
- bleed_text += " [body_part.name],"
- bleed_text += " and [bleeding_limbs[num_bleeds].name]"
-
- if(appears_dead)
- bleed_text += ", but it has pooled and is not flowing.\n"
- else
- if(reagents.has_reagent(/datum/reagent/toxin/heparin, needs_metabolizing = TRUE))
- bleed_text += " incredibly quickly"
-
- bleed_text += "!\n"
-
- for(var/i in grasped_limbs)
- var/obj/item/bodypart/grasped_part = i
- bleed_text += "[t_He] [t_is] holding [t_his] [grasped_part.name] to slow the bleeding!\n"
-
- msg += bleed_text.Join()
-
- if(reagents.has_reagent(/datum/reagent/teslium, needs_metabolizing = TRUE))
- msg += "[t_He] [t_is] emitting a gentle blue glow!\n"
-
- if(just_sleeping)
- msg += "[t_He] [t_is]n't responding to anything around [t_him] and seem[p_s()] to be asleep.\n"
-
- if(!appears_dead)
- var/mob/living/living_user = user
- if(src != user)
- if(HAS_TRAIT(user, TRAIT_EMPATH))
- if (combat_mode)
- msg += "[t_He] seem[p_s()] to be on guard.\n"
- if (getOxyLoss() >= 10)
- msg += "[t_He] seem[p_s()] winded.\n"
- if (getToxLoss() >= 10)
- msg += "[t_He] seem[p_s()] sickly.\n"
- if(mob_mood.sanity <= SANITY_DISTURBED)
- msg += "[t_He] seem[p_s()] distressed.\n"
- living_user.add_mood_event("empath", /datum/mood_event/sad_empath, src)
- if(is_blind())
- msg += "[t_He] appear[p_s()] to be staring off into space.\n"
- if (HAS_TRAIT(src, TRAIT_DEAF))
- msg += "[t_He] appear[p_s()] to not be responding to noises.\n"
- if (bodytemperature > dna.species.bodytemp_heat_damage_limit)
- msg += "[t_He] [t_is] flushed and wheezing.\n"
- if (bodytemperature < dna.species.bodytemp_cold_damage_limit)
- msg += "[t_He] [t_is] shivering.\n"
-
- msg += ""
-
- if(HAS_TRAIT(user, TRAIT_SPIRITUAL) && mind?.holy_role)
- msg += "[t_He] [t_has] a holy aura about [t_him].\n"
- living_user.add_mood_event("religious_comfort", /datum/mood_event/religiously_comforted)
-
- switch(stat)
- if(UNCONSCIOUS, HARD_CRIT)
- msg += "[t_He] [t_is]n't responding to anything around [t_him] and seem[p_s()] to be asleep.\n"
- if(SOFT_CRIT)
- msg += "[t_He] [t_is] barely conscious.\n"
- if(CONSCIOUS)
- if(HAS_TRAIT(src, TRAIT_DUMB))
- msg += "[t_He] [t_has] a stupid expression on [t_his] face.\n"
- if(get_organ_by_type(/obj/item/organ/internal/brain) && isnull(ai_controller))
- if(!key)
- msg += "[span_deadsay("[t_He] [t_is] totally catatonic. The stresses of life in deep-space must have been too much for [t_him]. Any recovery is unlikely.")]\n"
- else if(!client)
- msg += "[span_deadsay("[t_He] [t_has] a blank, absent-minded stare and appears completely unresponsive to anything. [t_He] may snap out of it soon.")]\n"
-
- var/scar_severity = 0
- for(var/i in all_scars)
- var/datum/scar/S = i
- if(S.is_visible(user))
- scar_severity += S.severity
-
- switch(scar_severity)
- if(1 to 4)
- msg += "[span_tinynoticeital("[t_He] [t_has] visible scarring, you can look again to take a closer look...")]\n"
- if(5 to 8)
- msg += "[span_smallnoticeital("[t_He] [t_has] several bad scars, you can look again to take a closer look...")]\n"
- if(9 to 11)
- msg += "[span_notice("[t_He] [t_has] significantly disfiguring scarring, you can look again to take a closer look...")]\n"
- if(12 to INFINITY)
- msg += "[span_notice("[t_He] [t_is] just absolutely fucked up, you can look again to take a closer look...")]\n"
- msg += "" // closes info class
-
- if (length(msg))
- . += span_warning("[msg.Join("")]")
-
- var/trait_exam = common_trait_examine()
- if (!isnull(trait_exam))
- . += trait_exam
-
- if(isliving(user))
- var/mob/living/privacy_invader = user
- if(HAS_MIND_TRAIT(privacy_invader, TRAIT_MORBID))
- if(HAS_TRAIT(src, TRAIT_DISSECTED))
- msg += "[span_notice("[t_He] appears to have been dissected. Useless for examination... for now.")]\n"
- if(HAS_TRAIT(src, TRAIT_SURGICALLY_ANALYZED))
- msg += "[span_notice("A skilled hand has mapped this one's internal intricacies. It will be far easier to perform future experimentations upon [t_him]. Exquisite.")]\n"
- if(HAS_MIND_TRAIT(privacy_invader, TRAIT_EXAMINE_FITNESS))
- . += compare_fitness(user)
-
- var/perpname = get_face_name(get_id_name(""))
- if(perpname && (HAS_TRAIT(user, TRAIT_SECURITY_HUD) || HAS_TRAIT(user, TRAIT_MEDICAL_HUD)))
- var/datum/record/crew/target_record = find_record(perpname)
- if(target_record)
- . += "Rank: [target_record.rank]\n\[Front photo\]\[Side photo\]"
- if(HAS_TRAIT(user, TRAIT_MEDICAL_HUD))
- var/cyberimp_detect
- for(var/obj/item/organ/internal/cyberimp/cyberimp in organs)
- if(IS_ROBOTIC_ORGAN(cyberimp) && !(cyberimp.organ_flags & ORGAN_HIDDEN))
- cyberimp_detect += "[!cyberimp_detect ? "[cyberimp.get_examine_string(user)]" : ", [cyberimp.get_examine_string(user)]"]"
- if(cyberimp_detect)
- . += "Detected cybernetic modifications:"
- . += "[cyberimp_detect]"
- if(target_record)
- var/health_record = target_record.physical_status
- . += "\[[health_record]\]"
- health_record = target_record.mental_status
- . += "\[[health_record]\]"
- target_record = find_record(perpname)
- if(target_record)
- . += "\[Medical evaluation\] "
- . += "\[See quirks\]"
-
- if(HAS_TRAIT(user, TRAIT_SECURITY_HUD))
- if((user.stat == CONSCIOUS || isobserver(user)) && user != src)
- //|| !user.canmove || user.restrained()) Fluff: Sechuds have eye-tracking technology and sets 'arrest' to people that the wearer looks and blinks at.
- var/wanted_status = WANTED_NONE
- var/security_note = "None."
-
- target_record = find_record(perpname)
- if(target_record)
- wanted_status = target_record.wanted_status
- if(target_record.security_note)
- security_note = target_record.security_note
- if(ishuman(user))
- . += "Criminal status: \[[wanted_status]\]"
- else
- . += "Criminal status: [wanted_status]"
- . += "Important Notes: [security_note]"
- . += "Security record: \[View\]"
- if(ishuman(user))
- . += jointext(list("\[Add citation\]",
- "\[Add crime\]",
- "\[Add note\]"), "")
- if(isobserver(user))
- . += span_info("\nQuirks: [get_quirk_string(FALSE, CAT_QUIRK_ALL)]")
- . += ""
-
- SEND_SIGNAL(src, COMSIG_ATOM_EXAMINE, user, .)
-
-/**
- * Shows any and all examine text related to any status effects the user has.
- */
-/mob/living/proc/get_status_effect_examinations()
- var/list/examine_list = list()
-
- for(var/datum/status_effect/effect as anything in status_effects)
- var/effect_text = effect.get_examine_text()
- if(!effect_text)
- continue
-
- examine_list += effect_text
-
- if(!length(examine_list))
- return
-
- return examine_list.Join("\n")
-
-/mob/living/carbon/human/examine_more(mob/user)
+/// Collects information displayed about src when examined by a user with a medical HUD.
+/mob/living/carbon/human/get_medhud_examine_info(mob/living/user, datum/record/crew/target_record)
. = ..()
- if ((wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE)))
- return
- var/age_text
- switch(age)
- if(-INFINITY to 25)
- age_text = "very young"
- if(26 to 35)
- age_text = "of adult age"
- if(36 to 55)
- age_text = "middle-aged"
- if(56 to 75)
- age_text = "rather old"
- if(76 to 100)
- age_text = "very old"
- if(101 to INFINITY)
- age_text = "withering away"
- . += list(span_notice("[p_They()] appear[p_s()] to be [age_text]."))
+
+ if(istype(w_uniform, /obj/item/clothing/under))
+ var/obj/item/clothing/under/undershirt = w_uniform
+ var/sensor_text = undershirt.get_sensor_text()
+ if(sensor_text)
+ . += "Sensor Status: [sensor_text]"
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index cf73704600c7f..903d489846ab2 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -88,6 +88,59 @@
/mob/living/carbon/human/Topic(href, href_list)
+ if(href_list["see_id"])
+ var/mob/viewer = usr
+ var/can_see_still = (viewer in viewers(src))
+
+ var/obj/item/card/id/id = wear_id?.GetID()
+ var/same_id = id && (href_list["id_ref"] == REF(id) || href_list["id_name"] == id.registered_name)
+ if(!same_id && can_see_still)
+ to_chat(viewer, span_notice("[p_They()] [p_are()] no longer wearing that ID card."))
+ return
+
+ var/viable_time = can_see_still ? 3 MINUTES : 1 MINUTES // assuming 3min is the length of a hop line visit - give some leeway if they're still in sight
+ if(!same_id || (text2num(href_list["examine_time"]) + viable_time) < world.time)
+ to_chat(viewer, span_notice("You don't have that good of a memory. Examine [p_them()] again."))
+ return
+ if(HAS_TRAIT(src, TRAIT_UNKNOWN))
+ to_chat(viewer, span_notice("You can't make out that ID anymore."))
+ return
+ if(get_dist(viewer, src) > ID_EXAMINE_DISTANCE + 1) // leeway
+ to_chat(viewer, span_notice("You can't make out that ID from here."))
+ return
+
+ var/id_name = id.registered_name
+ var/id_age = id.registered_age
+ var/id_job = id.assignment
+ // Should probably be recorded on the ID, but this is easier (albiet more restrictive) on chameleon ID users
+ var/datum/record/crew/record = find_record(id_name)
+ var/id_blood_type = record?.blood_type
+ var/id_gender = record?.gender
+ var/id_species = record?.species
+ var/id_icon = jointext(id.get_id_examine_strings(viewer), "")
+ // Fill in some blanks for chameleon IDs to maintain the illusion of a real ID
+ if(istype(id, /obj/item/card/id/advanced/chameleon))
+ id_gender ||= gender
+ id_species ||= dna.species.name
+ id_blood_type ||= dna.blood_type
+
+ var/id_examine = span_slightly_larger(separator_hr("This is [src]'s ID card."))
+ id_examine += "