diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_homestead.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_homestead.dmm index 80e245fa11ba..b6f057ca94df 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_homestead.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_homestead.dmm @@ -4,7 +4,7 @@ /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/nospawn) "cP" = ( -/mob/living/simple_animal/hostile/asteroid/polarbear, +/mob/living/basic/mining/polarbear, /turf/open/floor/wood/large{ initial_gas_mix = "ICEMOON_ATMOS" }, diff --git a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_plasma_facility.dmm b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_plasma_facility.dmm index fad279c0710e..df600c338fab 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_plasma_facility.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_underground_abandoned_plasma_facility.dmm @@ -204,7 +204,7 @@ /turf/open/floor/iron, /area/ruin/plasma_facility/operations) "dB" = ( -/mob/living/simple_animal/hostile/asteroid/polarbear{ +/mob/living/basic/mining/polarbear{ name = "Baloo" }, /turf/open/misc/asteroid/snow/icemoon, diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_cube.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_cube.dmm index 3e64a36fdb49..6c3818b43ecb 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_surface_cube.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_cube.dmm @@ -1,13 +1,13 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE "a" = ( -/turf/closed/mineral/volcanic/lava_land_surface, +/turf/closed/mineral/volcanic/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) "b" = ( -/turf/open/misc/asteroid/basalt/lava_land_surface, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) "c" = ( /obj/effect/decal/remains/human, -/turf/open/misc/asteroid/basalt/lava_land_surface, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) "d" = ( /turf/closed/indestructible/riveted, @@ -15,7 +15,7 @@ "e" = ( /obj/machinery/wish_granter, /obj/effect/mapping_helpers/no_lava, -/turf/open/misc/asteroid/basalt/lava_land_surface, +/turf/open/misc/asteroid/basalt/lava_land_surface/biome_replace, /area/lavaland/surface/outdoors) (1,1,1) = {" diff --git a/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm b/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm index 108f84c6ad6f..d8ca28b7b004 100644 --- a/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm +++ b/_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm @@ -7042,7 +7042,7 @@ /obj/machinery/status_display/supply{ pixel_y = 32 }, -/obj/machinery/disposal/delivery_chute{ +/obj/structure/disposaloutlet{ dir = 4 }, /obj/structure/window/spawner/directional/south, @@ -66111,10 +66111,6 @@ }, /turf/open/floor/iron/dark/textured_large, /area/station/medical/medbay/central) -"rDG" = ( -/obj/effect/turf_decal/tile/red, -/turf/closed/wall/r_wall, -/area/station/engineering/lobby) "rDN" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -197861,7 +197857,7 @@ weC lDI kOk kOk -rDG +kOk kOk kOk mAH diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index c2482fe215de..6fb2cc5a05d5 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -17522,7 +17522,7 @@ /turf/open/floor/iron/white, /area/station/medical/surgery/aft) "eUO" = ( -/mob/living/simple_animal/hostile/asteroid/polarbear{ +/mob/living/basic/mining/polarbear{ move_force = 999; name = "Dewey" }, @@ -23565,7 +23565,7 @@ /turf/open/floor/iron/white, /area/station/medical/treatment_center) "gHA" = ( -/mob/living/simple_animal/hostile/asteroid/polarbear{ +/mob/living/basic/mining/polarbear{ move_force = 999; name = "Louie" }, @@ -56852,7 +56852,7 @@ /turf/open/floor/plating, /area/station/maintenance/port/aft) "pZY" = ( -/mob/living/simple_animal/hostile/asteroid/polarbear{ +/mob/living/basic/mining/polarbear{ move_force = 999; name = "Huey" }, diff --git a/code/__DEFINES/ai/ai_blackboard.dm b/code/__DEFINES/ai/ai_blackboard.dm index 660a4e3c82c7..21ed0c47aa18 100644 --- a/code/__DEFINES/ai/ai_blackboard.dm +++ b/code/__DEFINES/ai/ai_blackboard.dm @@ -10,6 +10,8 @@ #define BB_FOOD_TARGET "bb_food_target" ///How close a mob must be for us to select it as a target, if that is less than how far we can maintain it as a target #define BB_AGGRO_RANGE "BB_aggro_range" +///If defined, mobs will use this distance instead of default aggro range to locate new targets +#define BB_AGGRO_GRAB_RANGE "BB_aggro_grab_range" ///are we hungry? determined by the udder component #define BB_CHECK_HUNGRY "BB_check_hungry" ///are we ready to breed? @@ -241,3 +243,8 @@ // Used to hold state without making bigass lists /// For /datum/ai_behavior/find_potential_targets, what if any field are we using currently #define BB_FIND_TARGETS_FIELD(type) "bb_find_targets_field_[type]" + +///Currently enraged +#define BB_BASIC_MOB_ENRAGE "BB_enraged" +///Previous melee cooldown +#define BB_BASIC_MOB_PREVIOUS_MELEE_COOLDOWN "BB_previous_melee_cooldown" diff --git a/code/__DEFINES/dcs/signals/signals_global.dm b/code/__DEFINES/dcs/signals/signals_global.dm index 01bce2073f6c..81c37f60afa5 100644 --- a/code/__DEFINES/dcs/signals/signals_global.dm +++ b/code/__DEFINES/dcs/signals/signals_global.dm @@ -108,3 +108,8 @@ /// Global signal whenever a camera network broadcast is started/stopped/updated: (camera_net, is_show_active, announcement) #define COMSIG_GLOB_NETWORK_BROADCAST_UPDATED "!network_broadcast_updated" +///Global signal sent when the player list grows. Called by [mob/add_to_player_list] (mob/player) +#define COMSIG_GLOB_PLAYER_LOGIN "!player_login" + +///Global signal sent when the player list shrinks. Called by [mob/remove_from_player_list] (mob/player) +#define COMSIG_GLOB_PLAYER_LOGOUT "!player_logout" diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm index b44d42ceedec..aac692d55ca8 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_living.dm @@ -36,6 +36,8 @@ #define DOORCRUSH_NO_WOUND (1<<0) ///from base of mob/living/resist() (/mob/living) #define COMSIG_LIVING_RESIST "living_resist" + // Block execute_resist() + #define COMPONENT_BLOCK_RESIST (1<<0) ///from base of mob/living/ignite_mob() (/mob/living) #define COMSIG_LIVING_IGNITED "living_ignite" ///from base of mob/living/extinguish_mob() (/mob/living) 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 86381d4ec22f..5fab93418bb7 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_silicon.dm @@ -8,9 +8,6 @@ #define COMSIG_BORG_HUG_HANDLED 1 ///called from /mob/living/silicon/attack_hand proc #define COMSIG_MOB_PAT_BORG "mob_pat_borg" -///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" ///called when an AI's control is toggled diff --git a/code/__DEFINES/icon_smoothing.dm b/code/__DEFINES/icon_smoothing.dm index bdc6be75782c..42e539ed6a81 100644 --- a/code/__DEFINES/icon_smoothing.dm +++ b/code/__DEFINES/icon_smoothing.dm @@ -129,23 +129,24 @@ DEFINE_BITFIELD(smoothing_junction, list( #define SMOOTH_GROUP_CARPET_SIMPLE_NEON_PINK_NODOTS S_TURF(51) //![turf/open/floor/carpet/neon/simple/pink/nodots] #define SMOOTH_GROUP_BAMBOO_FLOOR S_TURF(52) //![/turf/open/floor/bamboo] #define SMOOTH_GROUP_FLOOR_WATER_LAVALAND S_TURF(53) //![/turf/open/water/lavaland_atmos/basalt] -#define SMOOTH_GROUP_FLOOR_SIDERITE S_TURF(54) ///turf/open/misc/grass -#define SMOOTH_GROUP_FLOOR_SHALE S_TURF(55) ///turf/open/misc/grass - -#define SMOOTH_GROUP_CLOSED_TURFS S_TURF(56) ///turf/closed -#define SMOOTH_GROUP_MATERIAL_WALLS S_TURF(57) ///turf/closed/wall/material -#define SMOOTH_GROUP_SYNDICATE_WALLS S_TURF(58) ///turf/closed/wall/r_wall/plastitanium/syndicate, /turf/closed/indestructible/syndicate -#define SMOOTH_GROUP_HOTEL_WALLS S_TURF(59) ///turf/closed/indestructible/hotelwall -#define SMOOTH_GROUP_MINERAL_WALLS S_TURF(60) ///turf/closed/mineral, /turf/closed/indestructible -#define SMOOTH_GROUP_RED_ROCK_WALLS S_TURF(61) ///turf/closed/mineral/asteroid, /turf/closed/mineral/random/stationside/asteroid -#define SMOOTH_GROUP_SHALE_WALLS S_TURF(62) ///turf/closed/mineral/random/volcanic/shale -#define SMOOTH_GROUP_BOSS_WALLS S_TURF(63) ///turf/closed/indestructible/riveted/boss -#define SMOOTH_GROUP_SURVIVAL_TITANIUM_WALLS S_TURF(64) ///turf/closed/wall/mineral/titanium/survival -#define SMOOTH_GROUP_TURF_OPEN_CLIFF S_TURF(65) ///turf/open/cliff -#define SMOOTH_GROUP_HIEROPHANT S_TURF(66) ///turf/closed/indestructible/riveted/hierophant -#define SMOOTH_GROUP_PLASTINUM_WALLS S_TURF(67) ///turf/closed/indestructible/riveted/plastinum - -#define MAX_S_TURF 67 //Always match this value with the one above it. +#define SMOOTH_GROUP_FLOOR_SIDERITE S_TURF(54) //![/turf/open/misc/asteroid/basalt/smooth/siderite] +#define SMOOTH_GROUP_FLOOR_SHALE S_TURF(55) //![/turf/open/misc/asteroid/basalt/smooth/shale] +#define SMOOTH_GROUP_FLOOR_BASALT S_TURF(56) //![/turf/open/misc/asteroid/basalt] + +#define SMOOTH_GROUP_CLOSED_TURFS S_TURF(57) ///turf/closed +#define SMOOTH_GROUP_MATERIAL_WALLS S_TURF(58) ///turf/closed/wall/material +#define SMOOTH_GROUP_SYNDICATE_WALLS S_TURF(59) ///turf/closed/wall/r_wall/plastitanium/syndicate, /turf/closed/indestructible/syndicate +#define SMOOTH_GROUP_HOTEL_WALLS S_TURF(60) ///turf/closed/indestructible/hotelwall +#define SMOOTH_GROUP_MINERAL_WALLS S_TURF(61) ///turf/closed/mineral, /turf/closed/indestructible +#define SMOOTH_GROUP_RED_ROCK_WALLS S_TURF(62) ///turf/closed/mineral/asteroid, /turf/closed/mineral/random/stationside/asteroid +#define SMOOTH_GROUP_SHALE_WALLS S_TURF(63) ///turf/closed/mineral/random/volcanic/shale +#define SMOOTH_GROUP_BOSS_WALLS S_TURF(64) ///turf/closed/indestructible/riveted/boss +#define SMOOTH_GROUP_SURVIVAL_TITANIUM_WALLS S_TURF(65) ///turf/closed/wall/mineral/titanium/survival +#define SMOOTH_GROUP_TURF_OPEN_CLIFF S_TURF(66) ///turf/open/cliff +#define SMOOTH_GROUP_HIEROPHANT S_TURF(67) ///turf/closed/indestructible/riveted/hierophant +#define SMOOTH_GROUP_PLASTINUM_WALLS S_TURF(68) ///turf/closed/indestructible/riveted/plastinum + +#define MAX_S_TURF 68 //Always match this value with the one above it. #define S_OBJ(num) ("-" + #num + ",") /* /obj included */ diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm index 73da7d1924c0..91ce64a5d4bb 100644 --- a/code/__DEFINES/keybinding.dm +++ b/code/__DEFINES/keybinding.dm @@ -63,6 +63,7 @@ #define COMSIG_KB_LIVING_HOLDTHROWMODE_DOWN "keybinding_living_holdthrowmode_down" #define COMSIG_KB_LIVING_GIVEITEM_DOWN "keybinding_living_giveitem_down" #define COMSIG_KB_LIVING_VIEW_PET_COMMANDS "keybinding_living_view_pet_commands" +#define COMSIG_KB_LIVING_STOP_INTERACTIONS_DOWN "keybinding_living_stop_interactions_down" //Mob #define COMSIG_KB_MOB_FACENORTH_DOWN "keybinding_mob_facenorth_down" diff --git a/code/__DEFINES/move_force.dm b/code/__DEFINES/move_force.dm index d291e95579d3..64d1a8f1f1ba 100644 --- a/code/__DEFINES/move_force.dm +++ b/code/__DEFINES/move_force.dm @@ -18,3 +18,4 @@ #define MOVE_FORCE_WEAK (MOVE_FORCE_DEFAULT / 2) #define MOVE_FORCE_VERY_WEAK ((MOVE_FORCE_DEFAULT / MOVE_FORCE_CRUSH_RATIO) + 1) #define MOVE_FORCE_EXTREMELY_WEAK (MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3)) +#define MOVE_FORCE_NONE 0 diff --git a/code/__DEFINES/rust_g.dm b/code/__DEFINES/rust_g.dm index 877d06ed19f6..1fb72f050ed3 100644 --- a/code/__DEFINES/rust_g.dm +++ b/code/__DEFINES/rust_g.dm @@ -95,6 +95,32 @@ */ #define rustg_acreplace_with_replacements(key, text, replacements) RUSTG_CALL(RUST_G, "acreplace_with_replacements")(key, text, json_encode(replacements)) +/** + * Generates a procedural dungeon map using BSP tree partitioning, prefab placement, + * MST corridor generation, and Cellular Automata smoothing. + * + * Returns a plain binary grid string (matching cellularnoise format): + * A width*height string of '0' (wall) and '1' (floor) characters in row-major order. + * + * Arguments: + * * width - Grid width + * * height - Grid height + * * prefabs_json - JSON array of prefab configs: [{"x":55,"y":65,"w":10,"h":10,"isEnclosed":true},...] (if none use "[]") x = bottom-left turf x, y = bottom-left turf y, w = prefab width, h = prefab height, isEnclosed = whether prefab should be treated like its wall or floor by the generation + * * min_bsp_size - Minimum BSP leaf dimension + * * max_ratio - Maximum aspect ratio for BSP splits + * * padding - Room edge padding within BSP leaf + * * room_fill_percent - How much of each BSP leaf a room fills, 1-100 + * * corridor_width - Width of corridors between rooms + * * loop_percent - Chance to add extra MST edges for loops + * * noise_percent - Initial random floor density + * * ca_steps - Cellular Automata smoothing iterations + * * birth_limit - Neighbors to create floor (>=) + * * survival_limit - Neighbors to survive as floor (>=) + * * edge_is_alive - Whether out-of-bounds cells count as ALIVE (floor) for CA neighbor counts + */ +#define rustg_cave_system_generator_generate(width, height, prefabs_json, min_bsp_size, max_ratio, padding, room_fill_percent, corridor_width, loop_percent, noise_percent, ca_steps, birth_limit, survival_limit, edge_is_alive) \ + RUSTG_CALL(RUST_G, "cave_system_generator_generate")(width, height, prefabs_json, min_bsp_size, max_ratio, padding, room_fill_percent, corridor_width, loop_percent, noise_percent, ca_steps, birth_limit, survival_limit, edge_is_alive) + /** * This proc generates a cellular automata noise grid which can be used in procedural generation methods. * @@ -203,21 +229,70 @@ #define rustg_hash_string(algorithm, text) RUSTG_CALL(RUST_G, "hash_string")(algorithm, text) #define rustg_hash_file(algorithm, fname) RUSTG_CALL(RUST_G, "hash_file")(algorithm, fname) -#define rustg_hash_generate_totp(seed) RUSTG_CALL(RUST_G, "generate_totp")(seed) -#define rustg_hash_generate_totp_tolerance(seed, tolerance) RUSTG_CALL(RUST_G, "generate_totp_tolerance")(seed, tolerance) + +/// Supported algorithms: RUSTG_HASH_SHA1, RUSTG_HASH_SHA256, RUSTG_HASH_SHA512 +/// Seed must be between 10 bytes to 64 bytes (padded or unpadded) of base32. 20 bytes is recommended. Use a CSPRNG. +/// Refresh rate is fixed at 30sec and digit count is fixed at 6 +#define rustg_hash_generate_totp(algorithm, seed) RUSTG_CALL(RUST_G, "generate_totp")(algorithm, seed) +/// Supported algorithms: RUSTG_HASH_SHA1, RUSTG_HASH_SHA256, RUSTG_HASH_SHA512 +/// Seed must be between 10 bytes to 64 bytes (padded or unpadded) of base32. 20 bytes is recommended. Use a CSPRNG. +/// Refresh rate is fixed at 30sec and digit count is fixed at 6 +/// Tolerance is the number of codes +-30sec from the current one that are allowed. +#define rustg_hash_generate_totp_tolerance(algorithm, seed, tolerance) RUSTG_CALL(RUST_G, "generate_totp_tolerance")(algorithm, seed, tolerance) + +/// Creates a cryptographically-secure pseudorandom number generator using the OS-level PRNG as a seed +/// n_bytes is the number of bytes provided to the RNG, the length of the string output varies by format +/// The output string length and characters contained in each format is as follows: +/// RUSTG_RNG_FORMAT_HEX: n_bytes * 2, [a-z0-9] +/// RUSTG_RNG_FORMAT_ALPHANUMERIC: n_bytes, [A-Za-z0-9] +/// RUSTG_RNG_FORMAT_BASE32: ceil(n_bytes / 5 * 8) [A-Z2-7] +/// RUSTG_RNG_FORMAT_BASE32_PADDED: ceil(n_bytes / 5) * 8 [A-Z2-7=] +/// RUSTG_RNG_FORMAT_BASE64: 4 * ceil(n_bytes/3), [A-Za-z0-9+/=] +/// Outputs "ERROR: [reason]" if the format string provided is invalid, or n_bytes is not a positive non-zero integer +#define rustg_csprng_chacha20(format, n_bytes) RUSTG_CALL(RUST_G, "csprng_chacha20")(format, "[n_bytes]") + +/// Creates a seeded pseudorandom number generator using the SHA256 hash output bytes of the seed string +/// Note that this function is NOT suitable for use in cryptography and is intended for high-quality **predictable** RNG +/// Use rustg_csprng_chacha20 for a cryptographically-secure PRNG. +/// n_bytes is the number of bytes provided to the RNG, the length of the string output varies by format +/// The output string length and characters contained in each format is as follows: +/// RUSTG_RNG_FORMAT_HEX: n_bytes * 2, [a-z0-9] +/// RUSTG_RNG_FORMAT_ALPHANUMERIC: n_bytes, [A-Za-z0-9] +/// RUSTG_RNG_FORMAT_BASE32: ceil(n_bytes / 5 * 8) [A-Z2-7] +/// RUSTG_RNG_FORMAT_BASE32_PADDED: ceil(n_bytes / 5) * 8 [A-Z2-7=] +/// RUSTG_RNG_FORMAT_BASE64: 4 * ceil(n_bytes/3), [A-Za-z0-9+/=] +/// Outputs "ERROR: [reason]" if the format string provided is invalid, or n_bytes is not a positive non-zero integer +#define rustg_prng_chacha20_seeded(format, n_bytes, seed) RUSTG_CALL(RUST_G, "prng_chacha20_seeded")(format, "[n_bytes]", seed) + +#define RUSTG_RNG_FORMAT_HEX "hex" +#define RUSTG_RNG_FORMAT_ALPHANUMERIC "alphanumeric" +#define RUSTG_RNG_FORMAT_BASE32 "base32_rfc4648" +#define RUSTG_RNG_FORMAT_BASE32_PADDED "base32_rfc4648_pad" +#define RUSTG_RNG_FORMAT_BASE64 "base64" #define RUSTG_HASH_MD5 "md5" #define RUSTG_HASH_SHA1 "sha1" #define RUSTG_HASH_SHA256 "sha256" #define RUSTG_HASH_SHA512 "sha512" #define RUSTG_HASH_XXH64 "xxh64" +#define RUSTG_HASH_BASE32 "base32_rfc4648" +#define RUSTG_HASH_BASE32_PADDED "base32_rfc4648_pad" #define RUSTG_HASH_BASE64 "base64" /// Encode a given string into base64 #define rustg_encode_base64(str) rustg_hash_string(RUSTG_HASH_BASE64, str) -/// Decode a given base64 string +/// Decode a given base64 string. This expects padding. +/// Returns a blank string if the string is not valid base64. #define rustg_decode_base64(str) RUSTG_CALL(RUST_G, "decode_base64")(str) +/// Encode a given string into base32 (RFC4648) +/// If padding set to FALSE, will not output padding characters. +#define rustg_encode_base32(str, padding) rustg_hash_string(padding ? RUSTG_HASH_BASE32_PADDED : RUSTG_HASH_BASE32, str) +/// Decode a given base32 (RFC4648) string +/// If padding set to FALSE, decoding will not support padding characters. +/// Returns a blank string if the string is not valid base32. +#define rustg_decode_base32(str, padding) RUSTG_CALL(RUST_G, "decode_base32")(str, "[padding ? 1 : 0]") + #ifdef RUSTG_OVERRIDE_BUILTINS #define md5(thing) (isfile(thing) ? rustg_hash_file(RUSTG_HASH_MD5, "[thing]") : rustg_hash_string(RUSTG_HASH_MD5, thing)) #endif @@ -501,3 +576,19 @@ #define url_decode(text) rustg_url_decode(text) #endif +/// Generates a version 4 UUID. +/// See https://www.ietf.org/rfc/rfc9562.html#section-5.4 for specifics on version 4 UUIDs. +#define rustg_generate_uuid_v4(...) RUSTG_CALL(RUST_G, "uuid_v4")() + +/// Generates a version 7 UUID, with the current time. +/// See https://www.ietf.org/rfc/rfc9562.html#section-5.7 for specifics on version 7 UUIDs. +#define rustg_generate_uuid_v7(...) RUSTG_CALL(RUST_G, "uuid_v7")() + +/// Generates a random version 2 CUID. +/// See https://github.com/paralleldrive/cuid2 for specifics on version 2 CUIDs. +#define rustg_generate_cuid2(...) RUSTG_CALL(RUST_G, "cuid2")() + +/// Generates a random version 2 CUID with the given length. +/// See https://github.com/paralleldrive/cuid2 for specifics on version 2 CUIDs. +#define rustg_generate_cuid2_length(length) RUSTG_CALL(RUST_G, "cuid2_len")("[length]") + diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index a1babe0ca797..d75aeee503fb 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -67,9 +67,9 @@ ///The range deducted from sound range for things that are considered silent / sneaky #define SILENCED_SOUND_EXTRARANGE -11 ///Percentage of sound's range where no falloff is applied -#define SOUND_DEFAULT_FALLOFF_DISTANCE 1 //For a normal sound this would be 1 tile of no falloff +#define SOUND_DEFAULT_FALLOFF_DISTANCE 0 //Disabled because it doesn't actually have a nice effect, it just makes the jump to fall-off more shocking. maybe delete ///The default exponent of sound falloff -#define SOUND_FALLOFF_EXPONENT 6 +#define SOUND_FALLOFF_EXPONENT 2.5 #define SOUND_MINIMUM_PRESSURE 10 diff --git a/code/__HELPERS/_dreamluau.dm b/code/__HELPERS/_dreamluau.dm index 9436b95c5519..ab5e29eb34a7 100644 --- a/code/__HELPERS/_dreamluau.dm +++ b/code/__HELPERS/_dreamluau.dm @@ -294,10 +294,10 @@ * Hard deleting an object without clearing userdata corresponding to it leaves the userdata to become associated with * the next DM object to receive the old object's reference ID, which may be undesirable behavior. * - * @param object the object to disassociate from userdata. + * @param args the objects to disassociate from userdata. * * @return null on success */ -#define DREAMLUAU_CLEAR_REF_USERDATA(object) DREAMLUAU_CALL(clear_ref_userdata)((object)) +#define DREAMLUAU_CLEAR_REF_USERDATA(args...) DREAMLUAU_CALL(clear_ref_userdata)(##args) #endif diff --git a/code/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm index 733fd9e4f721..5101b02d31e3 100644 --- a/code/__HELPERS/icon_smoothing.dm +++ b/code/__HELPERS/icon_smoothing.dm @@ -378,6 +378,18 @@ xxx xxx xxx handback |= WEST_JUNCTION | NORTHWEST_JUNCTION | SOUTHWEST_JUNCTION return handback +/// Takes a direction, turns it into all the junctions that it lines up with +/proc/all_junctions_of_dir(dir) + if(dir == NORTH) + return NORTH_JUNCTION | NORTHEAST_JUNCTION | NORTHWEST_JUNCTION + if(dir == SOUTH) + return SOUTH_JUNCTION | SOUTHEAST_JUNCTION | SOUTHWEST_JUNCTION + if(dir == EAST) + return EAST_JUNCTION | SOUTHEAST_JUNCTION | NORTHEAST_JUNCTION + if(dir == WEST) + return WEST_JUNCTION | NORTHWEST_JUNCTION | SOUTHWEST_JUNCTION + return NONE + /proc/dir_to_junction(dir) switch(dir) if(NORTH) diff --git a/code/_compile_options.dm b/code/_compile_options.dm index cb1c70d04e3a..4afb312193c2 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -130,6 +130,12 @@ #define DISABLE_DREAMLUAU #endif +// Since 0.2.0, dreamluau depends on breaking changes made to byondapi in 1674. +// Get rid of this when BYOND_MINOR is >= 1674 AND we don't have any alternate tests on <1674. +#if DM_BUILD < 1674 +#define DISABLE_DREAMLUAU +#endif + /// If this is uncommented, force our verb processing into just the 2% of a tick /// We normally reserve for it /// NEVER run this on live, it's for simulating highpop only diff --git a/code/_onclick/click_ctrl.dm b/code/_onclick/click_ctrl.dm index 19c86970aeee..e80f8fa05cb9 100644 --- a/code/_onclick/click_ctrl.dm +++ b/code/_onclick/click_ctrl.dm @@ -41,7 +41,7 @@ if(grab(target) != GRAB_SKIP) changeNext_move(CLICK_CD_MELEE) return - start_pulling(target) + pulled(target) /** * Ctrl mouse wheel click diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index d90ffb54507e..92f7acb42598 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -73,7 +73,7 @@ return if(W == A) - W.attack_self(src) + W.attack_self(src, modifiers) return // cyborgs are prohibited from using storage items so we can I think safely remove (A.loc in contents) diff --git a/code/controllers/subsystem/dynamic/dynamic_ruleset_midround.dm b/code/controllers/subsystem/dynamic/dynamic_ruleset_midround.dm index 71a13ce43500..8631eaf46709 100644 --- a/code/controllers/subsystem/dynamic/dynamic_ruleset_midround.dm +++ b/code/controllers/subsystem/dynamic/dynamic_ruleset_midround.dm @@ -1032,7 +1032,7 @@ candidate_role = "Slaughter Demon" // preview_antag_datum = /datum/antagonist/slaughter // Doesn't actually have its own pref midround_type = HEAVY_MIDROUND - jobban_flag = ROLE_ALIEN + jobban_flag = ROLE_SENTIENCE ruleset_flags = RULESET_INVADER weight = 0 min_pop = 20 diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm index ae01679b0943..efd23d83163c 100644 --- a/code/controllers/subsystem/garbage.dm +++ b/code/controllers/subsystem/garbage.dm @@ -374,7 +374,7 @@ SUBSYSTEM_DEF(garbage) var/start_time = world.time var/start_tick = world.tick_usage SEND_SIGNAL(to_delete, COMSIG_QDELETING, force) // Let the (remaining) components know about the result of Destroy - var/hint = to_delete.Destroy(force) // Let our friend know they're about to get fucked up. + var/hint = UNLINT(to_delete.Destroy(force)) // Let our friend know they're about to get fucked up. if(world.time != start_time) trash.slept_destroy++ diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 8d98ff05bd5c..7b1b55341c51 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -17,6 +17,9 @@ SUBSYSTEM_DEF(mapping) var/list/ruins_templates = list() + ///Assoc list of all ruins spawned, key center of ruin spawn -> value ruin instance + var/list/active_ruins = alist() + ///List of ruins, separated by their theme var/list/themed_ruins = list() diff --git a/code/controllers/subsystem/processing/instruments.dm b/code/controllers/subsystem/processing/instruments.dm index 197c16047b7f..a68a8ff77a61 100644 --- a/code/controllers/subsystem/processing/instruments.dm +++ b/code/controllers/subsystem/processing/instruments.dm @@ -52,6 +52,6 @@ PROCESSING_SUBSYSTEM_DEF(instruments) /datum/controller/subsystem/processing/instruments/proc/reserve_instrument_channel(datum/instrument/I) if(current_instrument_channels > max_instrument_channels) return - . = SSsounds.reserve_sound_channel(I) + . = SSsounds.reserve_sound_channel_for_datum(I) if(!isnull(.)) current_instrument_channels++ diff --git a/code/controllers/subsystem/sound_tokens.dm b/code/controllers/subsystem/sound_tokens.dm new file mode 100644 index 000000000000..dc4bd1600c38 --- /dev/null +++ b/code/controllers/subsystem/sound_tokens.dm @@ -0,0 +1,24 @@ +SUBSYSTEM_DEF(sound_tokens) + name = "Sound Tokens" + wait = 1 + ss_flags = SS_TICKER | SS_BACKGROUND | SS_NO_INIT + + + var/list/clients_needing_update = list() + var/list/currentrun = list() + +/datum/controller/subsystem/sound_tokens/fire(resumed) + if(!resumed) + currentrun = clients_needing_update + clients_needing_update = list() + while(length(currentrun)) + var/client/client = currentrun[currentrun.len] + currentrun.len-- + var/mob/owned_mob = client.mob + if(!owned_mob) + continue + for(var/datum/sound_token/token in client.sound_tokens) + token.update_listener(owned_mob) + if(MC_TICK_CHECK) + break + diff --git a/code/controllers/subsystem/sounds.dm b/code/controllers/subsystem/sounds.dm index db3f94a080bd..360f8a883e53 100644 --- a/code/controllers/subsystem/sounds.dm +++ b/code/controllers/subsystem/sounds.dm @@ -114,10 +114,10 @@ SUBSYSTEM_DEF(sounds) var/text_channel = num2text(channel) var/using = using_channels[text_channel] using_channels -= text_channel - if(using != TRUE) // datum channel + if(using != DATUMLESS) // datum channel using_channels_by_datum[using] -= channel if(!length(using_channels_by_datum[using])) - using_channels_by_datum -= using + stop_tracking_datum(using) free_channel(channel) /// Frees all the channels a datum is using. @@ -128,14 +128,14 @@ SUBSYSTEM_DEF(sounds) for(var/channel in L) using_channels -= num2text(channel) free_channel(channel) - using_channels_by_datum -= D + stop_tracking_datum(D) /// Frees all datumless channels /datum/controller/subsystem/sounds/proc/free_datumless_channels() free_datum_channels(DATUMLESS) -/// NO AUTOMATIC CLEANUP - If you use this, you better manually free it later! Returns an integer for channel. -/datum/controller/subsystem/sounds/proc/reserve_sound_channel_datumless() +/// Reserve a sound channel. Free it later with free_sound_channel() +/datum/controller/subsystem/sounds/proc/reserve_sound_channel() . = reserve_channel() if(!.) //oh no.. return FALSE @@ -145,7 +145,7 @@ SUBSYSTEM_DEF(sounds) using_channels_by_datum[DATUMLESS] += . /// Reserves a channel for a datum. Automatic cleanup only when the datum is deleted. Returns an integer for channel. -/datum/controller/subsystem/sounds/proc/reserve_sound_channel(datum/D) +/datum/controller/subsystem/sounds/proc/reserve_sound_channel_for_datum(datum/D) if(!D) //i don't like typechecks but someone will fuck it up CRASH("Attempted to reserve sound channel without datum using the managed proc.") .= reserve_channel() @@ -156,6 +156,8 @@ SUBSYSTEM_DEF(sounds) LAZYINITLIST(using_channels_by_datum[D]) using_channels_by_datum[D] += . + RegisterSignal(D, COMSIG_QDELETING, PROC_REF(tracked_datum_deleted)) + /** * Reserves a channel and updates the datastructure. Private proc. */ @@ -260,4 +262,19 @@ SUBSYSTEM_DEF(sounds) if(!isnull(sfx.key)) GLOB.sfx_datum_by_key[sfx.key] = new sfx() + +///Call to free all channels reserved by a datum. +/datum/controller/subsystem/sounds/proc/stop_tracking_datum(datum/D) + PRIVATE_PROC(TRUE) + + using_channels_by_datum -= D + UnregisterSignal(D, COMSIG_QDELETING) + +/// Handles a tracked datum being deleted, automatically freeing the channels. +/datum/controller/subsystem/sounds/proc/tracked_datum_deleted(datum/source) + SIGNAL_HANDLER + PRIVATE_PROC(TRUE) + + free_datum_channels(source) + #undef DATUMLESS diff --git a/code/datums/actions/mobs/charge.dm b/code/datums/actions/mobs/charge.dm index 4dc016a66e67..dd80650065e0 100644 --- a/code/datums/actions/mobs/charge.dm +++ b/code/datums/actions/mobs/charge.dm @@ -212,18 +212,21 @@ if(!isliving(target)) source.visible_message(span_danger("[source] smashes into [target]!")) - living_source?.Stun(recoil_duration, ignore_canstun = TRUE) + if (recoil_duration >= 0) // Because 0 stun/knockdown is still a valid value + living_source?.Stun(recoil_duration, ignore_canstun = TRUE) return var/mob/living/living_target = target if(ishuman(living_target)) var/mob/living/carbon/human/human_target = living_target if(human_target.check_block(source, 0, "\the [source]", attack_type = LEAP_ATTACK) && living_source) - living_source.Stun(recoil_duration, ignore_canstun = TRUE) + if (recoil_duration >= 0) + living_source.Stun(recoil_duration, ignore_canstun = TRUE) return living_target.visible_message(span_danger("[source] charges into [living_target]!"), span_userdanger("[source] charges into you!")) - living_target.Knockdown(knockdown_duration) + if (knockdown_duration >= 0) + living_target.Knockdown(knockdown_duration) /datum/status_effect/tired_post_charge id = "tired_post_charge" diff --git a/code/datums/actions/mobs/dash.dm b/code/datums/actions/mobs/dash.dm index ad87ab93f9a7..36db85531c1e 100644 --- a/code/datums/actions/mobs/dash.dm +++ b/code/datums/actions/mobs/dash.dm @@ -6,8 +6,6 @@ cooldown_time = 1.5 SECONDS /// The range of the dash var/dash_range = 4 - /// The distance you will be from the target after you dash - var/pick_range = 5 /datum/action/cooldown/mob_cooldown/dash/Activate(atom/target_atom) disable_cooldown_actions() diff --git a/code/datums/actions/mobs/projectileattack.dm b/code/datums/actions/mobs/projectileattack.dm index 183df71e03f1..bdbea403e2fe 100644 --- a/code/datums/actions/mobs/projectileattack.dm +++ b/code/datums/actions/mobs/projectileattack.dm @@ -78,7 +78,7 @@ /datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/attack_sequence(mob/living/firer, atom/target) for(var/i in 1 to shot_count) shoot_projectile(firer, target, null, firer, rand(-default_projectile_spread, default_projectile_spread), null) - SLEEP_CHECK_DEATH(shot_delay, src) + SLEEP_CHECK_DEATH(shot_delay, firer) /datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/direct shot_count = 40 diff --git a/code/datums/actions/mobs/sequences/dash_attack.dm b/code/datums/actions/mobs/sequences/dash_attack.dm index b5fff4cb4922..42486f8d9f37 100644 --- a/code/datums/actions/mobs/sequences/dash_attack.dm +++ b/code/datums/actions/mobs/sequences/dash_attack.dm @@ -6,6 +6,12 @@ cooldown_time = 3 SECONDS shared_cooldown = MOB_SHARED_COOLDOWN_2 sequence_actions = list( - /datum/action/cooldown/mob_cooldown/dash = 0.1 SECONDS, - /datum/action/cooldown/mob_cooldown/projectile_attack/kinetic_accelerator = 0, + /datum/action/cooldown/mob_cooldown/charge/basic_charge/blood_drunk_miner = 0.22 SECONDS, // 0.1s windup + 0.12s max dash + /datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/kinetic_accelerator = 0, + ) + +/datum/action/cooldown/mob_cooldown/dash_attack/long_burst + sequence_actions = list( + /datum/action/cooldown/mob_cooldown/charge/basic_charge/blood_drunk_miner = 0.22 SECONDS, + /datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/kinetic_accelerator/long_burst = 0, ) diff --git a/code/datums/actions/mobs/transform_weapon.dm b/code/datums/actions/mobs/transform_weapon.dm deleted file mode 100644 index 6cb97e0c635c..000000000000 --- a/code/datums/actions/mobs/transform_weapon.dm +++ /dev/null @@ -1,26 +0,0 @@ -/datum/action/cooldown/mob_cooldown/transform_weapon - name = "Transform Weapon" - button_icon = 'icons/obj/mining_zones/artefacts.dmi' - button_icon_state = "cleaving_saw" - desc = "Transform weapon into a different state." - cooldown_time = 5 SECONDS - shared_cooldown = MOB_SHARED_COOLDOWN_2 - /// The max possible cooldown, cooldown is random between the default cooldown time and this - var/max_cooldown_time = 10 SECONDS - -/datum/action/cooldown/mob_cooldown/transform_weapon/Activate(atom/target_atom) - disable_cooldown_actions() - do_transform() - StartCooldown(rand(cooldown_time, max_cooldown_time), 0) - enable_cooldown_actions() - return TRUE - -/datum/action/cooldown/mob_cooldown/transform_weapon/proc/do_transform() - if(!istype(owner, /mob/living/basic/boss/blood_drunk_miner)) - return - var/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner = owner - blood_drunk_miner.miner_saw.attack_self(owner) - var/saw_open = HAS_TRAIT(blood_drunk_miner.miner_saw, TRAIT_TRANSFORM_ACTIVE) - blood_drunk_miner.rapid_melee_hits = saw_open ? 3 : 5 - blood_drunk_miner.icon_state = "miner[saw_open ? "_transformed":""]" - blood_drunk_miner.icon_living = "miner[saw_open ? "_transformed":""]" diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm index b2aaaa235788..ce46d4c1ab98 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeting.dm @@ -12,6 +12,8 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ var/vision_range = 9 /// Blackboard key for aggro range, uses vision range if not specified var/aggro_range_key = BB_AGGRO_RANGE + /// Range in which we can acquire a new target + var/aggro_grab_range_key = BB_AGGRO_GRAB_RANGE /// Blackboard key for the target priority strategy var/priority_strategy_key = BB_TARGET_PRIORITY_STRATEGY /// If we have a priority strategy set, how often do we refresh our target search? @@ -34,7 +36,12 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ if((!priority_strategy || controller.blackboard[BB_BASIC_MOB_TARGET_REFRESH_COOLDOWN] > world.time) && current_target && targeting_strategy.can_attack(living_mob, current_target, vision_range)) return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED - var/aggro_range = controller.blackboard[aggro_range_key] || vision_range + var/aggro_range = vision_range + if(isnull(current_target) && !isnull(controller.blackboard[aggro_grab_range_key])) + aggro_range = controller.blackboard[aggro_grab_range_key] + else if(!isnull(controller.blackboard[aggro_range_key])) + aggro_range = controller.blackboard[aggro_range_key] + controller.clear_blackboard_key(target_key) // If we're using a field rn, just don't do anything yeah? @@ -87,7 +94,12 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED /datum/ai_behavior/find_potential_targets/proc/failed_to_find_anyone(datum/ai_controller/controller, target_key, targeting_strategy_key, hiding_location_key) - var/aggro_range = controller.blackboard[aggro_range_key] || vision_range + var/aggro_range = vision_range + if(!isnull(controller.blackboard[aggro_grab_range_key])) + aggro_range = controller.blackboard[aggro_grab_range_key] + else if(!isnull(controller.blackboard[aggro_range_key])) + aggro_range = controller.blackboard[aggro_range_key] + // takes the larger between our range() input and our implicit hearers() input (world.view) aggro_range = max(aggro_range, ROUND_UP(max(getviewsize(world.view)) / 2)) // Alright, here's the interesting bit @@ -187,6 +199,3 @@ GLOBAL_LIST_INIT(target_interested_atoms, typecacheof(list(/mob, /obj/machinery/ if(length(priority_targets)) return ..(controller, priority_targets) return ..() - -/datum/ai_behavior/find_potential_targets/bigger_range - vision_range = 16 diff --git a/code/datums/ai/basic_mobs/basic_subtrees/enrage.dm b/code/datums/ai/basic_mobs/basic_subtrees/enrage.dm new file mode 100644 index 000000000000..cf3a922a5a44 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/enrage.dm @@ -0,0 +1,44 @@ +// Performs the enrage behavior when health is below given threshold, and calm down behavior if above that value afterwards +/datum/ai_planning_subtree/enrage + var/health_threshold = 0.5 + var/enrage_behavior = /datum/ai_behavior/enrage + +/datum/ai_planning_subtree/enrage/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + if(!isbasicmob(controller.pawn)) + return + var/mob/living/basic/basic_pawn = controller.pawn + var/low_health = (basic_pawn.health / basic_pawn.maxHealth) <= health_threshold + + var/is_enraged = controller.blackboard_key_exists(BB_BASIC_MOB_ENRAGE) + if(low_health && !is_enraged) + controller.queue_behavior(enrage_behavior, FALSE) + else if(!low_health && is_enraged) + controller.queue_behavior(enrage_behavior, TRUE) + + +/// Cuts down basic mob's melee attack cooldown in half +/datum/ai_behavior/enrage + +/datum/ai_behavior/enrage/perform(seconds_per_tick, datum/ai_controller/controller, calm_down) + var/mob/living/basic/basic_pawn = controller.pawn + if(calm_down) + var/previous_delay = controller.blackboard[BB_BASIC_MOB_PREVIOUS_MELEE_COOLDOWN] + // Technically something else could have modified the cooldown before/after but that requires further consideration so don't use this behavior in these scenarios + basic_pawn.melee_attack_cooldown = previous_delay + controller.clear_blackboard_key(BB_BASIC_MOB_PREVIOUS_MELEE_COOLDOWN) + controller.clear_blackboard_key(BB_BASIC_MOB_ENRAGE) + return AI_BEHAVIOR_SUCCEEDED + + var/current_cooldown = basic_pawn.melee_attack_cooldown + var/new_attack_cooldown = current_cooldown / 2 + + controller.set_blackboard_key(BB_BASIC_MOB_ENRAGE, TRUE) + controller.set_blackboard_key(BB_BASIC_MOB_PREVIOUS_MELEE_COOLDOWN, current_cooldown) + basic_pawn.melee_attack_cooldown = new_attack_cooldown + + if(controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)) + var/current_target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + controller.pawn.visible_message(span_danger("\The [controller.pawn] gets an enraged look at [current_target]!")) + else + controller.pawn.visible_message(span_danger("\The [controller.pawn] gets an enraged look!")) + return AI_BEHAVIOR_SUCCEEDED diff --git a/code/datums/ai/basic_mobs/basic_subtrees/simple_find_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/simple_find_target.dm index 9824d41f8238..57fb22b4a956 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/simple_find_target.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/simple_find_target.dm @@ -22,9 +22,5 @@ /datum/ai_planning_subtree/simple_find_target/to_flee target_key = BB_BASIC_MOB_FLEE_TARGET -/datum/ai_planning_subtree/simple_find_target/increased_range - target_behavior = /datum/ai_behavior/find_potential_targets/bigger_range - /datum/ai_planning_subtree/simple_find_target/hunt strategy_key = BB_HUNT_TARGETING_STRATEGY - diff --git a/code/datums/components/jetpack.dm b/code/datums/components/jetpack.dm index 3ebc9da823f2..1ca91a9cb2ea 100644 --- a/code/datums/components/jetpack.dm +++ b/code/datums/components/jetpack.dm @@ -136,13 +136,22 @@ if(source.client.intended_direction && check_on_move.Invoke(TRUE)) //You use jet when press keys. yes. trail?.generate_effect() +/// Handles all active 0g movement, including both manual (trying to move a direction in 0g) and automatic (drifting idly in 0g) /datum/component/jetpack/proc/stabilize(mob/source, movement_dir, continuous_move, backup) SIGNAL_HANDLER - if(!continuous_move && movement_dir) - return COMSIG_MOVABLE_STOP_SPACEMOVE - // Check if we have the fuel to stop this. Do NOT consume any fuel, just check - // This is done because things other then us can use our fuel - if(stabilize && check_on_move.Invoke(FALSE)) + /* + * Checks if we should stop any active movement + * + * Obviously we stop all forms of drifting if we have stabilizers active (allowing free space movement) + * Less obviously, we stop need to stop drift if we are trying to move *while passively drifting* + * (Without checking the latter, jetpacks will act very weird and jank. As you move in one direction, + * you will simultaneously drift in that direction, causing you to jump/skip a tile every so often.) + * + * Either way, we need to check that we have the "fuel" to stop this + * DO NOT CONSUME FUEL HERE, just check if we have it + * This is done because things other then us can use our fuel + */ + if((stabilize || (!continuous_move && movement_dir)) && check_on_move.Invoke(FALSE)) return COMSIG_MOVABLE_STOP_SPACEMOVE return NONE diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 39c0f9ab284c..0b46fe66554b 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -87,7 +87,7 @@ /** * Parent types. - * + * * Abstract-ness is a meta-property of a class that is used to indicate * that the class is intended to be used as a base class for others, and * should not (or cannot) be instantiated. @@ -123,6 +123,7 @@ * Returns [QDEL_HINT_QUEUE] */ /datum/proc/Destroy(force = FALSE) + PROTECTED_PROC(TRUE) SHOULD_CALL_PARENT(TRUE) SHOULD_NOT_SLEEP(TRUE) tag = null @@ -160,9 +161,16 @@ //END: ECS SHIT #ifndef DISABLE_DREAMLUAU + var/list/to_remove = list() + if(ismovable(src)) + var/atom/movable/src_movable = src + to_remove += list(src_movable.vis_contents, src_movable.vis_locs) if(!(datum_flags & DF_STATIC_OBJECT)) - DREAMLUAU_CLEAR_REF_USERDATA(vars) // vars ceases existing when src does, so we need to clear any lua refs to it that exist. - DREAMLUAU_CLEAR_REF_USERDATA(src) + if(isatom(src)) + var/atom/src_atom = src + to_remove += list(src_atom.contents, src_atom.filters, src_atom.underlays, src_atom.overlays) + to_remove += list(vars, src) // vars ceases existing when src does, so we need to clear any lua refs to it that exist. + DREAMLUAU_CLEAR_REF_USERDATA(arglist(to_remove)) #endif return QDEL_HINT_QUEUE diff --git a/code/datums/elements/move_force_on_death.dm b/code/datums/elements/move_force_on_death.dm index af2560d000c3..481f93581337 100644 --- a/code/datums/elements/move_force_on_death.dm +++ b/code/datums/elements/move_force_on_death.dm @@ -39,11 +39,11 @@ if(!isnull(move_resist)) source.move_resist = move_resist if(!isnull(pull_force)) - source.pull_force = pull_force + source.set_pull_force(pull_force) /datum/element/change_force_on_death/proc/on_revive(mob/living/source) SIGNAL_HANDLER source.move_force = initial(source.move_force) source.move_resist = initial(source.move_resist) - source.pull_force = initial(source.pull_force) + source.set_pull_force(initial(source.pull_force)) diff --git a/code/datums/keybinding/living.dm b/code/datums/keybinding/living.dm index 090e559c1490..33d24177b547 100644 --- a/code/datums/keybinding/living.dm +++ b/code/datums/keybinding/living.dm @@ -227,3 +227,27 @@ if(!HAS_TRAIT(living_user, TRAIT_CAN_HOLD_ITEMS)) return living_user.give() + +/datum/keybinding/living/view_pet_data + hotkey_keys = list("Shift") + name = "view_pet_commands" + full_name = "View Pet Commands" + description = "Hold down to see all the commands you can give your pets!" + keybind_signal = COMSIG_KB_LIVING_VIEW_PET_COMMANDS + +/datum/keybinding/living/cancel_interactions + name = "stop_interactions" + full_name = "Cancel Interactions" + description = "Cancels any ongoing interactions (such as using a tool, performing surgery, or climbing). \ + Note that some interactions cannot be interrupted, and you can't cancel other player's interaction with this hotkey." + keybind_signal = COMSIG_KB_LIVING_STOP_INTERACTIONS_DOWN + +/datum/keybinding/living/cancel_interactions/down(client/user, turf/target, mousepos_x, mousepos_y) + . = ..() + if(.) + return + var/mob/living/mob_user = user.mob + if(!LAZYLEN(mob_user.do_afters) || HAS_TRAIT(mob_user, TRAIT_INCAPACITATED)) + return + // this is currently the best way to stop all ongoing doafters + mob_user.incapacitate(0.1 SECONDS, ignore_canstun = TRUE) diff --git a/code/datums/keybinding/mob.dm b/code/datums/keybinding/mob.dm index 6d25e6ab2c4d..a35937fe5559 100644 --- a/code/datums/keybinding/mob.dm +++ b/code/datums/keybinding/mob.dm @@ -246,10 +246,3 @@ if(.) return user.movement_locked = FALSE - -/datum/keybinding/living/view_pet_data - hotkey_keys = list("Shift") - name = "view_pet_commands" - full_name = "View Pet Commands" - description = "Hold down to see all the commands you can give your pets!" - keybind_signal = COMSIG_KB_LIVING_VIEW_PET_COMMANDS diff --git a/code/datums/looping_sounds/_looping_sound.dm b/code/datums/looping_sounds/_looping_sound.dm index 7080033c63f3..d4bd2f0ddf94 100644 --- a/code/datums/looping_sounds/_looping_sound.dm +++ b/code/datums/looping_sounds/_looping_sound.dm @@ -67,6 +67,10 @@ var/reserve_random_channel = FALSE //If we reserve a random sound channel, store the channel number here so we can clean it up later. var/reserved_channel + ///Whether this looping sound uses sound tokens. This should only be true for sounds that need to update as the source or listeners move. (Generally long or important sounds like grav-gen) + var/use_sound_tokens = FALSE + ///The sound token instance for this looping sound. + var/datum/sound_token/sound_token_instance /datum/looping_sound/New( _parent, @@ -104,12 +108,12 @@ if(timer_id) return - if(!sound_channel && reserve_random_channel) - sound_channel = SSsounds.reserve_sound_channel_datumless() + if(!use_sound_tokens && !sound_channel && reserve_random_channel) + sound_channel = SSsounds.reserve_sound_channel() reserved_channel = sound_channel - on_start() + /** * The proc to call to stop the sound loop. * @@ -171,6 +175,14 @@ * * volume_override - The volume we want to play the sound at, overriding the `volume` variable. */ /datum/looping_sound/proc/play(soundfile, volume_override) + + if(use_sound_tokens) + if(sound_token_instance) + sound_token_instance.set_volume(volume_override || volume, FALSE) // Don't update, we'll do that after + sound_token_instance.update_sound(soundfile, TRUE) + else + sound_token_instance = new /datum/sound_token(parent, soundfile, SOUND_RANGE + extra_range, volume_override || volume, falloff_exponent, falloff_distance) + return var/sound/sound_to_play = sound(soundfile) sound_to_play.channel = sound_channel || SSsounds.random_available_channel() sound_to_play.volume = volume_override || volume //Use volume as fallback if theres no override @@ -248,6 +260,7 @@ /// Stops sound playing on current channel, if specified /datum/looping_sound/proc/stop_current() + QDEL_NULL(sound_token_instance) if(!sound_channel || !ismob(parent)) return var/mob/mob_parent = parent diff --git a/code/datums/looping_sounds/burning.dm b/code/datums/looping_sounds/burning.dm index 191ae88db892..8673a9ab1dec 100644 --- a/code/datums/looping_sounds/burning.dm +++ b/code/datums/looping_sounds/burning.dm @@ -7,3 +7,4 @@ volume = 50 vary = TRUE extra_range = MEDIUM_RANGE_SOUND_EXTRARANGE + use_sound_tokens = TRUE diff --git a/code/datums/looping_sounds/changeling_absorb.dm b/code/datums/looping_sounds/changeling_absorb.dm index 418c2a8dacf5..6b28c56ee47c 100644 --- a/code/datums/looping_sounds/changeling_absorb.dm +++ b/code/datums/looping_sounds/changeling_absorb.dm @@ -12,3 +12,4 @@ mid_length = 3 SECONDS volume = 80 ignore_walls = FALSE + use_sound_tokens = TRUE diff --git a/code/datums/looping_sounds/choking.dm b/code/datums/looping_sounds/choking.dm index 6d337b1c7d64..bf0222b038ec 100644 --- a/code/datums/looping_sounds/choking.dm +++ b/code/datums/looping_sounds/choking.dm @@ -10,3 +10,4 @@ vary = TRUE // Same as above ignore_walls = FALSE + use_sound_tokens = TRUE diff --git a/code/datums/looping_sounds/cyborg.dm b/code/datums/looping_sounds/cyborg.dm index 0a01a4c7116b..1b23271ee331 100644 --- a/code/datums/looping_sounds/cyborg.dm +++ b/code/datums/looping_sounds/cyborg.dm @@ -8,3 +8,4 @@ end_sound = 'sound/mobs/non-humanoids/cyborg/wash_end.ogg' vary = TRUE extra_range = 5 + use_sound_tokens = TRUE diff --git a/code/datums/looping_sounds/drip.dm b/code/datums/looping_sounds/drip.dm index 224d7850fc29..2c6d21e273a7 100644 --- a/code/datums/looping_sounds/drip.dm +++ b/code/datums/looping_sounds/drip.dm @@ -7,3 +7,4 @@ vary = TRUE ignore_walls = FALSE falloff_distance = 5 + use_sound_tokens = TRUE diff --git a/code/datums/looping_sounds/item_sounds.dm b/code/datums/looping_sounds/item_sounds.dm index e5950566d4f3..517dc752944d 100644 --- a/code/datums/looping_sounds/item_sounds.dm +++ b/code/datums/looping_sounds/item_sounds.dm @@ -2,16 +2,19 @@ mid_sounds = list('sound/effects/clock_tick.ogg' = 1) mid_length = 0.35 SECONDS volume = 25 + use_sound_tokens = TRUE /datum/looping_sound/reverse_bear_trap_beep mid_sounds = list('sound/machines/beep/beep.ogg' = 1) mid_length = 6 SECONDS volume = 10 + use_sound_tokens = TRUE /datum/looping_sound/siren mid_sounds = list('sound/items/weeoo1.ogg' = 1) mid_length = 1.5 SECONDS volume = 20 + use_sound_tokens = TRUE /datum/looping_sound/tape_recorder_hiss mid_sounds = list('sound/items/taperecorder/taperecorder_hiss_mid.ogg' = 1) @@ -29,6 +32,7 @@ falloff_exponent = 10 falloff_distance = 1 volume = 5 + use_sound_tokens = TRUE /datum/looping_sound/chainsaw start_sound = list('sound/items/weapons/chainsaw_start.ogg' = 1) @@ -39,6 +43,7 @@ end_volume = 35 volume = 40 ignore_walls = FALSE + use_sound_tokens = TRUE /datum/looping_sound/beesmoke mid_sounds = list('sound/items/weapons/beesmoke.ogg' = 1) @@ -59,3 +64,4 @@ end_volume = 15 ignore_walls = FALSE reserve_random_channel = TRUE + use_sound_tokens = TRUE diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm index 871a5ca7bf90..9ab93ae378e8 100644 --- a/code/datums/looping_sounds/machinery_sounds.dm +++ b/code/datums/looping_sounds/machinery_sounds.dm @@ -18,13 +18,15 @@ falloff_exponent = 10 falloff_distance = 5 vary = TRUE + use_sound_tokens = TRUE /datum/looping_sound/destabilized_crystal mid_sounds = list('sound/machines/sm/loops/delamming.ogg') mid_length = 6 SECONDS volume = 55 - extra_range = 15 + extra_range = 35 vary = TRUE + use_sound_tokens = TRUE /datum/looping_sound/hypertorus mid_sounds = list('sound/machines/hypertorus/loops/hypertorus_nominal.ogg') @@ -32,6 +34,7 @@ volume = 55 extra_range = 15 vary = TRUE + use_sound_tokens = TRUE /datum/looping_sound/generator start_sound = 'sound/machines/generator/generator_start.ogg' @@ -52,9 +55,10 @@ 'sound/machines/fryer/deep_fryer_1.ogg', 'sound/machines/fryer/deep_fryer_2.ogg', ) - mid_length = 0.2 SECONDS + mid_length = 1 SECONDS end_sound = 'sound/machines/fryer/deep_fryer_emerge.ogg' volume = 15 + use_sound_tokens = TRUE /datum/looping_sound/clock mid_sounds = list('sound/ambience/misc/ticking_clock.ogg') @@ -66,6 +70,7 @@ mid_sounds = list('sound/machines/grill/grillsizzle.ogg') mid_length = 18 volume = 50 + use_sound_tokens = TRUE /datum/looping_sound/oven start_sound = 'sound/machines/oven/oven_loop_start.ogg' //my immersions @@ -75,14 +80,7 @@ end_sound = 'sound/machines/oven/oven_loop_end.ogg' volume = 100 falloff_exponent = 4 - -/datum/looping_sound/deep_fryer - mid_length = 0.2 SECONDS - mid_sounds = list( - 'sound/machines/fryer/deep_fryer_1.ogg', - 'sound/machines/fryer/deep_fryer_2.ogg', - ) - volume = 30 + use_sound_tokens = TRUE /datum/looping_sound/microwave start_sound = 'sound/machines/microwave/microwave-start.ogg' @@ -94,6 +92,7 @@ mid_length = 1 SECONDS end_sound = 'sound/machines/microwave/microwave-end.ogg' volume = 90 + use_sound_tokens = TRUE /datum/looping_sound/lathe_print mid_sounds = list('sound/machines/lathe/lathe_print.ogg') @@ -103,12 +102,14 @@ ignore_walls = FALSE falloff_distance = 1 mid_length_vary = 1 SECONDS + use_sound_tokens = TRUE /datum/looping_sound/jackpot mid_length = 1.1 SECONDS mid_sounds = list('sound/machines/roulette/roulettejackpot.ogg') volume = 85 vary = TRUE + use_sound_tokens = TRUE /datum/looping_sound/server mid_sounds = list( @@ -140,9 +141,10 @@ end_sound = 'sound/machines/computer/computer_end.ogg' end_volume = 1 SECONDS volume = SOUND_AUDIBLE_VOLUME_MIN - falloff_exponent = 5 //Ultra quiet very fast + falloff_exponent = 4 //Ultra quiet very fast extra_range = -12 - falloff_distance = 1 //Instant falloff after initial tile + falloff_distance = 0 //Instant falloff after initial tile + use_sound_tokens = TRUE /datum/looping_sound/gravgen start_sound = 'sound/machines/gravgen/grav_gen_start.ogg' @@ -157,7 +159,8 @@ vary = TRUE volume = 70 falloff_distance = 5 - falloff_exponent = 20 + falloff_exponent = 10 + use_sound_tokens = TRUE /datum/looping_sound/firealarm mid_sounds = list( @@ -168,16 +171,19 @@ ) mid_length = 2.4 SECONDS volume = 30 + use_sound_tokens = TRUE /datum/looping_sound/gravgen/kinesis volume = 20 falloff_distance = 2 falloff_exponent = 5 + use_sound_tokens = TRUE /datum/looping_sound/boiling mid_sounds = list('sound/effects/bubbles/bubbles2.ogg') mid_length = 7 SECONDS volume = 25 + use_sound_tokens = TRUE /datum/looping_sound/typing mid_sounds = list( @@ -205,7 +211,8 @@ end_sound = 'sound/effects/soup_boil/soup_boil_end.ogg' end_volume = 60 extra_range = MEDIUM_RANGE_SOUND_EXTRARANGE - falloff_exponent = 4 + falloff_exponent = 3 + use_sound_tokens = TRUE /datum/looping_sound/soup/toxic volume = 40 @@ -225,3 +232,4 @@ ) mid_length = 5 SECONDS volume = 50 + use_sound_tokens = TRUE diff --git a/code/datums/looping_sounds/projectiles.dm b/code/datums/looping_sounds/projectiles.dm index ca96df698e92..1c2dc9b19b72 100644 --- a/code/datums/looping_sounds/projectiles.dm +++ b/code/datums/looping_sounds/projectiles.dm @@ -2,3 +2,4 @@ mid_sounds = list('sound/effects/moon_parade_soundloop.ogg' = 1) mid_length = 2 SECONDS volume = 20 + use_sound_tokens = TRUE diff --git a/code/datums/looping_sounds/vents.dm b/code/datums/looping_sounds/vents.dm index 183c337ef981..04e0246a4ee5 100644 --- a/code/datums/looping_sounds/vents.dm +++ b/code/datums/looping_sounds/vents.dm @@ -2,5 +2,6 @@ start_sound = 'sound/machines/fan/fan_start.ogg' start_length = 1.5 SECONDS end_sound = 'sound/machines/fan/fan_stop.ogg' - end_sound = 1.5 SECONDS mid_sounds = 'sound/machines/fan/fan_loop.ogg' + mid_length = 1.9 SECONDS + use_sound_tokens = TRUE diff --git a/code/datums/mapgen/CaveGenerator.dm b/code/datums/mapgen/CaveGenerator.dm index 63b47165db47..316f9659e91d 100644 --- a/code/datums/mapgen/CaveGenerator.dm +++ b/code/datums/mapgen/CaveGenerator.dm @@ -86,15 +86,31 @@ /// Radius around megafauna within which we avoid spawning tendrils var/megafauna_exclusion_radius = 7 - ///Chance of cells starting closed - var/initial_closed_chance = 45 - ///Amount of smoothing iterations - var/smoothing_iterations = 20 - ///How much neighbours does a dead cell need to become alive - var/birth_limit = 4 - ///How little neighbours does a alive cell need to die - var/death_limit = 3 + ///Cave gen settings below!! + + /// Minimum dimension of a BSP leaf in the generator. Raising this creates larger pockets but can end up making for big corridors + var/min_bsp_size = 25 + /// Maximum aspect ratio for BSP splits for lavaland generator + var/max_ratio = 1.5 + /// Room edge padding within BSP leaf for lavaland generator + var/padding = 1 + /// How much of each BSP leaf is considered untouchable by the cellular automata. Raising this generally means bigger pockets + var/room_fill_percent = 30 + /// Width of corridors between rooms for lavaland generator. Raising this just means corridors are AT LEAST this wide. but cellular automata can make them bigger + var/corridor_width = 1 + /// Chance to add extra MST edges for loops for lavaland generator. This basically results in more corridors / mazier generation + var/loop_percent = 15 + /// Initial random floor density for lavaland generator + var/noise_percent = 51 + /// Cellular Automata smoothing iterations for lavaland generator + var/ca_steps = 8 + /// Neighbors to create floor (>=) for lavaland generator + var/birth_limit = 6 + /// Neighbors to survive as floor (>=) for lavaland generator + var/survival_limit = 4 + ///Whether out-of-boudns counts as being alive. Setting this to FALSE results in the edges of the generator generating more closed. Default behavior tends to open up tunnels outside. + var/edges_are_alive = TRUE /datum/map_generator/cave_generator/New() . = ..() @@ -138,10 +154,11 @@ return generate_terrain_with_biomes(turfs, generate_in) var/start_time = REALTIMEOFDAY - string_gen = rustg_cnoise_generate("[initial_closed_chance]", "[smoothing_iterations]", "[birth_limit]", "[death_limit]", "[world.maxx]", "[world.maxy]") //Generate the raw CA data + + string_gen = generate_cave(generate_in) for(var/turf/gen_turf as anything in turfs) //Go through all the turfs and generate them - var/closed = string_gen[world.maxx * (gen_turf.y - 1) + gen_turf.x] != "0" + var/closed = string_gen[world.maxx * (gen_turf.y - 1) + gen_turf.x] != "1" var/turf/new_turf = pick(closed ? closed_turf_types : open_turf_types) // The assumption is this will be faster then changeturf, and changeturf isn't required since by this point @@ -190,7 +207,7 @@ heat_seed = rand(0, 50000) var/start_time = REALTIMEOFDAY - string_gen = rustg_cnoise_generate("[initial_closed_chance]", "[smoothing_iterations]", "[birth_limit]", "[death_limit]", "[world.maxx]", "[world.maxy]") //Generate the raw CA data + string_gen = generate_cave(generate_in) var/humidity_gen = list() humidity_gen[BIOME_HIGH_HUMIDITY] = rustg_dbp_generate("[humidity_seed]", "60", "[biome_stamp_size]", "[world.maxx]", "[high_heat_threshold]", "1.1") @@ -210,7 +227,7 @@ var/list/to_generate = list() for(var/turf/gen_turf as anything in turfs) //Go through all the turfs and generate them - var/closed = string_gen[world.maxx * (gen_turf.y - 1) + gen_turf.x] != "0" + var/closed = string_gen[world.maxx * (gen_turf.y - 1) + gen_turf.x] != "1" var/datum/biome/selected_biome // Here comes the meat of the biome code. @@ -405,6 +422,32 @@ to_chat(world, span_boldannounce("[message]"), MESSAGE_TYPE_DEBUG) log_world(message) + +///Generates the cave shape using Rust-G +/datum/map_generator/cave_generator/proc/generate_cave(area/generate_in) + + ///Loop through all the active ruins for this z-level and make a json format out of it so we can send it to the generator + var/list/active_ruins_list = list() + + for(var/turf/bottom_left_turf in SSmapping.active_ruins) + var/datum/map_template/ruin/active_ruin = SSmapping.active_ruins[bottom_left_turf] + if(bottom_left_turf.z != generate_in.z) + continue + active_ruins_list += list(list( + "x" = max(1, bottom_left_turf.x - active_ruin.terrain_padding), + "y" = max(1, bottom_left_turf.y - active_ruin.terrain_padding), + "w" = active_ruin.width + active_ruin.terrain_padding * 2, + "h" = active_ruin.height + active_ruin.terrain_padding * 2, + "isEnclosed" = active_ruin.enclosed_for_terrain, + )) + + var/active_ruin_string = json_encode(active_ruins_list) + + var/string_gen = rustg_cave_system_generator_generate("[world.maxx]", "[world.maxy]", active_ruin_string, "[min_bsp_size]","[max_ratio]", "[padding]", "[room_fill_percent]", "[corridor_width]","[loop_percent]", "[noise_percent]", "[ca_steps]", "[birth_limit]", "[survival_limit]", "[edges_are_alive]") + + return string_gen + + /datum/map_generator/cave_generator/jungle possible_biomes = list( BIOME_LOW_HEAT = list( diff --git a/code/datums/mapgen/Cavegens/IcemoonCaves.dm b/code/datums/mapgen/Cavegens/IcemoonCaves.dm index 153b0c22e804..45f429c0fe80 100644 --- a/code/datums/mapgen/Cavegens/IcemoonCaves.dm +++ b/code/datums/mapgen/Cavegens/IcemoonCaves.dm @@ -11,7 +11,7 @@ /mob/living/basic/mining/lobstrosity = 15, /mob/living/basic/mining/wolf = 50, /obj/effect/spawner/random/lavaland_mob/raptor = 15, - /mob/living/simple_animal/hostile/asteroid/polarbear = 30, + /mob/living/basic/mining/polarbear = 30, /obj/structure/spawner/ice_moon = 3, /obj/structure/spawner/ice_moon/polarbear = 3, ) @@ -39,10 +39,7 @@ weighted_open_turf_types = list(/turf/open/misc/asteroid/snow/icemoon = 1) flora_spawn_chance = 60 weighted_mob_spawn_list = null - initial_closed_chance = 0 - birth_limit = 5 - death_limit = 4 - smoothing_iterations = 10 + noise_percent = 100 // Full floor feature_spawn_chance = 0.15 weighted_feature_spawn_list = list( @@ -62,7 +59,7 @@ /// Surface snow generator variant for forested station trait, WITH FORESTSSSS /datum/map_generator/cave_generator/icemoon/surface/forested - initial_closed_chance = 10 + noise_percent = 65 //Few small rocks, but mostly open floor for the trees to spawn on flora_spawn_chance = 80 weighted_flora_spawn_list = list( @@ -80,7 +77,6 @@ weighted_mob_spawn_list = list(/mob/living/basic/deer/ice = 99, /mob/living/basic/tree = 1, /obj/effect/spawner/random/lavaland_mob/raptor = 15) /datum/map_generator/cave_generator/icemoon/surface/rocky - initial_closed_chance = 53 mob_spawn_chance = 0.5 /datum/map_generator/cave_generator/icemoon/surface/noruins //use this for when you don't want ruins to spawn in a certain area diff --git a/code/datums/mapgen/Cavegens/LavalandGenerator.dm b/code/datums/mapgen/Cavegens/LavalandGenerator.dm index 40c25b51c289..b40ea70beda8 100644 --- a/code/datums/mapgen/Cavegens/LavalandGenerator.dm +++ b/code/datums/mapgen/Cavegens/LavalandGenerator.dm @@ -23,8 +23,7 @@ high_heat_threshold = 0.15 high_humidity_threshold = 0.15 biome_stamp_size = 60 - smoothing_iterations = 50 - + /datum/map_generator/cave_generator/lavaland/ruin_version biome_population = FALSE weighted_open_turf_types = list(/turf/open/misc/asteroid/basalt/lava_land_surface/no_ruins = 1) diff --git a/code/datums/mood.dm b/code/datums/mood.dm index afc7824380f8..d9c031151671 100644 --- a/code/datums/mood.dm +++ b/code/datums/mood.dm @@ -495,6 +495,9 @@ else msg += "• [span_grey("I don't have much of a reaction to anything right now.")]
" + if(LAZYLEN(mob_parent.personalities)) + msg += span_notice("You know yourself to be [mob_parent.get_parsonality_string()].
") + if(LAZYLEN(mob_parent.quirks)) msg += span_notice("You have these quirks: [mob_parent.get_quirk_string(FALSE, CAT_QUIRK_ALL)].") diff --git a/code/datums/quirks/_quirk.dm b/code/datums/quirks/_quirk.dm index 46be5ac25a59..5fd9ad64dd20 100644 --- a/code/datums/quirks/_quirk.dm +++ b/code/datums/quirks/_quirk.dm @@ -248,45 +248,3 @@ to_chat(quirk_holder, chat_string) where_items_spawned = null - -/** - * get_quirk_string() is used to get a printable string of all the quirk traits someone has for certain criteria - * - * Arguments: - * * Medical- If we want the long, fancy descriptions that show up in medical records, or if not, just the name - * * Category- Which types of quirks we want to print out. Defaults to everything - * * from_scan- If the source of this call is like a health analyzer or HUD, in which case QUIRK_HIDE_FROM_MEDICAL hides the quirk. - */ -/mob/living/proc/get_quirk_string(medical = FALSE, category = CAT_QUIRK_ALL, from_scan = FALSE) - var/list/dat = list() - for(var/datum/quirk/candidate as anything in quirks) - if(from_scan && (candidate.quirk_flags & QUIRK_HIDE_FROM_SCAN)) - continue - switch(category) - if(CAT_QUIRK_MAJOR_DISABILITY) - if(candidate.value >= -4) - continue - if(CAT_QUIRK_MINOR_DISABILITY) - if(!ISINRANGE(candidate.value, -4, -1)) - continue - if(CAT_QUIRK_NOTES) - if(candidate.value < 0) - continue - dat += medical ? candidate.medical_record_text : candidate.name - - if(!length(dat)) - return medical ? "No issues have been declared." : "None" - return medical ? dat.Join("
") : dat.Join(", ") - -/mob/living/proc/cleanse_quirk_datums() //removes all trait datums - QDEL_LAZYLIST(quirks) - -/mob/living/proc/transfer_quirk_datums(mob/living/to_mob) - // We could be done before the client was moved or after the client was moved - var/datum/preferences/to_pass = client || to_mob.client - - for(var/datum/quirk/quirk as anything in quirks) - if(quirk.quirk_flags & QUIRK_NO_TRANSFER) - continue - quirk.remove_from_current_holder(quirk_transfer = TRUE) - quirk.add_to_holder(to_mob, quirk_transfer = TRUE, client_source = to_pass) diff --git a/code/datums/ruins.dm b/code/datums/ruins.dm index 703991f596e9..5741904e4906 100644 --- a/code/datums/ruins.dm +++ b/code/datums/ruins.dm @@ -28,6 +28,10 @@ var/suffix = null ///What flavor or ruin is this? eg ZTRAIT_SPACE_RUINS var/ruin_type = null + ///is this ruin "enclosed" by walls. This is relevant for terrain gen with cellular automata to know whether this ruin will spawn inside of walls, or should spawn in the open. + var/enclosed_for_terrain = FALSE + ///Padding to be used to ensure extra space around the ruin for terrain gen purposes. If a ruin is NOT enclosed and this is set to 1; there will be at least one layer of open terrain around the ruin. If a ruin IS enclosed and this is set to 1; there will be at least one layer of wall terrain around the ruin. + var/terrain_padding = 0 /datum/map_template/ruin/New() if(!name && id) diff --git a/code/datums/ruins/icemoon.dm b/code/datums/ruins/icemoon.dm index 5e293a70747a..35d1856d8ace 100644 --- a/code/datums/ruins/icemoon.dm +++ b/code/datums/ruins/icemoon.dm @@ -23,6 +23,7 @@ id = "lust" description = "Not exactly what you expected." suffix = "icemoon_surface_lust.dmm" + enclosed_for_terrain = TRUE /datum/map_template/ruin/icemoon/asteroid name = "Ice-Ruin Asteroid Site" @@ -103,6 +104,7 @@ suffix = "icemoon_surface_mining_site.dmm" always_place = TRUE always_spawn_with = list(/datum/map_template/ruin/icemoon/underground/mining_site_below = PLACE_BELOW) + enclosed_for_terrain = TRUE /datum/map_template/ruin/icemoon/underground/mining_site_below name = "Ice-Ruin Mining Site Underground" @@ -111,6 +113,7 @@ suffix = "icemoon_underground_mining_site.dmm" has_ceiling = FALSE unpickable = TRUE + enclosed_for_terrain = TRUE // below ground only @@ -124,18 +127,21 @@ id = "abandonedvillage" description = "Who knows what lies within?" suffix = "icemoon_underground_abandoned_village.dmm" + enclosed_for_terrain = TRUE /datum/map_template/ruin/icemoon/underground/library name = "Ice-Ruin Buried Library" id = "buriedlibrary" description = "A once grand library, now lost to the confines of the Ice Moon." suffix = "icemoon_underground_library.dmm" + enclosed_for_terrain = TRUE /datum/map_template/ruin/icemoon/underground/wrath name = "Ice-Ruin Ruin of Wrath" id = "wrath" description = "You'll fight and fight and just keep fighting." suffix = "icemoon_underground_wrath.dmm" + enclosed_for_terrain = TRUE /datum/map_template/ruin/icemoon/underground/hermit name = "Ice-Ruin Frozen Shack" @@ -148,6 +154,7 @@ id = "lavalandsite" description = "I guess we never really left you huh?" suffix = "icemoon_underground_lavaland.dmm" + enclosed_for_terrain = TRUE /datum/map_template/ruin/icemoon/underground/puzzle name = "Ice-Ruin Ancient Puzzle" @@ -180,6 +187,7 @@ id = "mailroom" description = "This is where all of your paychecks went. Signed, the management." suffix = "icemoon_underground_mailroom.dmm" + enclosed_for_terrain = TRUE /datum/map_template/ruin/icemoon/underground/biodome name = "Ice-Ruin Syndicate Bio-Dome" @@ -204,6 +212,7 @@ id = "syndie_lab" description = "A small laboratory and living space for Syndicate agents." suffix = "icemoon_underground_syndielab.dmm" + enclosed_for_terrain = TRUE /datum/map_template/ruin/icemoon/underground/o31 name = "Ice-Ruin Outpost 31" @@ -224,6 +233,7 @@ id = "hotsprings" description = "Just relax and take a dip, nothing will go wrong, I swear!" suffix = "icemoon_underground_hotsprings.dmm" + enclosed_for_terrain = TRUE /datum/map_template/ruin/icemoon/underground/vent name = "Ice-Ruin Icemoon Ore Vent" diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm index cd3c1065ec4d..206158c15d1f 100644 --- a/code/datums/ruins/lavaland.dm +++ b/code/datums/ruins/lavaland.dm @@ -15,6 +15,7 @@ description = "Seemingly plucked from a tropical destination, this beach is calm and cool, with the salty waves roaring softly in the background. \ Comes with a rustic wooden bar and suicidal bartender." suffix = "lavaland_biodome_beach.dmm" + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/biodome/winter name = "Lava-Ruin Biodome Winter" @@ -43,6 +44,7 @@ suffix = "lavaland_surface_cube.dmm" cost = 10 allow_duplicates = FALSE + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/seed_vault name = "Lava-Ruin Seed Vault" @@ -52,6 +54,7 @@ suffix = "lavaland_surface_seed_vault.dmm" cost = 10 allow_duplicates = FALSE + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/ash_walker name = "Lava-Ruin Ash Walker Nest" @@ -61,6 +64,7 @@ suffix = "lavaland_surface_ash_walker1.dmm" cost = 20 allow_duplicates = FALSE + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/syndicate_base name = "Lava-Ruin Syndicate Lava Base" @@ -87,6 +91,7 @@ cost = 5 suffix = "lavaland_surface_gaia.dmm" allow_duplicates = FALSE + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/sin cost = 10 @@ -124,6 +129,7 @@ suffix = "lavaland_surface_sloth.dmm" // Generates nothing but atmos runtimes and salt cost = 0 + terrain_padding = 2 /datum/map_template/ruin/lavaland/ratvar name = "Lava-Ruin Dead God" @@ -140,6 +146,7 @@ suffix = "lavaland_surface_hierophant.dmm" always_place = TRUE allow_duplicates = FALSE + terrain_padding = 2 /datum/map_template/ruin/lavaland/blood_drunk_miner name = "Lava-Ruin Blood-Drunk Miner" @@ -148,14 +155,17 @@ suffix = "lavaland_surface_blooddrunk1.dmm" cost = 0 allow_duplicates = FALSE //will only spawn one variant of the ruin + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/blood_drunk_miner/guidance name = "Lava-Ruin Blood-Drunk Miner (Guidance)" suffix = "lavaland_surface_blooddrunk2.dmm" + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/blood_drunk_miner/hunter name = "Lava-Ruin Blood-Drunk Miner (Hunter)" suffix = "lavaland_surface_blooddrunk3.dmm" + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/blood_drunk_miner/random name = "Lava-Ruin Blood-Drunk Miner (Random)" @@ -172,6 +182,7 @@ description = "Turns out that keeping your abductees unconscious is really important. Who knew?" suffix = "lavaland_surface_ufo_crash.dmm" cost = 5 + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/xeno_nest name = "Lava-Ruin Xenomorph Nest" @@ -180,6 +191,7 @@ Quality memes." suffix = "lavaland_surface_xeno_nest.dmm" cost = 20 + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/fountain name = "Lava-Ruin Fountain Hall" @@ -227,6 +239,7 @@ suffix = "lavaland_surface_random_ripley.dmm" allow_duplicates = FALSE cost = 5 + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/dark_wizards name = "Lava-Ruin Dark Wizard Altar" @@ -234,6 +247,7 @@ description = "A ruin with dark wizards. What secret do they guard?" suffix = "lavaland_surface_wizard.dmm" cost = 5 + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/strong_stone name = "Lava-Ruin Strong Stone" @@ -266,6 +280,7 @@ suffix = "lavaland_surface_elephant_graveyard.dmm" allow_duplicates = FALSE cost = 10 + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/bileworm_nest name = "Lava-Ruin Bileworm Nest" @@ -274,6 +289,7 @@ cost = 5 suffix = "lavaland_surface_bileworm_nest.dmm" allow_duplicates = FALSE + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/lava_phonebooth name = "Lava-Ruin Phonebooth" @@ -323,6 +339,8 @@ description = "Not every shuttle makes it back to CentCom." suffix = "lavaland_surface_shuttle_wreckage.dmm" allow_duplicates = FALSE + enclosed_for_terrain = TRUE + /datum/map_template/ruin/lavaland/crashsite name = "Lava-Ruin Pod Crashsite" @@ -330,6 +348,7 @@ description = "They launched too early" suffix = "lavaland_surface_crashsite.dmm" allow_duplicates = FALSE + enclosed_for_terrain = TRUE /datum/map_template/ruin/lavaland/shoe_facotry name = "Lava-Ruin Shoe Factory" diff --git a/code/datums/sound_token.dm b/code/datums/sound_token.dm new file mode 100644 index 000000000000..d9ad9903c283 --- /dev/null +++ b/code/datums/sound_token.dm @@ -0,0 +1,284 @@ +// Sound tokens, a datumized handler for spatial sound. +// Uses the spatial grid to track clients in range and add them as listeners +// Updated by the SSsound_tokens subsystem every tick when requested by client so that if the source or listener moves, the sound updates accordingly. +/datum/sound_token + /// The atom playing the sound. + var/atom/source + /// k:v list of mob : sound status + var/list/listeners = list() + + /// Sound maximum range + var/range + /// Sound volume + var/volume + /// Sound falloff + var/falloff_exponent + /// Sound falloff distance + var/falloff_distance + + /// The master copy of the playing sound. + var/sound/sound + /// Null sound for cancelling the sound entirely. + var/sound/null_sound + + /// Status of the playing sound + var/sound_status = NONE + /// The channel being used. + var/sound_channel + /// world.time when the sound started (or when the sound file was last changed). Used to calculate playback offset for new listeners. + var/start_time + /// Duration of the current sound file in deciseconds. Used to wrap offset for looping sounds. + var/sound_duration + /// Cell tracker managing spatial grid cells within range of the source. The wizards say this is the fastest. + var/datum/cell_tracker/cell_tracker + +/datum/sound_token/New(atom/_source, _sound, _range = 10, _volume = 50, _falloff_exponent = SOUND_FALLOFF_EXPONENT, _falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE) + source = _source + RegisterSignal(source, COMSIG_QDELETING, PROC_REF(source_deleted)) + RegisterSignal(source, COMSIG_MOVABLE_MOVED, PROC_REF(source_moved)) + RegisterSignal(source, COMSIG_ENTER_AREA, PROC_REF(on_enter_area)) + + range = _range + volume = _volume + falloff_exponent = _falloff_exponent + falloff_distance = _falloff_distance + + update_sound(_sound) + + null_sound = sound(channel = sound_channel) + + cell_tracker = new /datum/cell_tracker(range, range) + update_tracked_cells() + + RegisterSignal(SSdcs, COMSIG_GLOB_PLAYER_LOGIN, PROC_REF(player_login)) + RegisterSignal(SSdcs, COMSIG_GLOB_PLAYER_LOGOUT, PROC_REF(player_logout)) + +/datum/sound_token/Destroy(force, ...) + for(var/listener in listeners) + remove_listener(listener) + + listeners = null + source = null + return ..() + +///Lets us update the sound to a new one. +/datum/sound_token/proc/update_sound(_sound, start_playing = FALSE) + sound = sound(_sound) + if(!sound_channel) + sound_channel = SSsounds.reserve_sound_channel_for_datum(src) + sound.channel = sound_channel + sound_duration = SSsounds.get_sound_length(_sound) + start_time = REALTIMEOFDAY + if(start_playing) + force_update_all_listeners(FALSE) + +/// Updates the data of a listener, or adds them if they are not present. +/datum/sound_token/proc/add_or_update_listener(mob/listener_mob) + if(isnull(listeners[listener_mob])) + add_listener(listener_mob) + else + update_listener(listener_mob) + +/// Adds a listener to the sound. +/datum/sound_token/proc/add_listener(mob/listener_mob) + if(!isnull(listeners[listener_mob])) + return FALSE + + if(!listener_mob.client || isnewplayer(listener_mob)) + return + + listeners[listener_mob] = NONE + listener_mob.client.sound_tokens += src + RegisterSignal(listener_mob, COMSIG_QDELETING, PROC_REF(listener_deleted)) + RegisterSignals(listener_mob, list(SIGNAL_ADDTRAIT(TRAIT_DEAF), SIGNAL_REMOVETRAIT(TRAIT_DEAF)), PROC_REF(listener_deafness_update)) + update_listener(listener_mob, FALSE) + return TRUE + +/// Remove a listener from the sound. +/datum/sound_token/proc/remove_listener(mob/listener_mob) + + listeners -= listener_mob + + if(listener_mob.client) + listener_mob.client.sound_tokens -= src + + UnregisterSignal(listener_mob, list(COMSIG_QDELETING, SIGNAL_ADDTRAIT(TRAIT_DEAF),SIGNAL_REMOVETRAIT(TRAIT_DEAF))) + SEND_SOUND(listener_mob, null_sound) + +/datum/sound_token/proc/update_listener(mob/listener_mob, update_sound = TRUE) + if(QDELETED(src)) + return + if(isnull(listeners[listener_mob])) + return + + var/turf/source_turf = get_turf(source) + var/turf/listener_turf = get_turf(listener_mob) + + if(!source_turf || !listener_turf) + return + + var/is_muted = listeners[listener_mob] & SOUND_MUTE + var/should_be_muted = FALSE + + if(source_turf.z != listener_turf.z) + should_be_muted = TRUE + + var/distance = get_dist(source_turf, listener_turf) + if(distance > range) + should_be_muted = TRUE + if(should_be_muted && is_muted) + return + + should_be_muted ||= HAS_TRAIT(listener_mob, TRAIT_DEAF) + if(should_be_muted && is_muted) + return + + set_listener_status(listener_mob, should_be_muted ? SOUND_MUTE : NONE) + send_listener_sound(listener_mob, update_sound) + +/datum/sound_token/proc/send_listener_sound(mob/listener_mob, update_sound) + PRIVATE_PROC(TRUE) + + sound.status = SOUND_STREAM|sound_status|listeners[listener_mob] + if(update_sound) + sound.status |= SOUND_UPDATE + else + sound.offset = calculate_offset() + + if(sound.status & SOUND_MUTE) + SEND_SOUND(listener_mob, sound) + return + + if(!listener_mob.playsound_local(get_turf(source), vol = volume, falloff_exponent = falloff_exponent, channel = sound_channel, sound_to_use = sound, max_distance = range, falloff_distance = falloff_distance, use_reverb = TRUE)) + sound.status = SOUND_UPDATE|SOUND_MUTE + SEND_SOUND(listener_mob, sound) + sound.offset = null + +/datum/sound_token/proc/update_all_listeners() + for(var/mob/listener_mob in listeners) + if(listener_mob.client) + SSsound_tokens.clients_needing_update[listener_mob.client] = TRUE + +/datum/sound_token/proc/force_update_all_listeners(update_sound = TRUE) + for(var/mob/listener_mob in listeners) + if(listener_mob.client) + update_listener(listener_mob, update_sound) + +/// Setter for volume +/datum/sound_token/proc/set_volume(new_volume, update_listeners = TRUE) + volume = new_volume + if(update_listeners) + update_all_listeners() + +/// Set the status of a listener. Does not update the sound. +/datum/sound_token/proc/set_listener_status(mob/listener_mob, new_status) + if(isnull(listeners[listener_mob])) + return + + listeners[listener_mob] = new_status + +/// Respond to TRAIT_DEAF addition/removal +/datum/sound_token/proc/listener_deafness_update(atom/movable/source) + SIGNAL_HANDLER + update_listener(source) + +/datum/sound_token/proc/listener_deleted(datum/source) + SIGNAL_HANDLER + remove_listener(source) + +/// Respond to any mob in the world being logged into. Only adds if the mob is within range. +/datum/sound_token/proc/player_login(datum/source, mob/player) + SIGNAL_HANDLER + var/turf/player_turf = get_turf(player) + var/turf/source_turf = get_turf(src.source) + if(!player_turf || !source_turf) + return + if(player_turf.z != source_turf.z) + return + if(get_dist(source_turf, player_turf) > range) + return + add_or_update_listener(player) + +/// Respond to any cliented mob becoming uncliented +/datum/sound_token/proc/player_logout(datum/source, mob/player) + SIGNAL_HANDLER + remove_listener(player) + +/// If the sound source moves, update tracked cells then refresh all listener positions. +/datum/sound_token/proc/source_moved() + SIGNAL_HANDLER + update_tracked_cells() + update_all_listeners() + +/datum/sound_token/proc/source_deleted() + SIGNAL_HANDLER + + qdel(src) + +///Update env when source is entering new area +/datum/sound_token/proc/on_enter_area(datum/source, area/area_to_register) + SIGNAL_HANDLER + set_new_environment(area_to_register.sound_environment || SOUND_ENVIRONMENT_NONE) + +/datum/sound_token/proc/set_new_environment(new_env) + if(sound.environment == new_env) + return + sound.environment = new_env + update_all_listeners() + +///Calculates the offset to give the sound for people who start hearing it mid-play +/datum/sound_token/proc/calculate_offset() + var/elapsed = REALTIMEOFDAY - start_time + var/freq_factor = (sound.frequency || 100) / 100 + var/pitch_factor = (sound.pitch || 100) / 100 + var/offset = elapsed * freq_factor * pitch_factor + return offset + +///Update tracked cells; happens on movement. We need to check if anyone is now out of cell range and kick them out. +/datum/sound_token/proc/update_tracked_cells() + if(!get_turf(source)) + return + + var/list/new_and_old = cell_tracker.recalculate_cells(get_turf(source)) + var/list/datum/spatial_grid_cell/added_cells = new_and_old[1] + var/list/datum/spatial_grid_cell/removed_cells = new_and_old[2] + + for(var/datum/spatial_grid_cell/cell as anything in removed_cells) + UnregisterSignal(cell, list(SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS), SPATIAL_GRID_CELL_EXITED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS),)) + + // Remove listeners whose mob is no longer in any remaining member cell + if(removed_cells.len) + for(var/mob/listener_mob as anything in listeners) + var/still_in_range = FALSE + for(var/datum/spatial_grid_cell/cell as anything in cell_tracker.member_cells) + if(listener_mob in cell.client_contents) + still_in_range = TRUE + break + if(!still_in_range) + remove_listener(listener_mob) + + for(var/datum/spatial_grid_cell/cell as anything in added_cells) + RegisterSignal(cell, SPATIAL_GRID_CELL_ENTERED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS), PROC_REF(on_cell_client_entered)) + RegisterSignal(cell, SPATIAL_GRID_CELL_EXITED(SPATIAL_GRID_CONTENTS_TYPE_CLIENTS), PROC_REF(on_cell_client_exited)) + for(var/mob/listener_mob as anything in cell.client_contents) + add_or_update_listener(listener_mob) + +/// Signal handler for SPATIAL_GRID_CELL_ENTERED on tracked cells. Adds newly arriving mobs as listeners. +/datum/sound_token/proc/on_cell_client_entered(datum/source, list/entering_mobs) + SIGNAL_HANDLER + + for(var/mob/listener_mob as anything in entering_mobs) + if(!isnull(listeners[listener_mob])) // already added + continue + add_or_update_listener(listener_mob) + +/// Signal handler for SPATIAL_GRID_CELL_EXITED on tracked cells. Removes mobs who have left all member cells. +/datum/sound_token/proc/on_cell_client_exited(datum/source, list/exiting_mobs) + SIGNAL_HANDLER + for(var/mob/listener_mob as anything in exiting_mobs) + var/still_in_range = FALSE + if(SSspatial_grid.get_cell_of(listener_mob) in cell_tracker.member_cells) + still_in_range = TRUE + + if(!still_in_range) + remove_listener(listener_mob) diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index 76c5744c0f98..ad246f5ff825 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -192,15 +192,6 @@ if(smoothing_flags & SMOOTH_QUEUED) SSicon_smooth.remove_from_queues(src) -#ifndef DISABLE_DREAMLUAU - // These lists cease existing when src does, so we need to clear any lua refs to them that exist. - if(!(datum_flags & DF_STATIC_OBJECT)) - DREAMLUAU_CLEAR_REF_USERDATA(contents) - DREAMLUAU_CLEAR_REF_USERDATA(filters) - DREAMLUAU_CLEAR_REF_USERDATA(overlays) - DREAMLUAU_CLEAR_REF_USERDATA(underlays) -#endif - return ..() /atom/proc/handle_ricochet(obj/projectile/ricocheting_projectile) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index c6c604e60208..403af00e60fc 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -252,12 +252,6 @@ LAZYNULL(client_mobs_in_contents) -#ifndef DISABLE_DREAMLUAU - // These lists cease existing when src does, so we need to clear any lua refs to them that exist. - DREAMLUAU_CLEAR_REF_USERDATA(vis_contents) - DREAMLUAU_CLEAR_REF_USERDATA(vis_locs) -#endif - . = ..() for(var/movable_content in contents) diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm index fbc72b11eb85..13d2f6f4aa35 100644 --- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm @@ -1331,6 +1331,27 @@ /obj/item/stack/sheet/glass = 1) needs_anchored = FALSE +/obj/item/circuitboard/machine/hydroponics/proc/changeindicators(mob/living/user, obj/item/I) + if(build_path == /obj/machinery/hydroponics/constructable/oldstyle) + name = "Hydroponics Tray" + build_path = /obj/machinery/hydroponics/constructable + balloon_alert(user, "defaulting indicator location.") + else + name = "Old-Designed Hydropoincs Tray" + build_path = /obj/machinery/hydroponics/constructable/oldstyle + balloon_alert(user, "moving the indicators...") + return TRUE + +/obj/item/circuitboard/machine/hydroponics/item_interaction(mob/living/user, obj/item/I, list/modifiers) + if(istype(I, /obj/item/plant_analyzer)) + changeindicators(user) + else + return ..() + +/obj/item/circuitboard/machine/hydroponics/screwdriver_act(mob/living/user, obj/item/tool) + src.changeindicators(user) + return + /obj/item/circuitboard/machine/hydroponics/fullupgrade build_path = /obj/machinery/hydroponics/constructable/fullupgrade specific_parts = TRUE diff --git a/code/game/objects/items/food/pastries.dm b/code/game/objects/items/food/pastries.dm index 041dc116bcff..aedcbdf7c474 100644 --- a/code/game/objects/items/food/pastries.dm +++ b/code/game/objects/items/food/pastries.dm @@ -599,3 +599,18 @@ food_flags = FOOD_FINGER_FOOD w_class = WEIGHT_CLASS_SMALL crafting_complexity = FOOD_COMPLEXITY_3 + +/obj/item/food/apple_fritter + name = "apple fritter" + desc = "For something that looks like a pile of glazed dirt, it's suprisingly tart. It smells sweet enough to knock you unconscious." + icon_state = "apple_fritter" + food_reagents = list( + /datum/reagent/consumable/nutriment = 3, + /datum/reagent/consumable/nutriment/vitamin = 1, + /datum/reagent/consumable/sugar = 1, + /datum/reagent/consumable/applejuice = 1, + ) + tastes = list("apple" = 1, "glaze" = 1) + foodtypes = GRAIN|FRUIT|FRIED|DAIRY|BREAKFAST + w_class = WEIGHT_CLASS_SMALL + crafting_complexity = FOOD_COMPLEXITY_3 diff --git a/code/game/objects/items/robot/items/hypo.dm b/code/game/objects/items/robot/items/hypo.dm index 2a759a275a8b..3119037f0153 100644 --- a/code/game/objects/items/robot/items/hypo.dm +++ b/code/game/objects/items/robot/items/hypo.dm @@ -50,6 +50,7 @@ /datum/reagent/medicine/morphine,\ /datum/reagent/medicine/potass_iodide,\ /datum/reagent/medicine/syndicate_nanites,\ + /datum/reagent/medicine/atropine,\ ) #define BASE_SERVICE_REAGENTS list(/datum/reagent/consumable/applejuice, /datum/reagent/consumable/banana,\ /datum/reagent/consumable/berryjuice, /datum/reagent/consumable/cherryjelly, /datum/reagent/consumable/coffee,\ diff --git a/code/game/objects/items/robot/items/tools.dm b/code/game/objects/items/robot/items/tools.dm index 23fe0c97edf4..351949715451 100644 --- a/code/game/objects/items/robot/items/tools.dm +++ b/code/game/objects/items/robot/items/tools.dm @@ -252,7 +252,7 @@ return tool -/obj/item/borg/cyborg_omnitool/attack_self(mob/user) +/obj/item/borg/cyborg_omnitool/attack_self(mob/user, modifiers) //build the radial menu options var/list/radial_menu_options = list() var/list/tool_map = list() @@ -277,9 +277,8 @@ return ..() var/mob/living/silicon/robot/user = usr if (!(src in user.held_items)) - attack_self(user) - . = ..() - user.select_module(user.held_items.Find(src)) + attack_self(user, modifiers) + return ..() /obj/item/borg/cyborg_omnitool/update_icon_state() if (reference) @@ -334,19 +333,6 @@ . = ..() RegisterSignal(src, COMSIG_SILICON_MODULE_ACTIVATION, PROC_REF(welder_toggle)) -/obj/item/borg/cyborg_omnitool/engineering/add_context(atom/source, list/context, obj/item/held_item, mob/user) - . = ..() - if (!issilicon(user) || tool_behaviour != TOOL_WELDER) - return - - context[SCREENTIP_CONTEXT_CTRL_LMB] = "Toggle welder" - return CONTEXTUAL_SCREENTIP_SET - -/obj/item/borg/cyborg_omnitool/engineering/examine(mob/user) - . = ..() - if(tool_behaviour == TOOL_WELDER) - . += span_notice("Use [EXAMINE_HINT("Ctrl Click")] to toggle welder") - /obj/item/borg/cyborg_omnitool/engineering/update_overlays() . = ..() if(tool_behaviour == TOOL_WELDER) @@ -354,24 +340,19 @@ if(tool?.welding) . |= tool.update_overlays() -/obj/item/borg/cyborg_omnitool/engineering/set_internal_tool(obj/item/tool) - if(tool_behaviour == TOOL_WELDER) - welder_toggle(src, FALSE) +/obj/item/borg/cyborg_omnitool/engineering/attack_self(mob/user, modifiers) + if(tool_behaviour == TOOL_WELDER && LAZYACCESS(modifiers, LEFT_CLICK)) + welder_toggle(src, null, user) - . = ..() + return NONE - if(tool_behaviour == TOOL_WELDER) - welder_toggle(src, TRUE) + return ..() -/obj/item/borg/cyborg_omnitool/engineering/item_ctrl_click(mob/user) - . = NONE +/obj/item/borg/cyborg_omnitool/engineering/set_internal_tool(obj/item/tool) if(tool_behaviour == TOOL_WELDER) - var/mob/living/silicon/robot/borgy = loc - if(!istype(borgy) || borgy.module_active != src) - return + welder_toggle(src, FALSE) - welder_toggle(src, null) - return CLICK_ACTION_SUCCESS + return ..() ///Reflects internal welder icon onto the omnitool /obj/item/borg/cyborg_omnitool/engineering/proc/welder_update(source) @@ -381,7 +362,7 @@ update_appearance(UPDATE_OVERLAYS) ///Toggles welder on/off when module slot is selected/deselected -/obj/item/borg/cyborg_omnitool/engineering/proc/welder_toggle(datum/omnitool, state) +/obj/item/borg/cyborg_omnitool/engineering/proc/welder_toggle(datum/omnitool, state, mob/self_user) PRIVATE_PROC(TRUE) SIGNAL_HANDLER @@ -393,8 +374,9 @@ return if(state) - RegisterSignal(tool, COMSIG_ATOM_UPDATE_APPEARANCE, PROC_REF(welder_update)) - tool.switched_on(usr) + RegisterSignal(tool, COMSIG_ATOM_UPDATE_APPEARANCE, PROC_REF(welder_update), override = TRUE) + if(self_user) + tool.switched_on(self_user) else tool.switched_off() UnregisterSignal(tool, COMSIG_ATOM_UPDATE_APPEARANCE) diff --git a/code/game/objects/items/weaponry/melee/energy.dm b/code/game/objects/items/weaponry/melee/energy.dm index ca7f75e93a90..93571d4d710a 100644 --- a/code/game/objects/items/weaponry/melee/energy.dm +++ b/code/game/objects/items/weaponry/melee/energy.dm @@ -222,48 +222,6 @@ return ..() -/obj/item/melee/energy/sword/cyborg - name = "cyborg energy sword" - sword_color_icon = "red" - /// The cell cost of hitting something. - var/hitcost = 0.05 * STANDARD_CELL_CHARGE - -/obj/item/melee/energy/sword/cyborg/attack(mob/target, mob/living/silicon/robot/user) - if(!user.cell) - return - - var/obj/item/stock_parts/power_store/our_cell = user.cell - if(HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE) && !(our_cell.use(hitcost))) - attack_self(user) - to_chat(user, span_notice("It's out of charge!")) - return - return ..() - -/obj/item/melee/energy/sword/cyborg/cyborg_unequip(mob/user) - if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE)) - return - attack_self(user) - -/obj/item/melee/energy/sword/cyborg/saw //Used by medical Syndicate cyborgs - name = "energy saw" - desc = "For heavy duty cutting. It has a carbon-fiber blade in addition to a toggleable hard-light edge to dramatically increase sharpness." - icon = 'icons/obj/medical/surgery_tools.dmi' - icon_state = "esaw" - hitsound = 'sound/items/weapons/circsawhit.ogg' - force = 18 - hitcost = 0.075 * STANDARD_CELL_CHARGE // Costs more than a standard cyborg esword. - w_class = WEIGHT_CLASS_NORMAL - sharpness = SHARP_EDGED - light_color = LIGHT_COLOR_LIGHT_CYAN - tool_behaviour = TOOL_SAW - toolspeed = 0.7 // Faster than a normal saw. - - active_force = 30 - sword_color_icon = null // Stops icon from breaking when turned on. - -/obj/item/melee/energy/sword/cyborg/saw/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) - return FALSE - // The colored energy swords we all know and love. /obj/item/melee/energy/sword/saber /// Assoc list of all possible saber colors to color define. If you add a new color, make sure to update /obj/item/toy/sword too! @@ -325,6 +283,51 @@ to_chat(user, span_warning("RNBW_ENGAGE")) update_appearance(UPDATE_ICON_STATE) +/obj/item/melee/energy/sword/saber/cyborg + name = "cyborg energy sword" + hacked = TRUE + sword_color_icon = "rainbow" + /// The cell cost of hitting something. + var/hitcost = 0.05 * STANDARD_CELL_CHARGE + +/obj/item/melee/energy/sword/saber/cyborg/Initialize(mapload) + . = ..() + set_light_range(5) //Cyborgs don't have inhand sprites, so we compensate by making it glow brightly. + +/obj/item/melee/energy/sword/saber/cyborg/attack(mob/target, mob/living/silicon/robot/user) + if(!user.cell) + return + + var/obj/item/stock_parts/power_store/our_cell = user.cell + if(HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE) && !(our_cell.use(hitcost))) + attack_self(user) + to_chat(user, span_notice("It's out of charge!")) + return + return ..() + +/obj/item/melee/energy/sword/saber/cyborg/cyborg_unequip(mob/user) + if(!HAS_TRAIT(src, TRAIT_TRANSFORM_ACTIVE)) + return + attack_self(user) + +/obj/item/melee/energy/sword/saber/cyborg/saw //Used by medical Syndicate cyborgs + name = "energy saw" + desc = "For heavy duty cutting. It has a carbon-fiber blade in addition to a toggleable hard-light edge to dramatically increase sharpness." + icon = 'icons/obj/medical/surgery_tools.dmi' + icon_state = "esaw" + hitsound = 'sound/items/weapons/circsawhit.ogg' + force = 18 + hitcost = 0.075 * STANDARD_CELL_CHARGE // Costs more than a standard cyborg esword. + w_class = WEIGHT_CLASS_NORMAL + sharpness = SHARP_EDGED + light_color = LIGHT_COLOR_LIGHT_CYAN + tool_behaviour = TOOL_SAW + toolspeed = 0.7 // Faster than a normal saw. + hacked = FALSE + active_force = 30 + block_chance = 0 //Unlike assault cyborgs, syndicate medical cyborgs don't get any blocking capabilities + sword_color_icon = null // Stops icon from breaking when turned on. + /obj/item/melee/energy/sword/pirate name = "energy cutlass" desc = "Arrrr matey." diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm index 66f7e2780e56..83981f1beddf 100644 --- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm +++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm @@ -91,18 +91,20 @@ playsound(loc, 'sound/machines/chime.ogg', 50, FALSE, -5) /// Does the MGS ! animation -/atom/proc/do_alert_animation() +/atom/proc/do_alert_animation(duration = 1 SECONDS) var/mutable_appearance/alert = mutable_appearance('icons/obj/storage/closet.dmi', "cardboard_special") SET_PLANE_EXPLICIT(alert, ABOVE_LIGHTING_PLANE, src) - var/atom/movable/flick_visual/exclamation = flick_overlay_view(alert, 1 SECONDS) + var/atom/movable/flick_visual/exclamation = flick_overlay_view(alert, duration) exclamation.alpha = 0 exclamation.pixel_x = -pixel_x - animate(exclamation, pixel_z = 32, alpha = 255, time = 0.5 SECONDS, easing = ELASTIC_EASING) + animate(exclamation, pixel_z = 32, alpha = 255, time = duration * 0.5, easing = ELASTIC_EASING) + animate(time = duration * 0.35) + animate(pixel_z = 64, alpha = 0, time = duration * 0.15, easing = SINE_EASING) // We use this list to update plane values on parent z change, which is why we need the timer too // I'm sorry :( LAZYADD(update_on_z, exclamation) // Intentionally less time then the flick so we don't get weird shit - addtimer(CALLBACK(src, PROC_REF(forget_alert), exclamation), 0.8 SECONDS, TIMER_CLIENT_TIME) + addtimer(CALLBACK(src, PROC_REF(forget_alert), exclamation), max(0.05 SECONDS, duration - 0.1 SECONDS), TIMER_CLIENT_TIME) /atom/proc/forget_alert(atom/movable/flick_visual/exclamation) LAZYREMOVE(update_on_z, exclamation) diff --git a/code/game/objects/structures/icemoon/cave_entrance.dm b/code/game/objects/structures/icemoon/cave_entrance.dm index 408e409e5571..b58fff6bb705 100644 --- a/code/game/objects/structures/icemoon/cave_entrance.dm +++ b/code/game/objects/structures/icemoon/cave_entrance.dm @@ -66,7 +66,7 @@ GLOBAL_LIST_INIT(ore_probability, list( /obj/structure/spawner/ice_moon/polarbear max_mobs = 1 spawn_time = 60 SECONDS - mob_types = list(/mob/living/simple_animal/hostile/asteroid/polarbear) + mob_types = list(/mob/living/basic/mining/polarbear) mob_gps_id = "BR" // bear /obj/structure/spawner/ice_moon/polarbear/clear_rock() diff --git a/code/game/objects/structures/lavaland/ore_vent.dm b/code/game/objects/structures/lavaland/ore_vent.dm index 244e88102be5..3ab9542f6301 100644 --- a/code/game/objects/structures/lavaland/ore_vent.dm +++ b/code/game/objects/structures/lavaland/ore_vent.dm @@ -677,7 +677,7 @@ /mob/living/basic/mining/lobstrosity, /mob/living/basic/mining/legion/snow/spawner_made, /mob/living/basic/mining/wolf, - /mob/living/simple_animal/hostile/asteroid/polarbear, + /mob/living/basic/mining/polarbear, ) ore_vent_options = list( SMALL_VENT_TYPE, @@ -690,7 +690,7 @@ /mob/living/basic/mining/legion/snow/spawner_made, /mob/living/basic/mining/ice_demon, /mob/living/basic/mining/wolf, - /mob/living/simple_animal/hostile/asteroid/polarbear, + /mob/living/basic/mining/polarbear, ) ore_vent_options = list( SMALL_VENT_TYPE = 3, diff --git a/code/game/objects/structures/mystery_box.dm b/code/game/objects/structures/mystery_box.dm index 28700113381e..0e153b328131 100644 --- a/code/game/objects/structures/mystery_box.dm +++ b/code/game/objects/structures/mystery_box.dm @@ -189,7 +189,7 @@ GLOBAL_LIST_INIT(mystery_fishing, list( presented_item.vis_flags = VIS_INHERIT_PLANE vis_contents += presented_item presented_item.start_animation(src) - current_sound_channel = SSsounds.reserve_sound_channel(src) + current_sound_channel = SSsounds.reserve_sound_channel_for_datum(src) playsound(src, open_sound, 70, FALSE, channel = current_sound_channel, falloff_exponent = 10) playsound(src, crate_open_sound, 80) if(user.mind) diff --git a/code/game/sound/sound.dm b/code/game/sound/sound.dm index 451ee6d60a49..92b2e6f19e9b 100644 --- a/code/game/sound/sound.dm +++ b/code/game/sound/sound.dm @@ -145,7 +145,7 @@ //End Atmosphere affecting sound if(sound_to_use.volume < SOUND_AUDIBLE_VOLUME_MIN) - return //No sound + return FALSE var/dx = turf_source.x - turf_loc.x // Hearing from the right/left sound_to_use.x = dx * distance_multiplier @@ -175,12 +175,13 @@ var/client_volume_modifier = client.prefs.read_preference(volume_preference) sound_to_use.volume *= (client_volume_modifier / 100) if(sound_to_use.volume < SOUND_AUDIBLE_VOLUME_MIN) - return + return FALSE if(HAS_TRAIT(src, TRAIT_SOUND_DEBUGGED)) to_chat(src, span_admin("Max Range-[max_distance] Distance-[distance] Vol-[round(sound_to_use.volume, 0.01)] Sound-[sound_to_use.file]")) SEND_SOUND(src, sound_to_use) + return TRUE /proc/sound_to_playing_players(soundin, volume = 100, vary = FALSE, frequency = 0, channel = 0, pressure_affected = FALSE, sound/S, datum/preference/numeric/volume/volume_preference = null) if(!S) diff --git a/code/game/turfs/open/asteroid.dm b/code/game/turfs/open/asteroid.dm index 9ac571c2e6dd..c441871caa82 100644 --- a/code/game/turfs/open/asteroid.dm +++ b/code/game/turfs/open/asteroid.dm @@ -140,6 +140,7 @@ GLOBAL_LIST_EMPTY(dug_up_basalt) base_icon_state = "basalt" floor_variance = 15 dig_result = /obj/item/stack/ore/glass/basalt + smoothing_groups = SMOOTH_GROUP_FLOOR_BASALT /turf/open/misc/asteroid/basalt/getDug() . = ..() @@ -267,7 +268,7 @@ GLOBAL_LIST_EMPTY(dug_up_basalt) base_icon_state = "siderite" layer = HIGH_TURF_LAYER smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_FLOOR_SIDERITE - canSmoothWith = SMOOTH_GROUP_FLOOR_SIDERITE + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_FLOOR_LAVA + SMOOTH_GROUP_FLOOR_WATER_LAVALAND + SMOOTH_GROUP_FLOOR_SIDERITE + SMOOTH_GROUP_CLOSED_TURFS dig_result = /obj/item/stack/ore/glass/siderite /turf/open/misc/asteroid/basalt/smooth/siderite/lava_land_surface @@ -287,7 +288,7 @@ GLOBAL_LIST_EMPTY(dug_up_basalt) icon_state = "shale-255" base_icon_state = "shale" smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_FLOOR_SHALE - canSmoothWith = SMOOTH_GROUP_FLOOR_SHALE + SMOOTH_GROUP_CLOSED_TURFS + canSmoothWith = SMOOTH_GROUP_FLOOR_LAVA + SMOOTH_GROUP_FLOOR_WATER_LAVALAND + SMOOTH_GROUP_FLOOR_SHALE + SMOOTH_GROUP_CLOSED_TURFS /turf/open/misc/asteroid/basalt/smooth/shale/lava_land_surface initial_gas_mix = LAVALAND_DEFAULT_ATMOS diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm index a931748a577c..63a4e1e5045d 100644 --- a/code/game/turfs/open/lava.dm +++ b/code/game/turfs/open/lava.dm @@ -147,11 +147,6 @@ return result -/turf/open/lava/smooth_icon() - . = ..() - mask_state = icon_state - update_appearance(~UPDATE_SMOOTHING) - /turf/open/lava/ex_act(severity, target) if(fish_source) GLOB.preset_fish_sources[fish_source].spawn_reward_from_explosion(src, severity) @@ -375,10 +370,59 @@ base_icon_state = "lava" smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_FLOOR_LAVA - canSmoothWith = SMOOTH_GROUP_FLOOR_LAVA - underfloor_accessibility = 2 //This avoids strangeness when routing pipes / wires along catwalks over lava + canSmoothWith = SMOOTH_GROUP_FLOOR_LAVA + SMOOTH_GROUP_FLOOR_SIDERITE + SMOOTH_GROUP_FLOOR_SHALE + SMOOTH_GROUP_FLOOR_BASALT + SMOOTH_GROUP_MINERAL_WALLS + SMOOTH_GROUP_RED_ROCK_WALLS + SMOOTH_GROUP_SHALE_WALLS + underfloor_accessibility = 2 // This avoids strangeness when routing pipes / wires along catwalks over lava + /// *Inverse* smoothing bitflag for basalt overlays + var/basalt_junction = NONE + /// *Inverse* smoothing bitflag for siderite overlays + var/siderite_junction = NONE + /// *Inverse* smoothing bitflag for shale overlays + var/shale_junction = NONE + +/turf/open/lava/smooth/bitmask_smooth() + . = ..() + basalt_junction = ALL_SMOOTHING_JUNCTIONS + siderite_junction = ALL_SMOOTHING_JUNCTIONS + shale_junction = ALL_SMOOTHING_JUNCTIONS + // We need to convert basalt/siderite/shale groups into a readable format + var/static/basalt_group = null + var/static/siderite_group = null + var/static/shale_group = null + if (isnull(basalt_group)) + SET_SMOOTHING_GROUPS(SMOOTH_GROUP_FLOOR_BASALT + SMOOTH_GROUP_MINERAL_WALLS, basalt_group) + SET_SMOOTHING_GROUPS(SMOOTH_GROUP_FLOOR_SIDERITE + SMOOTH_GROUP_RED_ROCK_WALLS, siderite_group) + SET_SMOOTHING_GROUPS(SMOOTH_GROUP_FLOOR_SHALE + SMOOTH_GROUP_SHALE_WALLS, shale_group) + // After smoothing normally we can check our smoothed directions for possible basalt/siderite/shale tiles + for (var/check_dir in GLOB.alldirs) + var/junction = dir_to_junction(check_dir) | all_junctions_of_dir(check_dir) + if (!(junction & smoothing_junction)) + continue + var/turf/to_smooth = get_step(src, check_dir) + if (!istype(to_smooth) || !to_smooth.smoothing_groups) + continue + for(var/key, group in to_smooth.smoothing_groups) + if (group & basalt_group[key]) + basalt_junction &= ~junction + else if (group & siderite_group[key]) + siderite_junction &= ~junction + else if (group & shale_group[key]) + shale_junction &= ~junction + +/turf/open/lava/smooth/smooth_icon() + . = ..() + mask_state = "lava-[smoothing_junction & (basalt_junction & siderite_junction & shale_junction)]" + update_appearance(~UPDATE_SMOOTHING) -/// Smooth lava needs to take after basalt in order to blend better. If you make a /turf/open/lava/smooth subtype for an area NOT surrounded by basalt; you should override this proc. +/turf/open/lava/smooth/update_overlays() + . = ..() + if (basalt_junction != ALL_SMOOTHING_JUNCTIONS) + . += mutable_appearance('icons/turf/floors/basalt_outline.dmi', "basalt_outline-[basalt_junction]") + if (siderite_junction != ALL_SMOOTHING_JUNCTIONS) + . += mutable_appearance('icons/turf/floors/siderite_outline.dmi', "siderite_outline-[siderite_junction]") + if (shale_junction != ALL_SMOOTHING_JUNCTIONS) + . += mutable_appearance('icons/turf/floors/shale_outline.dmi', "shale_outline-[shale_junction]") + +/// Smooth lava needs to take after basalt in order to blend better. /turf/open/lava/smooth/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) underlay_appearance.icon = /turf/open/misc/asteroid/basalt::icon underlay_appearance.icon_state = /turf/open/misc/asteroid/basalt::icon_state diff --git a/code/game/turfs/open/water.dm b/code/game/turfs/open/water.dm index ecf75011189c..21fe6b5b01b6 100644 --- a/code/game/turfs/open/water.dm +++ b/code/game/turfs/open/water.dm @@ -144,8 +144,56 @@ base_icon_state = "water_lavaland" smoothing_flags = SMOOTH_BITMASK | SMOOTH_BORDER smoothing_groups = SMOOTH_GROUP_TURF_OPEN + SMOOTH_GROUP_FLOOR_WATER_LAVALAND - canSmoothWith = SMOOTH_GROUP_FLOOR_WATER_LAVALAND + canSmoothWith = SMOOTH_GROUP_FLOOR_WATER_LAVALAND + SMOOTH_GROUP_FLOOR_SIDERITE + SMOOTH_GROUP_FLOOR_SHALE + SMOOTH_GROUP_FLOOR_BASALT + SMOOTH_GROUP_MINERAL_WALLS + SMOOTH_GROUP_RED_ROCK_WALLS + SMOOTH_GROUP_SHALE_WALLS fishing_datum = /datum/fish_source/ocean + /// *Inverse* smoothing bitflag for basalt overlays + var/basalt_junction = NONE + /// *Inverse* smoothing bitflag for siderite overlays + var/siderite_junction = NONE + /// *Inverse* smoothing bitflag for shale overlays + var/shale_junction = NONE + +/turf/open/water/lavaland_atmos/basalt/bitmask_smooth() + . = ..() + basalt_junction = ALL_SMOOTHING_JUNCTIONS + siderite_junction = ALL_SMOOTHING_JUNCTIONS + shale_junction = ALL_SMOOTHING_JUNCTIONS + // We need to convert basalt/siderite/shale groups into a readable format + var/static/basalt_group = null + var/static/siderite_group = null + var/static/shale_group = null + if (isnull(basalt_group)) + SET_SMOOTHING_GROUPS(SMOOTH_GROUP_FLOOR_BASALT + SMOOTH_GROUP_MINERAL_WALLS, basalt_group) + SET_SMOOTHING_GROUPS(SMOOTH_GROUP_FLOOR_SIDERITE + SMOOTH_GROUP_RED_ROCK_WALLS, siderite_group) + SET_SMOOTHING_GROUPS(SMOOTH_GROUP_FLOOR_SHALE + SMOOTH_GROUP_SHALE_WALLS, shale_group) + // After smoothing normally we can check our smoothed directions for possible basalt/siderite/shale tiles + for (var/check_dir in GLOB.alldirs) + var/junction = dir_to_junction(check_dir) | all_junctions_of_dir(check_dir) + if (!(junction & smoothing_junction)) + continue + var/turf/to_smooth = get_step(src, check_dir) + if (!istype(to_smooth) || !to_smooth.smoothing_groups) + continue + for(var/key, group in to_smooth.smoothing_groups) + if (group & basalt_group[key]) + basalt_junction &= ~junction + else if (group & siderite_group[key]) + siderite_junction &= ~junction + else if (group & shale_group[key]) + shale_junction &= ~junction + +/turf/open/water/lavaland_atmos/basalt/smooth_icon() + . = ..() + update_appearance(~UPDATE_SMOOTHING) + +/turf/open/water/lavaland_atmos/basalt/update_overlays() + . = ..() + if (basalt_junction != ALL_SMOOTHING_JUNCTIONS) + . += mutable_appearance('icons/turf/floors/basalt_outline.dmi', "basalt_outline-[basalt_junction]") + if (siderite_junction != ALL_SMOOTHING_JUNCTIONS) + . += mutable_appearance('icons/turf/floors/siderite_outline.dmi', "siderite_outline-[siderite_junction]") + if (shale_junction != ALL_SMOOTHING_JUNCTIONS) + . += mutable_appearance('icons/turf/floors/shale_outline.dmi', "shale_outline-[shale_junction]") /turf/open/water/lavaland_atmos/basalt/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) underlay_appearance.icon = /turf/open/misc/asteroid/basalt::icon diff --git a/code/modules/admin/spawn_panel/spawn_panel.dm b/code/modules/admin/spawn_panel/spawn_panel.dm index cdde4d0b9024..449fb8c0469a 100644 --- a/code/modules/admin/spawn_panel/spawn_panel.dm +++ b/code/modules/admin/spawn_panel/spawn_panel.dm @@ -131,7 +131,7 @@ var/list/spawn_params = list( "selected_atom" = selected_atom, "offset" = params["offset"], - "atom_dir" = text2num(params["dir"]) || 1, + "atom_dir" = text2num(params["atom_dir"]) || 1, "atom_amount" = text2num(params["atom_amount"]) || 1, "atom_name" = params["atom_name"], "where_target_type" = params["where_target_type"] || WHERE_FLOOR_BELOW_MOB, diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index d45b0f0a37c7..ed7f8ffeec24 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -272,7 +272,7 @@ return if(used) return - var/mob/chosen_one = SSpolling.poll_ghosts_for_target(check_jobban = ROLE_ALIEN, role = ROLE_ALIEN, poll_time = 5 SECONDS, checked_target = src, alert_pic = demon_type, jump_target = src, role_name_text = initial(demon_type.name)) + var/mob/chosen_one = SSpolling.poll_ghosts_for_target(check_jobban = ROLE_SENTIENCE, role = ROLE_SENTIENCE, poll_time = 5 SECONDS, checked_target = src, alert_pic = demon_type, jump_target = src, role_name_text = initial(demon_type.name)) if(chosen_one) if(used || QDELETED(src)) return diff --git a/code/modules/antagonists/wizard/slaughter_antag.dm b/code/modules/antagonists/wizard/slaughter_antag.dm index effd5d2abf63..bce27599c8cd 100644 --- a/code/modules/antagonists/wizard/slaughter_antag.dm +++ b/code/modules/antagonists/wizard/slaughter_antag.dm @@ -3,7 +3,7 @@ roundend_category = "demons" show_name_in_check_antagonists = TRUE ui_name = "AntagInfoDemon" - pref_flag = ROLE_ALIEN + pref_flag = ROLE_SENTIENCE show_in_antagpanel = FALSE show_to_ghosts = TRUE antagpanel_category = ANTAG_GROUP_WIZARDS diff --git a/code/modules/cargo/exports/lavaland.dm b/code/modules/cargo/exports/lavaland.dm index 78f98cb04da6..576ca4d8c95a 100644 --- a/code/modules/cargo/exports/lavaland.dm +++ b/code/modules/cargo/exports/lavaland.dm @@ -45,6 +45,7 @@ /obj/item/jacobs_ladder, /obj/item/borg/upgrade/modkit/lifesteal, /obj/item/clockwork_alloy, + /obj/item/gun/energy/recharge/kinetic_accelerator/bdm, ) /datum/export/lavaland/major //valuable chest/ruin loot, minor megafauna loot diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index a49169b90846..84dd50428ef1 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -79,6 +79,9 @@ //SOUND STUFF// /////////////// + /// Sound tokens currently playing for this client. Managed by /datum/sound_token and the soundtoken subsystem!! SOUND TOKENS 2026 + var/list/datum/sound_token/sound_tokens = list() + //////////// //SECURITY// //////////// diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 2f0f10646e61..ff0077c7251d 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -635,6 +635,8 @@ GLOBAL_LIST_INIT(unrecommended_builds, list( SSambience.remove_ambience_client(src) SSmouse_entered.hovers -= src SSping.currentrun -= src + SSsound_tokens.clients_needing_update -= src + SSsound_tokens.currentrun -= src QDEL_NULL(view_size) QDEL_NULL(void) QDEL_NULL(tooltips) diff --git a/code/modules/clothing/outfits/standard.dm b/code/modules/clothing/outfits/standard.dm index 9dcb90b0e3d1..ba6fae4d6aa6 100644 --- a/code/modules/clothing/outfits/standard.dm +++ b/code/modules/clothing/outfits/standard.dm @@ -421,7 +421,7 @@ back = /obj/item/mod/control/pre_equipped/debug backpack_contents = list( /obj/item/melee/energy/axe = 1, - /obj/item/storage/part_replacer/bluespace/tier4 = 1, + /obj/item/storage/part_replacer/bluespace/AdminDebug = 1, /obj/item/gun/magic/wand/resurrection/debug = 1, /obj/item/gun/magic/wand/death/debug = 1, /obj/item/debug/human_spawner = 1, @@ -452,7 +452,7 @@ back = /obj/item/mod/control/pre_equipped/administrative backpack_contents = list( /obj/item/melee/energy/axe = 1, - /obj/item/storage/part_replacer/bluespace/tier4 = 1, + /obj/item/storage/part_replacer/bluespace/AdminDebug = 1, /obj/item/gun/magic/wand/resurrection/debug = 1, /obj/item/gun/magic/wand/death/debug = 1, /obj/item/debug/human_spawner = 1, diff --git a/code/modules/flufftext/dreaming.dm b/code/modules/flufftext/dreaming.dm index 73520a1625c2..a97054affe09 100644 --- a/code/modules/flufftext/dreaming.dm +++ b/code/modules/flufftext/dreaming.dm @@ -183,7 +183,7 @@ GLOBAL_LIST_INIT(dreams, populate_dream_list()) addtimer(CALLBACK(src, PROC_REF(StopSound), dreamer), 5 SECONDS) /datum/dream/hear_something/proc/ReserveSoundChannel() - reserved_sound_channel = SSsounds.reserve_sound_channel(src) + reserved_sound_channel = SSsounds.reserve_sound_channel_for_datum(src) UnregisterSignal(SSsounds, COMSIG_SUBSYSTEM_POST_INITIALIZE) /datum/dream/hear_something/proc/PlayRandomSound(mob/living/carbon/dreamer) 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 47b0b834a1ab..4d9f358a6f6d 100644 --- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm +++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_pastry.dm @@ -760,3 +760,14 @@ ) result = /obj/item/food/cookie/macaron dish_category = DISH_COOKIE + +/datum/crafting_recipe/food/apple_fritter + name = "Apple fritter" + reqs = list( + /obj/item/food/pastrybase = 1, + /obj/item/food/appleslice = 1, + ) + result = /obj/item/food/apple_fritter + added_foodtypes = GRAIN|FRUIT|FRIED|BREAKFAST + dish_category = DISH_PASTRY + meal_category = MEAL_BREAKFAST diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 4701c0026733..2e9b24675f17 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -1,4 +1,3 @@ - /obj/machinery/hydroponics name = "hydroponics tray" desc = "A basin used to grow plants in." @@ -53,6 +52,8 @@ var/datum/weakref/lastuser ///If the tray generates nutrients and water on its own var/self_sustaining = FALSE + ///If the tray is currently able to self-sustain + var/can_self_sustain = TRUE ///The icon state for the overlay used to represent that this tray is self-sustaining. var/self_sustaining_overlay_icon_state = "gaia_blessing" ///Whether the plant is currently being pollinated or polinating the nearby plants @@ -65,7 +66,12 @@ var/tray_flags = HYDROPONIC ///How many extra px to offset the plant sprite on the y axis, gets passed to the seed and added to the seeds offset var/plant_offset_y = 0 - + ///Suffix things + var/alt_tray = FALSE + var/indicatorsuffix = "" + ///Soil things + var/obj/machinery/hydroponics/soil/current_soil = null + var/current_soil_overlay = null /obj/machinery/hydroponics/Initialize(mapload) //ALRIGHT YOU DEGENERATES. YOU HAD REAGENT HOLDERS FOR AT LEAST 4 YEARS AND NONE OF YOU MADE HYDROPONICS TRAYS HOLD NUTRIENT CHEMS INSTEAD OF USING "Points". @@ -167,6 +173,12 @@ icon = 'icons/obj/service/hydroponics/equipment.dmi' icon_state = "hydrotray3" +/obj/machinery/hydroponics/constructable/oldstyle + name = "hydroponics tray" + icon = 'icons/obj/service/hydroponics/equipment.dmi' + icon_state = "hydrotray3-alt" + alt_tray = TRUE + /obj/machinery/hydroponics/constructable/fullupgrade name = "deluxe hydroponics tray" desc = "A basin used to grown plants in, packed full of cutting-edge technology." @@ -179,6 +191,9 @@ AddComponent(/datum/component/usb_port, typecacheof(list(/obj/item/circuit_component/hydroponics), only_root_path = TRUE)) AddComponent(/datum/component/fishing_spot, /datum/fish_source/hydro_tray) +/obj/machinery/hydroponics/constructable/on_deconstruction(disassembled) + current_soil?.forceMove(drop_location()) + /obj/machinery/hydroponics/constructable/RefreshParts() . = ..() var/tmp_capacity = 0 @@ -186,8 +201,12 @@ tmp_capacity += matter_bin.tier for (var/datum/stock_part/servo/servo in component_parts) rating = servo.tier - maxwater = tmp_capacity * 50 // Up to 300 - maxnutri = (tmp_capacity * 5) + STATIC_NUTRIENT_CAPACITY // Up to 50 Maximum + if(current_soil) + maxwater = current_soil.maxwater + ((tmp_capacity - 2)*50) + maxnutri = current_soil.maxnutri + ((tmp_capacity - 2)*5) + else + maxwater = tmp_capacity * 50 + maxnutri = (tmp_capacity * 5) + STATIC_NUTRIENT_CAPACITY reagents.maximum_volume = maxnutri nutridrain = 1/rating @@ -464,21 +483,32 @@ . += myseed.get_tray_overlay(age, plant_status, plant_offset_y) . += update_status_light_overlays() + if(current_soil && current_soil_overlay) + . += mutable_appearance(icon, current_soil_overlay, OBJ_LAYER + 0.001) + if(self_sustaining && self_sustaining_overlay_icon_state) - . += mutable_appearance(icon, self_sustaining_overlay_icon_state) + . += mutable_appearance(icon, self_sustaining_overlay_icon_state, OBJ_LAYER + 0.002) + . += emissive_appearance(icon, self_sustaining_overlay_icon_state, src, OBJ_LAYER + 0.002) /obj/machinery/hydroponics/proc/update_status_light_overlays() . = list() + if(alt_tray) + indicatorsuffix = "-alt" if(waterlevel <= 10) - . += mutable_appearance('icons/obj/service/hydroponics/equipment.dmi', "over_lowwater3") + . += mutable_appearance('icons/obj/service/hydroponics/equipment.dmi', "over_lowwater3[indicatorsuffix]") + . += emissive_appearance(icon, "over_lowwater3[indicatorsuffix]", src, alpha = src.alpha) if(reagents.total_volume <= 2) - . += mutable_appearance('icons/obj/service/hydroponics/equipment.dmi', "over_lownutri3") + . += mutable_appearance('icons/obj/service/hydroponics/equipment.dmi', "over_lownutri3[indicatorsuffix]") + . += emissive_appearance(icon, "over_lownutri3[indicatorsuffix]", src, alpha = src.alpha) if(plant_health <= (myseed.endurance / 2)) - . += mutable_appearance('icons/obj/service/hydroponics/equipment.dmi', "over_lowhealth3") + . += mutable_appearance('icons/obj/service/hydroponics/equipment.dmi', "over_lowhealth3[indicatorsuffix]") + . += emissive_appearance(icon, "over_lowhealth3[indicatorsuffix]", src, alpha = src.alpha) if(weedlevel >= 5 || pestlevel >= 5 || toxic >= 40) - . += mutable_appearance('icons/obj/service/hydroponics/equipment.dmi', "over_alert3") + . += mutable_appearance('icons/obj/service/hydroponics/equipment.dmi', "over_alert3[indicatorsuffix]") + . += emissive_appearance(icon, "over_alert3[indicatorsuffix]", src, alpha = src.alpha) if(plant_status == HYDROTRAY_PLANT_HARVESTABLE) - . += mutable_appearance('icons/obj/service/hydroponics/equipment.dmi', "over_harvest3") + . += mutable_appearance('icons/obj/service/hydroponics/equipment.dmi', "over_harvest3[indicatorsuffix]") + . += emissive_appearance(icon, "over_harvest3[indicatorsuffix]", src, alpha = src.alpha) ///Sets a new value for the myseed variable, which is the seed of the plant that's growing inside the tray. /obj/machinery/hydroponics/proc/set_seed(obj/item/seeds/new_seed, delete_old_seed = TRUE) @@ -1063,6 +1093,37 @@ flowergun.update_appearance() to_chat(user, span_notice("[myseed.plantname]'s mutation was set to [locked_mutation], depleting [flowergun]'s cell!")) return + else if(istype(O, /obj/item/soil_sack)) + var/obj/item/soil_sack/oursoil = O + + if(plant_status != HYDROTRAY_NO_PLANT) + balloon_alert(user, "remove the plants first!") + return + + if(!isnull(current_soil)) + balloon_alert(user, "tray is full") + return + + balloon_alert(user, "filling the tray...") + if(!do_after(user, 2 SECONDS, src)) + return + + if(!oursoil.stored_soil) + balloon_alert(user, "sack is empty!") + return + + current_soil = new oursoil.stored_soil(src) + + RefreshParts() + tray_flags = current_soil.tray_flags + current_soil_overlay = "[current_soil.icon_state]_tray" + name = "botanic tray" + desc = "A basin used to grow plants in. Filled with [current_soil.name]." + + qdel(oursoil) + update_appearance() + return + else return ..() @@ -1105,6 +1166,10 @@ update_use_power(NO_POWER_USE) return CLICK_ACTION_BLOCKING + if(!can_self_sustain) + balloon_alert(user, "no self-sustain mode!") + 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/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm index f0df050b5c46..04786785bde6 100644 --- a/code/modules/instruments/songs/_song.dm +++ b/code/modules/instruments/songs/_song.dm @@ -120,6 +120,10 @@ var/cached_exponential_dropoff = 1.045 ///////////////////////////////////////////////////////////////////////// + + ///Rate at which volume goes down to 0. Not controlled in menu. + var/exponential_falloff = 4 + /datum/song/New(atom/parent, list/instrument_ids, new_range) SSinstruments.on_song_new(src) lines = list() diff --git a/code/modules/instruments/songs/play_legacy.dm b/code/modules/instruments/songs/play_legacy.dm index bb60d63c3b54..8513b8582170 100644 --- a/code/modules/instruments/songs/play_legacy.dm +++ b/code/modules/instruments/songs/play_legacy.dm @@ -88,5 +88,5 @@ var/pref_volume = M?.client?.prefs.read_preference(/datum/preference/numeric/volume/sound_instruments) if(!pref_volume) continue - M.playsound_local(source, null, volume * using_instrument.volume_multiplier * (pref_volume/100), sound_to_use = music_played) + M.playsound_local(source, null, volume * using_instrument.volume_multiplier * (pref_volume/100), sound_to_use = music_played, falloff_exponent = exponential_falloff) // Could do environment and echo later but not for now diff --git a/code/modules/instruments/songs/play_synthesized.dm b/code/modules/instruments/songs/play_synthesized.dm index 6f9cf2280b28..92ae4d369c58 100644 --- a/code/modules/instruments/songs/play_synthesized.dm +++ b/code/modules/instruments/songs/play_synthesized.dm @@ -67,7 +67,7 @@ var/pref_volume = M?.client?.prefs.read_preference(/datum/preference/numeric/volume/sound_instruments) if(!pref_volume) continue - M.playsound_local(get_turf(parent), null, volume * (pref_volume/100), FALSE, K.frequency, null, channel, null, copy) + M.playsound_local(get_turf(parent), null, volume * (pref_volume/100), FALSE, K.frequency, exponential_falloff, channel, null, copy) // Could do environment and echo later but not for now /** diff --git a/code/modules/lootpanel/_lootpanel.dm b/code/modules/lootpanel/_lootpanel.dm index f7cfab8d8cd4..cc67696d6f22 100644 --- a/code/modules/lootpanel/_lootpanel.dm +++ b/code/modules/lootpanel/_lootpanel.dm @@ -23,6 +23,8 @@ /datum/lootpanel/Destroy(force) + SSlooting.backlog -= src + SSlooting.processing -= src reset_contents() owner = null source_turf = null diff --git a/code/modules/lootpanel/ss_looting.dm b/code/modules/lootpanel/ss_looting.dm index eb8cff36e696..6f2481844a16 100644 --- a/code/modules/lootpanel/ss_looting.dm +++ b/code/modules/lootpanel/ss_looting.dm @@ -18,7 +18,7 @@ SUBSYSTEM_DEF(looting) /datum/controller/subsystem/looting/fire(resumed) - if(!length(backlog)) + if(!length(backlog) && !length(processing)) return if(!resumed) diff --git a/code/modules/mapping/ruins.dm b/code/modules/mapping/ruins.dm index 94d5dd6195c2..c6ce163abf52 100644 --- a/code/modules/mapping/ruins.dm +++ b/code/modules/mapping/ruins.dm @@ -212,6 +212,9 @@ var/top_right_y = bottom_left_y + current_pick.height - 1 log_mapping("Successfully placed [current_pick.name] ruin ([bottom_left_x],[bottom_left_y],[placed_turf.z] to [top_right_x],[top_right_y],[placed_turf.z]).") + ///Keep track of the active ruins so we can take it in account for map generation. Using bottom lef turf as key + SSmapping.active_ruins[locate(bottom_left_x, bottom_left_y, placed_turf.z)] = current_pick + //Update the available list for(var/datum/map_template/ruin/R in ruins_available) if(R.cost > budget || R.mineral_cost > mineral_budget) diff --git a/code/modules/mining/equipment/kinetic_crusher/trophies_megafauna.dm b/code/modules/mining/equipment/kinetic_crusher/trophies_megafauna.dm index 5c69886c3711..09d15bef6ba2 100644 --- a/code/modules/mining/equipment/kinetic_crusher/trophies_megafauna.dm +++ b/code/modules/mining/equipment/kinetic_crusher/trophies_megafauna.dm @@ -38,7 +38,7 @@ living_target.adjust_fire_loss(bonus_value, forced = TRUE) /obj/item/crusher_trophy/tail_spike/proc/pushback(mob/living/target, mob/living/user) - if(!QDELETED(target) && !QDELETED(user) && (!target.anchored || ismegafauna(target))) //megafauna will always be pushed + if(!QDELETED(target) && !QDELETED(user) && (!target.anchored || ismegafauna(target)) && target.move_resist < INFINITY) //megafauna will always be pushed step(target, get_dir(user, target)) //bubblegum diff --git a/code/modules/mob/living/basic/blob_minions/blob_mob.dm b/code/modules/mob/living/basic/blob_minions/blob_mob.dm index ef7184d09ac0..b13e9f693451 100644 --- a/code/modules/mob/living/basic/blob_minions/blob_mob.dm +++ b/code/modules/mob/living/basic/blob_minions/blob_mob.dm @@ -21,7 +21,7 @@ initial_language_holder = /datum/language_holder/empty can_buckle_to = FALSE damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, STAMINA = 0, OXY = 1) - pull_force = 0 + pull_force = MOVE_FORCE_NONE /// Size of cloud produced from a dying spore var/death_cloud_size = BLOBMOB_CLOUD_NONE var/loot = /obj/item/food/spore_sack diff --git a/code/modules/mob/living/basic/boss/blood_drunk_miner/_blood_drunk_miner.dm b/code/modules/mob/living/basic/boss/blood_drunk_miner/_blood_drunk_miner.dm index 7462974381e0..0e9e734a21de 100644 --- a/code/modules/mob/living/basic/boss/blood_drunk_miner/_blood_drunk_miner.dm +++ b/code/modules/mob/living/basic/boss/blood_drunk_miner/_blood_drunk_miner.dm @@ -12,16 +12,17 @@ Difficulty: Medium /mob/living/basic/boss/blood_drunk_miner name = "blood-drunk miner" desc = "A miner destined to wander forever, engaged in an endless hunt." - health = 900 - maxHealth = 900 + health = 1300 + maxHealth = 1300 icon_state = "miner" icon_living = "miner" + base_icon_state = "miner" icon = 'icons/mob/simple/broadMobs.dmi' health_doll_icon = "miner" mob_biotypes = MOB_ORGANIC|MOB_HUMANOID|MOB_SPECIAL|MOB_MINING light_color = COLOR_LIGHT_GRAYISH_RED speak_emote = list("roars") - speed = 3 + speed = 2.5 pixel_x = -16 base_pixel_x = -16 basic_mob_flags = DEL_ON_DEATH @@ -43,7 +44,7 @@ Difficulty: Medium victor_memory_type = /datum/memory/megafauna_slayer crusher_loot = list(/obj/item/crusher_trophy/miner_eye, /obj/item/knife/hunting/wildhunter) - regular_loot = list(/obj/item/melee/cleaving_saw, /obj/item/gun/energy/recharge/kinetic_accelerator) + regular_loot = list(/obj/item/melee/cleaving_saw, /obj/item/gun/energy/recharge/kinetic_accelerator/bdm) /// Their little saw var/obj/item/melee/cleaving_saw/miner/miner_saw @@ -87,9 +88,9 @@ Difficulty: Medium /// Returns a list of innate actions for the blood-drunk miner. /mob/living/basic/boss/blood_drunk_miner/proc/get_innate_actions() - var/static/list/innate_abilities = list( - /datum/action/cooldown/mob_cooldown/dash = BB_BDM_DASH_ABILITY, - /datum/action/cooldown/mob_cooldown/projectile_attack/kinetic_accelerator = BB_BDM_KINETIC_ACCELERATOR_ABILITY, + var/list/innate_abilities = list( + /datum/action/cooldown/mob_cooldown/charge/basic_charge/blood_drunk_miner = BB_BDM_DASH_ABILITY, + /datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/kinetic_accelerator = BB_BDM_KINETIC_ACCELERATOR_ABILITY, /datum/action/cooldown/mob_cooldown/dash_attack = BB_BDM_DASH_ATTACK_ABILITY, /datum/action/cooldown/mob_cooldown/transform_weapon = BB_BDM_TRANSFORM_WEAPON_ABILITY, ) @@ -101,6 +102,13 @@ Difficulty: Medium INVOKE_ASYNC(ai_controller.blackboard[BB_BDM_TRANSFORM_WEAPON_ABILITY], TYPE_PROC_REF(/datum/action, Trigger), src, NONE) +/mob/living/basic/boss/blood_drunk_miner/proc/transform_saw() + miner_saw.attack_self(src) + var/saw_open = HAS_TRAIT(miner_saw, TRAIT_TRANSFORM_ACTIVE) + rapid_melee_hits = saw_open ? 3 : 5 + icon_state = "[base_icon_state][saw_open ? "_transformed":""]" + icon_living = "[base_icon_state][saw_open ? "_transformed":""]" + /mob/living/basic/boss/blood_drunk_miner/ex_act(severity, target) var/datum/action/cooldown/mob_cooldown/dash_ability = ai_controller.blackboard[BB_BDM_DASH_ABILITY] if(dash_ability.Trigger(target = target)) @@ -145,28 +153,58 @@ Difficulty: Medium /// Namely, we just use the miner saw to rapidly hit the target multiple times /mob/living/basic/boss/blood_drunk_miner/proc/attack_override(mob/living/source, atom/target, proximity, modifiers) SIGNAL_HANDLER - if(!istype(target, /mob/living)) + + if(!isliving(target)) return var/mob/living/victim = target - if(should_devour(target)) - devour(target) + if(should_devour(victim)) + devour(victim) return COMPONENT_HOSTILE_NO_ATTACK + do_chain_attack(victim, modifiers) + return COMPONENT_HOSTILE_NO_ATTACK + +/mob/living/basic/boss/blood_drunk_miner/proc/do_chain_attack(mob/living/victim, modifiers, sequence_hit = 1) + if (!Adjacent(victim)) + post_attack_effects(victim, modifiers) + return + changeNext_move(CLICK_CD_MELEE) victim.visible_message( span_danger("[src] slashes at [victim] with [p_their()] cleaving saw!"), span_userdanger("You are slashed at by [src]'s cleaving saw!"), ) - var/datum/callback/melee_callback = CALLBACK(miner_saw, TYPE_PROC_REF(/obj/item/melee/cleaving_saw/miner, melee_attack_chain), src, victim, modifiers) - var/delay = 0.2 SECONDS - for(var/i in 1 to rapid_melee_hits) - addtimer(melee_callback, (i - 1) * delay) + var/delay = HAS_TRAIT(miner_saw, TRAIT_TRANSFORM_ACTIVE) ? 0.5 SECONDS : 0.3 SECONDS + apply_status_effect(/datum/status_effect/saw_slashes_slowdown, delay) + INVOKE_ASYNC(miner_saw, TYPE_PROC_REF(/obj/item/melee/cleaving_saw/miner, melee_attack_chain), src, victim, modifiers) - post_attack_effects(victim, modifiers) + if (sequence_hit >= rapid_melee_hits) + post_attack_effects(victim, modifiers) + return - return COMPONENT_HOSTILE_NO_ATTACK + addtimer(CALLBACK(src, PROC_REF(do_chain_attack), victim, modifiers, sequence_hit + 1), delay) + +/datum/status_effect/saw_slashes_slowdown + id = "saw_slashes_slowdown" + alert_type = null + status_type = STATUS_EFFECT_REFRESH + +/datum/status_effect/saw_slashes_slowdown/on_creation(mob/living/new_owner, new_duration) + duration = new_duration + return ..() + +/datum/status_effect/saw_slashes_slowdown/refresh(effect, new_duration) + duration = new_duration + +/datum/status_effect/saw_slashes_slowdown/on_apply() + . = ..() + owner.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/saw_slashes_slowdown) + +/datum/status_effect/saw_slashes_slowdown/on_remove() + . = ..() + owner.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/saw_slashes_slowdown) /// Hook for potential additional behaviors after attacking /mob/living/basic/boss/blood_drunk_miner/proc/post_attack_effects(mob/living/victim, list/modifiers) diff --git a/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_actions.dm b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_actions.dm new file mode 100644 index 000000000000..468d01ce3a98 --- /dev/null +++ b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_actions.dm @@ -0,0 +1,82 @@ +/datum/action/cooldown/mob_cooldown/charge/basic_charge/blood_drunk_miner + cooldown_time = 1.5 SECONDS + charge_delay = 0.1 SECONDS + shake_duration = 0.2 SECONDS // A bit longer so he shakes during the dash too + charge_distance = 6 + // Don't stun ourselves or the target + recoil_duration = -1 + knockdown_duration = -1 + destroy_objects = FALSE + charge_damage = 0 + charge_speed = 0.3 + +/datum/action/cooldown/mob_cooldown/charge/basic_charge/blood_drunk_miner/hit_target(atom/movable/source, atom/target, damage_dealt) + . = ..() + if(!isbasicmob(source) || !isliving(target)) + return + var/mob/living/basic/basic_source = source + basic_source.melee_attack(target, ignore_cooldown = TRUE) + +/datum/action/cooldown/mob_cooldown/transform_weapon + name = "Transform Weapon" + button_icon = 'icons/obj/mining_zones/artefacts.dmi' + button_icon_state = "cleaving_saw" + desc = "Transform weapon into a different state." + cooldown_time = 5 SECONDS + shared_cooldown = MOB_SHARED_COOLDOWN_2 + /// The max possible cooldown, cooldown is random between the default cooldown time and this + var/max_cooldown_time = 10 SECONDS + +/datum/action/cooldown/mob_cooldown/transform_weapon/Activate(atom/target_atom) + disable_cooldown_actions() + do_transform() + StartCooldown(rand(cooldown_time, max_cooldown_time), 0) + enable_cooldown_actions() + return TRUE + +/datum/action/cooldown/mob_cooldown/transform_weapon/proc/do_transform() + if(!istype(owner, /mob/living/basic/boss/blood_drunk_miner)) + return + var/mob/living/basic/boss/blood_drunk_miner/blood_drunk_miner = owner + blood_drunk_miner.transform_saw() + +/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/kinetic_accelerator + name = "Fire Kinetic Accelerator" + desc = "Fires a kinetic accelerator projectile at the target." + button_icon = 'icons/obj/weapons/guns/energy.dmi' + button_icon_state = "kineticgun" + cooldown_time = 1.5 SECONDS + projectile_type = /obj/projectile/kinetic/miner + projectile_sound = 'sound/items/weapons/kinetic_accel.ogg' + shot_count = 3 + shot_delay = 0.15 SECONDS + default_projectile_spread = 10 + can_move = FALSE + /// Delay for the alert + var/alert_delay = 0.5 SECONDS + /// Delay before we start shooting during which we cannot move + var/prefire_delay = 0.2 SECONDS + /// Delay before the user can move or act again after firing + var/reload_delay = 0.1 SECONDS + +/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/kinetic_accelerator/Activate(atom/target_atom) + owner.visible_message(span_danger("[owner] fires the proto-kinetic accelerator!")) + owner.face_atom(target_atom) + owner.do_alert_animation(alert_delay + (shot_count - 1) * shot_delay) + disable_cooldown_actions() + if (alert_delay > prefire_delay) // As to delay movement blocking + SLEEP_CHECK_DEATH(alert_delay - prefire_delay, owner) + return ..() + +/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/kinetic_accelerator/attack_sequence(mob/living/firer, atom/target) + SLEEP_CHECK_DEATH(prefire_delay, firer) + . = ..() + sleep(reload_delay) + +/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/kinetic_accelerator/shoot_projectile(atom/origin, atom/target, set_angle, mob/firer, projectile_spread, speed_multiplier, override_projectile_type, override_homing) + . = ..() + new /obj/effect/temp_visual/dir_setting/firing_effect(get_turf(firer), firer.dir) + +/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/kinetic_accelerator/long_burst + shot_count = 5 + shot_delay = 0.1 SECONDS diff --git a/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_ai.dm b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_ai.dm index f41400aa52a0..64efdb0b8a96 100644 --- a/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_ai.dm +++ b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_ai.dm @@ -12,7 +12,7 @@ BB_BDM_RANGED_ATTACK_COOLDOWN = 0, ) - movement_delay = 0.3 SECONDS + movement_delay = 0.25 SECONDS ai_movement = /datum/ai_movement/basic_avoidance idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( @@ -63,4 +63,4 @@ return TRUE /datum/ai_controller/blood_drunk_miner/doom - movement_delay = 0.8 SECONDS + movement_delay = 0.5 SECONDS diff --git a/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_objects.dm b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_objects.dm index 81b49ea593a7..df5028557705 100644 --- a/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_objects.dm +++ b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_objects.dm @@ -1,15 +1,10 @@ /// A slightly nerfed saw as the normal one is much too murdery. /obj/item/melee/cleaving_saw/miner - force = 6 - open_force = 10 - -/obj/item/melee/cleaving_saw/miner/attack(mob/living/target, mob/living/carbon/human/user) - target.add_stun_absorption(source = "miner", duration = 1 SECONDS, priority = INFINITY) - return ..() + force = 8 + open_force = 12 /obj/projectile/kinetic/miner - damage = 20 - speed = 1.1 + damage = 18 icon_state = "ka_tracer" range = 4 diff --git a/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_subtypes.dm b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_subtypes.dm index a9e6c4d50b11..414fafe2b49f 100644 --- a/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_subtypes.dm +++ b/code/modules/mob/living/basic/boss/blood_drunk_miner/blood_drunk_subtypes.dm @@ -1,6 +1,6 @@ #define HUNTER_DASH_PROBABILITY 12 -/// heals slightly on melee hits +/// Heals slightly on melee hits /mob/living/basic/boss/blood_drunk_miner/guidance /mob/living/basic/boss/blood_drunk_miner/guidance/attack_override(mob/living/source, atom/target, proximity, modifiers) @@ -19,10 +19,11 @@ if(!isnull(dash_attack)) INVOKE_ASYNC(dash_attack, TYPE_PROC_REF(/datum/action, Trigger), src, NONE, victim) +/// Slow but constantly dashes and has longer barrages /mob/living/basic/boss/blood_drunk_miner/doom name = "hostile-environment miner" desc = "A miner destined to hop across dimensions for all eternity, hunting anomalous creatures." - speed = 8 + speed = 5 ranged_attack_cooldown_duration = 0.8 SECONDS ai_controller = /datum/ai_controller/blood_drunk_miner/doom @@ -32,4 +33,17 @@ if(!isnull(dash_ability)) dash_ability.cooldown_time = 0.8 SECONDS + var/datum/action/cooldown/dash_attack = ai_controller.blackboard[BB_BDM_DASH_ATTACK_ABILITY] + if(!isnull(dash_attack)) + dash_attack.cooldown_time = 4 SECONDS + +/mob/living/basic/boss/blood_drunk_miner/doom/get_innate_actions() + var/list/innate_abilities = list( + /datum/action/cooldown/mob_cooldown/charge/basic_charge/blood_drunk_miner = BB_BDM_DASH_ABILITY, + /datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/kinetic_accelerator/long_burst = BB_BDM_KINETIC_ACCELERATOR_ABILITY, + /datum/action/cooldown/mob_cooldown/dash_attack = BB_BDM_DASH_ATTACK_ABILITY, + /datum/action/cooldown/mob_cooldown/transform_weapon = BB_BDM_TRANSFORM_WEAPON_ABILITY, + ) + return innate_abilities + #undef HUNTER_DASH_PROBABILITY diff --git a/code/modules/mob/living/basic/boss/thing/thing_ai.dm b/code/modules/mob/living/basic/boss/thing/thing_ai.dm index be6033ec2b26..e8956cac2b13 100644 --- a/code/modules/mob/living/basic/boss/thing/thing_ai.dm +++ b/code/modules/mob/living/basic/boss/thing/thing_ai.dm @@ -5,14 +5,15 @@ BB_THETHING_ATTACKMODE = TRUE, //Whether we are using our melee abilities right now BB_THETHING_NOAOE = TRUE, // Restricts us to only melee abilities BB_THETHING_LASTAOE = null, // Last AOE ability key executed - BB_AGGRO_RANGE = 6, //lets not execute hearers for a 16 tile radius + BB_AGGRO_RANGE = 16, + BB_AGGRO_GRAB_RANGE = 6, ) ai_movement = /datum/ai_movement/basic_avoidance // dont need anything better because the arena is a square lol idle_behavior = null planning_subtrees = list( /datum/ai_planning_subtree/escape_captivity, - /datum/ai_planning_subtree/simple_find_target/increased_range, //aggros at 6, sees 16 tiles + /datum/ai_planning_subtree/simple_find_target, /datum/ai_planning_subtree/thing_boss_aoe, /datum/ai_planning_subtree/thing_boss_melee, ) diff --git a/code/modules/mob/living/basic/farm_animals/bee/_bee.dm b/code/modules/mob/living/basic/farm_animals/bee/_bee.dm index 66cd498ccd60..c3a1c5d91395 100644 --- a/code/modules/mob/living/basic/farm_animals/bee/_bee.dm +++ b/code/modules/mob/living/basic/farm_animals/bee/_bee.dm @@ -199,7 +199,7 @@ /// Picks a random toxin and assigns it to the bee /mob/living/basic/bee/proc/assign_random_toxin_reagent() - assign_reagent(get_random_reagent_id(whitelist = subtypesof(/datum/reagent/toxin))) + assign_reagent(GLOB.chemical_reagents_list[get_random_reagent_id(whitelist = subtypesof(/datum/reagent/toxin))]) /mob/living/basic/bee/mutate() . = ..() diff --git a/code/modules/mob/living/basic/icemoon/polar_bear/polar_bear.dm b/code/modules/mob/living/basic/icemoon/polar_bear/polar_bear.dm new file mode 100644 index 000000000000..b1dc69d06bf0 --- /dev/null +++ b/code/modules/mob/living/basic/icemoon/polar_bear/polar_bear.dm @@ -0,0 +1,72 @@ +// The taxonomy of polar vs space bear left to imagination of the reader +/mob/living/basic/mining/polarbear + name = "polar bear" + desc = "An aggressive animal that defends its territory with incredible power. These beasts don't run from their enemies." + icon = 'icons/mob/simple/icemoon/icemoon_monsters.dmi' + icon_state = "polarbear" + icon_living = "polarbear" + icon_dead = "polarbear_dead" + mob_biotypes = MOB_ORGANIC|MOB_BEAST|MOB_MINING + + friendly_verb_continuous = "growls at" + friendly_verb_simple = "growl at" + response_help_continuous = "pets" + response_help_simple = "pet" + response_disarm_continuous = "gently pushes aside" + response_disarm_simple = "gently push aside" + attack_verb_continuous = "claws" + attack_verb_simple = "claw" + attack_sound = 'sound/items/weapons/bladeslice.ogg' + attack_vis_effect = ATTACK_EFFECT_CLAW + + habitable_atmos = null + speed = 2 + maxHealth = 300 + health = 300 + obj_damage = 40 + melee_damage_lower = 25 + melee_damage_upper = 25 + wound_bonus = -5 + exposed_wound_bonus = 10 + sharpness = SHARP_EDGED + + move_force = MOVE_FORCE_VERY_STRONG + move_resist = MOVE_FORCE_VERY_STRONG + pull_force = MOVE_FORCE_VERY_STRONG + butcher_results = list(/obj/item/food/meat/slab/bear = 3, /obj/item/stack/sheet/bone = 2) + guaranteed_butcher_results = list(/obj/item/stack/sheet/animalhide/goliath_hide/polar_bear_hide = 1) + crusher_loot = /obj/item/crusher_trophy/bear_paw + + ai_controller = /datum/ai_controller/basic_controller/polar + +/mob/living/basic/mining/polarbear/Initialize(mapload) + . = ..() + + add_traits(list(TRAIT_SPACEWALK, TRAIT_SWIMMER, TRAIT_FENCE_CLIMBER, TRAIT_SNOWSTORM_IMMUNE), INNATE_TRAIT) + AddElement(/datum/element/ai_retaliate) + AddElement(/datum/element/swabable, CELL_LINE_TABLE_BEAR, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5) + AddElement(/datum/element/change_force_on_death, move_force = MOVE_FORCE_DEFAULT, move_resist = MOVE_RESIST_DEFAULT, pull_force = PULL_FORCE_DEFAULT) + AddElement(/datum/element/footstep, footstep_type = FOOTSTEP_MOB_CLAW) + +/mob/living/basic/mining/polarbear/lesser + name = "magic polar bear" + desc = "It seems sentient somehow." + faction = list(FACTION_NEUTRAL) + +/datum/ai_controller/basic_controller/polar + blackboard = list( + BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, + BB_AGGRO_RANGE = 2, + ) + + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + planning_subtrees = list( + /datum/ai_planning_subtree/enrage, + /datum/ai_planning_subtree/escape_captivity, + /datum/ai_planning_subtree/target_retaliate, + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + /datum/ai_planning_subtree/random_speech/bear, + ) diff --git a/code/modules/mob/living/basic/lavaland/tendril/tendril_actions.dm b/code/modules/mob/living/basic/lavaland/tendril/tendril_actions.dm index ba4bb3b219d8..1a2272fd85b7 100644 --- a/code/modules/mob/living/basic/lavaland/tendril/tendril_actions.dm +++ b/code/modules/mob/living/basic/lavaland/tendril/tendril_actions.dm @@ -266,6 +266,8 @@ cooldown_time = 10 SECONDS melee_cooldown_time = 0 shared_cooldown = NONE + /// Range in which we create spikes + var/spike_range = 7 /// Health threshold at which we reduce the amount of empty spots on the ground var/health_threshold = 0.3 @@ -288,7 +290,7 @@ if (as_living.health / as_living.maxHealth <= health_threshold) reduced_spawns = TRUE - for (var/turf/open/target_turf in oview(7, owner_turf)) + for (var/turf/open/target_turf in oview(spike_range, owner_turf)) if (sqrt((target_turf.x - owner_turf.x) ** 2 + (target_turf.y - owner_turf.y) ** 2) > 9.5) // big circle is a lie continue diff --git a/code/modules/mob/living/basic/lavaland/tendril/tendril_ai.dm b/code/modules/mob/living/basic/lavaland/tendril/tendril_ai.dm index 77da7086dfa6..5da4fcedf285 100644 --- a/code/modules/mob/living/basic/lavaland/tendril/tendril_ai.dm +++ b/code/modules/mob/living/basic/lavaland/tendril/tendril_ai.dm @@ -2,6 +2,8 @@ blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_TARGET_MINIMUM_STAT = HARD_CRIT, + BB_AGGRO_RANGE = 9, // Keeps an eye on you even if you flee + BB_AGGRO_GRAB_RANGE = 5, // Only aggros if you get real close and personal ) planning_subtrees = list( @@ -22,7 +24,8 @@ ability_key = BB_TENDRIL_LASH /datum/ai_planning_subtree/use_mob_ability/tendril_lash/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if (!controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]) + var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + if (isnull(target) || get_dist(controller.pawn, target) > /obj/projectile/tentacle_lash::range) return FALSE return ..() @@ -30,6 +33,8 @@ ability_key = BB_TENDRIL_SPIKES /datum/ai_planning_subtree/use_mob_ability/tendril_spikes/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) - if (!controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]) + var/atom/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + var/datum/action/cooldown/mob_cooldown/tendril_cross_spikes/ability = controller.blackboard[ability_key] + if (isnull(target) || !istype(ability) || get_dist(controller.pawn, target) > ability.spike_range) return FALSE return ..() diff --git a/code/modules/mob/living/basic/pets/orbie/orbie.dm b/code/modules/mob/living/basic/pets/orbie/orbie.dm index fbbdfd89d22d..a75f1f5b0a2c 100644 --- a/code/modules/mob/living/basic/pets/orbie/orbie.dm +++ b/code/modules/mob/living/basic/pets/orbie/orbie.dm @@ -21,7 +21,7 @@ pass_flags = PASSMOB move_force = 0 move_resist = 0 - pull_force = 0 + pull_force = MOVE_FORCE_NONE minimum_survivable_temperature = TCMB maximum_survivable_temperature = INFINITY death_message = "fades out of existence!" diff --git a/code/modules/mob/living/basic/space_fauna/lightgeist.dm b/code/modules/mob/living/basic/space_fauna/lightgeist.dm index 50d59efbf2a3..b86d50e2efba 100644 --- a/code/modules/mob/living/basic/space_fauna/lightgeist.dm +++ b/code/modules/mob/living/basic/space_fauna/lightgeist.dm @@ -41,7 +41,7 @@ minimum_survivable_temperature = 0 maximum_survivable_temperature = 1500 obj_damage = 0 - pull_force = 0 + pull_force = MOVE_FORCE_NONE environment_smash = ENVIRONMENT_SMASH_NONE ai_controller = /datum/ai_controller/basic_controller/lightgeist diff --git a/code/modules/mob/living/init_signals.dm b/code/modules/mob/living/init_signals.dm index 34579f82174b..39525bd46ab2 100644 --- a/code/modules/mob/living/init_signals.dm +++ b/code/modules/mob/living/init_signals.dm @@ -186,11 +186,13 @@ mobility_flags &= ~(MOBILITY_PULL) if(pulling) stop_pulling() + pull_force_change() /// Called when [TRAIT_PULL_BLOCKED] is removed from the mob. /mob/living/proc/on_pull_blocked_trait_loss(datum/source) SIGNAL_HANDLER mobility_flags |= MOBILITY_PULL + pull_force_change() /// Called when [TRAIT_INCAPACITATED] is added to the mob. diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 17c77733e224..83561f37f9f4 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1134,7 +1134,9 @@ return changeNext_move(CLICK_CD_RESIST) - SEND_SIGNAL(src, COMSIG_LIVING_RESIST, src) + if(SEND_SIGNAL(src, COMSIG_LIVING_RESIST) & COMPONENT_BLOCK_RESIST) + return + //resisting grabs (as if it helps anyone...) if(!HAS_TRAIT(src, TRAIT_RESTRAINED) && pulledby) log_combat(src, pulledby, "resisted grab") @@ -2093,10 +2095,7 @@ GLOBAL_LIST_EMPTY(fire_appearances) update_transform(var_value/current_size) . = TRUE if(NAMEOF(src, pull_force)) - if(var_value == 0) //no more pulling - remove_verb(src, /mob/living/verb/pulled) - else - add_verb(src, /mob/living/verb/pulled) + set_pull_force(var_value) . = TRUE @@ -3041,3 +3040,15 @@ GLOBAL_LIST_EMPTY(fire_appearances) if(HAS_TRAIT(src, TRAIT_ANALGESIA) && !force) return INVOKE_ASYNC(src, PROC_REF(emote), "scream") + +/mob/living/proc/set_pull_force(new_pull_force) + if(pull_force == new_pull_force) + return + pull_force = new_pull_force + pull_force_change() + +/mob/living/proc/pull_force_change() + if(!pull_force || HAS_TRAIT(src, TRAIT_PULL_BLOCKED)) + remove_verb(src, /mob/living/verb/pulled) + else + add_verb(src, /mob/living/verb/pulled) diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index b3c899b25421..ab191bf71069 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -214,6 +214,18 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real return NONE +//Checks blockchance of any items in any module slots +/mob/living/silicon/robot/check_block(atom/hit_by, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0, damage_type = BRUTE) + . = ..() + if(. == SUCCESSFUL_BLOCK) + return SUCCESSFUL_BLOCK + + var/block_chance_modifier = round(damage / -3) + for(var/obj/item/module in held_items) + var/final_block_chance = module.block_chance - (clamp((armour_penetration - module.armour_penetration) / 2, 0, 100)) + block_chance_modifier + if(module.hit_reaction(src, hit_by, attack_text, final_block_chance, damage, attack_type, damage_type)) + return SUCCESSFUL_BLOCK + // This has to go at the very end of interaction so we don't block every interaction with ID-like items /mob/living/silicon/robot/base_item_interaction(mob/living/user, obj/item/tool, list/modifiers) . = ..() @@ -522,6 +534,8 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real return TRUE /mob/living/silicon/robot/bullet_act(obj/projectile/hitting_projectile, def_zone, piercing_hit = FALSE) + if(check_block(hitting_projectile, hitting_projectile.damage, "\the [hitting_projectile]", PROJECTILE_ATTACK, hitting_projectile.armour_penetration, hitting_projectile.damage_type)) + return ..(hitting_projectile, def_zone, piercing_hit, 100) . = ..() if(prob(25) || . != BULLET_ACT_HIT) return diff --git a/code/modules/mob/living/silicon/robot/robot_model.dm b/code/modules/mob/living/silicon/robot/robot_model.dm index deee0d543053..af9b1e6d49fa 100644 --- a/code/modules/mob/living/silicon/robot/robot_model.dm +++ b/code/modules/mob/living/silicon/robot/robot_model.dm @@ -903,7 +903,7 @@ name = "Syndicate Assault" basic_modules = list( /obj/item/assembly/flash/cyborg, - /obj/item/melee/energy/sword/cyborg, + /obj/item/melee/energy/sword/saber/cyborg, /obj/item/gun/energy/printer, /obj/item/gun/ballistic/revolver/grenadelauncher/cyborg, /obj/item/card/emag, @@ -936,7 +936,7 @@ /obj/item/borg/cyborg_omnitool/medical, /obj/item/borg/cyborg_omnitool/medical, /obj/item/blood_filter, - /obj/item/melee/energy/sword/cyborg/saw, + /obj/item/melee/energy/sword/saber/cyborg/saw, /obj/item/emergency_bed/silicon, /obj/item/crowbar/cyborg, /obj/item/extinguisher/mini, diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/polarbear.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/polarbear.dm deleted file mode 100644 index 8a73d1465966..000000000000 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/polarbear.dm +++ /dev/null @@ -1,67 +0,0 @@ -/mob/living/simple_animal/hostile/asteroid/polarbear - name = "polar bear" - desc = "An aggressive animal that defends its territory with incredible power. These beasts don't run from their enemies." - icon = 'icons/mob/simple/icemoon/icemoon_monsters.dmi' - icon_state = "polarbear" - icon_living = "polarbear" - icon_dead = "polarbear_dead" - mob_biotypes = MOB_ORGANIC|MOB_BEAST|MOB_MINING - mouse_opacity = MOUSE_OPACITY_ICON - friendly_verb_continuous = "growls at" - friendly_verb_simple = "growl at" - speak_emote = list("growls") - speed = 3 - move_to_delay = 8 - maxHealth = 300 - health = 300 - obj_damage = 40 - melee_damage_lower = 25 - melee_damage_upper = 25 - attack_verb_continuous = "claws" - attack_verb_simple = "claw" - attack_sound = 'sound/items/weapons/bladeslice.ogg' - attack_vis_effect = ATTACK_EFFECT_CLAW - vision_range = 2 // don't aggro unless you basically antagonize it, though they will kill you worse than a goliath will - aggro_vision_range = 9 - move_force = MOVE_FORCE_VERY_STRONG - move_resist = MOVE_FORCE_VERY_STRONG - pull_force = MOVE_FORCE_VERY_STRONG - butcher_results = list(/obj/item/food/meat/slab/bear = 3, /obj/item/stack/sheet/bone = 2) - guaranteed_butcher_results = list(/obj/item/stack/sheet/animalhide/goliath_hide/polar_bear_hide = 1) - loot = list() - crusher_loot = /obj/item/crusher_trophy/bear_paw - stat_attack = HARD_CRIT - robust_searching = TRUE - footstep_type = FOOTSTEP_MOB_CLAW - /// Message for when the polar bear starts to attack faster - var/aggressive_message_said = FALSE - -/mob/living/simple_animal/hostile/asteroid/polarbear/Initialize(mapload) - . = ..() - AddElement(\ - /datum/element/change_force_on_death,\ - move_force = MOVE_FORCE_DEFAULT,\ - move_resist = MOVE_RESIST_DEFAULT,\ - pull_force = PULL_FORCE_DEFAULT,\ - ) - -/mob/living/simple_animal/hostile/asteroid/polarbear/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - . = ..() - if(health > maxHealth*0.5) - rapid_melee = initial(rapid_melee) - return - if(!aggressive_message_said && target) - visible_message(span_danger("\The [src] gets an enraged look at [target]!")) - aggressive_message_said = TRUE - rapid_melee = 2 - -/mob/living/simple_animal/hostile/asteroid/polarbear/Life(seconds_per_tick = SSMOBS_DT) - . = ..() - if(!. || target) - return - aggressive_message_said = FALSE - -/mob/living/simple_animal/hostile/asteroid/polarbear/lesser - name = "magic polar bear" - desc = "It seems sentient somehow." - faction = list(FACTION_NEUTRAL) diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 2c5f0af4451a..50a15e3ed984 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -489,6 +489,49 @@ return quirk return null +/** + * get_quirk_string() is used to get a printable string of all the quirk traits someone has for certain criteria + * + * Arguments: + * * Medical- If we want the long, fancy descriptions that show up in medical records, or if not, just the name + * * Category- Which types of quirks we want to print out. Defaults to everything + * * from_scan- If the source of this call is like a health analyzer or HUD, in which case QUIRK_HIDE_FROM_MEDICAL hides the quirk. + */ +/mob/living/proc/get_quirk_string(medical = FALSE, category = CAT_QUIRK_ALL, from_scan = FALSE) + var/list/dat = list() + for(var/datum/quirk/candidate as anything in quirks) + if(from_scan && (candidate.quirk_flags & QUIRK_HIDE_FROM_SCAN)) + continue + switch(category) + if(CAT_QUIRK_MAJOR_DISABILITY) + if(candidate.value >= -4) + continue + if(CAT_QUIRK_MINOR_DISABILITY) + if(!ISINRANGE(candidate.value, -4, -1)) + continue + if(CAT_QUIRK_NOTES) + if(candidate.value < 0) + continue + dat += medical ? candidate.medical_record_text : candidate.name + + if(!length(dat)) + return medical ? "No issues have been declared." : "None" + return medical ? dat.Join("
") : dat.Join(", ") + +/mob/living/proc/cleanse_quirk_datums() //removes all trait datums + QDEL_LAZYLIST(quirks) + +/mob/living/proc/transfer_quirk_datums(mob/living/to_mob) + // We could be done before the client was moved or after the client was moved + var/datum/preferences/to_pass = client || to_mob.client + + for(var/datum/quirk/quirk as anything in quirks) + if(quirk.quirk_flags & QUIRK_NO_TRANSFER) + continue + quirk.remove_from_current_holder(quirk_transfer = TRUE) + quirk.add_to_holder(to_mob, quirk_transfer = TRUE, client_source = to_pass) + + /// Helper to easily add a personality by a typepath /mob/living/proc/add_personality(personality_type) var/datum/personality/personality = SSpersonalities.personalities_by_type[personality_type] @@ -509,6 +552,14 @@ for(var/personality_type in personalities) remove_personality(personality_type) +/// Returns a string with the names of the personalities of this mob, and their description as tooltip +/mob/living/proc/get_parsonality_string() + var/list/return_list = list() + for(var/personality_type in personalities) + var/datum/personality/personality = SSpersonalities.personalities_by_type[personality_type] + return_list += span_tooltip(personality.desc, personality.name) + return english_list(return_list) + /mob/living/proc/cure_husk(source) REMOVE_TRAIT(src, TRAIT_HUSK, source) if(HAS_TRAIT(src, TRAIT_HUSK)) diff --git a/code/modules/mob/mob_lists.dm b/code/modules/mob/mob_lists.dm index f47f316c078c..d020f9df0f2c 100644 --- a/code/modules/mob/mob_lists.dm +++ b/code/modules/mob/mob_lists.dm @@ -46,21 +46,26 @@ ///Adds the cliented mob reference to the list of all player-mobs, besides to either the of dead or alive player-mob lists, as appropriate. Called on Login(). /mob/proc/add_to_player_list() SHOULD_CALL_PARENT(TRUE) + + GLOB.player_list |= src if(stat == DEAD) add_to_current_dead_players() else add_to_current_living_players() + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_PLAYER_LOGIN, src) ///Removes the mob reference from the list of all player-mobs, besides from either the of dead or alive player-mob lists, as appropriate. Called on Logout(). /mob/proc/remove_from_player_list() SHOULD_CALL_PARENT(TRUE) + GLOB.player_list -= src GLOB.keyloop_list -= src if(stat == DEAD) remove_from_current_dead_players() else remove_from_current_living_players() + SEND_GLOBAL_SIGNAL(COMSIG_GLOB_PLAYER_LOGOUT, src) ///Adds the cliented mob reference to either the list of dead player-mobs or to the list of observers, depending on how they joined the game. diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 9d5bfd2a5b6c..93c9dce974ea 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -599,3 +599,8 @@ if(new_turf && (istype(new_turf, /turf/cordon/secret) || is_secret_level(new_turf.z)) && !client?.holder) return return ..() + +/mob/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change) + . = ..() + if(client?.sound_tokens.len) + SSsound_tokens.clients_needing_update[client] = TRUE diff --git a/code/modules/movespeed/modifiers/status_effects.dm b/code/modules/movespeed/modifiers/status_effects.dm index df114aca4070..265362f7b8cf 100644 --- a/code/modules/movespeed/modifiers/status_effects.dm +++ b/code/modules/movespeed/modifiers/status_effects.dm @@ -41,6 +41,9 @@ /datum/movespeed_modifier/status_effect/tired_post_charge/lesser multiplicative_slowdown = 2 +/datum/movespeed_modifier/status_effect/saw_slashes_slowdown + multiplicative_slowdown = 0.5 + /// Get slower the more gold is in your system. /datum/movespeed_modifier/status_effect/midas_blight id = MOVESPEED_ID_MIDAS_BLIGHT diff --git a/code/modules/pai/pai.dm b/code/modules/pai/pai.dm index 2573d75f5e8c..37c9e943debf 100644 --- a/code/modules/pai/pai.dm +++ b/code/modules/pai/pai.dm @@ -25,7 +25,7 @@ move_resist = 0 name = "pAI" pass_flags = PASSTABLE | PASSMOB - pull_force = 0 + pull_force = MOVE_FORCE_NONE radio = /obj/item/radio/headset/silicon/pai worn_slot_flags = ITEM_SLOT_HEAD diff --git a/code/modules/power/apc/apc_malf.dm b/code/modules/power/apc/apc_malf.dm index eb0526e32ca1..8cdcc3584590 100644 --- a/code/modules/power/apc/apc_malf.dm +++ b/code/modules/power/apc/apc_malf.dm @@ -56,8 +56,6 @@ disk_pinpointers.switch_mode_to(TRACK_MALF_AI) //Pinpointer will track the shunted AI var/datum/action/innate/core_return/return_action = new return_action.Grant(occupier) - SEND_SIGNAL(src, COMSIG_SILICON_AI_OCCUPY_APC, occupier) - SEND_SIGNAL(occupier, COMSIG_SILICON_AI_OCCUPY_APC, occupier) occupier.cancel_camera() /obj/machinery/power/apc/proc/malfvacate(forced) @@ -71,6 +69,12 @@ occupier.gib(DROP_ALL_REMAINS) occupier = null return + + if(!occupier.nuking) //Pinpointers go back to tracking the nuke disk, as long as the AI (somehow) isn't mid-nuking. + for(var/obj/item/pinpointer/nuke/disk_pinpointers in GLOB.pinpointer_list) + disk_pinpointers.switch_mode_to(TRACK_NUKE_DISK) + disk_pinpointers.alert = FALSE + if(occupier.linked_core) occupier.shunted = FALSE occupier.resolve_core_link() @@ -78,10 +82,6 @@ else stack_trace("An AI: [occupier] has vacated an APC with no linked core and without being gibbed.") - if(!occupier.nuking) //Pinpointers go back to tracking the nuke disk, as long as the AI (somehow) isn't mid-nuking. - for(var/obj/item/pinpointer/nuke/disk_pinpointers in GLOB.pinpointer_list) - disk_pinpointers.switch_mode_to(TRACK_NUKE_DISK) - disk_pinpointers.alert = FALSE /obj/machinery/power/apc/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) . = ..() diff --git a/code/modules/power/lighting/light.dm b/code/modules/power/lighting/light.dm index 52184d2c9592..545855c30bfd 100644 --- a/code/modules/power/lighting/light.dm +++ b/code/modules/power/lighting/light.dm @@ -677,17 +677,19 @@ var/obj/item/light/light_tube = drop_light_tube() return light_tube.attack_tk(user) -// break the light and make sparks if was on +// break the light and make sparks if was on, state is mutated BEFORE firing side-effects to prevent re-entrancy loops from synchronous signals. /obj/machinery/light/proc/break_light_tube(skip_sound_and_sparks = FALSE) if(status == LIGHT_EMPTY || status == LIGHT_BROKEN) return + var/was_ok = (status == LIGHT_OK || status == LIGHT_BURNED) + status = LIGHT_BROKEN + if(!skip_sound_and_sparks) - if(status == LIGHT_OK || status == LIGHT_BURNED) + if(was_ok) playsound(loc, 'sound/effects/glass/glasshit.ogg', 75, TRUE) if(on) do_sparks(3, TRUE, src) - status = LIGHT_BROKEN update() /obj/machinery/light/proc/fix() diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index 80d9aa26d849..c91985e71ea5 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -80,7 +80,8 @@ to_chat(user, span_notice("You pry all the modifications out.")) I.play_tool_sound(src, 100) for(var/obj/item/borg/upgrade/modkit/modkit_upgrade as anything in modkits) - modkit_upgrade.forceMove(drop_location()) //uninstallation handled in Exited(), or /mob/living/silicon/robot/remove_from_upgrades() for borgs + if (modkit_upgrade.removable) + modkit_upgrade.forceMove(drop_location()) //uninstallation handled in Exited(), or /mob/living/silicon/robot/remove_from_upgrades() for borgs else to_chat(user, span_notice("There are no modifications currently installed.")) @@ -98,7 +99,9 @@ var/list/display_names = list() var/list/items = list() for(var/modkits_length in 1 to length(modkits)) - var/obj/item/thing = modkits[modkits_length] + var/obj/item/borg/upgrade/modkit/thing = modkits[modkits_length] + if (!thing.removable) + continue display_names["[thing.name] ([modkits_length])"] = REF(thing) var/image/item_image = image(icon = thing.icon, icon_state = thing.icon_state) if(length(thing.overlays)) @@ -156,6 +159,16 @@ for(var/obj/item/borg/upgrade/modkit/modkit_upgrade as anything in modkits) modkit_upgrade.modify_projectile(kinetic_projectile) +/obj/item/gun/energy/recharge/kinetic_accelerator/bdm + name = "infernal proto-kinetic accelerator" + icon_state = "kineticgun_evil" + inhand_icon_state = "kineticgun_evil" + +/obj/item/gun/energy/recharge/kinetic_accelerator/bdm/Initialize(mapload) + . = ..() + var/obj/item/borg/upgrade/modkit/cooldown/repeater/bdm/repeater_mod = new() + repeater_mod.install(src) + /obj/item/gun/energy/recharge/kinetic_accelerator/cyborg icon_state = "kineticgun_b" holds_charge = TRUE @@ -299,6 +312,8 @@ var/modifier = 1 //For use in any mod kit that has numerical modifiers var/minebot_upgrade = TRUE var/minebot_exclusive = FALSE + /// Can it be removed? + var/removable = TRUE /obj/item/borg/upgrade/modkit/examine(mob/user) . = ..() @@ -320,10 +335,12 @@ . = TRUE if(minebot_upgrade) if(minebot_exclusive && !istype(KA.loc, /mob/living/basic/mining_drone)) - to_chat(user, span_notice("The modkit you're trying to install is only rated for minebot use.")) + if (user) + to_chat(user, span_notice("The modkit you're trying to install is only rated for minebot use.")) return FALSE else if(istype(KA.loc, /mob/living/basic/mining_drone)) - to_chat(user, span_notice("The modkit you're trying to install is not rated for minebot use.")) + if (user) + to_chat(user, span_notice("The modkit you're trying to install is not rated for minebot use.")) return FALSE var/type_to_limit = denied_type @@ -337,21 +354,24 @@ if(istype(modkit_upgrade, type_to_limit)) number_of_denied++ if(maximum_of_type && number_of_denied >= maximum_of_type || !maximum_of_type && number_of_denied) //if we denied a type, or we have a maximum to reach, break - . = FALSE - break - - if(KA.get_remaining_mod_capacity() >= cost) - if(.) - if(transfer_to_loc && !user.transferItemToLoc(src, KA)) - return - to_chat(user, span_notice("You install the modkit.")) - playsound(loc, 'sound/items/tools/screwdriver.ogg', 100, TRUE) - KA.modkits |= src - else - to_chat(user, span_notice("The modkit you're trying to install would conflict with an already installed modkit. Remove existing modkits first.")) - else + if (user) + to_chat(user, span_notice("The modkit you're trying to install would conflict with an already installed modkit. Remove existing modkits first.")) + return FALSE + + if(KA.get_remaining_mod_capacity() < cost) to_chat(user, span_notice("You don't have room([KA.get_remaining_mod_capacity()]% remaining, [cost]% needed) to install this modkit. Use a crowbar or right click with an empty hand to remove existing modkits.")) - . = FALSE + return FALSE + + if(transfer_to_loc) + if (user && !user.transferItemToLoc(src, KA)) + return FALSE + else if (!user) + forceMove(KA) + + if (user) + to_chat(user, span_notice("You install the modkit.")) + playsound(loc, 'sound/items/tools/screwdriver.ogg', 100, TRUE) + KA.modkits |= src /obj/item/borg/upgrade/modkit/deactivate(mob/living/silicon/robot/R, user = usr) . = ..() @@ -554,6 +574,12 @@ KA.cell.use(KA.cell.charge) KA.attempt_reload(KA.recharge_time * 0.25) //If you hit, the cooldown drops to 0.75 seconds. +/obj/item/borg/upgrade/modkit/cooldown/repeater/bdm + name = "infernal repeater" + removable = FALSE + cost = 30 + modifier = -10 + /obj/item/borg/upgrade/modkit/lifesteal name = "lifesteal crystal" desc = "Causes kinetic accelerator shots to slightly heal the firer on striking a living target." diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 0f8f7b35177c..89e74b81a32e 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -154,9 +154,11 @@ /datum/reagent/medicine/cryoxadone/on_mob_life(mob/living/carbon/affected_mob, seconds_per_tick, metabolization_ratio) . = ..() metabolization_rate = REAGENTS_METABOLISM * (0.00001 * (affected_mob.bodytemperature ** 2) + 0.5) - if(affected_mob.bodytemperature >= T0C || !HAS_TRAIT(affected_mob, TRAIT_KNOCKEDOUT)) + if(affected_mob.bodytemperature >= T0C) return var/power = -0.00003 * (affected_mob.bodytemperature ** 2) + 3 + if(HAS_TRAIT(affected_mob, TRAIT_KNOCKEDOUT)) //Significantly more effective when unconscious + power *= 2 var/need_mob_update need_mob_update = affected_mob.adjust_oxy_loss(-1.5 * power * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_biotype = affected_biotype, required_respiration_type = affected_respiration_type) need_mob_update += affected_mob.adjust_brute_loss(-0.5 * power * metabolization_ratio * seconds_per_tick, updating_health = FALSE, required_bodytype = affected_bodytype) diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index b052473d55f8..efc98614861d 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -35,6 +35,8 @@ var/last_rigger = "" /// is it climbable? some of our wall-mounted dispensers should not have this var/climbable = FALSE + /// Flags passed to the reagents datum upon creation + var/reagent_flags = DRAINABLE | AMOUNT_VISIBLE // This check is necessary for assemblies to automatically detect that we are compatible /obj/structure/reagent_dispensers/IsSpecialAssembly() @@ -152,7 +154,7 @@ UnregisterSignal(src, COMSIG_IGNITER_ACTIVATE) /obj/structure/reagent_dispensers/Initialize(mapload) - create_reagents(tank_volume, DRAINABLE | AMOUNT_VISIBLE) + create_reagents(tank_volume, reagent_flags) if(reagent_id) reagents.add_reagent(reagent_id, tank_volume) . = ..() @@ -358,6 +360,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/reagent_dispensers/wall/peppertank, 3 desc = "A machine that cools and dispenses liquids to drink. The 'hot' handle doesn't seem to do anything." icon_state = "water_cooler" anchored = TRUE + reagent_flags = DRAINABLE | TRANSPARENT tank_volume = 200 can_be_tanked = FALSE max_integrity = 150 diff --git a/code/modules/research/part_replacer.dm b/code/modules/research/part_replacer.dm index 581c1d6e28be..3b022850f654 100644 --- a/code/modules/research/part_replacer.dm +++ b/code/modules/research/part_replacer.dm @@ -156,6 +156,16 @@ new /obj/item/stock_parts/power_store/battery/bluespace(src) new /obj/item/stack/cable_coil/thirty(src) +/obj/item/storage/part_replacer/bluespace/AdminDebug/PopulateContents() + for(var/i in 1 to 40) + new /obj/item/stock_parts/capacitor/quadratic(src) + new /obj/item/stock_parts/scanning_module/triphasic(src) + new /obj/item/stock_parts/servo/femto(src) + new /obj/item/stock_parts/micro_laser/quadultra(src) + new /obj/item/stock_parts/matter_bin/bluespace(src) + new /obj/item/stock_parts/power_store/cell/bluespace(src) + new /obj/item/stack/cable_coil/thirty(src) + //used in a cargo crate /obj/item/storage/part_replacer/cargo/PopulateContents() for(var/i in 1 to 10) diff --git a/code/modules/spells/spell_types/shapeshift/polar_bear.dm b/code/modules/spells/spell_types/shapeshift/polar_bear.dm index 93e1f6cea917..65a7342054fe 100644 --- a/code/modules/spells/spell_types/shapeshift/polar_bear.dm +++ b/code/modules/spells/spell_types/shapeshift/polar_bear.dm @@ -6,4 +6,4 @@ invocation_type = INVOCATION_EMOTE spell_requirements = NONE - possible_shapes = list(/mob/living/simple_animal/hostile/asteroid/polarbear/lesser) + possible_shapes = list(/mob/living/basic/mining/polarbear/lesser) diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index 72e120160300..5b1794ef33bf 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -18,8 +18,6 @@ /mob/living/simple_animal/hostile/asteroid/elite/legionnaire, /mob/living/simple_animal/hostile/asteroid/elite/legionnairehead, /mob/living/simple_animal/hostile/asteroid/elite/pandora, - /mob/living/simple_animal/hostile/asteroid/polarbear, - /mob/living/simple_animal/hostile/asteroid/polarbear/lesser, /mob/living/simple_animal/hostile/megafauna, /mob/living/simple_animal/hostile/megafauna/bubblegum, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination, diff --git a/dependencies.sh b/dependencies.sh index 2869f8c53d9d..bf5a66f6e7c7 100644 --- a/dependencies.sh +++ b/dependencies.sh @@ -8,7 +8,7 @@ export BYOND_MAJOR=516 export BYOND_MINOR=1659 #rust_g git tag -export RUST_G_VERSION=4.2.0 +export RUST_G_VERSION=6.2.0 # node version export NODE_VERSION_LTS=22.11.0 @@ -26,11 +26,11 @@ export PYTHON_VERSION=3.11.0 export DREAMLUAU_REPO="tgstation/dreamluau" #dreamluau git tag -export DREAMLUAU_VERSION=0.1.2 +export DREAMLUAU_VERSION=0.2.1 #hypnagogic repo export CUTTER_REPO=spacestation13/hypnagogic #hypnagogic git tag -export CUTTER_VERSION=v5.0.0 +export CUTTER_VERSION=v5.0.1 diff --git a/dreamluau.dll b/dreamluau.dll index cc2d56d2d1e4..6975fad39969 100644 Binary files a/dreamluau.dll and b/dreamluau.dll differ diff --git a/html/changelogs/archive/2026-06.yml b/html/changelogs/archive/2026-06.yml index 576d4d9390e5..ca7ab1a64ccc 100644 --- a/html/changelogs/archive/2026-06.yml +++ b/html/changelogs/archive/2026-06.yml @@ -70,6 +70,8 @@ timothymtorres: - spellcheck: Add security misspelling checks to CI 2026-06-04: + AnturK: + - balance: polar bears are tiny bit faster Arturlang: - bugfix: moonatics switching bodies (including slimeperson swap) giving you duplicate action buttons @@ -77,6 +79,17 @@ Frfor17: - bugfix: fixed NT shop printing infiniti papers, now its linked to computer's(laptop, pda, any) paper storage + JohnFulpWillard: + - qol: People who are handcuffed/resting/anyone who can't pull people won't be prompted + to pull things in the dropdown menu. + MelokG000: + - rscadd: added botanic trays - hydroponic trays with soil instead of water, just + fill a tray with any soil and done! + - image: resprited auto-growth mode and added emissives to it + - image: resprited hydroponic trays + - rscadd: added old-designed mode for hydroponic trays - use a screwdriver or a + plant analyzer on the circuit! + - image: added emissives to hydroponic tray indicators QuiteLiterallyAnything: - bugfix: Made discounts from toxins papers show up in research UIs properly. - refactor: Refactored research techwebs such that toxins discounts are now stored @@ -85,6 +98,14 @@ - bugfix: Fixed brimdust overlay on wide mobs leaking onto other objects - bugfix: Drake swoop no longer misleadingly claims you fall into lava if it fails to spawn said lava + - bugfix: Tendrils no longer try to attack you when you're out of their reach + - code_imp: Cleaned up The Thing's aggro code + - bugfix: Drake tail trophy no longer pushes immovable mobs + TheRyeGuyWhoWillNowDie: + - balance: instead of the hard requirement for sleeping, cryox's healing power is + doubled by being asleep instead + - bugfix: on default settings a cryo tube filled with cryoxadone can no longer softlock + you by outdamaging its own healing potential Wisemonster: - code_imp: Wizard Apprentices now use the mid-round wizard pref, instead of the round start wizard pref. @@ -92,3 +113,65 @@ - bugfix: Material spears no longer partially avoid the force malus from being unwielded - balance: Smartfridges no longer block atmos when broken & no longer let creatures pass until broken + - bugfix: Toxin bees get toxin +2026-06-05: + Ghommie: + - qol: You can now review your character's personality from the mood message box + (by clicking the mood hud element). + Melbert: + - qol: Adds "cancel interaction" keybinding, unbound by default, it cancels (most) + interactions you're doing without needing to move or swap items. + SmArtKar: + - refactor: Implemented overlay-based smoothing for lavaland lava and water + - rscadd: Blood-drunk miner now drops an infernal PKA with an in-built improved + rapid repeater modification + - balance: Blood-drunk miner has been reworked with new attacks and patterns + - bugfix: Blood-drunk miner's combos are no longer guaranteed to land even if you + move away from it + UgoManzo: + - bugfix: Fixed a server crash caused by igniting plasma-covered floor lights. + Wisemonster: + - code_imp: Slaughter/laughter demons now use the sentient creature pref, instead + of the xenomorph pref + lelandkemble: + - map: Catwalk disposals outputs correctly. +2026-06-06: + CabinetOnFire: + - refactor: Lavaland/Icemoon now uses a smarter algorithm to generate caves, making + them more interconnected and making ruins easier to locate. + - bugfix: wishgranter ruin now has /biome_replace subtypes on its floors/walls, + ensuring it spawns in correctly (if enabled in config) + CabinetOnFire, Kapu: + - sound: Changed our falloff to be less intense, as it was punching volume down + way too fast. + - refactor: Implements a system for spatial audio to improve our looping sounds + Floyd: + - code_imp: Rust-G has been updated to 6.2.0 + Frfor17: + - qol: now Admin RPED has 40 parts each + - code_imp: now Admin outfit and Debug outfit(what is that) has AdminDebug RPED + with 40 parts, and not just tier4 RPED, which is like game thing + Melbert: + - bugfix: Fix moths being able to fly in space + QuiteLiterallyAnything: + - image: Added a directional sprite for coffins. + Rhials, Dendydoom: + - rscadd: Apple Fritters have been added to your local chef's cookbook! + SyncIt21: + - code_imp: borg attack chain now passes modifiers to its item `attack_self()` proc + - qol: omni tool welder now won't turn on automatically when switching to it via + radial menu or using inventory button. This stops accidental combustion in hazardous + areas + - qol: omni tool welder can now be turned on/off via LMB like regular welder & not + CTRL+LMB. Use RMB to open radial menu to switch to other tools when using welder + TealSeer: + - bugfix: fixed TGUI spawn panel direction option not working + TheRyeGuyWhoWillNowDie: + - balance: Syndicate assault cyborgs can now block attacks with their energy swords + - balance: Syndicate medical cyborgs now feature atropine in their hypospray to + prevent microbomb detonations. + UgoManzo: + - bugfix: Water coolers now properly reveal their custom reagents when examined + by ghosts or players with science goggles. + lelandkemble: + - bugfix: Pinpointers stop tracking a shunted ai when that ai is no longer shunted diff --git a/icons/map_icons/clothing/_clothing.dmi b/icons/map_icons/clothing/_clothing.dmi index 6a9956844f4d..778966219458 100644 Binary files a/icons/map_icons/clothing/_clothing.dmi and b/icons/map_icons/clothing/_clothing.dmi differ diff --git a/icons/map_icons/clothing/accessory.dmi b/icons/map_icons/clothing/accessory.dmi index e41990215450..55df7680741f 100644 Binary files a/icons/map_icons/clothing/accessory.dmi and b/icons/map_icons/clothing/accessory.dmi differ diff --git a/icons/map_icons/clothing/head/_head.dmi b/icons/map_icons/clothing/head/_head.dmi index 40a524f88dc4..bf12dcac4524 100644 Binary files a/icons/map_icons/clothing/head/_head.dmi and b/icons/map_icons/clothing/head/_head.dmi differ diff --git a/icons/map_icons/clothing/head/beret.dmi b/icons/map_icons/clothing/head/beret.dmi index fb920ce83276..56e229a0badb 100644 Binary files a/icons/map_icons/clothing/head/beret.dmi and b/icons/map_icons/clothing/head/beret.dmi differ diff --git a/icons/map_icons/clothing/mask.dmi b/icons/map_icons/clothing/mask.dmi index 2841cb9effc4..660a0c223705 100644 Binary files a/icons/map_icons/clothing/mask.dmi and b/icons/map_icons/clothing/mask.dmi differ diff --git a/icons/map_icons/clothing/neck.dmi b/icons/map_icons/clothing/neck.dmi index 3a5094318ef1..b95176c3deda 100644 Binary files a/icons/map_icons/clothing/neck.dmi and b/icons/map_icons/clothing/neck.dmi differ diff --git a/icons/map_icons/clothing/shoes.dmi b/icons/map_icons/clothing/shoes.dmi index 6d790cf92eb1..8ada17028560 100644 Binary files a/icons/map_icons/clothing/shoes.dmi and b/icons/map_icons/clothing/shoes.dmi differ diff --git a/icons/map_icons/clothing/suit/_suit.dmi b/icons/map_icons/clothing/suit/_suit.dmi index cdbb28e990c8..62a890ded65f 100644 Binary files a/icons/map_icons/clothing/suit/_suit.dmi and b/icons/map_icons/clothing/suit/_suit.dmi differ diff --git a/icons/map_icons/clothing/suit/costume.dmi b/icons/map_icons/clothing/suit/costume.dmi index b47fc0c68ab3..b4cc21296e79 100644 Binary files a/icons/map_icons/clothing/suit/costume.dmi and b/icons/map_icons/clothing/suit/costume.dmi differ diff --git a/icons/map_icons/clothing/under/_under.dmi b/icons/map_icons/clothing/under/_under.dmi index 5fdc8b31664c..1ea52de10fa6 100644 Binary files a/icons/map_icons/clothing/under/_under.dmi and b/icons/map_icons/clothing/under/_under.dmi differ diff --git a/icons/map_icons/clothing/under/color.dmi b/icons/map_icons/clothing/under/color.dmi index 87189da75482..ec7ae58f833a 100644 Binary files a/icons/map_icons/clothing/under/color.dmi and b/icons/map_icons/clothing/under/color.dmi differ diff --git a/icons/map_icons/clothing/under/costume.dmi b/icons/map_icons/clothing/under/costume.dmi index 17e56c8eaa12..da6eaec2611b 100644 Binary files a/icons/map_icons/clothing/under/costume.dmi and b/icons/map_icons/clothing/under/costume.dmi differ diff --git a/icons/map_icons/clothing/under/dress.dmi b/icons/map_icons/clothing/under/dress.dmi index 14e11b72a7fc..15861b7f3581 100644 Binary files a/icons/map_icons/clothing/under/dress.dmi and b/icons/map_icons/clothing/under/dress.dmi differ diff --git a/icons/map_icons/items/_item.dmi b/icons/map_icons/items/_item.dmi index 16535bf205f1..6fad912023d0 100644 Binary files a/icons/map_icons/items/_item.dmi and b/icons/map_icons/items/_item.dmi differ diff --git a/icons/map_icons/items/encryptionkey.dmi b/icons/map_icons/items/encryptionkey.dmi index 360180773f39..4c6cd37c691e 100644 Binary files a/icons/map_icons/items/encryptionkey.dmi and b/icons/map_icons/items/encryptionkey.dmi differ diff --git a/icons/map_icons/items/pda.dmi b/icons/map_icons/items/pda.dmi index c28a9b7043fc..40ca680e0f6e 100644 Binary files a/icons/map_icons/items/pda.dmi and b/icons/map_icons/items/pda.dmi differ diff --git a/icons/map_icons/mobs.dmi b/icons/map_icons/mobs.dmi index 6a9956844f4d..778966219458 100644 Binary files a/icons/map_icons/mobs.dmi and b/icons/map_icons/mobs.dmi differ diff --git a/icons/map_icons/objects.dmi b/icons/map_icons/objects.dmi index dd3a926e2a7c..c6f765f81e8b 100644 Binary files a/icons/map_icons/objects.dmi and b/icons/map_icons/objects.dmi differ diff --git a/icons/map_icons/turfs.dmi b/icons/map_icons/turfs.dmi index 6a9956844f4d..778966219458 100644 Binary files a/icons/map_icons/turfs.dmi and b/icons/map_icons/turfs.dmi differ diff --git a/icons/map_icons/unsorted.dmi b/icons/map_icons/unsorted.dmi index 6a9956844f4d..778966219458 100644 Binary files a/icons/map_icons/unsorted.dmi and b/icons/map_icons/unsorted.dmi differ diff --git a/icons/mob/inhands/weapons/guns_lefthand.dmi b/icons/mob/inhands/weapons/guns_lefthand.dmi index de6061573904..df7edf9077b7 100644 Binary files a/icons/mob/inhands/weapons/guns_lefthand.dmi and b/icons/mob/inhands/weapons/guns_lefthand.dmi differ diff --git a/icons/mob/inhands/weapons/guns_righthand.dmi b/icons/mob/inhands/weapons/guns_righthand.dmi index 4e97ea35e56a..62518d7646bf 100644 Binary files a/icons/mob/inhands/weapons/guns_righthand.dmi and b/icons/mob/inhands/weapons/guns_righthand.dmi differ diff --git a/icons/mob/simple/broadMobs.dmi b/icons/mob/simple/broadMobs.dmi index 00d01226f8ee..ae99a6ad7f05 100644 Binary files a/icons/mob/simple/broadMobs.dmi and b/icons/mob/simple/broadMobs.dmi differ diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi index 84c9248a3641..0ed1aaae4b97 100644 Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ diff --git a/icons/obj/service/hydroponics/equipment.dmi b/icons/obj/service/hydroponics/equipment.dmi index 24a8d7bc5f93..4378a61cf8f3 100644 Binary files a/icons/obj/service/hydroponics/equipment.dmi and b/icons/obj/service/hydroponics/equipment.dmi differ diff --git a/icons/obj/storage/crates.dmi b/icons/obj/storage/crates.dmi index 3770375556b7..cf62706c5a18 100644 Binary files a/icons/obj/storage/crates.dmi and b/icons/obj/storage/crates.dmi differ diff --git a/icons/obj/weapons/guns/energy.dmi b/icons/obj/weapons/guns/energy.dmi index 5d3834b6030b..46e2ceefdc46 100644 Binary files a/icons/obj/weapons/guns/energy.dmi and b/icons/obj/weapons/guns/energy.dmi differ diff --git a/icons/turf/floors/basalt_outline.dmi b/icons/turf/floors/basalt_outline.dmi new file mode 100644 index 000000000000..e2d774c00a3f Binary files /dev/null and b/icons/turf/floors/basalt_outline.dmi differ diff --git a/icons/turf/floors/basalt_outline.png b/icons/turf/floors/basalt_outline.png new file mode 100644 index 000000000000..ce68171f9601 Binary files /dev/null and b/icons/turf/floors/basalt_outline.png differ diff --git a/icons/turf/floors/basalt_outline.png.toml b/icons/turf/floors/basalt_outline.png.toml new file mode 100644 index 000000000000..ebc026f21ca9 --- /dev/null +++ b/icons/turf/floors/basalt_outline.png.toml @@ -0,0 +1,2 @@ +output_name = "basalt_outline" +template = "bitmask/diagonal_32x32.toml" diff --git a/icons/turf/floors/shale_outline.dmi b/icons/turf/floors/shale_outline.dmi new file mode 100644 index 000000000000..7c1313a093bb Binary files /dev/null and b/icons/turf/floors/shale_outline.dmi differ diff --git a/icons/turf/floors/shale_outline.png b/icons/turf/floors/shale_outline.png new file mode 100644 index 000000000000..dd4104b06ee8 Binary files /dev/null and b/icons/turf/floors/shale_outline.png differ diff --git a/icons/turf/floors/shale_outline.png.toml b/icons/turf/floors/shale_outline.png.toml new file mode 100644 index 000000000000..3c104a643f64 --- /dev/null +++ b/icons/turf/floors/shale_outline.png.toml @@ -0,0 +1,2 @@ +output_name = "shale_outline" +template = "bitmask/diagonal_32x32.toml" diff --git a/icons/turf/floors/siderite_outline.dmi b/icons/turf/floors/siderite_outline.dmi new file mode 100644 index 000000000000..adbf092ee485 Binary files /dev/null and b/icons/turf/floors/siderite_outline.dmi differ diff --git a/icons/turf/floors/siderite_outline.png b/icons/turf/floors/siderite_outline.png new file mode 100644 index 000000000000..14da6135eda9 Binary files /dev/null and b/icons/turf/floors/siderite_outline.png differ diff --git a/icons/turf/floors/siderite_outline.png.toml b/icons/turf/floors/siderite_outline.png.toml new file mode 100644 index 000000000000..ec6db4a74167 --- /dev/null +++ b/icons/turf/floors/siderite_outline.png.toml @@ -0,0 +1,2 @@ +output_name = "siderite_outline" +template = "bitmask/diagonal_32x32.toml" diff --git a/rust_g.dll b/rust_g.dll index 62c8ba24f015..3fafd146ec81 100644 Binary files a/rust_g.dll and b/rust_g.dll differ diff --git a/tgstation.dme b/tgstation.dme index d9914a272a00..58f475443e64 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -759,6 +759,7 @@ #include "code\controllers\subsystem\shuttle.dm" #include "code\controllers\subsystem\skills.dm" #include "code\controllers\subsystem\sound_loops.dm" +#include "code\controllers\subsystem\sound_tokens.dm" #include "code\controllers\subsystem\sounds.dm" #include "code\controllers\subsystem\spatial_gridmap.dm" #include "code\controllers\subsystem\speech_controller.dm" @@ -893,6 +894,7 @@ #include "code\datums\ruins.dm" #include "code\datums\saymode.dm" #include "code\datums\signals.dm" +#include "code\datums\sound_token.dm" #include "code\datums\spawners_menu.dm" #include "code\datums\sprite_accessories.dm" #include "code\datums\station_alert.dm" @@ -960,7 +962,6 @@ #include "code\datums\actions\mobs\sign_language.dm" #include "code\datums\actions\mobs\sneak.dm" #include "code\datums\actions\mobs\teleport.dm" -#include "code\datums\actions\mobs\transform_weapon.dm" #include "code\datums\actions\mobs\sequences\dash_attack.dm" #include "code\datums\actions\mobs\sequences\projectile.dm" #include "code\datums\ai\_ai_behavior.dm" @@ -1004,6 +1005,7 @@ #include "code\datums\ai\basic_mobs\basic_subtrees\capricious_retaliate.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\climb_tree.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\drag_items.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\enrage.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\escape_captivity.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\express_happiness.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\find_food.dm" @@ -5229,6 +5231,7 @@ #include "code\modules\mob\living\basic\blob_minions\blobbernaut.dm" #include "code\modules\mob\living\basic\boss\boss.dm" #include "code\modules\mob\living\basic\boss\blood_drunk_miner\_blood_drunk_miner.dm" +#include "code\modules\mob\living\basic\boss\blood_drunk_miner\blood_drunk_actions.dm" #include "code\modules\mob\living\basic\boss\blood_drunk_miner\blood_drunk_ai.dm" #include "code\modules\mob\living\basic\boss\blood_drunk_miner\blood_drunk_objects.dm" #include "code\modules\mob\living\basic\boss\blood_drunk_miner\blood_drunk_subtypes.dm" @@ -5345,6 +5348,7 @@ #include "code\modules\mob\living\basic\icemoon\ice_whelp\ice_whelp.dm" #include "code\modules\mob\living\basic\icemoon\ice_whelp\ice_whelp_abilities.dm" #include "code\modules\mob\living\basic\icemoon\ice_whelp\ice_whelp_ai.dm" +#include "code\modules\mob\living\basic\icemoon\polar_bear\polar_bear.dm" #include "code\modules\mob\living\basic\icemoon\wolf\wolf.dm" #include "code\modules\mob\living\basic\icemoon\wolf\wolf_ai.dm" #include "code\modules\mob\living\basic\icemoon\wolf\wolf_extras.dm" @@ -5758,7 +5762,6 @@ #include "code\modules\mob\living\simple_animal\hostile\megafauna\legion.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\wendigo.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\mining_mobs.dm" -#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\polarbear.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\elites\elite.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\elites\goliath_broodmother.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\elites\herald.dm" diff --git a/tools/UpdatePaths/Scripts/96295_polarbear.txt b/tools/UpdatePaths/Scripts/96295_polarbear.txt new file mode 100644 index 000000000000..c3b0c19872f7 --- /dev/null +++ b/tools/UpdatePaths/Scripts/96295_polarbear.txt @@ -0,0 +1 @@ +/mob/living/simple_animal/hostile/asteroid/polarbear : /mob/living/basic/mining/polarbear{@OLD}